diff --git a/README.md b/README.md index bf5dc4a70..2ddb79974 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,8 @@ to C3 and compiled with the c3c compiler: - [x] Trailing body macros e.g. `@foo(1, 100; int a) { bar(a); };` - [x] Complex macros - [x] CT type constants +- [x] Simd vector types *partly implemented* +- [x] Subarray initializers - [ ] Anonymous structs - [ ] Complete C ABI conformance *in progress* - [ ] Debug info *in progress* @@ -131,7 +133,6 @@ to C3 and compiled with the c3c compiler: - [ ] `global` / `shared` for globals - [ ] Escape macros - [ ] Implicit capturing macros -- [ ] Subarray initializers - [ ] Bitstructs - [ ] `asm` section - [ ] `$switch` @@ -139,7 +140,6 @@ to C3 and compiled with the c3c compiler: - [ ] Pre-post conditions - [ ] Stdlib inclusion - [ ] String functions -- [ ] Simd vector types #### What can you help with? diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 837bf9e86..9b0551d37 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1892,6 +1892,7 @@ Decl *sema_resolve_normal_symbol(Context *context, TokenId symbol, Path *path, b Decl *sema_resolve_string_symbol(Context *context, const char *symbol, SourceSpan span, Path *path, bool report_error); bool sema_resolve_type(Context *context, Type *type); +bool sema_resolve_array_like_len(Context *context, TypeInfo *type_info, ArrayIndex *len_ref); bool sema_resolve_type_info(Context *context, TypeInfo *type_info); bool sema_resolve_type_info_maybe_inferred(Context *context, TypeInfo *type_info, bool allow_inferred_type); bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info, bool allow_inferred_type, bool in_shallow); @@ -1967,6 +1968,7 @@ Type *type_find_largest_union_element(Type *type); Type *type_find_max_type(Type *type, Type *other); Type *type_abi_find_single_struct_element(Type *type); const char *type_generate_qname(Type *type); +bool type_is_valid_for_vector(Type *type); Type *type_get_array(Type *arr_type, ByteSize len); Type *type_get_indexed_type(Type *type); Type *type_get_ptr(Type *ptr_type); @@ -1976,7 +1978,7 @@ Type *type_get_inferred_array(Type *arr_type); Type *type_get_vector(Type *vector_type, unsigned len); Type *type_cint(void); Type *type_cuint(void); -Type *type_int_signed_by_bitsize(unsigned bytesize); +Type *type_int_signed_by_bitsize(unsigned bitsize); Type *type_int_unsigned_by_bitsize(unsigned bytesize); bool type_is_abi_aggregate(Type *type); static inline bool type_is_any_integer(Type *type); @@ -2006,6 +2008,7 @@ bool type_is_union_struct(Type *type); bool type_is_user_defined(Type *type); bool type_is_structurally_equivalent(Type *type1, Type *type); static inline Type *type_lowering(Type *type); +static inline bool type_is_vector(Type *type) { return type_lowering(type)->type_kind == TYPE_VECTOR; }; bool type_may_have_sub_elements(Type *type); static inline bool type_ok(Type *type); static inline Type *type_reduced_from_expr(Expr *expr); @@ -2042,6 +2045,7 @@ static inline bool type_is_pointer_sized(Type *type) return type_is_integer(type) && type_size(type) == type_size(type_iptr); } + static inline bool type_is_integer(Type *type) { assert(type == type->canonical); @@ -2224,6 +2228,12 @@ static inline Type *type_flatten(Type *type) } } +static Type *type_vector_type(Type *type) +{ + Type *flatten = type_flatten(type); + return flatten->type_kind == TYPE_VECTOR ? flatten->vector.base : NULL; +} + static inline bool type_is_builtin(TypeKind kind) { return kind >= TYPE_VOID && kind <= TYPE_TYPEID; } static inline bool type_kind_is_signed(TypeKind kind) { return kind >= TYPE_I8 && kind < TYPE_U8; } static inline bool type_kind_is_unsigned(TypeKind kind) { return kind >= TYPE_U8 && kind < TYPE_IXX; } @@ -2232,11 +2242,13 @@ static inline bool type_is_signed(Type *type) { return type->type_kind >= TYPE_I static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE_U8 && type->type_kind < TYPE_IXX; } static inline bool type_ok(Type *type) { return !type || type->type_kind != TYPE_POISONED; } static inline bool type_info_ok(TypeInfo *type_info) { return !type_info || type_info->kind != TYPE_INFO_POISON; } + bool type_is_scalar(Type *type); static inline bool type_is_numeric(Type *type) { - return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_FXX; + TypeKind kind = type->type_kind; + return (kind >= TYPE_I8 && kind <= TYPE_FXX) || kind == TYPE_VECTOR; } static inline bool type_underlying_is_numeric(Type *type) diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 239925e39..716bedece 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -413,6 +413,7 @@ TypeInfo *copy_type_info(TypeInfo *source) assert(source->resolve_status == RESOLVE_NOT_DONE); copy->unresolved_type_expr = copy_expr(source->unresolved_type_expr); return copy; + case TYPE_INFO_VECTOR: case TYPE_INFO_ARRAY: assert(source->resolve_status == RESOLVE_NOT_DONE); copy->array.len = copy_expr(source->array.len); diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 88e198714..871fe002b 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -294,6 +294,7 @@ typedef enum TYPE_INFO_IDENTIFIER, TYPE_INFO_EXPRESSION, TYPE_INFO_ARRAY, + TYPE_INFO_VECTOR, TYPE_INFO_INC_ARRAY, TYPE_INFO_INFERRED_ARRAY, TYPE_INFO_SUBARRAY, @@ -348,6 +349,7 @@ typedef enum TOKEN_GREATER_EQ, // >= TOKEN_LESS_EQ, // <= TOKEN_LBRAPIPE, // {| + TOKEN_LVEC, // [< TOKEN_MINUS_ASSIGN, // -= TOKEN_MINUSMINUS, // -- TOKEN_MOD_ASSIGN, // %= @@ -358,6 +360,7 @@ typedef enum TOKEN_PLUS_ASSIGN, // += TOKEN_PLUSPLUS, // ++ TOKEN_RBRAPIPE, // |} + TOKEN_RVEC, // >] TOKEN_QUESTQUEST, // ?? TOKEN_SCOPE, // :: TOKEN_SHL, // << diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index 06b12c9d3..d11764402 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -1535,6 +1535,7 @@ static bool lexer_scan_token_inner(Lexer *lexer, LexMode mode) case ')': return add_token(lexer, TOKEN_RPAREN, ")"); case '[': + if (match(lexer, '<')) return add_token(lexer, TOKEN_LVEC, "[<"); return add_token(lexer, TOKEN_LBRACKET, "["); case ']': return add_token(lexer, TOKEN_RBRACKET, "]"); @@ -1584,6 +1585,7 @@ static bool lexer_scan_token_inner(Lexer *lexer, LexMode mode) if (match(lexer, '=')) return add_token(lexer, TOKEN_SHR_ASSIGN, ">>="); return add_token(lexer, TOKEN_SHR, ">>"); } + if (match(lexer, ']')) return add_token(lexer, TOKEN_RVEC, ">]"); return match(lexer, '=') ? add_token(lexer, TOKEN_GREATER_EQ, ">=") : add_token(lexer, TOKEN_GREATER, ">"); case '%': return match(lexer, '=') ? add_token(lexer, TOKEN_MOD_ASSIGN, "%=") : add_token(lexer, TOKEN_MOD, "%"); diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index e7c71cf09..6acf1443c 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -94,7 +94,7 @@ LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_ Type *element_type = array_type->array.base; LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type); ConstInitializer **elements = const_init->init_array_full; - assert(array_type->type_kind == TYPE_ARRAY); + assert(array_type->type_kind == TYPE_ARRAY || array_type->type_kind == TYPE_VECTOR); ArrayIndex size = array_type->array.len; assert(size > 0); LLVMValueRef *parts = VECNEW(LLVMValueRef, size); @@ -104,6 +104,10 @@ LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_ if (element_type_llvm != LLVMTypeOf(element)) was_modified = true; vec_add(parts, element); } + if (array_type->type_kind == TYPE_VECTOR) + { + return LLVMConstVector(parts, vec_size(parts)); + } if (was_modified) { return LLVMConstStructInContext(c->context, parts, vec_size(parts), true); @@ -277,6 +281,7 @@ void llvm_emit_ptr_from_array(GenContext *c, BEValue *value) value->kind = BE_ADDRESS; return; case TYPE_ARRAY: + case TYPE_VECTOR: return; case TYPE_SUBARRAY: { diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index ad69c486d..eb915a263 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -19,6 +19,16 @@ LLVMValueRef llvm_emit_is_no_error_value(GenContext *c, BEValue *value) return LLVMBuildICmp(c->builder, LLVMIntEQ, value->value, llvm_get_zero(c, type_anyerr), "not_err"); } +void llvm_convert_vector_comparison(GenContext *c, BEValue *be_value, LLVMValueRef val, Type *vector_type) +{ + vector_type = type_flatten(vector_type); + ByteSize width = vector_type->vector.len; + ByteSize element_size = type_size(vector_type->vector.base); + Type *result_type = type_get_vector(type_int_signed_by_bitsize(element_size * 8), width); + val = LLVMBuildSExt(c->builder, val, llvm_get_type(c, result_type), ""); + llvm_value_set(be_value, val, result_type); +} + LLVMValueRef llvm_emit_aggregate_value(GenContext *c, Type *type, ...) { @@ -383,6 +393,7 @@ static inline LLVMValueRef llvm_emit_subscript_addr_with_base_new(GenContext *c, case TYPE_POINTER: return LLVMBuildInBoundsGEP(c->builder, parent->value, &index->value, 1, "ptridx"); case TYPE_ARRAY: + case TYPE_VECTOR: { if (active_target.feature.safe_mode) { @@ -416,12 +427,36 @@ static inline LLVMValueRef llvm_emit_subscript_addr_with_base_new(GenContext *c, } } +static inline void llvm_emit_vector_subscript(GenContext *c, BEValue *value, Expr *expr) +{ + llvm_emit_expr(c, value, expr->subscript_expr.expr); + llvm_value_rvalue(c, value); + Type *element = value->type->array.base; + LLVMValueRef vector = value->value; + llvm_emit_expr(c, value, expr->subscript_expr.index); + llvm_value_rvalue(c, value); + LLVMValueRef index = value->value; + if (LLVMIsAConstant(index) && LLVMIsAConstant(vector)) + { + llvm_value_set(value, LLVMConstExtractElement(vector, index), element); + } + else + { + llvm_value_set(value, LLVMBuildExtractElement(c->builder, vector, index, ""), element); + } +} + /** * Expand foo[123] or someCall()[n] or some such. * Evaluation order is left to right. */ static inline void gencontext_emit_subscript(GenContext *c, BEValue *value, Expr *expr) { + if (type_lowering(expr->subscript_expr.expr->type)->type_kind == TYPE_VECTOR) + { + llvm_emit_vector_subscript(c, value, expr); + return; + } BEValue ref; // First, get thing being subscripted. llvm_emit_subscript_addr_base(c, &ref, expr->subscript_expr.expr); @@ -1142,8 +1177,10 @@ static inline void llvm_emit_const_initialize_reference(GenContext *c, BEValue * { assert(expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_LIST); ConstInitializer *initializer = expr->const_expr.list; + assert(!type_is_vector(initializer->type) && "Vectors should be handled elsewhere."); if (initializer->kind == CONST_INIT_ZERO) { + REMINDER("Optimize this for few elements"); // In case of a zero, optimize. llvm_emit_memclear(c, ref); return; @@ -1732,9 +1769,19 @@ void llvm_emit_int_comparison(GenContext *c, BEValue *result, BEValue *lhs, BEVa } void llvm_emit_int_comp(GenContext *c, BEValue *result, Type *lhs_type, Type *rhs_type, LLVMValueRef lhs_value, LLVMValueRef rhs_value, BinaryOp binary_op) { - assert(type_is_integer(lhs_type)); - bool lhs_signed = type_is_signed(lhs_type); - bool rhs_signed = type_is_signed(rhs_type); + bool lhs_signed, rhs_signed; + Type *vector_type = type_vector_type(lhs_type); + if (vector_type) + { + lhs_signed = type_is_signed(vector_type); + rhs_signed = type_is_signed(type_vector_type(rhs_type)); + } + else + { + assert(type_is_integer(lhs_type)); + lhs_signed = type_is_signed(lhs_type); + rhs_signed = type_is_signed(rhs_type); + } if (lhs_signed != rhs_signed) { // Swap sides if needed. @@ -1795,6 +1842,11 @@ void llvm_emit_int_comp(GenContext *c, BEValue *result, Type *lhs_type, Type *rh default: UNREACHABLE } + if (vector_type) + { + llvm_convert_vector_comparison(c, result, value, lhs_type); + return; + } llvm_value_set_bool(result, value); return; } @@ -1829,6 +1881,11 @@ void llvm_emit_int_comp(GenContext *c, BEValue *result, Type *lhs_type, Type *rh // If right side is also signed then this is fine. if (rhs_signed) { + if (vector_type) + { + llvm_convert_vector_comparison(c, result, comp_value, lhs_type); + return; + } llvm_value_set_bool(result, comp_value); return; } @@ -1870,6 +1927,11 @@ void llvm_emit_int_comp(GenContext *c, BEValue *result, Type *lhs_type, Type *rh default: UNREACHABLE } + if (vector_type) + { + llvm_convert_vector_comparison(c, result, comp_value, lhs_type); + return; + } llvm_value_set_bool(result, comp_value); } @@ -2016,7 +2078,7 @@ static void llvm_emit_subarray_comp(GenContext *c, BEValue *be_value, BEValue *l } -static void llvm_emit_float_comp(GenContext *c, BEValue *be_value, BEValue *lhs, BEValue *rhs, BinaryOp binary_op) +static void llvm_emit_float_comp(GenContext *c, BEValue *be_value, BEValue *lhs, BEValue *rhs, BinaryOp binary_op, Type *vector_type) { llvm_value_rvalue(c, lhs); llvm_value_rvalue(c, rhs); @@ -2048,6 +2110,11 @@ static void llvm_emit_float_comp(GenContext *c, BEValue *be_value, BEValue *lhs, default: UNREACHABLE } + if (vector_type) + { + llvm_convert_vector_comparison(c, be_value, val, vector_type); + return; + } llvm_value_set_bool(be_value, val); } @@ -2068,7 +2135,7 @@ void llvm_emit_comparison(GenContext *c, BEValue *be_value, BEValue *lhs, BEValu } if (type_is_float(lhs->type)) { - llvm_emit_float_comp(c, be_value, lhs, rhs, binary_op); + llvm_emit_float_comp(c, be_value, lhs, rhs, binary_op, NULL); return; } if (lhs->type->type_kind == TYPE_SUBARRAY) @@ -2076,9 +2143,22 @@ void llvm_emit_comparison(GenContext *c, BEValue *be_value, BEValue *lhs, BEValu llvm_emit_subarray_comp(c, be_value, lhs, rhs, binary_op); return; } + if (lhs->type->type_kind == TYPE_VECTOR) + { + Type *type = type_vector_type(lhs->type); + if (type_is_float(type)) + { + llvm_emit_float_comp(c, be_value, lhs, rhs, binary_op, lhs->type); + } + else + { + llvm_emit_int_comp(c, be_value, lhs->type, rhs->type, lhs->value, rhs->value, binary_op); + } + return; + } TODO } -void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValue *lhs_addr, BinaryOp binary_op) +void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValue *lhs_loaded, BinaryOp binary_op) { if (binary_op == BINARYOP_AND || binary_op == BINARYOP_OR) @@ -2087,9 +2167,9 @@ void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValu return; } BEValue lhs; - if (lhs_addr) + if (lhs_loaded) { - lhs = *lhs_addr; + lhs = *lhs_loaded; } else { @@ -2110,7 +2190,8 @@ void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValu Type *lhs_type = lhs.type; Type *rhs_type = rhs.type; - bool is_float = type_is_float(lhs_type); + Type *vector_type = lhs_type->type_kind == TYPE_VECTOR ? lhs_type->vector.base : NULL; + bool is_float = type_is_float(lhs_type) || (vector_type && type_is_float(vector_type)); LLVMValueRef val = NULL; LLVMValueRef lhs_value = lhs.value; LLVMValueRef rhs_value = rhs.value; @@ -2532,34 +2613,78 @@ static inline void gencontext_emit_guard_expr(GenContext *c, BEValue *be_value, } -static void llvm_emit_binary_expr(GenContext *context, BEValue *be_value, Expr *expr) +static bool expr_is_vector_index(Expr *expr) +{ + return expr->expr_kind == EXPR_SUBSCRIPT + && type_lowering(expr->subscript_expr.expr->type)->type_kind == TYPE_VECTOR; +} + +static void llvm_emit_vector_assign_expr(GenContext *c, BEValue *be_value, Expr *expr) +{ + Expr *left = expr->binary_expr.left; + BinaryOp binary_op = expr->binary_expr.operator; + BEValue addr; + BEValue index; + + // Emit the variable + llvm_emit_expr(c, &addr, left->subscript_expr.expr); + llvm_value_addr(c, &addr); + LLVMValueRef vector_value = llvm_value_rvalue_store(c, &addr); + + // Emit the index + llvm_emit_expr(c, &index, left->subscript_expr.index); + LLVMValueRef index_val = llvm_value_rvalue_store(c, &index); + + if (binary_op > BINARYOP_ASSIGN) + { + BinaryOp base_op = binaryop_assign_base_op(binary_op); + assert(base_op != BINARYOP_ERROR); + BEValue lhs; + llvm_value_set(&lhs, LLVMBuildExtractElement(c->builder, vector_value, index_val, "elem"), expr->type); + gencontext_emit_binary(c, be_value, expr, &lhs, base_op); + } + else + { + llvm_emit_expr(c, be_value, expr->binary_expr.right); + } + + LLVMValueRef new_value = LLVMBuildInsertElement(c->builder, vector_value, llvm_value_rvalue_store(c, be_value), index_val, "elemset"); + llvm_store_bevalue_raw(c, &addr, new_value); +} + +static void llvm_emit_binary_expr(GenContext *c, BEValue *be_value, Expr *expr) { BinaryOp binary_op = expr->binary_expr.operator; + if (binary_op >= BINARYOP_ASSIGN && expr_is_vector_index(expr->binary_expr.left)) + { + llvm_emit_vector_assign_expr(c, be_value, expr); + return; + } if (binary_op > BINARYOP_ASSIGN) { BinaryOp base_op = binaryop_assign_base_op(binary_op); assert(base_op != BINARYOP_ERROR); BEValue addr; - llvm_emit_expr(context, &addr, expr->binary_expr.left); - llvm_value_addr(context, &addr); - gencontext_emit_binary(context, be_value, expr, &addr, base_op); - llvm_store_bevalue(context, &addr, be_value); + llvm_emit_expr(c, &addr, expr->binary_expr.left); + llvm_value_addr(c, &addr); + gencontext_emit_binary(c, be_value, expr, &addr, base_op); + llvm_store_bevalue(c, &addr, be_value); return; } if (binary_op == BINARYOP_ASSIGN) { - llvm_emit_expr(context, be_value, expr->binary_expr.left); + llvm_emit_expr(c, be_value, expr->binary_expr.left); assert(llvm_value_is_addr(be_value)); LLVMValueRef failable_ref = NULL; if (expr->binary_expr.left->expr_kind == EXPR_IDENTIFIER) { failable_ref = decl_failable_ref(expr->binary_expr.left->identifier_expr.decl); } - *be_value = llvm_emit_assign_expr(context, be_value, expr->binary_expr.right, failable_ref); + *be_value = llvm_emit_assign_expr(c, be_value, expr->binary_expr.right, failable_ref); return; } - gencontext_emit_binary(context, be_value, expr, NULL, binary_op); + gencontext_emit_binary(c, be_value, expr, NULL, binary_op); } void gencontext_emit_elvis_expr(GenContext *c, BEValue *value, Expr *expr) @@ -2684,7 +2809,7 @@ static LLVMValueRef llvm_emit_real(LLVMTypeRef type, Real f) static inline void llvm_emit_const_initializer_list_expr(GenContext *c, BEValue *value, Expr *expr) { - if (!c->builder) + if (!c->builder || type_is_vector(expr->type)) { llvm_value_set(value, llvm_emit_const_initializer(c, expr->const_expr.list), expr->type); return; @@ -3627,7 +3752,12 @@ BEValue llvm_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, LLVMValue } } BEValue value; - if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_LIST) + if (type_is_vector(expr->type)) + { + llvm_emit_expr(c, &value, expr); + llvm_store_bevalue(c, ref, &value); + } + else if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_LIST) { llvm_emit_const_initialize_reference(c, ref, expr); value = *ref; @@ -3682,9 +3812,86 @@ static inline void gencontext_emit_failable(GenContext *context, BEValue *be_val llvm_value_set(be_value, LLVMGetUndef(llvm_get_type(context, expr->type)), expr->type); } +static inline LLVMValueRef llvm_update_vector(GenContext *c, LLVMValueRef vector, LLVMValueRef value, ArrayIndex index, bool *is_const) +{ + LLVMValueRef index_value = llvm_const_int(c, type_usize, index); + if (*is_const && LLVMIsConstant(value)) + { + return LLVMConstInsertElement(vector, value, index_value); + } + else + { + *is_const = false; + return LLVMBuildInsertElement(c->builder, vector, value, index_value, ""); + } + +} +static inline void llvm_emit_vector_initializer_list(GenContext *c, BEValue *value, Expr *expr) +{ + Type *type = type_lowering(expr->type); + Type *element_type = type->vector.base; + + LLVMTypeRef llvm_type = llvm_get_type(c, type); + + BEValue val; + LLVMValueRef vec_value; + + bool is_const = true; + if (expr->expr_kind == EXPR_INITIALIZER_LIST) + { + vec_value = LLVMGetUndef(llvm_type); + Expr **elements = expr->initializer_list; + + // Now walk through the elements. + VECEACH(elements, i) + { + Expr *element = elements[i]; + llvm_emit_expr(c, &val, element); + llvm_value_rvalue(c, &val); + vec_value = llvm_update_vector(c, vec_value, val.value, i, &is_const); + } + } + else + { + vec_value = LLVMConstNull(llvm_type); + Expr **elements = expr->designated_init_list; + + VECEACH(elements, i) + { + Expr *designator = elements[i]; + assert(vec_size(designator->designator_expr.path) == 1); + DesignatorElement *element = designator->designator_expr.path[0]; + llvm_emit_expr(c, &val, designator->designator_expr.value); + llvm_value_rvalue(c, &val); + switch (element->kind) + { + case DESIGNATOR_ARRAY: + { + vec_value = llvm_update_vector(c, vec_value, val.value, element->index, &is_const); + break; + } + case DESIGNATOR_RANGE: + for (ArrayIndex idx = element->index; idx <= element->index_end; idx++) + { + vec_value = llvm_update_vector(c, vec_value, val.value, idx, &is_const); + } + break; + case DESIGNATOR_FIELD: + default: + UNREACHABLE + } + } + } + llvm_value_set(value, vec_value, type); +} static inline void llvm_emit_initializer_list_expr(GenContext *c, BEValue *value, Expr *expr) { + if (type_is_vector(expr->type)) + { + llvm_emit_vector_initializer_list(c, value, expr); + return; + } llvm_value_set_address(value, llvm_emit_alloca_aligned(c, expr->type, "literal"), expr->type); llvm_emit_initialize_reference(c, value, expr); } diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index f63a3e170..5512a51dd 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -663,6 +663,27 @@ static inline TypeInfo *parse_array_type_index(Context *context, TypeInfo *type) return array; } +/** + * vector_type_index + * : '[<' constant_expression '>]' + * ; + * + * @param type the type to wrap, may not be poisoned. + * @return type (poisoned if fails) + */ +static inline TypeInfo *parse_vector_type_index(Context *context, TypeInfo *type) +{ + assert(type_info_ok(type)); + + advance_and_verify(context, TOKEN_LVEC); + TypeInfo *vector = type_info_new(TYPE_INFO_VECTOR, type->span); + vector->array.base = type; + ASSIGN_EXPR_ELSE(vector->array.len, parse_expr(context), poisoned_type_info); + CONSUME_OR(TOKEN_RVEC, poisoned_type_info); + RANGE_EXTEND_PREV(vector); + return vector; +} + /** * type * : base_type @@ -678,6 +699,9 @@ TypeInfo *parse_type_with_base(Context *context, TypeInfo *type_info) { switch (context->tok.type) { + case TOKEN_LVEC: + type_info = parse_vector_type_index(context, type_info); + break; case TOKEN_LBRACKET: type_info = parse_array_type_index(context, type_info); break; diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 0e5ed15b7..32f4d4240 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -1060,6 +1060,8 @@ Ast *parse_stmt(Context *context) case TOKEN_PRIVATE: case TOKEN_PLACEHOLDER: case TOKEN_BITSTRUCT: + case TOKEN_LVEC: + case TOKEN_RVEC: SEMA_TOKEN_ERROR(context->tok, "Unexpected '%s' found when expecting a statement.", token_type_to_string(context->tok.type)); advance(context); return poisoned_ast; diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 2167b9a09..fc914fbe6 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -639,6 +639,7 @@ bool cast_may_implicit(Type *from_type, Type *to_type) return false; } + // 7. In the case of distinct types, we allow implicit conversion from literal types. if (to->type_kind == TYPE_DISTINCT) { diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 5a0648f12..c4e5d7cd2 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -75,9 +75,17 @@ static inline bool both_const(Expr *left, Expr *right) return left->expr_kind == EXPR_CONST && right->expr_kind == EXPR_CONST; } -static inline bool both_any_integer(Expr *left, Expr *right) + + +static inline bool both_any_integer_or_integer_vector(Expr *left, Expr *right) { - return type_is_any_integer(type_flatten(left->type)) && type_is_any_integer(type_flatten(right->type)); + Type *flatten_left = type_flatten(left->type); + Type *flatten_right = type_flatten(right->type); + if (type_is_any_integer(flatten_left) && type_is_any_integer(flatten_right)) return true; + + if (flatten_left->type_kind != TYPE_VECTOR || flatten_right->type_kind != TYPE_VECTOR) return false; + + return type_is_any_integer(flatten_left->vector.base) && type_is_any_integer(flatten_right->vector.base); } void expr_copy_properties(Expr *to, Expr *from) @@ -2022,8 +2030,10 @@ static bool expr_check_index_in_range(Context *context, Type *type, Expr *index_ assert(!from_end); return true; case TYPE_ARRAY: + case TYPE_VECTOR: { int64_t len = (int64_t)type->array.len; + bool is_vector = type->type_kind == TYPE_VECTOR; if (from_end) { index = len - index; @@ -2031,12 +2041,17 @@ static bool expr_check_index_in_range(Context *context, Type *type, Expr *index_ // Checking end can only be done for arrays. if (end_index && index >= len) { - SEMA_ERROR(index_expr, "Array end index out of bounds, was %lld, exceeding array length %lld.", (long long)index, (long long)len); + SEMA_ERROR(index_expr, + is_vector ? "End index out of bounds, was %lld, exceeding vector width %lld." + : "Array end index out of bounds, was %lld, exceeding array length %lld.", + (long long)index, (long long)len); return false; } if (!end_index && index >= len) { - SEMA_ERROR(index_expr, "Array index out of bounds, was %lld, exceeding max array index %lld.", (long long)index, (long long)len - 1); + SEMA_ERROR(index_expr, + is_vector ? "Index out of bounds, was %lld, exceeding max vector width %lld." + : "Array index out of bounds, was %lld, exceeding max array index %lld.", (long long)index, (long long)len - 1); return false; } break; @@ -2798,11 +2813,25 @@ static Type *sema_find_type_of_element(Context *context, Type *type, DesignatorE DesignatorElement *element = elements[*curr_index]; if (element->kind == DESIGNATOR_ARRAY || element->kind == DESIGNATOR_RANGE) { - if (type_lowered->type_kind != TYPE_ARRAY && type_lowered->type_kind != TYPE_INFERRED_ARRAY) + ByteSize len; + Type *base; + switch (type_lowered->type_kind) { - return NULL; + case TYPE_INFERRED_ARRAY: + len = MAX_ARRAYINDEX; + base = type_lowered->array.base; + break; + case TYPE_ARRAY: + len = type_lowered->array.len; + base = type_lowered->array.base; + break; + case TYPE_VECTOR: + len = type_lowered->vector.len; + base = type_lowered->vector.base; + break; + default: + return NULL; } - ByteSize len = type_lowered->type_kind == TYPE_INFERRED_ARRAY ? MAX_ARRAYINDEX : type_lowered->array.len; ArrayIndex index = sema_analyse_designator_index(context, element->index_expr); if (index < 0) { @@ -2841,7 +2870,7 @@ static Type *sema_find_type_of_element(Context *context, Type *type, DesignatorE element->index_end = end_index; if (max_index && *max_index < end_index) *max_index = end_index; } - return type_lowered->array.base; + return base; } assert(element->kind == DESIGNATOR_FIELD); if (!type_is_structlike(type_lowered)) @@ -3297,7 +3326,7 @@ static inline bool sema_expr_analyse_array_plain_initializer(Context *context, T unsigned size = vec_size(elements); unsigned expected_members = assigned->array.len; - if (assigned->type_kind != TYPE_ARRAY) expected_members = size; + if (assigned->type_kind != TYPE_ARRAY && assigned->type_kind != TYPE_VECTOR) expected_members = size; assert(size > 0 && "We should already have handled the size == 0 case."); if (expected_members == 0) @@ -3425,7 +3454,11 @@ static inline bool sema_expr_analyse_initializer(Context *context, Type *externa return sema_expr_analyse_untyped_initializer(context, expr); } // 3. Otherwise use the plain initializer. - if (assigned->type_kind == TYPE_UNTYPED_LIST || assigned->type_kind == TYPE_ARRAY || assigned->type_kind == TYPE_INFERRED_ARRAY || assigned->type_kind == TYPE_SUBARRAY) + if (assigned->type_kind == TYPE_UNTYPED_LIST || + assigned->type_kind == TYPE_ARRAY || + assigned->type_kind == TYPE_INFERRED_ARRAY || + assigned->type_kind == TYPE_SUBARRAY || + assigned->type_kind == TYPE_VECTOR) { return sema_expr_analyse_array_plain_initializer(context, assigned, expr); } @@ -3447,6 +3480,7 @@ static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to case TYPE_UNION: case TYPE_ARRAY: case TYPE_INFERRED_ARRAY: + case TYPE_VECTOR: return sema_expr_analyse_initializer(context, to, assigned, expr); case TYPE_SUBARRAY: { @@ -4319,7 +4353,7 @@ static bool sema_expr_analyse_bit(Context *context, Type *to, Expr *expr, Expr * if (!sema_expr_analyse_binary_sub_expr(context, to, left, right)) return false; // 2. Check that both are integers. - if (!both_any_integer(left, right)) + if (!both_any_integer_or_integer_vector(left, right)) { return sema_type_error_on_binop(expr); } @@ -4372,7 +4406,7 @@ static bool sema_expr_analyse_shift(Context *context, Type *to, Expr *expr, Expr if (!sema_analyse_expr(context, NULL, right)) return false; // 3. Only integers may be shifted. - if (!both_any_integer(left, right)) + if (!both_any_integer_or_integer_vector(left, right)) { return sema_type_error_on_binop(expr); } @@ -4473,7 +4507,7 @@ static bool sema_expr_analyse_shift_assign(Context *context, Expr *expr, Expr *l } // 3. Only integers may be shifted. - if (!both_any_integer(left, right)) return sema_type_error_on_binop(expr); + if (!both_any_integer_or_integer_vector(left, right)) return sema_type_error_on_binop(expr); // 4. For a constant right hand side we will make a series of checks. if (is_const(right)) @@ -4580,51 +4614,66 @@ static bool sema_expr_analyse_comp(Context *context, Expr *expr, Expr *left, Exp { // 2a. Resize so that both sides have the same bit width. This will always work. cast_to_max_bit_size(context, left, right, left_type, right_type); + goto DONE; } - else + + if (left_type->type_kind == TYPE_VECTOR && right_type->type_kind == TYPE_VECTOR) { - // 3. In the normal case, treat this as a binary op, finding the max type. - Type *max = type_find_max_type(left->type->canonical, right->type->canonical); - - // 4. If no common type, then that's an error: - if (!max) + if (left_type->vector.len == right_type->vector.len) { - SEMA_ERROR(expr, "'%s' and '%s' are different types and cannot be compared.", - type_to_error_string(left->type), type_to_error_string(right->type)); - return false; - }; + Type *left_vec = type_vector_type(left_type); + Type *right_vec = type_vector_type(right_type); + if (left_vec == right_vec) goto DONE; + if (type_size(left_vec) != type_size(right_vec)) goto DONE; + if (type_is_integer(left_vec) && type_is_integer(right_vec)) goto DONE; + } + SEMA_ERROR(expr, "Vector types '%s' and '%s' cannot be compared.", + type_to_error_string(left->type), type_to_error_string(right->type)); + return false; + } - if (!type_is_comparable(max)) + // 3. In the normal case, treat this as a binary op, finding the max type. + Type *max = type_find_max_type(left->type->canonical, right->type->canonical); + + // 4. If no common type, then that's an error: + if (!max) + { + SEMA_ERROR(expr, "'%s' and '%s' are different types and cannot be compared.", + type_to_error_string(left->type), type_to_error_string(right->type)); + return false; + } + + if (!type_is_comparable(max)) + { + SEMA_ERROR(expr, "%s does not support comparisons, you need to manually implement a comparison if you need it.", + type_quoted_error_string(left->type)); + return false; + } + if (!is_equality_type_op) + { + if (!type_is_ordered(max)) { - SEMA_ERROR(expr, "%s does not support comparisons, you need to manually implement a comparison if you need it.", + SEMA_ERROR(expr, "%s can only be compared using '!=' and '==' it cannot be ordered, did you make a mistake?", type_quoted_error_string(left->type)); return false; } - if (!is_equality_type_op) + if (type_flatten(max)->type_kind == TYPE_POINTER) { - if (!type_is_ordered(max)) + // Only comparisons between the same type is allowed. Subtypes not allowed. + if (left_type != right_type && left_type != type_voidptr && right_type != type_voidptr) { - SEMA_ERROR(expr, "%s can only be compared using '!=' and '==' it cannot be ordered, did you make a mistake?", - type_quoted_error_string(left->type)); + SEMA_ERROR(expr, "You are not allowed to compare pointers of different types, " + "if you need to do, first convert all pointers to void*."); return false; } - if (type_flatten(max)->type_kind == TYPE_POINTER) - { - // Only comparisons between the same type is allowed. Subtypes not allowed. - if (left_type != right_type && left_type != type_voidptr && right_type != type_voidptr) - { - SEMA_ERROR(expr, "You are not allowed to compare pointers of different types, " - "if you need to do, first convert all pointers to void*."); - return false; - } - } } - - // 6. Do the implicit cast. - if (!cast_implicit(left, max)) goto ERR; - if (!cast_implicit(right, max)) goto ERR; } + // 6. Do the implicit cast. + if (!cast_implicit(left, max)) goto ERR; + if (!cast_implicit(right, max)) goto ERR; +DONE: + // 7. Do constant folding. if (both_const(left, right)) { @@ -4635,6 +4684,14 @@ static bool sema_expr_analyse_comp(Context *context, Expr *expr, Expr *left, Exp // 8. Set the type to bool expr_unify_binary_properties(expr, left, right); + + // 8a. Except for vector, set to signed type with the correct size. + if (left_type->type_kind == TYPE_VECTOR) + { + ByteSize size = type_size(left_type->vector.base); + expr_set_type(expr, type_get_vector(type_int_signed_by_bitsize(size * 8), left_type->vector.len)); + return true; + } expr_set_type(expr, type_bool); return true; @@ -6021,7 +6078,8 @@ static inline bool sema_expr_resolve_maybe_identifier(Context *c, Expr *expr, De } -static inline Type *sema_expr_check_type_exists(Context *context, TypeInfo *type_info) + +static Type *sema_expr_check_type_exists(Context *context, TypeInfo *type_info) { if (type_info->resolve_status == RESOLVE_DONE) { @@ -6031,20 +6089,30 @@ static inline Type *sema_expr_check_type_exists(Context *context, TypeInfo *type { case TYPE_INFO_POISON: return poisoned_type; - case TYPE_INFO_ARRAY: + case TYPE_INFO_VECTOR: { - // If it's an array, make sure we can resolve the length - Expr *len = type_info->array.len; - if (!sema_analyse_expr(context, type_usize, len)) return false; - if (len->expr_kind != EXPR_CONST || !type_is_any_integer(len->type->canonical)) - { - SEMA_ERROR(len, "Expected a constant integer value here."); - return poisoned_type; - } + ArrayIndex size; + if (!sema_resolve_array_like_len(context, type_info, &size)) return poisoned_type; Type *type = sema_expr_check_type_exists(context, type_info->array.base); if (!type) return NULL; if (!type_ok(type)) return type; - return type_get_array(type, bigint_as_unsigned(&len->const_expr.i)); + if (!type_is_valid_for_vector(type)) + { + SEMA_ERROR(type_info->array.base, + "%s cannot be vectorized. Only integers, floats and booleans are allowed.", + type_quoted_error_string(type)); + return poisoned_type; + } + return type_get_vector(type, size); + } + case TYPE_INFO_ARRAY: + { + ArrayIndex size; + if (!sema_resolve_array_like_len(context, type_info, &size)) return poisoned_type; + Type *type = sema_expr_check_type_exists(context, type_info->array.base); + if (!type) return NULL; + if (!type_ok(type)) return type; + return type_get_array(type, size); } case TYPE_INFO_IDENTIFIER: { diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index a1572005c..4b4573f95 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -15,6 +15,58 @@ static inline bool sema_resolve_ptr_type(Context *context, TypeInfo *type_info) return true; } +bool sema_resolve_array_like_len(Context *context, TypeInfo *type_info, ArrayIndex *len_ref) +{ + Expr *len_expr = type_info->array.len; + if (!sema_analyse_expr(context, type_usize, len_expr)) return type_info_poison(type_info); + + if (len_expr->expr_kind != EXPR_CONST) + { + SEMA_ERROR(len_expr, "Expected a constant value as size."); + return type_info_poison(type_info); + } + if (!type_is_any_integer(len_expr->type->canonical)) + { + SEMA_ERROR(len_expr, "Expected an integer size."); + return type_info_poison(type_info); + } + BigInt *big_len = &len_expr->const_expr.i; + bool is_vector = type_info->kind == TYPE_INFO_VECTOR; + switch (bigint_cmp_zero(big_len)) + { + case CMP_LT: + SEMA_ERROR(len_expr, + is_vector ? "A vector may not have a negative width." : + "An array may not have a negative size."); + return type_info_poison(type_info); + case CMP_EQ: + if (is_vector) + { + SEMA_ERROR(len_expr, "A vector may not have a zero width."); + return type_info_poison(type_info); + } + break; + case CMP_GT: + break; + } + BigInt max; + bigint_init_unsigned(&max, is_vector ? MAX_VECTOR_WIDTH : MAX_ARRAY_SIZE); + if (bigint_cmp(big_len, &max) == CMP_GT) + { + if (is_vector) + { + SEMA_ERROR(len_expr, "A vector may not exceed %d in width.", MAX_VECTOR_WIDTH); + } + else + { + SEMA_ERROR(len_expr, "The array size may not exceed %lld.", MAX_ARRAY_SIZE); + } + return type_info_poison(type_info); + } + *len_ref = bigint_as_signed(big_len); + return true; +} + // TODO cleanup. static inline bool sema_resolve_array_type(Context *context, TypeInfo *type, bool shallow) { @@ -33,7 +85,6 @@ static inline bool sema_resolve_array_type(Context *context, TypeInfo *type, boo } } - uint64_t len; switch (type->kind) { case TYPE_INFO_SUBARRAY: @@ -42,32 +93,20 @@ static inline bool sema_resolve_array_type(Context *context, TypeInfo *type, boo case TYPE_INFO_INFERRED_ARRAY: type->type = type_get_inferred_array(type->array.base->type); break; - case TYPE_INFO_ARRAY: - if (!sema_analyse_expr(context, type_usize, type->array.len)) return type_info_poison(type); - if (type->array.len->expr_kind != EXPR_CONST) - { - SEMA_ERROR(type->array.len, "Expected a constant value as array size."); - return type_info_poison(type); - } - if (!type_is_any_integer(type->array.len->type->canonical)) - { - SEMA_ERROR(type->array.len, "Expected an integer size."); - return type_info_poison(type); - } - if (bigint_cmp_zero(&type->array.len->const_expr.i) == CMP_LT) - { - SEMA_ERROR(type->array.len, "An array may not have a negative size."); - return type_info_poison(type); - } - if (!bigint_fits_in_bits(&type->array.len->const_expr.i, 64, true)) - { - SEMA_ERROR(type->array.len, "An array length may not exceed the max of an 64 bit signed int."); - return type_info_poison(type); - } - - len = bigint_as_unsigned(&type->array.len->const_expr.i); - type->type = type_get_array(type->array.base->type, len); + case TYPE_INFO_VECTOR: + { + ArrayIndex width; + if (!sema_resolve_array_like_len(context, type, &width)) return type_info_poison(type); + type->type = type_get_vector(type->array.base->type, width); break; + } + case TYPE_INFO_ARRAY: + { + ArrayIndex size; + if (!sema_resolve_array_like_len(context, type, &size)) return type_info_poison(type); + type->type = type_get_array(type->array.base->type, size); + break; + } default: UNREACHABLE } @@ -226,6 +265,7 @@ bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info, bool allow FALLTHROUGH; case TYPE_INFO_SUBARRAY: case TYPE_INFO_ARRAY: + case TYPE_INFO_VECTOR: if (!sema_resolve_array_type(context, type_info, in_shallow)) return false; break; case TYPE_INFO_POINTER: diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 8e9dd129b..7147fc819 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -94,6 +94,8 @@ const char *token_type_to_string(TokenType type) return "<="; case TOKEN_LBRAPIPE: return "{|"; + case TOKEN_LVEC: + return "[<"; case TOKEN_MINUS_ASSIGN: return "-="; case TOKEN_MINUSMINUS: @@ -116,6 +118,8 @@ const char *token_type_to_string(TokenType type) return "??"; case TOKEN_RBRAPIPE: return "|}"; + case TOKEN_RVEC: + return ">]"; case TOKEN_SCOPE: return "::"; case TOKEN_SHL: diff --git a/src/compiler/types.c b/src/compiler/types.c index 2e93827d5..14c01c36c 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -69,16 +69,16 @@ Type *type_cuint(void) return type_int_unsigned_by_bitsize(platform_target.width_c_int); } -Type *type_int_signed_by_bitsize(unsigned bytesize) +Type *type_int_signed_by_bitsize(unsigned bitsize) { - switch (bytesize) + switch (bitsize) { case 8: return type_ichar; case 16: return type_short; case 32: return type_int; case 64: return type_long; case 128: return type_i128; - default: FATAL_ERROR("Illegal bitsize %d", bytesize); + default: FATAL_ERROR("Illegal bitsize %d", bitsize); } } Type *type_int_unsigned_by_bitsize(unsigned bytesize) @@ -656,6 +656,7 @@ bool type_is_ordered(Type *type) case TYPE_POINTER: case TYPE_BOOL: case TYPE_ENUM: + case TYPE_VECTOR: return true; case TYPE_TYPEDEF: type = type->canonical; @@ -977,6 +978,7 @@ Type *type_get_indexed_type(Type *type) case TYPE_ARRAY: case TYPE_SUBARRAY: case TYPE_INFERRED_ARRAY: + case TYPE_VECTOR: return type->array.base; case TYPE_STRLIT: return type_char; @@ -1043,6 +1045,24 @@ Type *type_get_array(Type *arr_type, ByteSize len) return type_create_array(arr_type, len, false, false); } +bool type_is_valid_for_vector(Type *type) +{ + RETRY: + switch (type->type_kind) + { + case ALL_SIGNED_INTS: + case ALL_UNSIGNED_INTS: + case ALL_REAL_FLOATS: + case TYPE_BOOL: + return true; + case TYPE_TYPEDEF: + type = type->canonical; + goto RETRY; + default: + return false; + } +} + Type *type_get_vector(Type *vector_type, unsigned len) { return type_create_array(vector_type, len, true, false); diff --git a/src/utils/common.h b/src/utils/common.h index 3633ca3de..287735297 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -14,6 +14,8 @@ #include "errors.h" #include +#define MAX_VECTOR_WIDTH 65536 +#define MAX_ARRAY_SIZE INT64_MAX #define MAX_IDENTIFIER_LENGTH 31 #define PROJECT_TOML "project.toml" #ifndef __unused diff --git a/src/version.h b/src/version.h index 3f0d9f7be..1e7dbbf91 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "A235" \ No newline at end of file +#define COMPILER_VERSION "A236" \ No newline at end of file diff --git a/test/test_suite/arrays/negative_array.c3 b/test/test_suite/arrays/negative_array.c3 index e2fc1d88c..44d656f15 100644 --- a/test/test_suite/arrays/negative_array.c3 +++ b/test/test_suite/arrays/negative_array.c3 @@ -1,3 +1,5 @@ int[-1] a; // #error: An array may not have a negative size -int[10-20] b; // #error: An array may not have a negative size \ No newline at end of file +int[10-20] b; // #error: An array may not have a negative size +int[<-1>] c; // #error: A vector may not have a negative width +int[<10-20>] d; // #error: A vector may not have a negative width diff --git a/test/test_suite/vector/vector_init.c3t b/test/test_suite/vector/vector_init.c3t new file mode 100644 index 000000000..26940aecb --- /dev/null +++ b/test/test_suite/vector/vector_init.c3t @@ -0,0 +1,52 @@ +int[<4>] baz = { 1, 4, 5, 7 }; + +func void main() +{ + int[<4>] foo = { 1, 2, 3, 4 }; + int z = foo[0]; + foo[2] = z + 1; + int[<4>] bar = {}; + int[<4>] bar2 = { 1, z, 3, 4 }; + int[<4>] bar3 = { [1] = z }; + int[<4>] bar4 = { [0..3] = z }; +} + +/* #expect: vector_init.ll + +@vector_init.baz = global <4 x i32> , align 16 + +; Function Attrs: nounwind +define void @main() #0 { +entry: + %foo = alloca <4 x i32>, align 16 + %z = alloca i32, align 4 + %bar = alloca <4 x i32>, align 16 + %bar2 = alloca <4 x i32>, align 16 + %bar3 = alloca <4 x i32>, align 16 + %bar4 = alloca <4 x i32>, align 16 + store <4 x i32> , <4 x i32>* %foo, align 16 + %0 = load <4 x i32>, <4 x i32>* %foo, align 16 + %1 = extractelement <4 x i32> %0, i64 0 + store i32 %1, i32* %z, align 4 + %2 = load <4 x i32>, <4 x i32>* %foo, align 16 + %3 = load i32, i32* %z, align 4 + %add = add i32 %3, 1 + %elemset = insertelement <4 x i32> %2, i32 %add, i64 2 + store <4 x i32> %elemset, <4 x i32>* %foo, align 16 + store <4 x i32> zeroinitializer, <4 x i32>* %bar, align 16 + %4 = load i32, i32* %z, align 4 + %5 = insertelement <4 x i32> , i32 %4, i64 1 + %6 = insertelement <4 x i32> %5, i32 3, i64 2 + %7 = insertelement <4 x i32> %6, i32 4, i64 3 + store <4 x i32> %7, <4 x i32>* %bar2, align 16 + %8 = load i32, i32* %z, align 4 + %9 = insertelement <4 x i32> zeroinitializer, i32 %8, i64 1 + store <4 x i32> %9, <4 x i32>* %bar3, align 16 + %10 = load i32, i32* %z, align 4 + %11 = insertelement <4 x i32> zeroinitializer, i32 %10, i64 0 + %12 = insertelement <4 x i32> %11, i32 %10, i64 1 + %13 = insertelement <4 x i32> %12, i32 %10, i64 2 + %14 = insertelement <4 x i32> %13, i32 %10, i64 3 + store <4 x i32> %14, <4 x i32>* %bar4, align 16 + ret void +} \ No newline at end of file diff --git a/test/test_suite/vector/vector_ops.c3t b/test/test_suite/vector/vector_ops.c3t new file mode 100644 index 000000000..86d1a591b --- /dev/null +++ b/test/test_suite/vector/vector_ops.c3t @@ -0,0 +1,288 @@ +import libc; + +func void testf() +{ + float[<4>] y = { 1, 2, 3, 4 }; + float[<4>] z = { 2, 2, 2, -100 }; + float[<4>] w = y + z; + libc::printf("%f %f %f %f\n", w[0], w[1], w[2], w[3]); + w = y * z; + libc::printf("%f %f %f %f\n", w[0], w[1], w[2], w[3]); + w = y / z; + libc::printf("%f %f %f %f\n", w[0], w[1], w[2], w[3]); + w = y - z; + libc::printf("%f %f %f %f\n", w[0], w[1], w[2], w[3]); + int[<4>] ww = y < z; + libc::printf("%d %d %d %d\n", ww[0], ww[1], ww[2], ww[3]); +} + +func void testb() +{ + bool[<4>] d = { true, false, true, true }; +} + +func void testi() +{ + int[<4>] y = { 1, 2, 3, 4 }; + int[<4>] z = { 2, 2, 2, -100 }; + int[<4>] w = y + z; + libc::printf("%d %d %d %d\n", w[0], w[1], w[2], w[3]); + w = y * z; + libc::printf("%d %d %d %d\n", w[0], w[1], w[2], w[3]); + w = y / z; + libc::printf("%d %d %d %d\n", w[0], w[1], w[2], w[3]); + w = y - z; + libc::printf("%d %d %d %d\n", w[0], w[1], w[2], w[3]); + w = z >> y; + libc::printf("%d %d %d %d\n", w[0], w[1], w[2], w[3]); + w = z << y; + libc::printf("%d %d %d %d\n", w[0], w[1], w[2], w[3]); + w = z > y; + libc::printf("%d %d %d %d\n", w[0], w[1], w[2], w[3]); + uint[<4>] uz = { 2, 6, 2, 1 }; + w = uz > y; + libc::printf("%d %d %d %d\n", w[0], w[1], w[2], w[3]); +} + +func void main() +{ + testf(); + testi(); + testb(); +} + +/* #expect: vector_ops.ll + +; Function Attrs: nounwind +define void @vector_ops.testf() #0 { +entry: + %y = alloca <4 x float>, align 16 + %z = alloca <4 x float>, align 16 + %w = alloca <4 x float>, align 16 + %ww = alloca <4 x i32>, align 16 + store <4 x float> , <4 x float>* %y, align 16 + store <4 x float> , <4 x float>* %z, align 16 + %0 = load <4 x float>, <4 x float>* %y, align 16 + %1 = load <4 x float>, <4 x float>* %z, align 16 + %fadd = fadd <4 x float> %0, %1 + store <4 x float> %fadd, <4 x float>* %w, align 16 + %2 = load <4 x float>, <4 x float>* %w, align 16 + %3 = extractelement <4 x float> %2, i64 0 + %fpfpext = fpext float %3 to double + %4 = load <4 x float>, <4 x float>* %w, align 16 + %5 = extractelement <4 x float> %4, i64 1 + %fpfpext1 = fpext float %5 to double + %6 = load <4 x float>, <4 x float>* %w, align 16 + %7 = extractelement <4 x float> %6, i64 2 + %fpfpext2 = fpext float %7 to double + %8 = load <4 x float>, <4 x float>* %w, align 16 + %9 = extractelement <4 x float> %8, i64 3 + %fpfpext3 = fpext float %9 to double + %10 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str, i32 0, i32 0), double %fpfpext, double %fpfpext1, double %fpfpext2, double %fpfpext3) + %11 = load <4 x float>, <4 x float>* %y, align 16 + %12 = load <4 x float>, <4 x float>* %z, align 16 + %fmul = fmul <4 x float> %11, %12 + store <4 x float> %fmul, <4 x float>* %w, align 16 + %13 = load <4 x float>, <4 x float>* %w, align 16 + %14 = extractelement <4 x float> %13, i64 0 + %fpfpext4 = fpext float %14 to double + %15 = load <4 x float>, <4 x float>* %w, align 16 + %16 = extractelement <4 x float> %15, i64 1 + %fpfpext5 = fpext float %16 to double + %17 = load <4 x float>, <4 x float>* %w, align 16 + %18 = extractelement <4 x float> %17, i64 2 + %fpfpext6 = fpext float %18 to double + %19 = load <4 x float>, <4 x float>* %w, align 16 + %20 = extractelement <4 x float> %19, i64 3 + %fpfpext7 = fpext float %20 to double + %21 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.1, i32 0, i32 0), double %fpfpext4, double %fpfpext5, double %fpfpext6, double %fpfpext7) + %22 = load <4 x float>, <4 x float>* %y, align 16 + %23 = load <4 x float>, <4 x float>* %z, align 16 + %fdiv = fdiv <4 x float> %22, %23 + store <4 x float> %fdiv, <4 x float>* %w, align 16 + %24 = load <4 x float>, <4 x float>* %w, align 16 + %25 = extractelement <4 x float> %24, i64 0 + %fpfpext8 = fpext float %25 to double + %26 = load <4 x float>, <4 x float>* %w, align 16 + %27 = extractelement <4 x float> %26, i64 1 + %fpfpext9 = fpext float %27 to double + %28 = load <4 x float>, <4 x float>* %w, align 16 + %29 = extractelement <4 x float> %28, i64 2 + %fpfpext10 = fpext float %29 to double + %30 = load <4 x float>, <4 x float>* %w, align 16 + %31 = extractelement <4 x float> %30, i64 3 + %fpfpext11 = fpext float %31 to double + %32 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.2, i32 0, i32 0), double %fpfpext8, double %fpfpext9, double %fpfpext10, double %fpfpext11) + %33 = load <4 x float>, <4 x float>* %y, align 16 + %34 = load <4 x float>, <4 x float>* %z, align 16 + %fsub = fsub <4 x float> %33, %34 + store <4 x float> %fsub, <4 x float>* %w, align 16 + %35 = load <4 x float>, <4 x float>* %w, align 16 + %36 = extractelement <4 x float> %35, i64 0 + %fpfpext12 = fpext float %36 to double + %37 = load <4 x float>, <4 x float>* %w, align 16 + %38 = extractelement <4 x float> %37, i64 1 + %fpfpext13 = fpext float %38 to double + %39 = load <4 x float>, <4 x float>* %w, align 16 + %40 = extractelement <4 x float> %39, i64 2 + %fpfpext14 = fpext float %40 to double + %41 = load <4 x float>, <4 x float>* %w, align 16 + %42 = extractelement <4 x float> %41, i64 3 + %fpfpext15 = fpext float %42 to double + %43 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.3, i32 0, i32 0), double %fpfpext12, double %fpfpext13, double %fpfpext14, double %fpfpext15) + %44 = load <4 x float>, <4 x float>* %y, align 16 + %45 = load <4 x float>, <4 x float>* %z, align 16 + %lt = fcmp olt <4 x float> %44, %45 + %46 = sext <4 x i1> %lt to <4 x i32> + store <4 x i32> %46, <4 x i32>* %ww, align 16 + %47 = load <4 x i32>, <4 x i32>* %ww, align 16 + %48 = extractelement <4 x i32> %47, i64 0 + %49 = load <4 x i32>, <4 x i32>* %ww, align 16 + %50 = extractelement <4 x i32> %49, i64 1 + %51 = load <4 x i32>, <4 x i32>* %ww, align 16 + %52 = extractelement <4 x i32> %51, i64 2 + %53 = load <4 x i32>, <4 x i32>* %ww, align 16 + %54 = extractelement <4 x i32> %53, i64 3 + %55 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.4, i32 0, i32 0), i32 %48, i32 %50, i32 %52, i32 %54) + ret void +} + +; Function Attrs: nounwind +define void @vector_ops.testb() #0 { +entry: + %d = alloca <4 x i8>, align 4 + store <4 x i8> , <4 x i8>* %d, align 4 + ret void +} + +; Function Attrs: nounwind +define void @vector_ops.testi() #0 { +entry: + %y = alloca <4 x i32>, align 16 + %z = alloca <4 x i32>, align 16 + %w = alloca <4 x i32>, align 16 + %uz = alloca <4 x i32>, align 16 + store <4 x i32> , <4 x i32>* %y, align 16 + store <4 x i32> , <4 x i32>* %z, align 16 + %0 = load <4 x i32>, <4 x i32>* %y, align 16 + %1 = load <4 x i32>, <4 x i32>* %z, align 16 + %add = add <4 x i32> %0, %1 + store <4 x i32> %add, <4 x i32>* %w, align 16 + %2 = load <4 x i32>, <4 x i32>* %w, align 16 + %3 = extractelement <4 x i32> %2, i64 0 + %4 = load <4 x i32>, <4 x i32>* %w, align 16 + %5 = extractelement <4 x i32> %4, i64 1 + %6 = load <4 x i32>, <4 x i32>* %w, align 16 + %7 = extractelement <4 x i32> %6, i64 2 + %8 = load <4 x i32>, <4 x i32>* %w, align 16 + %9 = extractelement <4 x i32> %8, i64 3 + %10 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.5, i32 0, i32 0), i32 %3, i32 %5, i32 %7, i32 %9) + %11 = load <4 x i32>, <4 x i32>* %y, align 16 + %12 = load <4 x i32>, <4 x i32>* %z, align 16 + %mul = mul <4 x i32> %11, %12 + store <4 x i32> %mul, <4 x i32>* %w, align 16 + %13 = load <4 x i32>, <4 x i32>* %w, align 16 + %14 = extractelement <4 x i32> %13, i64 0 + %15 = load <4 x i32>, <4 x i32>* %w, align 16 + %16 = extractelement <4 x i32> %15, i64 1 + %17 = load <4 x i32>, <4 x i32>* %w, align 16 + %18 = extractelement <4 x i32> %17, i64 2 + %19 = load <4 x i32>, <4 x i32>* %w, align 16 + %20 = extractelement <4 x i32> %19, i64 3 + %21 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.6, i32 0, i32 0), i32 %14, i32 %16, i32 %18, i32 %20) + %22 = load <4 x i32>, <4 x i32>* %y, align 16 + %23 = load <4 x i32>, <4 x i32>* %z, align 16 + %sdiv = sdiv <4 x i32> %22, %23 + store <4 x i32> %sdiv, <4 x i32>* %w, align 16 + %24 = load <4 x i32>, <4 x i32>* %w, align 16 + %25 = extractelement <4 x i32> %24, i64 0 + %26 = load <4 x i32>, <4 x i32>* %w, align 16 + %27 = extractelement <4 x i32> %26, i64 1 + %28 = load <4 x i32>, <4 x i32>* %w, align 16 + %29 = extractelement <4 x i32> %28, i64 2 + %30 = load <4 x i32>, <4 x i32>* %w, align 16 + %31 = extractelement <4 x i32> %30, i64 3 + %32 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.7, i32 0, i32 0), i32 %25, i32 %27, i32 %29, i32 %31) + %33 = load <4 x i32>, <4 x i32>* %y, align 16 + %34 = load <4 x i32>, <4 x i32>* %z, align 16 + %sub = sub <4 x i32> %33, %34 + store <4 x i32> %sub, <4 x i32>* %w, align 16 + %35 = load <4 x i32>, <4 x i32>* %w, align 16 + %36 = extractelement <4 x i32> %35, i64 0 + %37 = load <4 x i32>, <4 x i32>* %w, align 16 + %38 = extractelement <4 x i32> %37, i64 1 + %39 = load <4 x i32>, <4 x i32>* %w, align 16 + %40 = extractelement <4 x i32> %39, i64 2 + %41 = load <4 x i32>, <4 x i32>* %w, align 16 + %42 = extractelement <4 x i32> %41, i64 3 + %43 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.8, i32 0, i32 0), i32 %36, i32 %38, i32 %40, i32 %42) + %44 = load <4 x i32>, <4 x i32>* %z, align 16 + %45 = load <4 x i32>, <4 x i32>* %y, align 16 + %ashr = ashr <4 x i32> %44, %45 + %46 = freeze <4 x i32> %ashr + store <4 x i32> %46, <4 x i32>* %w, align 16 + %47 = load <4 x i32>, <4 x i32>* %w, align 16 + %48 = extractelement <4 x i32> %47, i64 0 + %49 = load <4 x i32>, <4 x i32>* %w, align 16 + %50 = extractelement <4 x i32> %49, i64 1 + %51 = load <4 x i32>, <4 x i32>* %w, align 16 + %52 = extractelement <4 x i32> %51, i64 2 + %53 = load <4 x i32>, <4 x i32>* %w, align 16 + %54 = extractelement <4 x i32> %53, i64 3 + %55 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.9, i32 0, i32 0), i32 %48, i32 %50, i32 %52, i32 %54) + %56 = load <4 x i32>, <4 x i32>* %z, align 16 + %57 = load <4 x i32>, <4 x i32>* %y, align 16 + %shl = shl <4 x i32> %56, %57 + %58 = freeze <4 x i32> %shl + store <4 x i32> %58, <4 x i32>* %w, align 16 + %59 = load <4 x i32>, <4 x i32>* %w, align 16 + %60 = extractelement <4 x i32> %59, i64 0 + %61 = load <4 x i32>, <4 x i32>* %w, align 16 + %62 = extractelement <4 x i32> %61, i64 1 + %63 = load <4 x i32>, <4 x i32>* %w, align 16 + %64 = extractelement <4 x i32> %63, i64 2 + %65 = load <4 x i32>, <4 x i32>* %w, align 16 + %66 = extractelement <4 x i32> %65, i64 3 + %67 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.10, i32 0, i32 0), i32 %60, i32 %62, i32 %64, i32 %66) + %68 = load <4 x i32>, <4 x i32>* %z, align 16 + %69 = load <4 x i32>, <4 x i32>* %y, align 16 + %gt = icmp sgt <4 x i32> %68, %69 + %70 = sext <4 x i1> %gt to <4 x i32> + store <4 x i32> %70, <4 x i32>* %w, align 16 + %71 = load <4 x i32>, <4 x i32>* %w, align 16 + %72 = extractelement <4 x i32> %71, i64 0 + %73 = load <4 x i32>, <4 x i32>* %w, align 16 + %74 = extractelement <4 x i32> %73, i64 1 + %75 = load <4 x i32>, <4 x i32>* %w, align 16 + %76 = extractelement <4 x i32> %75, i64 2 + %77 = load <4 x i32>, <4 x i32>* %w, align 16 + %78 = extractelement <4 x i32> %77, i64 3 + %79 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.11, i32 0, i32 0), i32 %72, i32 %74, i32 %76, i32 %78) + store <4 x i32> , <4 x i32>* %uz, align 16 + %80 = load <4 x i32>, <4 x i32>* %uz, align 16 + %81 = load <4 x i32>, <4 x i32>* %y, align 16 + %lt = icmp slt <4 x i32> %81, %80 + %check = icmp slt <4 x i32> %80, zeroinitializer + %siui-lt = or <4 x i1> %check, %lt + %82 = sext <4 x i1> %siui-lt to <4 x i32> + store <4 x i32> %82, <4 x i32>* %w, align 16 + %83 = load <4 x i32>, <4 x i32>* %w, align 16 + %84 = extractelement <4 x i32> %83, i64 0 + %85 = load <4 x i32>, <4 x i32>* %w, align 16 + %86 = extractelement <4 x i32> %85, i64 1 + %87 = load <4 x i32>, <4 x i32>* %w, align 16 + %88 = extractelement <4 x i32> %87, i64 2 + %89 = load <4 x i32>, <4 x i32>* %w, align 16 + %90 = extractelement <4 x i32> %89, i64 3 + %91 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.12, i32 0, i32 0), i32 %84, i32 %86, i32 %88, i32 %90) + ret void +} + +; Function Attrs: nounwind +define void @main() #0 { +entry: + call void @vector_ops.testf() + call void @vector_ops.testi() + call void @vector_ops.testb() + ret void +} \ No newline at end of file