diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index c8e422632..b13d30c00 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1106,6 +1106,12 @@ typedef struct TypeIdInfoKind kind; } ExprTypeidInfo; +typedef struct +{ + ExprId parent; + const char *swizzle; +} ExprSwizzle; + typedef struct { Decl *argc; @@ -1146,6 +1152,7 @@ struct Expr_ ExprSubscript subscript_expr; // 12 ExprSubscriptAssign subscript_assign_expr; ExprAccess access_expr; // 16 + ExprSwizzle swizzle_expr; ExprDesignator designator_expr; // 16 ExprIdentifier identifier_expr; // 24 ExprIdentifierRaw ct_ident_expr; // 24 diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 0fce7f555..88079f36c 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -293,6 +293,9 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) MACRO_COPY_DECL_LIST(expr->body_expansion_expr.declarations); MACRO_COPY_ASTID(expr->body_expansion_expr.first_stmt); return expr; + case EXPR_SWIZZLE: + MACRO_COPY_EXPRID(expr->swizzle_expr.parent); + return expr; case EXPR_FLATPATH: case EXPR_NOP: case EXPR_BUILTIN: diff --git a/src/compiler/enums.h b/src/compiler/enums.h index e9c00f6e7..f1d5bb0bf 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -253,6 +253,7 @@ typedef enum EXPR_SLICE_COPY, EXPR_STRINGIFY, EXPR_SUBSCRIPT, + EXPR_SWIZZLE, EXPR_SUBSCRIPT_ADDR, EXPR_SUBSCRIPT_ASSIGN, EXPR_TERNARY, diff --git a/src/compiler/expr.c b/src/compiler/expr.c index abef2a05b..6110f1691 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -135,6 +135,7 @@ bool expr_may_addr(Expr *expr) case EXPR_VARIANT: case EXPR_VARIANTSWITCH: case EXPR_VASPLAT: + case EXPR_SWIZZLE: return false; } UNREACHABLE @@ -158,6 +159,8 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) RETRY: switch (expr->expr_kind) { + case EXPR_SWIZZLE: + return false; case EXPR_POINTER_OFFSET: return exprid_is_constant_eval(expr->pointer_offset_expr.ptr, eval_kind) && exprid_is_constant_eval(expr->pointer_offset_expr.offset, eval_kind); case EXPR_RETVAL: @@ -639,6 +642,8 @@ bool expr_is_pure(Expr *expr) case EXPR_BUILTIN: case EXPR_TEST_HOOK: return false; + case EXPR_SWIZZLE: + return exprid_is_pure(expr->swizzle_expr.parent); case EXPR_BUILTIN_ACCESS: return exprid_is_pure(expr->builtin_access_expr.inner); case EXPR_VARIANT: diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 5a55e1535..e8bdc5d29 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -6108,6 +6108,26 @@ static void llmv_emit_test_hook(GenContext *c, BEValue *value, Expr *expr) llvm_value_set_address_abi_aligned(value, get_global, expr->type); } +static void llvm_emit_swizzle(GenContext *c, BEValue *value, Expr *expr) +{ + llvm_emit_exprid(c, value, expr->swizzle_expr.parent); + llvm_value_rvalue(c, value); + LLVMValueRef parent = value->value; + LLVMTypeRef result_type = llvm_get_type(c, expr->type); + unsigned vec_len = LLVMGetVectorSize(result_type); + LLVMValueRef mask_val[4]; + assert(vec_len <= 4); + const char *sw_ptr = expr->swizzle_expr.swizzle; + char ch; + for (unsigned i = 0; i < vec_len; i++) + { + int index = (sw_ptr[i] + 3 - 'w') % 4; + mask_val[i] = llvm_const_int(c, type_uint, index); + } + LLVMValueRef res = LLVMBuildShuffleVector(c->builder, parent, LLVMGetUndef(LLVMTypeOf(parent)), LLVMConstVector(mask_val, vec_len), sw_ptr); + llvm_value_set(value, res, expr->type); +} + void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) { EMIT_LOC(c, expr); @@ -6120,6 +6140,9 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_VASPLAT: case EXPR_CT_CHECKS: UNREACHABLE + case EXPR_SWIZZLE: + llvm_emit_swizzle(c, value, expr); + return; case EXPR_TEST_HOOK: llmv_emit_test_hook(c, value, expr); return; diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 055756157..e8b6d1719 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -769,6 +769,7 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type) case EXPR_OPERATOR_CHARS: case EXPR_CT_CHECKS: case EXPR_SUBSCRIPT_ASSIGN: + case EXPR_SWIZZLE: UNREACHABLE case EXPR_BUILTIN_ACCESS: case EXPR_TEST_HOOK: @@ -935,6 +936,7 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type) case EXPR_VASPLAT: case EXPR_OPERATOR_CHARS: case EXPR_CT_CHECKS: + case EXPR_SWIZZLE: UNREACHABLE case EXPR_TEST_HOOK: return false; @@ -1085,6 +1087,7 @@ static inline bool cast_subarray(SemaContext *context, Expr *expr, Type *from, T if (type_array_element_is_equivalent(from->array.base, to->array.base, is_explicit)) goto CAST; return sema_error_cannot_convert(expr, to, !is_explicit && type_array_element_is_equivalent(from->array.base, to->array.base, true), silent); case TYPE_POINTER: + if (to == type_voidptr) goto CAST; if (type_array_element_is_equivalent(from->array.base, to->pointer, is_explicit)) goto CAST; return sema_error_cannot_convert(expr, to, !is_explicit && type_array_element_is_equivalent(from->array.base, to->pointer, true), silent); case TYPE_BOOL: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index dfc10bee9..65291e304 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -370,6 +370,8 @@ static bool sema_binary_is_expr_lvalue(Expr *top_expr, Expr *expr) { switch (expr->expr_kind) { + case EXPR_SWIZZLE: + return false; case EXPR_SUBSCRIPT_ASSIGN: case EXPR_CT_IDENT: return true; @@ -3366,6 +3368,40 @@ static bool sema_expr_rewrite_to_type_property(SemaContext *context, Expr *expr, UNREACHABLE } +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(len > 0); + int index; + for (unsigned i = 0; i < len; i++) + { + index = (kw[0] + 3 - 'w') % 4; + if (index >= vec_len) + { + SEMA_ERROR(expr, "The '%c' component is not present in a vector of length %d.", kw[i], vec_len); + return false; + } + } + if (len == 1) + { + expr->expr_kind = EXPR_SUBSCRIPT_ADDR; + expr->subscript_expr = (ExprSubscript) { + .range.start = exprid(expr_new_const_int(expr->span, type_usize, index, true)), + .expr = exprid(parent) + }; + expr->resolve_status = RESOLVE_DONE; + expr->type = type_get_ptr(indexed_type); + expr_rewrite_insert_deref(expr); + return true; + } + Type *result = type_get_vector(indexed_type, len); + expr->expr_kind = EXPR_SWIZZLE; + expr->swizzle_expr = (ExprSwizzle) { exprid(parent), kw }; + expr->type = result; + return true; +} + /** * Analyse "x.y" */ @@ -3480,6 +3516,20 @@ CHECK_DEEPER: if (sema_expr_rewrite_to_typeid_property(context, expr, parent, kw)) return true; } + if (type_flat_is_vector(flat_type)) + { + unsigned len = strlen(kw); + if (len <= 4) + { + for (unsigned i = 0; i < len; i++) + { + char c = kw[i]; + if (c < 'w' || c > 'z') goto NOT_SWIZZLE; + } + return sema_expr_analyse_swizzle(context, expr, parent, flat_type, kw, len); + NOT_SWIZZLE:; + } + } // Hard coded ptr on subarrays and variant if (kw == kw_ptr) { @@ -6787,6 +6837,7 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) case EXPR_ASM: case EXPR_OPERATOR_CHARS: case EXPR_TEST_HOOK: + case EXPR_SWIZZLE: UNREACHABLE case EXPR_VASPLAT: SEMA_ERROR(expr, "'$vasplat' can only be used inside of macros."); diff --git a/src/compiler/sema_initializers.c b/src/compiler/sema_initializers.c index e0d6a0885..bf4fa7bca 100644 --- a/src/compiler/sema_initializers.c +++ b/src/compiler/sema_initializers.c @@ -238,12 +238,11 @@ static inline bool sema_expr_analyse_array_plain_initializer(SemaContext *contex Type *inner_type = type_get_indexed_type(assigned); assert(inner_type); - unsigned size = vec_size(elements); + unsigned count = vec_size(elements); unsigned expected_members = flattened->array.len; - if (type_is_len_inferred(flattened)) expected_members = size; + assert(count > 0 && "We should already have handled the size == 0 case."); - assert(size > 0 && "We should already have handled the size == 0 case."); - if (expected_members == 0) + if (expected_members == 0 && !inferred_len) { // Generate a nice error message for zero. SEMA_ERROR(elements[0], "Too many elements in initializer, it must be empty."); @@ -251,17 +250,59 @@ static inline bool sema_expr_analyse_array_plain_initializer(SemaContext *contex } bool optional = false; - unsigned count = vec_size(elements); + bool is_vector = type_flat_is_vector(assigned); for (unsigned i = 0; i < count; i++) { Expr *element = elements[i]; - if (i >= expected_members) + if (!inferred_len && i >= expected_members) { SEMA_ERROR(element, "Too many elements in initializer, expected only %d.", expected_members); return false; } - if (!sema_analyse_expr_rhs(context, inner_type, element, true)) return false; - Type *element_type = type_no_optional(element->type); + if (is_vector) + { + if (!sema_analyse_inferred_expr(context, inner_type, element)) return false; + Type *element_type = element->type; + Type *element_flat = type_flatten(element_type); + if (element_flat->type_kind == TYPE_VECTOR + && type_flatten(type_get_indexed_type(element_type)) == type_flatten(inner_type)) + { + unsigned len = element_flat->array.len; + if (!inferred_len && i + len > expected_members) + { + SEMA_ERROR(element, "Too many elements in initializer when expanding, expected only %d.", expected_members); + return false; + } + Expr *expr_list = expr_new_expr(EXPR_EXPRESSION_LIST, element); + Decl *decl = decl_new_generated_var(element_type, VARDECL_LOCAL, element->span); + Expr *decl_expr = expr_generate_decl(decl, element); + vec_add(expr_list->expression_list, decl_expr); + Expr *sub = expr_new_expr(EXPR_SUBSCRIPT, element); + sub->subscript_expr.expr = exprid(expr_variable(decl)); + sub->subscript_expr.range.start = exprid(expr_new_const_int(element->span, type_usize, 0, true)); + vec_add(expr_list->expression_list, sub); + if (!sema_analyse_expr_rhs(context, inner_type, expr_list, true)) return false; + elements[i] = expr_list; + for (unsigned j = 1; j < len; j++) + { + sub = expr_new_expr(EXPR_SUBSCRIPT, element); + sub->subscript_expr.expr = exprid(expr_variable(decl)); + sub->subscript_expr.range.start = exprid(expr_new_const_int(element->span, type_usize, 1, true)); + vec_insert_at(elements, i + j, sub); + if (!sema_analyse_expr_rhs(context, inner_type, sub, true)) return false; + } + initializer->initializer_list = elements; + count += len - 1; + i += len - 1; + optional = optional || IS_OPTIONAL(element); + continue; + } + if (!cast_implicit_maybe_optional(context, element, inner_type, true)) return false; + } + else + { + if (!sema_analyse_expr_rhs(context, inner_type, element, true)) return false; + } optional = optional || IS_OPTIONAL(element); } if (inferred_len) @@ -276,9 +317,9 @@ static inline bool sema_expr_analyse_array_plain_initializer(SemaContext *contex assert(initializer->type); if (optional) initializer->type = type_get_optional(initializer->type); - if (expected_members > size) + if (!inferred_len && expected_members > count) { - SEMA_ERROR(elements[size - 1], "Too few elements in initializer, %d elements are needed.", expected_members); + SEMA_ERROR(elements[count - 1], "Too few elements in initializer, %d elements are needed.", expected_members); return false; } diff --git a/src/utils/lib.h b/src/utils/lib.h index 861d31af8..434fefba1 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -302,6 +302,14 @@ void* CONCAT(foreach_vec_, __LINE__) = (vec__); unsigned CONCAT(foreach_len_, __ (vec_)[0] = value_; \ } while (0) +#define vec_insert_at(vec_, _at, value_) do { \ + void *__temp = expand_((vec_), sizeof(*(vec_))); \ + (vec_) = __temp; \ + unsigned __xsize = vec_size(vec_); \ + for (unsigned __x = __xsize - 1; __x > _at; __x--) (vec_)[__x] = (vec_)[__x - 1]; \ + (vec_)[_at] = value_; \ + } while (0) + #if IS_GCC || IS_CLANG #define VECLAST(_vec) ({ unsigned _size = vec_size(_vec); _size ? (_vec)[_size - 1] : NULL; }) #else diff --git a/src/version.h b/src/version.h index f743f7cb7..ebd300783 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.16" \ No newline at end of file +#define COMPILER_VERSION "0.4.17" \ No newline at end of file