diff --git a/src/build/project.c b/src/build/project.c index 658ea29f2..b52335016 100644 --- a/src/build/project.c +++ b/src/build/project.c @@ -11,7 +11,7 @@ TomlArray *get_array(TomlTable *table, const char *key) if (!value) return NULL; if (value->type != TOML_ARRAY) { - error_exit("The key '%s' was not an array element. Did you type '[%s]' instead of '[[%s]]'?"); + error_exit("The key '%s' was not an array field. Did you type '[%s]' instead of '[[%s]]'?"); } return value->value.array; } diff --git a/src/compiler/ast.c b/src/compiler/ast.c index ef174dfdd..0486632cd 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -46,7 +46,7 @@ static TypeInfo poison_type_info = { .kind = TYPE_INFO_POISON }; Type *poisoned_type = &poison_type; TypeInfo *poisoned_type_info = &poison_type_info; -unsigned decl_abi_alignment(Decl *decl) +AlignSize decl_abi_alignment(Decl *decl) { return decl->alignment ?: type_abi_alignment(decl->type); } @@ -506,6 +506,9 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent) if (!expr) return; switch (expr->expr_kind) { + case EXPR_DESIGNATOR: + DUMP("(named param)"); + return; case EXPR_MEMBER_ACCESS: DUMP("(member access)"); return; @@ -652,13 +655,13 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent) DUMPEND(); case EXPR_INITIALIZER_LIST: fprintf_indented(file, indent, "(initializerlist "); - switch (expr->expr_initializer.init_type) + switch (expr->initializer_expr.init_type) { case INITIALIZER_UNKNOWN: fprintf(file, "not-analyzed\n"); break; - case INITIALIZER_ZERO: - fprintf(file, "zero\n"); + case INITIALIZER_CONST: + fprintf(file, "const\n"); break; case INITIALIZER_NORMAL: fprintf(file, "normal\n"); @@ -668,9 +671,9 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent) break; } DUMPEXPC(expr); - VECEACH(expr->expr_initializer.initializer_expr, i) + VECEACH(expr->initializer_expr.initializer_expr, i) { - DUMPEXPR(expr->expr_initializer.initializer_expr[i]); + DUMPEXPR(expr->initializer_expr.initializer_expr[i]); } DUMPEND(); case EXPR_SUBSCRIPT: @@ -735,11 +738,6 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent) DUMPEXPR(expr->expr_scope.expr); // TODO defers. DUMPEND(); - case EXPR_DESIGNATED_INITIALIZER: - DUMP("(designated-initializer"); - // TODO path - DUMPEXPR(expr->designated_init_expr.value); - DUMPEND(); case EXPR_COMPOUND_LITERAL: DUMP("(compound-literal"); DUMPTI(expr->expr_compound_literal.type_info); diff --git a/src/compiler/c_abi_internal.h b/src/compiler/c_abi_internal.h index a7c6fee22..f6f7e475d 100644 --- a/src/compiler/c_abi_internal.h +++ b/src/compiler/c_abi_internal.h @@ -12,7 +12,7 @@ typedef enum } ByVal; static inline ABIArgInfo *abi_arg_by_reg_attr(ABIArgInfo *info); -size_t abi_arg_expanded_size(ABIArgInfo *type_info, Type *type); +ByteSize abi_arg_expanded_size(ABIArgInfo *type_info, Type *type); bool abi_arg_is_indirect(ABIArgInfo *info); ABIArgInfo *abi_arg_ignore(void); ABIArgInfo *abi_arg_new_direct_pair(AbiType *low_type, AbiType *high_type); @@ -27,12 +27,12 @@ ABIArgInfo *abi_arg_new_indirect_realigned(unsigned alignment); ABIArgInfo *abi_arg_new_indirect_by_val(void); ABIArgInfo *abi_arg_new_indirect_not_by_val(void); -size_t abi_type_abi_alignment(AbiType *type); +ByteSize abi_type_abi_alignment(AbiType *type); bool abi_type_is_integer(AbiType *type); bool abi_type_is_float(AbiType *type); AbiType *abi_type_new_plain(Type *type); AbiType *abi_type_new_int_bits(unsigned bits); -size_t abi_type_size(AbiType *type); +ByteSize abi_type_size(AbiType *type); typedef struct { diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index a364b4246..1cbd69c66 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -15,6 +15,14 @@ #include "target.h" #include "utils/malloc.h" +typedef uint64_t ByteSize; +typedef int64_t ArrayIndex; +typedef int32_t MemberIndex; +typedef int32_t AlignSize; +typedef int32_t ScopeId; + + + typedef uint32_t SourceLoc; typedef struct { @@ -31,7 +39,9 @@ typedef struct #define MAX_MACRO_NESTING 1024 #define MAX_FUNCTION_SIGNATURE_SIZE 2048 #define MAX_PARAMS 512 -#define MAX_ALIGNMENT (1U << 29U) +#define MAX_MEMBERS ((MemberIndex)(((uint64_t)2) << 28)) +#define MAX_ALIGNMENT ((ArrayIndex)(((uint64_t)2) << 28)) +#define MAX_OFFSET ((ArrayIndex)(((uint64_t)2) << 60)) typedef struct _Ast Ast; typedef struct _Decl Decl; @@ -158,25 +168,6 @@ typedef struct _Path uint32_t len; } Path; -typedef enum -{ - DESIGNATED_IDENT, - DESIGNATED_SUBSCRIPT, -} DesignatedPathKind; - -typedef struct _DesignatedPath -{ - DesignatedPathKind kind : 3; - bool constant : 1; - bool pure : 1; - struct _DesignatedPath *sub_path; - Type *type; - union - { - unsigned index; - Expr *index_expr; - }; -} DesignatedPath; typedef struct { @@ -195,13 +186,13 @@ typedef struct typedef struct { Type *base; - size_t len; + ByteSize len; } TypeArray; typedef struct { Type *base; - size_t len; + ByteSize len; } TypeVector; typedef struct @@ -277,6 +268,7 @@ typedef struct { uint64_t size; Decl **members; + MemberIndex union_rep; } StructDecl; @@ -428,7 +420,7 @@ typedef struct bool next_target : 1; void *break_target; void *continue_target; - unsigned scope_id; + ScopeId scope_id; AstId parent; } LabelDecl; @@ -447,9 +439,9 @@ typedef struct _Decl bool needs_additional_pad : 1; void *backend_ref; const char *cname; - uint32_t alignment; + AlignSize alignment; const char *section; - size_t offset; + ArrayIndex offset; /* bool is_exported : 1; bool is_used : 1; bool is_used_public : 1; @@ -591,6 +583,72 @@ typedef struct }; } ExprAccess; +typedef struct DesignatorElement_ +{ + DesignatorType kind : 4; + union + { + const char *field; + struct + { + Expr *index_expr; + Expr *index_end_expr; + }; + }; + ArrayIndex index; + ArrayIndex index_end; +} DesignatorElement; + +typedef enum +{ + CONST_INIT_ZERO, + CONST_INIT_EXPANDED, + CONST_SELECTED, + CONST_VALUE, + CONST_INIT_ARRAY_SPLIT, + CONST_INIT_ARRAY_RANGE_ZERO, + CONST_INIT_ARRAY_VALUE_FRAGMENT +} ConstInitType; + + +typedef struct ConstInitializer_ +{ + Type *type; + ConstInitType kind; + union + { + struct ConstInitializer_ **elements; + Expr *value; + struct + { + struct ConstInitializer_ *element; + MemberIndex index; + } union_const; + struct + { + struct ConstInitializer_ *low; + struct ConstInitializer_ *mid; + struct ConstInitializer_ *hi; + } split_const; + struct + { + ArrayIndex low; + ArrayIndex high; + } array_range_zero; + struct + { + struct ConstInitializer_ *element; + ArrayIndex index; + } single_array_index; + }; +} ConstInitializer; + +typedef struct +{ + DesignatorElement **path; + Expr *value; +} ExprDesignator; + typedef struct { Path *path; @@ -646,15 +704,19 @@ typedef struct typedef enum { INITIALIZER_UNKNOWN, - INITIALIZER_ZERO, INITIALIZER_DESIGNATED, - INITIALIZER_NORMAL + INITIALIZER_NORMAL, + INITIALIZER_CONST, } InitializerType; typedef struct { InitializerType init_type; - Expr** initializer_expr; + union + { + Expr** initializer_expr; + ConstInitializer *initializer; + }; } ExprInitializer; typedef struct @@ -663,12 +725,6 @@ typedef struct TypeInfo *type_info; } ExprCompoundLiteral; -typedef struct -{ - DesignatedPath *path; - Expr *value; -} ExprDesignatedInit; - typedef struct { const char *name; @@ -686,6 +742,7 @@ typedef struct Expr *inner; } ExprLen; + struct _Expr { ExprKind expr_kind : 8; @@ -697,7 +754,6 @@ struct _Expr SourceSpan span; Type *type; union { - ExprDesignatedInit designated_init_expr; Expr *group_expr; ExprLen len_expr; ExprCast cast_expr; @@ -718,13 +774,14 @@ struct _Expr ExprSlice slice_expr; ExprSubscript subscript_expr; ExprAccess access_expr; + ExprDesignator designator_expr; ExprIdentifier identifier_expr; ExprIdentifier macro_identifier_expr; ExprIdentifierRaw ct_ident_expr; ExprIdentifierRaw ct_macro_ident_expr; ExprIdentifierRaw hash_ident_expr; TypeInfo *typeid_expr; - ExprInitializer expr_initializer; + ExprInitializer initializer_expr; Decl *expr_enum; ExprCompoundLiteral expr_compound_literal; Expr** expression_list; @@ -819,7 +876,7 @@ typedef struct { struct { - unsigned scope_id; + ScopeId scope_id; }; struct { @@ -856,7 +913,7 @@ typedef struct FlowCommon flow; bool is_switch : 1; bool has_err_var : 1; - unsigned scope_id; + ScopeId scope_id; AstId defer; union { @@ -1031,7 +1088,7 @@ typedef struct _Module typedef struct _DynamicScope { - unsigned scope_id; + ScopeId scope_id; bool allow_dead_code : 1; bool jump_end : 1; ScopeFlags flags; @@ -1061,12 +1118,12 @@ typedef union typedef struct { - unsigned lexer_index; + uint32_t lexer_index; const char *file_begin; const char *lexing_start; const char *current; uint16_t source_file; - unsigned current_line; + uint32_t current_line; const char *line_start; File *current_file; SourceLoc last_in_range; @@ -1098,7 +1155,7 @@ typedef struct _Context Token *lead_comment; Token *trailing_comment; Token *next_lead_comment; - unsigned scope_id; + ScopeId scope_id; AstId break_target; AstId break_defer; AstId continue_target; @@ -1178,14 +1235,14 @@ typedef struct union { Type *type; - unsigned int_bits; + uint32_t int_bits; }; } AbiType; typedef struct ABIArgInfo_ { - unsigned param_index_start : 16; - unsigned param_index_end : 16; + MemberIndex param_index_start : 16; + MemberIndex param_index_end : 16; ABIKind kind : 6; struct { @@ -1207,11 +1264,11 @@ typedef struct ABIArgInfo_ } direct_pair; struct { - unsigned char offset_lo; - unsigned char padding_hi; - unsigned char lo_index; - unsigned char hi_index; - unsigned char offset_hi; + uint8_t offset_lo; + uint8_t padding_hi; + uint8_t lo_index; + uint8_t hi_index; + uint8_t offset_hi; bool packed : 1; AbiType *lo; AbiType *hi; @@ -1223,13 +1280,13 @@ typedef struct ABIArgInfo_ struct { AbiType *type; - unsigned elements : 3; + uint8_t elements : 3; bool prevent_flatten : 1; } direct_coerce; struct { // We may request a certain alignment of the parameters. - unsigned realignment : 16; + AlignSize realignment; bool by_val : 1; } indirect; }; @@ -1367,7 +1424,7 @@ static inline Decl *decl_raw(Decl *decl); static inline bool decl_ok(Decl *decl) { return !decl || decl->decl_kind != DECL_POISONED; } static inline bool decl_poison(Decl *decl) { decl->decl_kind = DECL_POISONED; decl->resolve_status = RESOLVE_DONE; return false; } static inline bool decl_is_struct_type(Decl *decl); -unsigned decl_abi_alignment(Decl *decl); +AlignSize decl_abi_alignment(Decl *decl); static inline DeclKind decl_from_token(TokenType type); #pragma mark --- Diag functions @@ -1512,7 +1569,7 @@ bool token_is_any_type(TokenType type); bool token_is_symbol(TokenType type); const char *token_type_to_string(TokenType type); -unsigned type_abi_alignment(Type *type); +AlignSize type_abi_alignment(Type *type); unsigned type_alloca_alignment(Type *type); void type_append_signature_name(Type *type, char *dst, size_t *offset); static inline bool type_convert_will_trunc(Type *destination, Type *source); @@ -1544,6 +1601,7 @@ static inline bool type_is_integer_signed(Type *type); static inline bool type_is_integer_kind(Type *type); static inline bool type_is_numeric(Type *type); static inline bool type_is_pointer(Type *type); +static inline bool type_is_promotable_float(Type *type); static inline bool type_is_promotable_integer(Type *type); static inline bool type_is_signed(Type *type); static inline bool type_is_structlike(Type *type); @@ -1555,7 +1613,7 @@ static inline Type *type_lowering(Type *type); bool type_may_have_sub_elements(Type *type); static inline bool type_ok(Type *type); static inline Type *type_reduced_from_expr(Expr *expr); -size_t type_size(Type *type); +ByteSize type_size(Type *type); const char *type_to_error_string(Type *type); static inline TypeInfo *type_info_new(TypeInfoKind kind, SourceSpan span); @@ -1636,7 +1694,7 @@ static inline bool type_is_pointer(Type *type) return type->type_kind == TYPE_POINTER || type->type_kind == TYPE_VARARRAY; } -static inline size_t aligned_offset(size_t offset, size_t alignment) +static inline uint64_t aligned_offset(uint64_t offset, uint64_t alignment) { return ((offset + alignment - 1) / alignment) * alignment; } @@ -1786,7 +1844,13 @@ static inline DeclKind decl_from_token(TokenType type) static inline bool type_is_promotable_integer(Type *type) { // If we support other architectures, update this. - return type_is_integer_kind(type) && type->builtin.bytesize < type_c_int->builtin.bytesize; + return type_is_integer_kind(type) && type->builtin.bytesize < type_c_int->canonical->builtin.bytesize; +} + +static inline bool type_is_promotable_float(Type *type) +{ + // If we support other architectures, update this. + return type_is_float(type->canonical) && type->builtin.bytesize < type_double->builtin.bytesize; } /** diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 7786006bf..7f6378bd1 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -83,12 +83,6 @@ typedef enum AST_SCOPED_STMT, } AstKind; -typedef enum -{ - ATTR_INVALID, - ATTR_UNRESOLVED, -} AttrKind; - typedef enum { @@ -162,16 +156,6 @@ typedef enum case DECL_CT_SWITCH: case DECL_CT_CASE: case DECL_ATTRIBUTE: case DECL_LABEL: \ case DECL_DEFINE -// Ordering here is in priority if two branches should have the same exit. -typedef enum -{ - EXIT_NONE = 0, - EXIT_CONTINUE, - EXIT_RETURN, - EXIT_NEXT, - EXIT_BREAK, -} ExitType; - typedef enum { EXPR_POISONED, @@ -206,15 +190,22 @@ typedef enum EXPR_SCOPED_EXPR, EXPR_EXPR_BLOCK, EXPR_MACRO_BLOCK, - EXPR_DESIGNATED_INITIALIZER, EXPR_COMPOUND_LITERAL, EXPR_FAILABLE, EXPR_DECL_LIST, EXPR_LEN, EXPR_UNDEF, EXPR_ENUM_CONSTANT, + EXPR_DESIGNATOR, } ExprKind; +typedef enum +{ + DESIGNATOR_FIELD, + DESIGNATOR_ARRAY, + DESIGNATOR_RANGE +} DesignatorType; + typedef enum { FAILABLE_NO, @@ -312,7 +303,7 @@ typedef enum TOKEN_EQEQ, // == TOKEN_GREATER_EQ, // >= TOKEN_LESS_EQ, // <= - TOKEN_LPARBRA, // ({ + TOKEN_LBRAPIPE, // {| TOKEN_MINUS_ASSIGN, // -= TOKEN_MINUS_MOD, // -% TOKEN_MINUSMINUS, // -- @@ -324,7 +315,7 @@ typedef enum TOKEN_PLUS_ASSIGN, // += TOKEN_PLUS_MOD, // +% TOKEN_PLUSPLUS, // ++ - TOKEN_RPARBRA, // }) + TOKEN_RBRAPIPE, // |} TOKEN_SCOPE, // :: TOKEN_SHL, // << TOKEN_SHR, // >> diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index 9ffed8299..4b613bb14 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -739,11 +739,11 @@ static bool lexer_scan_token_inner(Lexer *lexer) case ';': return add_token(lexer, TOKEN_EOS, ";"); case '{': - return add_token(lexer, TOKEN_LBRACE, "{"); + return match(lexer, '|') ? add_token(lexer, TOKEN_LBRAPIPE, "{|") : add_token(lexer, TOKEN_LBRACE, "{"); case '}': - return match(lexer, ')') ? add_token(lexer, TOKEN_RPARBRA, "})") : add_token(lexer, TOKEN_RBRACE, "}"); + return add_token(lexer, TOKEN_RBRACE, "}"); case '(': - return match(lexer, '{') ? add_token(lexer, TOKEN_LPARBRA, "({") : add_token(lexer, TOKEN_LPAREN, "("); + return add_token(lexer, TOKEN_LPAREN, "("); case ')': return add_token(lexer, TOKEN_RPAREN, ")"); case '[': @@ -804,6 +804,7 @@ static bool lexer_scan_token_inner(Lexer *lexer) if (match(lexer, '&')) return add_token(lexer, TOKEN_AND, "&&"); return match(lexer, '=') ? add_token(lexer, TOKEN_BIT_AND_ASSIGN, "&=") : add_token(lexer, TOKEN_AMP, "&"); case '|': + if (match(lexer, '}')) return add_token(lexer, TOKEN_RBRAPIPE, "|}"); if (match(lexer, '|')) return add_token(lexer, TOKEN_OR, "||"); return match(lexer, '=') ? add_token(lexer, TOKEN_BIT_OR_ASSIGN, "|=") : add_token(lexer, TOKEN_BIT_OR, diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index fc4a2ab7a..11cb9ec04 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -53,35 +53,143 @@ LLVMValueRef llvm_emit_memclear_size_align(GenContext *c, LLVMValueRef ref, uint } -LLVMValueRef llvm_emit_memclear(GenContext *c, LLVMValueRef ref, Type *type) +LLVMValueRef llvm_emit_memclear(GenContext *c, BEValue *ref) { // TODO avoid bitcast on those that do not need them. - return llvm_emit_memclear_size_align(c, ref, type_size(type), - type_abi_alignment(type), true); + llvm_value_addr(c, ref); + return llvm_emit_memclear_size_align(c, ref->value, type_size(ref->type), ref->alignment, true); } +LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_init, bool *modified) +{ + switch (const_init->kind) + { + case CONST_INIT_ZERO: + return LLVMConstNull(llvm_get_type(c, const_init->type)); + case CONST_VALUE: + { + BEValue val; + llvm_emit_expr(c, &val, const_init->value); + return llvm_value_rvalue_store(c, &val); + } + case CONST_INIT_ARRAY_SPLIT: + { + *modified = true; + LLVMValueRef pieces[3]; + unsigned count = 0; + if (const_init->split_const.low) + { + pieces[count++] = llvm_emit_const_initializer(c, const_init->split_const.low, modified); + } + pieces[count++] = llvm_emit_const_initializer(c, const_init->split_const.mid, modified); + if (const_init->split_const.low) + { + pieces[count++] = llvm_emit_const_initializer(c, const_init->split_const.low, modified); + } + return LLVMConstStructInContext(c->context, pieces, count, true); + } + case CONST_INIT_ARRAY_VALUE_FRAGMENT: + *modified = true; + return llvm_emit_const_initializer(c, const_init->single_array_index.element, modified); + case CONST_SELECTED: + { + Type *member_type = const_init->union_const.element->type; + ByteSize union_size = type_size(const_init->type); + Type *rep_type = const_init->type->decl->strukt.members[const_init->type->decl->strukt.union_rep]->type; + LLVMTypeRef rep_type_llvm = llvm_get_type(c, rep_type); + LLVMTypeRef member_type_llvm = llvm_get_type(c, member_type); + bool is_modified = rep_type_llvm != member_type_llvm; + if (is_modified) *modified = true; -static void gencontext_emit_global_variable_definition(GenContext *context, Decl *decl) + // Emit our value. + LLVMValueRef result = llvm_emit_const_initializer(c, const_init->union_const.element, modified); + + LLVMValueRef values[2] = { + result, + NULL + }; + + // There is a case, where we need additional padding for this member, in that case + // Create a struct for it. + ByteSize member_size = type_size(member_type); + if (union_size > member_size) + { + values[1] = llvm_const_padding(c, union_size - member_size); + // Now we use an unnamed struct. + return LLVMConstStructInContext(c->context, values, 2, const_init->type->decl->is_packed); + } + + return LLVMConstNamedStruct(llvm_get_type(c, const_init->type), values, 1); + } + case CONST_INIT_EXPANDED: + { + MemberIndex count = vec_size(const_init->type->decl->strukt.members); + LLVMValueRef *entries = MALLOC(sizeof(LLVMValueRef) * count); + for (MemberIndex i = 0; i < count; i++) + { + entries[i] = llvm_emit_const_initializer(c, const_init->elements[i], modified); + } + if (*modified) + { + return LLVMConstStructInContext(c->context, entries, count, const_init->type->decl->is_packed); + } + return LLVMConstNamedStruct(llvm_get_type(c, const_init->type), entries, count); + } + case CONST_INIT_ARRAY_RANGE_ZERO: + return LLVMConstNull(llvm_get_type(c, const_init->type)); + } + UNREACHABLE +} +LLVMValueRef llvm_emit_const_aggregate(GenContext *c, Expr *expr, bool *modified) +{ + switch (expr->initializer_expr.init_type) + { + case INITIALIZER_UNKNOWN: + case INITIALIZER_DESIGNATED: + UNREACHABLE + case INITIALIZER_CONST: + return llvm_emit_const_initializer(c, expr->initializer_expr.initializer, modified); + case INITIALIZER_NORMAL: + TODO + } + UNREACHABLE +} + +static void gencontext_emit_global_variable_definition(GenContext *c, Decl *decl) { assert(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST); // Skip real constants. if (!decl->type) return; - // TODO fix name - decl->backend_ref = LLVMAddGlobal(context->module, llvm_get_type(context, decl->type), decl->name); + bool modified = false; + LLVMValueRef init_value; + ByteSize alignment = type_abi_alignment(decl->type); if (decl->var.init_expr) { - BEValue value; - llvm_emit_expr(context, &value, decl->var.init_expr); - LLVMSetInitializer(decl->backend_ref, llvm_value_rvalue_store(context, &value)); + if (decl->var.init_expr->expr_kind == EXPR_INITIALIZER_LIST) + { + init_value = llvm_emit_const_aggregate(c, decl->var.init_expr, &modified); + } + else + { + BEValue value; + assert(decl->var.init_expr->expr_kind == EXPR_CONST); + llvm_emit_expr(c, &value, decl->var.init_expr); + init_value = llvm_value_rvalue_store(c, &value); + } } else { - LLVMSetInitializer(decl->backend_ref, LLVMConstNull(llvm_get_type(context, decl->type))); + init_value = LLVMConstNull(llvm_get_type(c, decl->type)); } + // TODO fix name + decl->backend_ref = LLVMAddGlobal(c->module, LLVMTypeOf(init_value), decl->name); + LLVMSetAlignment(decl->backend_ref, alignment); + LLVMSetInitializer(decl->backend_ref, init_value); + LLVMSetGlobalConstant(decl->backend_ref, decl->var.kind == VARDECL_CONST); switch (decl->visibility) @@ -98,11 +206,14 @@ static void gencontext_emit_global_variable_definition(GenContext *context, Decl break; } - int alignment = 64; // TODO - // Should we set linkage here? - if (llvm_use_debug(context)) + if (modified) { - llvm_emit_debug_global_var(context, decl); + decl->backend_ref = LLVMConstBitCast(decl->backend_ref, llvm_get_ptr_type(c, decl->type)); + } + // Should we set linkage here? + if (llvm_use_debug(c)) + { + llvm_emit_debug_global_var(c, decl); } } static void gencontext_verify_ir(GenContext *context) @@ -678,9 +789,9 @@ unsigned llvm_abi_size(LLVMTypeRef type) return LLVMABISizeOfType(target_data_layout(), type); } -unsigned llvm_abi_alignment(LLVMTypeRef type) +AlignSize llvm_abi_alignment(LLVMTypeRef type) { - return LLVMABIAlignmentOfType(target_data_layout(), type); + return (AlignSize)LLVMABIAlignmentOfType(target_data_layout(), type); } void llvm_store_bevalue_aligned(GenContext *c, LLVMValueRef destination, BEValue *value, unsigned alignment) @@ -705,7 +816,7 @@ void llvm_store_bevalue_aligned(GenContext *c, LLVMValueRef destination, BEValue case BE_ADDRESS: { // Here we do an optimized(?) memcopy. - size_t size = type_size(value->type); + ByteSize size = type_size(value->type); LLVMValueRef copy_size = llvm_const_int(c, size <= UINT32_MAX ? type_uint : type_usize, size); destination = LLVMBuildBitCast(c->builder, destination, llvm_get_ptr_type(c, type_byte), ""); LLVMValueRef source = LLVMBuildBitCast(c->builder, value->value, llvm_get_ptr_type(c, type_byte), ""); @@ -750,11 +861,22 @@ void llvm_store_aligned_decl(GenContext *context, Decl *decl, LLVMValueRef value llvm_store_aligned(context, decl->backend_ref, value, decl->alignment); } +void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLVMValueRef source, unsigned src_align, uint64_t len) +{ + assert(dest_align && src_align); + if (len <= UINT32_MAX) + { + LLVMBuildMemCpy(c->builder, dest, dest_align, source, src_align, llvm_const_int(c, type_uint, len)); + return; + } + LLVMBuildMemCpy(c->builder, dest, dest_align, source, src_align, llvm_const_int(c, type_ulong, len)); +} + void llvm_emit_memcpy_to_decl(GenContext *c, Decl *decl, LLVMValueRef source, unsigned source_alignment) { if (source_alignment == 0) source_alignment = type_abi_alignment(decl->type); - LLVMBuildMemCpy(c->builder, decl->backend_ref, decl->alignment ?: type_abi_alignment(decl->type), - source, source_alignment, llvm_const_int(c, type_usize, type_size(decl->type))); + llvm_emit_memcpy(c, decl->backend_ref, decl->alignment ?: type_abi_alignment(decl->type), + source, source_alignment, type_size(decl->type)); } LLVMValueRef llvm_emit_load_aligned(GenContext *context, LLVMTypeRef type, LLVMValueRef pointer, unsigned alignment, const char *name) diff --git a/src/compiler/llvm_codegen_c_abi.c b/src/compiler/llvm_codegen_c_abi.c index 2b2dcda37..0b6996c2e 100644 --- a/src/compiler/llvm_codegen_c_abi.c +++ b/src/compiler/llvm_codegen_c_abi.c @@ -44,7 +44,7 @@ bool abi_type_is_float(AbiType *type) return type->kind != ABI_TYPE_INT_BITS && type_is_float(type->type); } -size_t abi_type_size(AbiType *type) +ByteSize abi_type_size(AbiType *type) { switch (type->kind) { @@ -56,7 +56,7 @@ size_t abi_type_size(AbiType *type) UNREACHABLE; } -size_t abi_type_abi_alignment(AbiType *type) +ByteSize abi_type_abi_alignment(AbiType *type) { switch (type->kind) { @@ -107,7 +107,7 @@ ABIArgInfo *abi_arg_new_indirect_not_by_val(void) return info; } -size_t abi_arg_expanded_size(ABIArgInfo *type_info, Type *type) +ByteSize abi_arg_expanded_size(ABIArgInfo *type_info, Type *type) { switch (type->type_kind) { @@ -118,7 +118,7 @@ size_t abi_arg_expanded_size(ABIArgInfo *type_info, Type *type) case TYPE_STRUCT: { Decl **members = type->decl->strukt.members; - size_t result = 0; + ByteSize result = 0; VECEACH(members, i) { members += abi_arg_expanded_size(type_info, members[i]->type); diff --git a/src/compiler/llvm_codegen_c_abi_aarch64.c b/src/compiler/llvm_codegen_c_abi_aarch64.c index a240bbcec..a0a207977 100644 --- a/src/compiler/llvm_codegen_c_abi_aarch64.c +++ b/src/compiler/llvm_codegen_c_abi_aarch64.c @@ -26,7 +26,7 @@ ABIArgInfo *aarch64_classify_argument_type(Type *type) return aarch64_coerce_illegal_vector(type); } - size_t size = type_size(type); + ByteSize size = type_size(type); if (!type_is_abi_aggregate(type)) { @@ -56,7 +56,7 @@ ABIArgInfo *aarch64_classify_argument_type(Type *type) if (size <= 16) { // For RenderScript <= 16 needs to be coerced. - unsigned alignment = type_abi_alignment(type); + AlignSize alignment = type_abi_alignment(type); if (build_target.aarch64.is_aapcs) { alignment = alignment < 16 ? 8 : 16; @@ -90,7 +90,7 @@ ABIArgInfo *aarch64_classify_return_type(Type *type, bool variadic) return aarch64_coerce_illegal_vector(type); } - size_t size = type_size(type); + ByteSize size = type_size(type); // Large vectors by mem. if (type->type_kind == TYPE_VECTOR && size > 16) diff --git a/src/compiler/llvm_codegen_c_abi_riscv.c b/src/compiler/llvm_codegen_c_abi_riscv.c index b59149937..a1c9b37fb 100644 --- a/src/compiler/llvm_codegen_c_abi_riscv.c +++ b/src/compiler/llvm_codegen_c_abi_riscv.c @@ -68,7 +68,7 @@ static bool riscv_detect_fpcc_struct_internal(Type *type, unsigned current_offse // Is the first field already occupied? // Then fail because both needs to be available. if (*field1) return false; - // If the element doesn't fit a register - bail. + // If the field doesn't fit a register - bail. Type *element_type = type->complex; unsigned element_size = type_size(element_type); if (element_size > flen) return false; @@ -82,10 +82,10 @@ static bool riscv_detect_fpcc_struct_internal(Type *type, unsigned current_offse if (type->type_kind == TYPE_ARRAY) { - size_t array_len = type->array.len; + ByteSize array_len = type->array.len; Type *element_type = type->array.base; unsigned element_size = type_size(element_type); - for (size_t i = 0; i < array_len; i++) + for (ByteSize i = 0; i < array_len; i++) { if (!riscv_detect_fpcc_struct_internal(element_type, current_offset, @@ -167,7 +167,7 @@ static ABIArgInfo *riscv_classify_argument_type(Type *type, bool is_fixed, unsig // Ignore empty structs/unions. if (type_is_empty_union_struct(type, true)) return abi_arg_ignore(); - size_t size = type_size(type); + ByteSize size = type_size(type); // Pass floating point values via FPRs if possible. if (is_fixed && type_is_float(type) && build_target.riscv.flen >= size && *fprs) @@ -254,7 +254,7 @@ static ABIArgInfo *riscv_classify_argument_type(Type *type, bool is_fixed, unsig if (size <= 2 * xlen) { // Use a single XLen int if possible, 2*XLen if 2*XLen alignment is - // required, and a 2-element XLen array if only XLen alignment is required. + // required, and a 2-field XLen array if only XLen alignment is required. if (size <= xlen) { return abi_arg_new_direct_coerce(abi_type_new_int_bits(xlen * 8)); diff --git a/src/compiler/llvm_codegen_c_abi_wasm.c b/src/compiler/llvm_codegen_c_abi_wasm.c index 4c952467f..52a937854 100644 --- a/src/compiler/llvm_codegen_c_abi_wasm.c +++ b/src/compiler/llvm_codegen_c_abi_wasm.c @@ -11,8 +11,8 @@ static ABIArgInfo *wasm_classify_argument_type(Type *type) { // Ignore empty structs/unions. if (type_is_empty_union_struct(type, true)) return abi_arg_ignore(); - // Clang: Lower single-element structs to just pass a regular value. TODO: We - // could do reasonable-size multiple-element structs too, using getExpand(), + // Clang: Lower single-field structs to just pass a regular value. TODO: We + // could do reasonable-size multiple-field structs too, using getExpand(), // though watch out for things like bitfields. Type *single_type = type_abi_find_single_struct_element(type); if (single_type) return abi_arg_new_direct_coerce(abi_type_new_plain(single_type)); diff --git a/src/compiler/llvm_codegen_c_abi_win64.c b/src/compiler/llvm_codegen_c_abi_win64.c index 3283034b3..08ce6ad4c 100644 --- a/src/compiler/llvm_codegen_c_abi_win64.c +++ b/src/compiler/llvm_codegen_c_abi_win64.c @@ -48,7 +48,7 @@ ABIArgInfo *win64_classify(Regs *regs, Type *type, bool is_return, bool is_vecto // => to main handling. } } - size_t size = type_size(type); + ByteSize size = type_size(type); if (type_is_abi_aggregate(type)) { // Not 1, 2, 4, 8? Pass indirect. diff --git a/src/compiler/llvm_codegen_c_abi_x64.c b/src/compiler/llvm_codegen_c_abi_x64.c index 8ba13af6f..c00f5c5ce 100644 --- a/src/compiler/llvm_codegen_c_abi_x64.c +++ b/src/compiler/llvm_codegen_c_abi_x64.c @@ -51,7 +51,7 @@ ABIArgInfo *x64_indirect_return_result(Type *type) } return abi_arg_new_direct(); } -static size_t x64_native_vector_size_for_avx(void) +static ByteSize x64_native_vector_size_for_avx(void) { switch (build_target.x64.avx_level) { @@ -174,7 +174,7 @@ ABIArgInfo *x64_classify_reg_call_struct_type(Type *return_type, Registers *avai return info; } -static void x64_classify(Type *type, size_t offset_base, X64Class *lo_class, X64Class *hi_class, NamedArgument named); +static void x64_classify(Type *type, ByteSize offset_base, X64Class *lo_class, X64Class *hi_class, NamedArgument named); static X64Class x64_merge(X64Class accum, X64Class field) { @@ -211,7 +211,7 @@ static X64Class x64_merge(X64Class accum, X64Class field) } UNREACHABLE } -void x64_classify_post_merge(size_t size, X64Class *lo_class, X64Class *hi_class) +void x64_classify_post_merge(ByteSize size, X64Class *lo_class, X64Class *hi_class) { // If one is MEM => both is mem // If X87UP is not before X87 => mem @@ -230,9 +230,9 @@ void x64_classify_post_merge(size_t size, X64Class *lo_class, X64Class *hi_class *lo_class = CLASS_MEMORY; } -void x64_classify_struct_union(Type *type, size_t offset_base, X64Class *current, X64Class *lo_class, X64Class *hi_class, NamedArgument named_arg) +void x64_classify_struct_union(Type *type, ByteSize offset_base, X64Class *current, X64Class *lo_class, X64Class *hi_class, NamedArgument named_arg) { - size_t size = type_size(type); + ByteSize size = type_size(type); // 64 byte max. if (size > 64) return; @@ -244,9 +244,9 @@ void x64_classify_struct_union(Type *type, size_t offset_base, X64Class *current VECEACH(members, i) { Decl *member = members[i]; - size_t offset = offset_base + member->offset; + ByteSize offset = offset_base + member->offset; // The only case a 256-bit or a 512-bit wide vector could be used is when - // the struct contains a single 256-bit or 512-bit element. Early check + // the struct contains a single 256-bit or 512-bit field. Early check // and fallback to memory. if (size > 16 && ((!is_union && size != type_size(member->type)) @@ -275,11 +275,11 @@ void x64_classify_struct_union(Type *type, size_t offset_base, X64Class *current x64_classify_post_merge(size, lo_class, hi_class); } -void x64_classify_array(Type *type, size_t offset_base, X64Class *current, X64Class *lo_class, X64Class *hi_class, NamedArgument named_arg) +void x64_classify_array(Type *type, ByteSize offset_base, X64Class *current, X64Class *lo_class, X64Class *hi_class, NamedArgument named_arg) { - size_t size = type_size(type); + ByteSize size = type_size(type); Type *element = type->array.base; - size_t element_size = type_size(element); + ByteSize element_size = type_size(element); // Bigger than 64 bytes => MEM if (size > 64) return; @@ -293,7 +293,7 @@ void x64_classify_array(Type *type, size_t offset_base, X64Class *current, X64Cl // Re-classify *current = CLASS_NO_CLASS; // The only case a 256-bit or a 512-bit wide vector could be used is when - // the struct contains a single 256-bit or 512-bit element. Early check + // the struct contains a single 256-bit or 512-bit field. Early check // and fallback to memory. if (size > 16 && (size != type_size(element) || size > x64_native_vector_size_for_avx())) { @@ -301,8 +301,8 @@ void x64_classify_array(Type *type, size_t offset_base, X64Class *current, X64Cl return; } - size_t offset = offset_base; - for (size_t i = 0; i < type->array.len; i++) + ByteSize offset = offset_base; + for (ByteSize i = 0; i < type->array.len; i++) { X64Class field_lo; X64Class field_hi; @@ -316,7 +316,7 @@ void x64_classify_array(Type *type, size_t offset_base, X64Class *current, X64Cl assert(*hi_class != CLASS_SSEUP || *lo_class == CLASS_SSE); } -void x64_classify_vector(Type *type, size_t offset_base, X64Class *current, X64Class *lo_class, X64Class *hi_class, +void x64_classify_vector(Type *type, ByteSize offset_base, X64Class *current, X64Class *lo_class, X64Class *hi_class, NamedArgument named_arg) { unsigned size = type_size(type); @@ -325,8 +325,8 @@ void x64_classify_vector(Type *type, size_t offset_base, X64Class *current, X64C { *current = CLASS_INTEGER; // Check boundary crossing - size_t lo = offset_base / 8; - size_t hi = (offset_base + size - 1) / 8; + ByteSize lo = offset_base / 8; + ByteSize hi = (offset_base + size - 1) / 8; // If it crosses boundary, split it. if (hi != lo) @@ -362,10 +362,10 @@ void x64_classify_vector(Type *type, size_t offset_base, X64Class *current, X64C // Default pass by mem } -void x64_classify_complex(Type *type, size_t offset_base, X64Class *current, X64Class *lo_class, X64Class *hi_class) +void x64_classify_complex(Type *type, ByteSize offset_base, X64Class *current, X64Class *lo_class, X64Class *hi_class) { Type *element = type->complex; - size_t element_size = type_size(element); + ByteSize element_size = type_size(element); switch (type->type_kind) { case TYPE_I8: @@ -396,8 +396,8 @@ void x64_classify_complex(Type *type, size_t offset_base, X64Class *current, X64 default: UNREACHABLE } - size_t real = offset_base / 8; - size_t imag = (offset_base + element_size) / 8; + ByteSize real = offset_base / 8; + ByteSize imag = (offset_base + element_size) / 8; // If it crosses boundary, split it. if (*hi_class == CLASS_NO_CLASS && real != imag) { @@ -419,7 +419,7 @@ Decl *x64_get_member_at_offset(Decl *decl, unsigned offset) return last_match; } -static void x64_classify(Type *type, size_t offset_base, X64Class *lo_class, X64Class *hi_class, NamedArgument named) +static void x64_classify(Type *type, ByteSize offset_base, X64Class *lo_class, X64Class *hi_class, NamedArgument named) { *lo_class = CLASS_NO_CLASS; *hi_class = CLASS_NO_CLASS; @@ -497,16 +497,16 @@ bool x64_bits_contain_no_user_data(Type *type, unsigned start, unsigned end) // If the bytes being queried are off the end of the type, there is no user // data hiding here. This handles analysis of builtins, vectors and other // types that don't contain interesting padding. - size_t size = type_size(type); + ByteSize size = type_size(type); if (size <= start) return true; if (type->type_kind == TYPE_ARRAY) { - // Check each element to see if the element overlaps with the queried range. - size_t element_size = type_size(type->array.base); + // Check each field to see if the field overlaps with the queried range. + ByteSize element_size = type_size(type->array.base); for (unsigned i = 0; i < type->array.len; i++) { - // If the element is after the span we care about, then we're done.. - size_t offset = i * element_size; + // If the field is after the span we care about, then we're done.. + ByteSize offset = i * element_size; if (offset >= end) break; unsigned element_start = offset < start ? start - offset : 0; if (!x64_bits_contain_no_user_data(type->array.base, element_start, end - offset)) return false; @@ -617,8 +617,8 @@ AbiType *x64_get_int_type_at_offset(Type *type, unsigned offset, Type *source_ty case TYPE_ARRAY: { Type *element = type->array.base; - size_t element_size = type_size(element); - size_t element_offset = (offset / element_size) * element_size; + ByteSize element_size = type_size(element); + ByteSize element_offset = (offset / element_size) * element_size; return x64_get_int_type_at_offset(element, offset - element_offset, source_type, source_offset); } case TYPE_POISONED: @@ -643,7 +643,7 @@ AbiType *x64_get_int_type_at_offset(Type *type, unsigned offset, Type *source_ty case TYPE_COMPLEX: break; } - size_t size = type_size(source_type); + ByteSize size = type_size(source_type); assert(size != source_offset); if (size - source_offset > 8) return abi_type_new_plain(type_ulong); return abi_type_new_int_bits((size - source_offset) * 8); diff --git a/src/compiler/llvm_codegen_c_abi_x86.c b/src/compiler/llvm_codegen_c_abi_x86.c index 20589b696..a14d8b312 100644 --- a/src/compiler/llvm_codegen_c_abi_x86.c +++ b/src/compiler/llvm_codegen_c_abi_x86.c @@ -179,7 +179,7 @@ ABIArgInfo *x86_classify_return(CallConvention call, Regs *regs, Type *type) return abi_arg_new_direct_coerce(abi_type_new_plain(type_get_vector(type_long, 2))); } // Always return in register if it fits in a general purpose - // register, or if it is 64 bits and has a single element. + // register, or if it is 64 bits and has a single field. if (size == 1 || size == 2 || size == 4 || (size == 8 && type->vector.len == 1)) { return abi_arg_new_direct_coerce(abi_type_new_int_bits(size * 8)); @@ -205,8 +205,8 @@ ABIArgInfo *x86_classify_return(CallConvention call, Regs *regs, Type *type) // Check if we can return it in a register. if (x86_should_return_type_in_reg(type)) { - size_t size = type_size(type); - // Special case is floats and pointers in single element structs (except for MSVC) + ByteSize size = type_size(type); + // Special case is floats and pointers in single field structs (except for MSVC) Type *single_element = type_abi_find_single_struct_element(type); if (single_element) { @@ -219,7 +219,7 @@ ABIArgInfo *x86_classify_return(CallConvention call, Regs *regs, Type *type) return abi_arg_new_expand(); } } - // This is not a single element struct, so we wrap it in an int. + // This is not a single field struct, so we wrap it in an int. return abi_arg_new_direct_coerce(abi_type_new_int_bits(size * 8)); } return create_indirect_return_x86(regs); @@ -291,7 +291,7 @@ static inline bool x86_can_expand_indirect_aggregate_arg(Type *type) if (type->canonical->type_kind == TYPE_ERRTYPE) return true; if (!type_is_union_struct(type)) return false; - size_t size = 0; + ByteSize size = 0; Decl **members = type->decl->strukt.members; VECEACH(members, i) { @@ -307,7 +307,7 @@ static inline bool x86_can_expand_indirect_aggregate_arg(Type *type) break; case TYPE_COMPLEX: { - size_t complex_type_size = type_size(member_type->complex); + ByteSize complex_type_size = type_size(member_type->complex); if (complex_type_size != 4 && complex_type_size != 8) return false; size += type_size(member_type); break; diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 251dc09d1..75f65231a 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -13,6 +13,7 @@ static inline void llvm_emit_pre_inc_dec(GenContext *c, BEValue *value, Expr *ex static inline void llvm_emit_inc_dec_change(GenContext *c, bool use_mod, BEValue *addr, BEValue *after, BEValue *before, Expr *expr, int diff); static void llvm_emit_post_unary_expr(GenContext *context, BEValue *be_value, Expr *expr); static inline LLVMValueRef llvm_emit_subscript_addr_with_base_new(GenContext *c, BEValue *parent, BEValue *index); +static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, uint64_t offset, DesignatorElement** current, DesignatorElement **last, Expr *expr, BEValue *emitted_value); LLVMValueRef llvm_emit_is_no_error(GenContext *c, LLVMValueRef error) { @@ -20,6 +21,10 @@ LLVMValueRef llvm_emit_is_no_error(GenContext *c, LLVMValueRef error) return LLVMBuildICmp(c->builder, LLVMIntEQ, domain, llvm_get_zero(c, type_usize), "noerr"); } +LLVMValueRef llvm_const_padding(GenContext *c, ByteSize size) +{ + return LLVMGetUndef(LLVMArrayType(llvm_get_type(c, type_byte), size)); +} static inline LLVMValueRef gencontext_emit_add_int(GenContext *context, Type *type, bool use_mod, LLVMValueRef left, LLVMValueRef right) { @@ -55,8 +60,8 @@ static inline LLVMValueRef gencontext_emit_add_int(GenContext *context, Type *ty LLVMValueRef llvm_emit_coerce(GenContext *context, LLVMTypeRef coerced, BEValue *value, Type *original_type) { LLVMValueRef cast; - unsigned target_alignment = llvm_abi_alignment(coerced); - unsigned max_align = MAX(((unsigned)value->alignment), llvm_abi_alignment(coerced)); + AlignSize target_alignment = llvm_abi_alignment(coerced); + AlignSize max_align = MAX(value->alignment, llvm_abi_alignment(coerced)); // If we are loading something with greater alignment than what we have, we cannot directly memcpy. if (llvm_value_is_addr(value) && value->alignment < target_alignment) @@ -143,7 +148,7 @@ static inline void gencontext_emit_subscript_addr_base(GenContext *context, BEVa assert(value->kind == BE_ADDRESS); LLVMValueRef pointer_addr = LLVMBuildStructGEP2(context->builder, subarray_type, value->value, 0, "subarray_ptr"); LLVMTypeRef pointer_type = llvm_get_type(context, type_get_ptr(type->array.base)); - unsigned alignment = type_abi_alignment(type_voidptr); + AlignSize alignment = type_abi_alignment(type_voidptr); // We need to pick the worst alignment in case this is packed in an array. if (value->alignment < alignment) alignment = value->alignment; llvm_value_set_address_align(value, @@ -342,7 +347,7 @@ static void gencontext_emit_scoped_expr(GenContext *context, BEValue *value, Exp } -static inline LLVMValueRef llvm_emit_initializer_list_expr_addr(GenContext *c, Expr *expr, LLVMValueRef optional_ref); +static inline void llvm_emit_initialize_reference(GenContext *c, BEValue *value, Expr *expr); static inline void gencontext_emit_identifier(GenContext *c, BEValue *value, Decl *decl) { @@ -369,7 +374,7 @@ static void gencontext_emit_arr_to_subarray_cast(GenContext *c, BEValue *value, { llvm_value_rvalue(c, value); printf("TODO optimize subarray cast\n"); - size_t size = from_type->pointer->array.len; + ByteSize size = from_type->pointer->array.len; LLVMTypeRef subarray_type = llvm_get_type(c, to_type); LLVMValueRef result = LLVMGetUndef(subarray_type); value->value = llvm_emit_bitcast(c, value->value, type_get_ptr(from_type->pointer->array.base)); @@ -407,9 +412,12 @@ static LLVMValueRef gencontext_emit_cast_inner(GenContext *c, CastKind cast_kind gencontext_emit_arr_to_subarray_cast(c, value, to_type, from_type); return value->value; case CAST_SAPTR: - if (value->kind == BE_ADDRESS) + if (llvm_value_is_addr(value)) { - LLVMBuildStructGEP2(c->builder, llvm_get_ptr_type(c, from_type), value->value, 0, ""); + llvm_value_fold_failable(c, value); + return llvm_emit_load_aligned(c, llvm_get_type(c, to_type), + LLVMBuildStructGEP(c->builder, value->value, 0, ""), + value->alignment, ""); } return LLVMBuildExtractValue(c->builder, value->value, 0, ""); case CAST_VARRPTR: @@ -516,19 +524,13 @@ static inline void gencontext_emit_cast_expr(GenContext *context, BEValue *be_va expr->cast_expr.expr->type->canonical); } -static void gencontext_emit_initializer_list_expr_const(GenContext *context, BEValue *be_value, Expr *expr); +static LLVMValueRef llvm_emit_initializer_list_expr_const(GenContext *c, Expr *expr); static void gencontext_construct_const_value(GenContext *context, BEValue *be_value, Expr *expr) { - NESTED_RETRY: - if (expr->expr_kind == EXPR_COMPOUND_LITERAL) - { - expr = expr->expr_compound_literal.initializer; - goto NESTED_RETRY; - } if (expr->expr_kind == EXPR_INITIALIZER_LIST) { - gencontext_emit_initializer_list_expr_const(context, be_value, expr); + llvm_emit_initializer_list_expr_const(context, expr); return; } llvm_emit_expr(context, be_value, expr); @@ -549,73 +551,76 @@ static void gencontext_construct_const_union_struct(GenContext *context, BEValue LLVMConstStructInContext(context->context, values, size_diff > 0 ? 2 : 1, false), canonical); } -static void gencontext_recursive_set_const_value(GenContext *context, BEValue *be_value, DesignatedPath *path, LLVMValueRef parent, Type *parent_type, Expr *value) + +static LLVMValueRef llvm_recursive_set_value(GenContext *c, DesignatorElement **current_element_ptr, LLVMValueRef parent, DesignatorElement **last_element_ptr, Expr *value) { - switch (path->kind) + DesignatorElement *current_element = current_element_ptr[0]; + if (current_element_ptr == last_element_ptr) { - case DESIGNATED_IDENT: - if (!path->sub_path) - { - if (parent_type->canonical->type_kind == TYPE_UNION) - { - gencontext_construct_const_union_struct(context, be_value, parent_type, value); - return; - } - gencontext_construct_const_value(context, be_value, value); - llvm_value_set(be_value, - LLVMConstInsertValue(parent, llvm_value_rvalue_store(context, be_value), &path->index, 1), - parent_type); - return; - } - else - { - parent_type = path->type; - gencontext_recursive_set_const_value(context, be_value, path->sub_path, - LLVMConstExtractValue(parent, &path->index, 1), parent_type, - value); - llvm_value_set(be_value, - LLVMConstInsertValue(parent, llvm_value_rvalue_store(context, be_value), &path->index, 1), - parent_type); - return; - } - case DESIGNATED_SUBSCRIPT: + BEValue res; + llvm_emit_expr(c, &res, value); + unsigned index = current_element->index; + LLVMValueRef val = llvm_value_rvalue_store(c, &res); + switch (current_element->kind) { - // TODO range, more arrays - assert(path->index_expr->expr_kind == EXPR_CONST); - unsigned int index = (unsigned int)bigint_as_unsigned(&path->index_expr->const_expr.i); - if (!path->sub_path) - { - gencontext_construct_const_value(context, be_value, value); - llvm_value_set(be_value, - LLVMConstInsertValue(parent, llvm_value_rvalue_store(context, be_value), &index, 1), - parent_type); - return ; - } - parent_type = path->type; - gencontext_recursive_set_const_value(context, be_value, path->sub_path, - LLVMConstExtractValue(parent, &index, 1), parent_type, - value); - llvm_value_set(be_value, - LLVMConstInsertValue(parent, llvm_value_rvalue_store(context, be_value), &index, 1), - parent_type); - return; + case DESIGNATOR_FIELD: + return LLVMConstInsertValue(parent, val, &index, 1); + case DESIGNATOR_ARRAY: + return LLVMConstInsertElement(parent, val, llvm_const_int(c, type_isize, current_element->index)); + case DESIGNATOR_RANGE: + for (int64_t i = current_element->index; i <= current_element->index_end; i++) + { + parent = LLVMConstInsertElement(parent, val, llvm_const_int(c, type_isize, i)); + } + return parent; } - default: - UNREACHABLE; - + UNREACHABLE } - + LLVMValueRef current_val; + switch (current_element->kind) + { + case DESIGNATOR_FIELD: + { + unsigned index = current_element->index; + current_val = LLVMConstExtractValue(parent, &index, 1); + current_val = llvm_recursive_set_value(c, current_element_ptr + 1, current_val, last_element_ptr, value); + return LLVMConstInsertValue(parent, current_val, &index, 1); + } + case DESIGNATOR_ARRAY: + { + LLVMValueRef index = llvm_const_int(c, type_isize, current_element->index); + current_val = LLVMConstExtractElement(parent, index); + current_val = llvm_recursive_set_value(c, current_element_ptr + 1, current_val, last_element_ptr, value); + return LLVMConstInsertElement(parent, current_val, index); + } + case DESIGNATOR_RANGE: + for (int64_t i = current_element->index; i <= current_element->index_end; i++) + { + LLVMValueRef index = llvm_const_int(c, type_isize, i); + current_val = LLVMConstExtractElement(parent, index); + current_val = llvm_recursive_set_value(c, current_element_ptr + 1, current_val, last_element_ptr, value); + parent = LLVMConstInsertElement(parent, current_val, index); + } + return parent; + default: + UNREACHABLE + } +} +static LLVMValueRef llvm_recursive_set_const_value(GenContext *context, DesignatorElement **path, LLVMValueRef value, Type *parent_type, Expr *assign) +{ + unsigned path_count = vec_size(path); + return llvm_recursive_set_value(context, path, value, path + (path_count - 1), assign); } -static void gencontext_emit_initializer_list_expr_const(GenContext *context, BEValue *be_value, Expr *expr) + +static LLVMValueRef llvm_emit_initializer_list_expr_const(GenContext *c, Expr *expr) { Type *canonical = expr->type->canonical; - LLVMTypeRef type = llvm_get_type(context, canonical); + LLVMTypeRef type = llvm_get_type(c, canonical); - if (expr->expr_initializer.init_type == INITIALIZER_ZERO) + if (expr->initializer_expr.init_type == INITIALIZER_CONST) { - llvm_value_set(be_value, LLVMConstNull(type), canonical); - return; + return LLVMConstNull(type); } bool is_error = expr->type->canonical->type_kind == TYPE_ERRTYPE; @@ -625,129 +630,378 @@ static void gencontext_emit_initializer_list_expr_const(GenContext *context, BEV TODO } - Expr **elements = expr->expr_initializer.initializer_expr; + Expr **elements = expr->initializer_expr.initializer_expr; bool is_union = expr->type->canonical->type_kind == TYPE_UNION; - if (expr->expr_initializer.init_type == INITIALIZER_NORMAL && is_union) + if (expr->initializer_expr.init_type == INITIALIZER_NORMAL && is_union) { assert(vec_size(elements) == 1); - gencontext_construct_const_union_struct(context, be_value, canonical, elements[0]); - return; + + TODO + // gencontext_construct_const_union_struct(c, be_value, canonical, elements[0]); } - if (expr->expr_initializer.init_type == INITIALIZER_NORMAL) + if (expr->initializer_expr.init_type == INITIALIZER_NORMAL) { LLVMValueRef value = LLVMGetUndef(type); VECEACH(elements, i) { Expr *element = elements[i]; - if (element->expr_kind == EXPR_CONST) + if (element->expr_kind == EXPR_INITIALIZER_LIST) { - + value = LLVMConstInsertValue(value, llvm_emit_initializer_list_expr_const(c, element), &i, 1); + continue; } - llvm_emit_expr(context, be_value, element); - value = LLVMConstInsertValue(value, llvm_value_rvalue_store(context, be_value), &i, 1); + BEValue be_value; + llvm_emit_expr(c, &be_value, element); + value = LLVMConstInsertValue(value, llvm_value_rvalue_store(c, &be_value), &i, 1); } - llvm_value_set(be_value, value, expr->type); - return; + return value; } LLVMValueRef value = LLVMConstNull(type); VECEACH(elements, i) { Expr *element = elements[i]; - DesignatedPath *path = element->designated_init_expr.path; Type *parent_type = expr->type->canonical; - gencontext_recursive_set_const_value(context, - be_value, - path, - value, - parent_type, - element->designated_init_expr.value); - value = llvm_value_rvalue_store(context, be_value); + value = llvm_recursive_set_const_value(c, + element->designator_expr.path, + value, + parent_type, + element->designator_expr.value); + } + return value; +} + +static inline void llvm_emit_initialize_reference_small_const(GenContext *c, BEValue *ref, Expr *expr) +{ + // First create the constant value. + LLVMValueRef value = llvm_emit_initializer_list_expr_const(c, expr); + + // Create a global const. + LLVMTypeRef type = llvm_get_type(c, expr->type); + LLVMValueRef global_copy = LLVMAddGlobal(c->module, type, ""); + + // Set a nice alignment + unsigned alignment = LLVMPreferredAlignmentOfGlobal(target_data_layout(), global_copy); + LLVMSetAlignment(global_copy, alignment); + + // Set the value and make it constant + LLVMSetInitializer(global_copy, value); + LLVMSetGlobalConstant(global_copy, true); + + // Ensure we have a reference. + llvm_value_addr(c, ref); + + // Perform the memcpy. + llvm_emit_memcpy(c, ref->value, ref->alignment, global_copy, alignment, type_size(expr->type)); +} + +static inline void llvm_emit_initialize_reference_list(GenContext *c, BEValue *ref, Expr *expr) +{ + // Getting ready to initialize, get the real type. + Type *real_type = ref->type->canonical; + Expr **elements = expr->initializer_expr.initializer_expr; + + // Make sure we have an address. + llvm_value_addr(c, ref); + LLVMValueRef value = ref->value; + + // If this is a union, we assume it's initializing the first element. + if (real_type->type_kind == TYPE_UNION) + { + assert(vec_size(elements) == 1); + real_type = type_lowering(real_type->decl->strukt.members[0]->type); + value = LLVMBuildBitCast(c->builder, ref->value, llvm_get_ptr_type(c, real_type), ""); + } + + LLVMTypeRef llvm_type = llvm_get_type(c, real_type); + bool is_struct = type_is_structlike(real_type); + bool is_array = real_type->type_kind == TYPE_ARRAY; + + // Now walk through the elements. + VECEACH(elements, i) + { + Expr *element = elements[i]; + BEValue init_value; + if (element->expr_kind == EXPR_COMPOUND_LITERAL) + { + element = element->expr_compound_literal.initializer; + } + unsigned offset = 0; + LLVMValueRef pointer; + if (is_struct) + { + Decl *member = real_type->decl->strukt.members[i]; + offset = member->offset; + pointer = LLVMBuildStructGEP2(c->builder, llvm_type, value, i, ""); + } + else if (is_array) + { + // Todo optimize + offset = i * type_size(element->type); + LLVMValueRef indices[2] = { 0, llvm_const_int(c, type_uint, i) }; + pointer = LLVMBuildInBoundsGEP2(c->builder, llvm_type, value, indices, 2, ""); + } + else + { + pointer = value; + } + unsigned alignment = aligned_offset(offset, ref->alignment); + // If this is an initializer, we want to actually run the initialization recursively. + if (element->expr_kind == EXPR_INITIALIZER_LIST) + { + llvm_value_set_address_align(&init_value, pointer, element->type, alignment); + llvm_emit_initialize_reference(c, &init_value, element); + continue; + } + llvm_emit_expr(c, &init_value, element); + llvm_store_aligned(c, pointer, llvm_value_rvalue_store(c, &init_value), alignment); + } +} + +static void llvm_emit_initialize_designated_const_range(GenContext *c, BEValue *ref, uint64_t offset, DesignatorElement** current, DesignatorElement **last, Expr *expr, BEValue *emitted_value) +{ + DesignatorElement *curr = current[0]; + llvm_value_addr(c, ref); + + assert(curr->kind == DESIGNATOR_RANGE); + + BEValue emitted_local; + if (!emitted_value) + { + llvm_emit_expr(c, &emitted_local, expr); + emitted_value = &emitted_local; + } + LLVMTypeRef ref_type = llvm_get_type(c, ref->type); + // Assign the index_ptr to the start value. + LLVMValueRef indices[2] = { + llvm_get_zero(c, curr->index_expr->type), + NULL + }; + for (unsigned i = curr->index; i <= curr->index_end; i++) + { + indices[1] = llvm_const_int(c, curr->index_expr->type, i); + BEValue new_ref; + LLVMValueRef ptr = LLVMBuildInBoundsGEP2(c->builder, ref_type, ref->value, indices, 2, ""); + llvm_value_set_address(&new_ref, ptr, type_get_indexed_type(ref->type)); + llvm_emit_initialize_designated(c, &new_ref, offset, current + 1, last, expr, emitted_value); + } +} + +static void llvm_emit_initialize_designated_range(GenContext *c, BEValue *ref, uint64_t offset, DesignatorElement** current, DesignatorElement **last, Expr *expr, BEValue *emitted_value) +{ + DesignatorElement *curr = current[0]; + llvm_value_addr(c, ref); + assert(curr->kind == DESIGNATOR_RANGE); + + BEValue emitted_local; + if (!emitted_value) + { + llvm_emit_expr(c, &emitted_local, expr); + emitted_value = &emitted_local; + } + LLVMBasicBlockRef start_block = llvm_basic_block_new(c, "set_start"); + LLVMBasicBlockRef store_block = llvm_basic_block_new(c, "set_store"); + LLVMBasicBlockRef after_block = llvm_basic_block_new(c, "set_done"); + + BEValue start; + BEValue end; + llvm_emit_expr(c, &start, curr->index_expr); + LLVMValueRef max_index = llvm_const_int(c, start.type, ref->type->canonical->array.len); + llvm_emit_array_bounds_check(c, &start, max_index); + llvm_emit_expr(c, &end, curr->index_end_expr); + llvm_emit_array_bounds_check(c, &end, max_index); + LLVMTypeRef ref_type = llvm_get_type(c, ref->type); + LLVMValueRef end_value = llvm_value_rvalue_store(c, &end); + LLVMTypeRef index_type = llvm_get_type(c, start.type); + LLVMValueRef index_ptr = llvm_emit_alloca(c, index_type, start.alignment, "rangeidx"); + LLVMValueRef add_one = llvm_const_int(c, start.type, 1); + // Assign the index_ptr to the start value. + llvm_store_bevalue_dest_aligned(c, index_ptr, &start); + LLVMValueRef indices[2] = { + llvm_get_zero(c, start.type), + NULL + }; + llvm_emit_br(c, start_block); + llvm_emit_block(c, start_block); + LLVMValueRef index_val = llvm_emit_load_aligned(c, index_type, index_ptr, start.alignment, ""); + BEValue comp; + llvm_value_set_bool(&comp, llvm_emit_int_comparison(c, start.type, end.type, index_val, end_value, BINARYOP_LE)); + llvm_emit_cond_br(c, &comp, store_block, after_block); + llvm_emit_block(c, store_block); + indices[1] = index_val; + LLVMValueRef ptr_next = LLVMBuildInBoundsGEP2(c->builder, ref_type, ref->value, indices, 2, ""); + BEValue new_ref; + llvm_value_set_address(&new_ref, ptr_next, type_get_indexed_type(ref->type)); + llvm_emit_initialize_designated(c, &new_ref, offset, current + 1, last, expr, emitted_value); + LLVMValueRef new_index = LLVMBuildAdd(c->builder, index_val, add_one, ""); + llvm_store_aligned(c, index_ptr, new_index, start.alignment); + llvm_emit_br(c, start_block); + llvm_emit_block(c, after_block); +} +static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, uint64_t offset, DesignatorElement** current, + DesignatorElement **last, Expr *expr, BEValue *emitted_value) +{ + BEValue value; + if (current > last) + { + if (emitted_value) + { + llvm_store_bevalue(c, ref, emitted_value); + return; + } + if (expr->expr_kind == EXPR_INITIALIZER_LIST) + { + llvm_emit_initialize_reference(c, ref, expr); + return; + } + BEValue val; + llvm_emit_expr(c, &val, expr); + llvm_store_bevalue(c, ref, &val); + return; + } + DesignatorElement *curr = current[0]; + switch (curr->kind) + { + case DESIGNATOR_FIELD: + { + Decl *decl = ref->type->canonical->decl->strukt.members[curr->index]; + offset += decl->offset; + Type *type = decl->type->canonical; + unsigned decl_alignment = decl_abi_alignment(decl); + if (ref->type->type_kind == TYPE_UNION) + { + llvm_value_set_address_align(&value, llvm_emit_bitcast(c, ref->value, type_get_ptr(type)), type, aligned_offset(offset, decl_alignment)); + } + else + { + unsigned alignment = 1; + LLVMValueRef ptr = llvm_emit_struct_gep(c, ref->value, llvm_get_type(c, ref->type), curr->index, decl_alignment, offset, &alignment); + llvm_value_set_address_align(&value, ptr, type, alignment); + } + llvm_emit_initialize_designated(c, &value, offset, current + 1, last, expr, emitted_value); + break; + } + case DESIGNATOR_ARRAY: + { + Type *type = ref->type->canonical->array.base; + LLVMValueRef indices[2]; + if (curr->index_expr->expr_kind == EXPR_CONST) + { + offset += curr->index * type_size(type); + Type *index_type = curr->index > UINT32_MAX ? type_uint : type_ulong; + indices[0] = llvm_const_int(c, index_type, 0); + indices[1] = llvm_const_int(c, index_type, curr->index); + } + else + { + BEValue res; + llvm_emit_expr(c, &res, curr->index_expr); + indices[0] = llvm_const_int(c, curr->index_expr->type, 0); + indices[1] = llvm_value_rvalue_store(c, &res); + } + LLVMValueRef ptr = LLVMBuildInBoundsGEP2(c->builder, llvm_get_type(c, ref->type), ref->value, indices, 2, ""); + llvm_value_set_address_align(&value, ptr, type, aligned_offset(offset, type_abi_alignment(type))); + llvm_emit_initialize_designated(c, &value, offset, current + 1, last, expr, emitted_value); + break; + } + case DESIGNATOR_RANGE: + if (curr->index_expr->expr_kind == EXPR_CONST && curr->index_end_expr->expr_kind == EXPR_CONST) + { + llvm_emit_initialize_designated_const_range(c, ref, offset, current, last, expr, emitted_value); + } + else + { + llvm_emit_initialize_designated_range(c, ref, offset, current, last, expr, emitted_value); + } + break; + default: + UNREACHABLE + } +} + +static inline void llvm_emit_initialize_reference_designated(GenContext *c, BEValue *ref, Expr *expr) +{ + // Getting ready to initialize, get the real type. + Type *real_type = ref->type->canonical; + Expr **elements = expr->initializer_expr.initializer_expr; + + // Make sure we have an address. + llvm_value_addr(c, ref); + + // Clear the memory + llvm_emit_memclear(c, ref); + + LLVMValueRef value = ref->value; + + // Now walk through the elements. + VECEACH(elements, i) + { + Expr *designator = elements[i]; + DesignatorElement **last_element = designator->designator_expr.path + vec_size(designator->designator_expr.path) - 1; + llvm_emit_initialize_designated(c, ref, 0, designator->designator_expr.path, last_element, designator->designator_expr.value, NULL); } - llvm_value_set(be_value, value, expr->type); } /** - * Emit a Foo { .... } literal. + * Initialize an aggregate type. * - * Improve: Direct assign in the case where this is assigning to a variable. - * Improve: Create constant initializer for the constant case and do a memcopy + * There are three methods: + * 1. Create a constant and store it in a global, followed by a memcopy from this global. + * this is what Clang does for elements up to 4 pointers wide. + * 2. For empty elements, we do a memclear. + * 3. For the rest use GEP into the appropriate elements. */ -static inline LLVMValueRef llvm_emit_initializer_list_expr_addr(GenContext *c, Expr *expr, LLVMValueRef optional_ref) +static inline void llvm_emit_initialize_reference(GenContext *c, BEValue *ref, Expr *expr) { - if (expr->constant && type_size(expr->type) <= type_size(type_voidptr) * 4) + // In the case of a const, we have some optimizations: + if (expr->constant) { - LLVMTypeRef type = llvm_get_type(c, expr->type); - BEValue be_value; - gencontext_emit_initializer_list_expr_const(c, &be_value, expr); - LLVMValueRef ref = LLVMAddGlobal(c->module, type, ""); - LLVMSetInitializer(ref, llvm_value_rvalue_store(c, &be_value)); - LLVMSetGlobalConstant(ref, true); - if (optional_ref) + ConstInitializer *initializer = expr->initializer_expr.initializer; + if (initializer->kind == CONST_INIT_ZERO) { - LLVMBuildMemCpy(c->builder, optional_ref, LLVMGetAlignment(optional_ref), ref, LLVMGetAlignment(ref), LLVMSizeOf(type)); + // In case of a zero, optimize. + llvm_emit_memclear(c, ref); + return; } - return ref; - } - LLVMTypeRef type = llvm_get_type(c, expr->type); - LLVMValueRef ref = optional_ref ?: llvm_emit_alloca_aligned(c, expr->type, "literal"); - - Type *canonical = expr->type->canonical; - if (expr->expr_initializer.init_type != INITIALIZER_NORMAL) - { - llvm_emit_memclear(c, ref, canonical); - } - - bool is_error = expr->type->canonical->type_kind == TYPE_ERRTYPE; - - if (is_error) - { - LLVMValueRef err_type = LLVMBuildStructGEP2(c->builder, type, ref, 0, ""); - LLVMBuildStore(c->builder, expr->type->canonical->backend_typeid, err_type); - } - - if (expr->expr_initializer.init_type == INITIALIZER_ZERO) - { - return ref; - } - - Expr **elements = expr->expr_initializer.initializer_expr; - - bool is_union = expr->type->canonical->type_kind == TYPE_UNION; - - if (expr->expr_initializer.init_type == INITIALIZER_NORMAL) - { - if (is_union) + // In case of small const initializers, use copy. + if (type_size(expr->type) <= 1000 * type_size(type_voidptr) * 4) { - assert(vec_size(elements) == 1); - BEValue init_value; - llvm_emit_expr(c, &init_value, elements[0]); - LLVMValueRef u = LLVMBuildBitCast(c->builder, ref, llvm_get_ptr_type(c, elements[0]->type->canonical), ""); - LLVMBuildStore(c->builder, llvm_value_rvalue_store(c, &init_value), u); - return ref; + llvm_emit_initialize_reference_small_const(c, ref, expr); + return; } - VECEACH(elements, i) - { - Expr *element = elements[i]; - BEValue init_value; - llvm_emit_expr(c, &init_value, element); - LLVMValueRef subref = LLVMBuildStructGEP2(c->builder, type, ref, i + (int)is_error, ""); - llvm_store_self_aligned(c, subref, llvm_value_rvalue_store(c, &init_value), element->type); - } - return ref; + } + + switch (expr->initializer_expr.init_type) + { + case INITIALIZER_CONST: + // Just clear memory + llvm_emit_memclear(c, ref); + break; + case INITIALIZER_NORMAL: + llvm_emit_initialize_reference_list(c, ref, expr); + break; + case INITIALIZER_DESIGNATED: + llvm_emit_initialize_reference_designated(c, ref, expr); + break; + default: + UNREACHABLE } + return; + TODO +/* VECEACH(elements, i) { if (is_error) TODO - Expr *element = elements[i]; - DesignatedPath *path = element->designated_init_expr.path; + Expr *field = elements[i]; + + DesignatedPath *path = field->designated_init_expr.path; BEValue sub_value; - llvm_emit_expr(c, &sub_value, element->designated_init_expr.value); + llvm_emit_expr(c, &sub_value, field->designated_init_expr.value); LLVMValueRef sub_ref = ref; Type *parent_type = expr->type->canonical; while (path) @@ -789,6 +1043,7 @@ static inline LLVMValueRef llvm_emit_initializer_list_expr_addr(GenContext *c, E LLVMBuildStore(c->builder, llvm_value_rvalue_store(c, &sub_value), sub_ref); } return ref; + */ } static inline void llvm_emit_inc_dec_change(GenContext *c, bool use_mod, BEValue *addr, BEValue *after, BEValue *before, Expr *expr, int diff) @@ -1078,11 +1333,12 @@ gencontext_emit_slice_values(GenContext *context, Expr *slice, Type **parent_typ // Check that index does not extend beyond the length. if (parent_type->type_kind != TYPE_POINTER && build_options.debug_mode) { + LLVMValueRef exceeds_size = llvm_emit_int_comparison(context, type_usize, start_type, - len, start_index.value, + len, BINARYOP_GE); llvm_emit_panic_on_true(context, exceeds_size, "Index exceeds array length."); } @@ -1107,6 +1363,7 @@ gencontext_emit_slice_values(GenContext *context, Expr *slice, Type **parent_typ if (slice->slice_expr.end_from_back) { end_index.value = gencontext_emit_sub_int(context, end_type, false, len, end_index.value); + llvm_value_rvalue(context, &end_index); } // This will trap any bad negative index, so we're fine. @@ -1116,7 +1373,7 @@ gencontext_emit_slice_values(GenContext *context, Expr *slice, Type **parent_typ start_type, end_type, start_index.value, - *end_index_ref, + end_index.value, BINARYOP_GT); llvm_emit_panic_on_true(context, excess, "Negative size"); @@ -1135,8 +1392,8 @@ gencontext_emit_slice_values(GenContext *context, Expr *slice, Type **parent_typ else { assert(len && "Pointer should never end up here."); - // Otherwise everything is fine and dandy. Our len is our end index. - end_index.value = len; + // Otherwise everything is fine and dandy. Our len - 1 is our end index. + end_index.value = LLVMBuildSub(context->builder, len, LLVMConstInt(LLVMTypeOf(len), 1, false), ""); end_type = type_usize; } @@ -1163,7 +1420,7 @@ static void gencontext_emit_slice(GenContext *context, BEValue *be_value, Expr * // Calculate the size - LLVMValueRef size = LLVMBuildSub(context->builder, end_index, start_index, "size"); + LLVMValueRef size = LLVMBuildSub(context->builder, LLVMBuildAdd(context->builder, end_index, llvm_const_int(context, start_type, 1), ""), start_index, "size"); LLVMValueRef start_pointer; switch (parent_type->type_kind) @@ -1182,6 +1439,12 @@ static void gencontext_emit_slice(GenContext *context, BEValue *be_value, Expr * start_pointer = LLVMBuildInBoundsGEP(context->builder, parent_base, &start_index, 1, "offsetsub"); break; } + case TYPE_POINTER: + { + // Move pointer + start_pointer = LLVMBuildInBoundsGEP2(context->builder, llvm_get_type(context, parent_type->pointer), parent_base, &start_index, 1, "offset"); + break; + } default: TODO } @@ -2045,7 +2308,7 @@ static void gencontext_expand_array_to_args(GenContext *c, Type *param_type, LLV LLVMTypeRef element_type = llvm_get_type(c, param_type->array.base); LLVMValueRef zero = llvm_get_zero(c, type_int); LLVMValueRef indices[2] = { zero, zero, }; - for (size_t i = 0; i < param_type->array.len; i++) + for (ByteSize i = 0; i < param_type->array.len; i++) { indices[1] = llvm_const_int(c, type_int, i); LLVMValueRef element_ptr = LLVMBuildGEP2(c->builder, element_type, expand_ptr, indices, 2, ""); @@ -2221,8 +2484,8 @@ void llvm_emit_parameter(GenContext *context, LLVMValueRef **args, ABIArgInfo *i return; } LLVMValueRef cast; - unsigned target_alignment = llvm_abi_alignment(coerce_type); - unsigned max_align = MAX(((unsigned)be_value->alignment), llvm_abi_alignment(coerce_type)); + AlignSize target_alignment = llvm_abi_alignment(coerce_type); + AlignSize max_align = MAX((be_value->alignment), llvm_abi_alignment(coerce_type)); // If we are loading something with greater alignment than what we have, we cannot directly memcpy. if (llvm_value_is_addr(be_value) && be_value->alignment < target_alignment) @@ -2433,7 +2696,7 @@ void gencontext_emit_call_expr(GenContext *context, BEValue *be_value, Expr *exp type_abi_alignment(return_type), ret_info->coerce_expand.offset_lo, &alignment); - // If there is only a single element, we simply store the value. + // If there is only a single field, we simply store the value. if (!ret_info->coerce_expand.hi) { llvm_store_aligned(context, lo, call, alignment); @@ -2654,9 +2917,20 @@ LLVMValueRef llvm_emit_assign_expr(GenContext *c, LLVMValueRef ref, Expr *expr, } } BEValue value; - llvm_emit_expr(c, &value, expr); - printf("TODO: // Optimize store \n"); - llvm_store_bevalue_aligned(c, ref, &value, 0); + if (expr->expr_kind == EXPR_COMPOUND_LITERAL) expr = expr->expr_compound_literal.initializer; + if (expr->expr_kind == EXPR_INITIALIZER_LIST) + { + BEValue val; + printf("TODO: Fix alignment and return!\n"); + llvm_value_set_address(&val, ref, expr->type); + llvm_emit_initialize_reference(c, &val, expr); + } + else + { + llvm_emit_expr(c, &value, expr); + printf("TODO: // Optimize store \n"); + llvm_store_bevalue_aligned(c, ref, &value, 0); + } if (failable) { @@ -2697,12 +2971,18 @@ 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 void llvm_emit_initializer_list_expr(GenContext *c, BEValue *value, Expr *expr) +{ + llvm_value_set_address(value, llvm_emit_alloca_aligned(c, expr->type, "literal"), expr->type); + llvm_emit_initialize_reference(c, value, expr); +} + void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) { EMIT_LOC(c, expr); -NESTED_RETRY: switch (expr->expr_kind) { + case EXPR_DESIGNATOR: case EXPR_MEMBER_ACCESS: case EXPR_POISONED: case EXPR_DECL_LIST: @@ -2716,9 +2996,6 @@ NESTED_RETRY: case EXPR_UNDEF: // Should never reach this. UNREACHABLE - case EXPR_DESIGNATED_INITIALIZER: - // Should only appear when generating designated initializers. - UNREACHABLE case EXPR_SLICE_ASSIGN: gencontext_emit_slice_assign(c, value, expr); return; @@ -2742,12 +3019,9 @@ NESTED_RETRY: gencontext_emit_macro_block(c, value, expr); return; case EXPR_COMPOUND_LITERAL: - expr = expr->expr_compound_literal.initializer; - goto NESTED_RETRY; + UNREACHABLE case EXPR_INITIALIZER_LIST: - llvm_value_set_address(value, - llvm_emit_initializer_list_expr_addr(c, expr, NULL), - expr->type); + llvm_emit_initializer_list_expr(c, value, expr); return; case EXPR_EXPR_BLOCK: gencontext_emit_expr_block(c, value, expr); diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index c845a6146..f9a2984d9 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -156,18 +156,18 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, unsig LLVMTypeRef lo = llvm_abi_type(c, info->direct_pair.lo); LLVMTypeRef hi = llvm_abi_type(c, info->direct_pair.hi); LLVMTypeRef struct_type = llvm_get_twostruct(c, lo, hi); - unsigned decl_alignment = decl_abi_alignment(decl); + AlignSize decl_alignment = decl_abi_alignment(decl); // Cast to { lo, hi } LLVMValueRef cast = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(struct_type, 0), "pair"); // Point to the lo value. LLVMValueRef lo_ptr = LLVMBuildStructGEP2(c->builder, struct_type, cast, 0, "lo"); // Store it in the struct. - unsigned lo_alignment = MIN(llvm_abi_alignment(lo), decl_alignment); + AlignSize lo_alignment = MIN(llvm_abi_alignment(lo), decl_alignment); llvm_store_aligned(c, lo_ptr, llvm_get_next_param(c, index), lo_alignment); // Point to the hi value. LLVMValueRef hi_ptr = LLVMBuildStructGEP2(c->builder, struct_type, cast, 1, "hi"); // Store it in the struct. - unsigned hi_alignment = MIN(llvm_abi_alignment(hi), decl_alignment); + AlignSize hi_alignment = MIN(llvm_abi_alignment(hi), decl_alignment); llvm_store_aligned(c, hi_ptr, llvm_get_next_param(c, index), decl_alignment); return; } @@ -181,7 +181,7 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, unsig } // Cast to the coerce type. LLVMValueRef cast = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(coerce_type, 0), "coerce"); - unsigned decl_alignment = decl_abi_alignment(decl); + AlignSize decl_alignment = decl_abi_alignment(decl); // If we're not flattening, we simply do a store. if (!abi_info_should_flatten(info)) @@ -302,7 +302,7 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl LLVMTypeRef lo_type = llvm_abi_type(c, info->coerce_expand.hi); lo_val = llvm_emit_load_aligned(c, lo_type, lo, alignment, ""); - // We're done if there's a single element. + // We're done if there's a single field. if (!info->coerce_expand.hi) { llvm_emit_return_value(c, lo_val); diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 4020c5f88..8ffec04ef 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -32,7 +32,7 @@ typedef enum typedef struct { BackendValueKind kind : 5; - unsigned alignment : 16; + AlignSize alignment; Type *type; LLVMValueRef value; LLVMValueRef failable; @@ -192,7 +192,7 @@ LLVMValueRef llvm_value_rvalue_store(GenContext *c, BEValue *value); LLVMTypeRef llvm_abi_type(GenContext *c, AbiType *type); unsigned llvm_abi_size(LLVMTypeRef type); -unsigned llvm_abi_alignment(LLVMTypeRef type); +AlignSize llvm_abi_alignment(LLVMTypeRef type); void llvm_attribute_add_range(GenContext *c, LLVMValueRef value_to_add_attribute_to, unsigned attribute_id, int index_start, int index_end); void llvm_attribute_add(GenContext *c, LLVMValueRef value_to_add_attribute_to, unsigned attribute_id, int index); void llvm_attribute_add_string(GenContext *c, LLVMValueRef value_to_add_attribute_to, const char *attribute, const char *value, int index); @@ -213,6 +213,7 @@ void llvm_emit_function_body(GenContext *context, Decl *decl); void llvm_emit_function_decl(GenContext *c, Decl *decl); LLVMValueRef llvm_emit_call_intrinsic(GenContext *c, unsigned intrinsic_id, LLVMTypeRef *types, unsigned type_count, LLVMValueRef *values, unsigned arg_count); void llvm_emit_cast(GenContext *c, CastKind cast_kind, BEValue *value, Type *to_type, Type *from_type); +LLVMValueRef llvm_const_padding(GenContext *c, ByteSize size); void llvm_emit_cond_br(GenContext *context, BEValue *value, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block); void llvm_emit_debug_function(GenContext *c, Decl *decl); void llvm_emit_debug_location(GenContext *context, SourceSpan location); @@ -225,7 +226,8 @@ LLVMValueRef llvm_emit_is_no_error(GenContext *c, LLVMValueRef error); LLVMValueRef llvm_emit_load_aligned(GenContext *c, LLVMTypeRef type, LLVMValueRef pointer, unsigned alignment, const char *name); void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr); LLVMValueRef llvm_emit_memclear_size_align(GenContext *c, LLVMValueRef ref, uint64_t size, unsigned align, bool bitcast); -LLVMValueRef llvm_emit_memclear(GenContext *c, LLVMValueRef ref, Type *type); +LLVMValueRef llvm_emit_memclear(GenContext *c, BEValue *ref); +void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLVMValueRef source, unsigned src_align, uint64_t len); void llvm_emit_memcpy_to_decl(GenContext *c, Decl *decl, LLVMValueRef source, unsigned source_alignment); void llvm_emit_stmt(GenContext *c, Ast *ast); static inline LLVMValueRef llvm_emit_store(GenContext *context, Decl *decl, LLVMValueRef value); @@ -233,7 +235,6 @@ void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *pani void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failable); void llvm_emit_return_implicit(GenContext *c); LLVMValueRef llvm_emit_struct_gep(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index, unsigned struct_alignment, unsigned offset, unsigned *alignment); - LLVMValueRef llvm_get_next_param(GenContext *context, unsigned *index); LLVMTypeRef llvm_get_coerce_type(GenContext *c, ABIArgInfo *arg_info); static inline LLVMBasicBlockRef llvm_get_current_block_if_in_use(GenContext *context); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 6e7ed1c67..d3d641624 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -34,20 +34,20 @@ void gencontext_emit_ct_compound_stmt(GenContext *context, Ast *ast) } -static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) +static LLVMValueRef llvm_emit_decl(GenContext *c, Ast *ast) { Decl *decl = ast->declare_stmt; - LLVMTypeRef alloc_type = llvm_get_type(context, type_lowering(decl->type)); - decl->backend_ref = llvm_emit_decl_alloca(context, decl); + LLVMTypeRef alloc_type = llvm_get_type(c, type_lowering(decl->type)); + decl->backend_ref = llvm_emit_decl_alloca(c, decl); if (decl->var.failable) { - decl->var.failable_ref = llvm_emit_alloca_aligned(context, type_error, decl->name); - LLVMBuildStore(context->builder, LLVMConstNull(llvm_get_type(context, type_error)), decl->var.failable_ref); + decl->var.failable_ref = llvm_emit_alloca_aligned(c, type_error, decl->name); + LLVMBuildStore(c->builder, LLVMConstNull(llvm_get_type(c, type_error)), decl->var.failable_ref); } - if (llvm_use_debug(context)) + if (llvm_use_debug(c)) { - llvm_emit_debug_local_var(context, decl); + llvm_emit_debug_local_var(c, decl); } Expr *init = decl->var.init_expr; if (init) @@ -55,14 +55,21 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) // If we don't have undef, then make an assign. if (init->expr_kind != EXPR_UNDEF) { - llvm_emit_assign_expr(context, decl->backend_ref, decl->var.init_expr, decl->var.failable_ref); + llvm_emit_assign_expr(c, decl->backend_ref, decl->var.init_expr, decl->var.failable_ref); } // TODO trap on undef in debug mode. } else { // Normal case, zero init. - llvm_emit_store(context, decl, LLVMConstNull(alloc_type)); + if (type_is_builtin(decl->type->canonical->type_kind)) + { + llvm_emit_store(c, decl, LLVMConstNull(alloc_type)); + } + else + { + llvm_emit_memclear_size_align(c, decl->backend_ref, type_size(decl->type), decl_abi_alignment(decl), true); + } } return decl->backend_ref; } @@ -79,14 +86,14 @@ void llvm_emit_decl_expr_list_ignore_result(GenContext *context, Expr *expr) void gencontext_emit_decl_expr_list(GenContext *context, BEValue *be_value, Expr *expr, bool bool_cast) { assert(expr->expr_kind == EXPR_DECL_LIST); - size_t size = vec_size(expr->dexpr_list_expr); - size_t last_index = size - 1; - for (size_t i = 0; i < last_index; i++) + ByteSize size = vec_size(expr->dexpr_list_expr); + ByteSize last_index = size - 1; + for (ByteSize i = 0; i < last_index; i++) { Ast *ast = expr->dexpr_list_expr[i]; if (ast->ast_kind == AST_DECLARE_STMT) { - gencontext_emit_decl(context, ast); + llvm_emit_decl(context, ast); } else { @@ -106,7 +113,7 @@ void gencontext_emit_decl_expr_list(GenContext *context, BEValue *be_value, Expr case AST_DECLARE_STMT: type = last->declare_stmt->var.type_info->type; { - LLVMValueRef decl_value = gencontext_emit_decl(context, last); + LLVMValueRef decl_value = llvm_emit_decl(context, last); if (bool_cast && last->declare_stmt->var.unwrap) { llvm_value_set_bool(be_value, LLVMConstInt(context->bool_type, 1, false)); @@ -486,7 +493,7 @@ void gencontext_emit_do_stmt(GenContext *c, Ast *ast) void gencontext_emit_if_switch_body(GenContext *context, LLVMValueRef switch_value, Ast **cases) { - size_t case_count = vec_size(cases); + ByteSize case_count = vec_size(cases); if (!case_count) { // No body or default is empty, just exit after the value. @@ -569,7 +576,7 @@ void gencontext_emit_if_switch_body(GenContext *context, LLVMValueRef switch_val static void gencontext_emit_switch_body(GenContext *context, BEValue *switch_value, Ast *switch_ast) { Ast **cases = switch_ast->switch_stmt.cases; - size_t case_count = vec_size(cases); + ByteSize case_count = vec_size(cases); if (!case_count) { // No body or default is empty, just exit after the value. @@ -798,7 +805,7 @@ void gencontext_emit_try_stmt(GenContext *c, Ast *ast) llvm_value_rvalue(c, &be_value); break; case AST_DECLARE_STMT: - gencontext_emit_decl(c, dexpr); + llvm_emit_decl(c, dexpr); break; default: UNREACHABLE @@ -976,17 +983,33 @@ void gencontext_emit_catch_stmt(GenContext *c, Ast *ast) llvm_emit_block(c, after_catch); } -void llvm_emit_panic_on_true(GenContext *context, LLVMValueRef value, const char *panic_name) +void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *panic_name) { - LLVMBasicBlockRef panic_block = llvm_basic_block_new(context, "panic"); - LLVMBasicBlockRef ok_block = llvm_basic_block_new(context, "checkok"); + LLVMBasicBlockRef panic_block = llvm_basic_block_new(c, "panic"); + LLVMBasicBlockRef ok_block = llvm_basic_block_new(c, "checkok"); BEValue be_value; llvm_value_set_bool(&be_value, value); - llvm_emit_cond_br(context, &be_value, panic_block, ok_block); - llvm_emit_block(context, panic_block); - llvm_emit_call_intrinsic(context, intrinsic_id_trap, NULL, 0, NULL, 0); - llvm_emit_br(context, ok_block); - llvm_emit_block(context, ok_block); + llvm_emit_cond_br(c, &be_value, panic_block, ok_block); + llvm_emit_block(c, panic_block); + LLVMTypeRef char_ptr_type = llvm_get_ptr_type(c, type_char); + LLVMTypeRef type = LLVMFunctionType(LLVMVoidTypeInContext(c->context), &char_ptr_type, 1, false); + LLVMValueRef puts_func = LLVMGetNamedFunction(c->module, "puts"); + if (!puts_func) + { + puts_func = LLVMAddFunction(c->module, "puts", type); + } + LLVMValueRef global_name = LLVMAddGlobal(c->module, LLVMArrayType(llvm_get_type(c, type_char), strlen(panic_name) + 1), ""); + LLVMSetLinkage(global_name, LLVMInternalLinkage); + LLVMSetGlobalConstant(global_name, 1); + LLVMSetInitializer(global_name, LLVMConstStringInContext(c->context, panic_name, strlen(panic_name), 0)); + + LLVMValueRef zero = llvm_get_zero(c, type_usize); + LLVMValueRef string = LLVMBuildInBoundsGEP2(c->builder, LLVMTypeOf(global_name), global_name, &zero, 1, ""); + string = LLVMBuildBitCast(c->builder, string, char_ptr_type, ""); + LLVMBuildCall(c->builder, puts_func, &string, 1, ""); + llvm_emit_call_intrinsic(c, intrinsic_id_trap, NULL, 0, NULL, 0); + llvm_emit_br(c, ok_block); + llvm_emit_block(c, ok_block); } @@ -1012,7 +1035,7 @@ void llvm_emit_stmt(GenContext *c, Ast *ast) gencontext_emit_expr_stmt(c, ast); break; case AST_DECLARE_STMT: - gencontext_emit_decl(c, ast); + llvm_emit_decl(c, ast); break; case AST_BREAK_STMT: gencontext_emit_break(c, ast); diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 67e8585f9..cfa514a46 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -61,25 +61,25 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *context, Decl *decl) } case DECL_UNION: { - Decl *max_type = NULL; - unsigned long long max_size = 0; LLVMTypeRef type = LLVMStructCreateNamed(context->context, decl->external_name); // Avoid recursive issues. decl->type->backend_type = type; - VECEACH(decl->strukt.members, i) + Decl **members = decl->strukt.members; + if (vec_size(members)) { - Decl *member = decl->strukt.members[i]; - unsigned size = type_size(member->type); - if (size > max_size || !max_type) + + Decl *rep_type = members[decl->strukt.union_rep]; + LLVMTypeRef type_ref[2] = { + llvm_get_type(context, rep_type->type), + NULL + }; + unsigned elements = 1; + if (decl->needs_additional_pad) { - max_size = size; - max_type = member; + type_ref[elements++] = LLVMArrayType(llvm_get_type(context, type_bool), type_size(decl->type) - type_size(rep_type->type)); + } - } - if (max_type) - { - LLVMTypeRef type_ref = llvm_get_type(context, max_type->type); - LLVMStructSetBody(type, &type_ref, 1, false); + LLVMStructSetBody(type, type_ref, elements, decl->is_packed); } else { @@ -147,7 +147,7 @@ static void param_expand(GenContext *context, LLVMTypeRef** params_ref, Type *ty case TYPE_TYPEDEF: UNREACHABLE case TYPE_ARRAY: - for (size_t i = type->array.len; i > 0; i--) + for (ByteSize i = type->array.len; i > 0; i--) { param_expand(context, params_ref, type->array.base); } @@ -174,7 +174,7 @@ static void param_expand(GenContext *context, LLVMTypeRef** params_ref, Type *ty return; case TYPE_UNION: { - size_t largest = 0; + ByteSize largest = 0; Type *largest_type = NULL; Decl **members = type->decl->strukt.members; VECEACH(members, i) diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 2ae6937a3..6eff7bb0b 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -18,6 +18,8 @@ typedef struct extern ParseRule rules[TOKEN_EOF + 1]; +static Expr *parse_identifier_with_path(Context *context, Path *path); + inline Expr *parse_precedence_with_left_side(Context *context, Expr *left_side, Precedence precedence) { while (precedence <= rules[context->tok.type].precedence) @@ -50,6 +52,15 @@ static Expr *parse_precedence(Context *context, Precedence precedence) return parse_precedence_with_left_side(context, left_side, precedence); } +static inline Expr *parse_expr_or_initializer_list(Context *context) +{ + if (TOKEN_IS(TOKEN_LBRACE)) + { + return parse_initializer_list(context); + } + return parse_expr(context); +} + inline Expr* parse_expr(Context *context) { return parse_precedence(context, PREC_ASSIGNMENT); @@ -60,6 +71,50 @@ inline Expr* parse_constant_expr(Context *context) return parse_precedence(context, PREC_TERNARY); } +/** + * param_path : ('[' expression ']' | '.' IDENT)* + * + * @param context + * @param path reference to the path to return + * @return true if parsing succeeds, false otherwise. + */ +static bool parse_param_path(Context *context, DesignatorElement ***path) +{ + *path = NULL; + while (true) + { + if (TOKEN_IS(TOKEN_LBRACKET)) + { + // Parse the inside of [ ] + DesignatorElement *element = CALLOCS(DesignatorElement); + element->kind = DESIGNATOR_ARRAY; + advance_and_verify(context, TOKEN_LBRACKET); + element->index_expr = TRY_EXPR_OR(parse_expr(context), false); + // Possible range + if (try_consume(context, TOKEN_DOTDOT)) + { + element->index_end_expr = TRY_EXPR_OR(parse_expr(context), false); + element->kind = DESIGNATOR_RANGE; + } + CONSUME_OR(TOKEN_RBRACKET, false); + // Include ] in the expr + vec_add(*path, element); + continue; + } + if (TOKEN_IS(TOKEN_DOT)) + { + advance(context); + DesignatorElement *element = CALLOCS(DesignatorElement); + element->kind = DESIGNATOR_FIELD; + element->field = TOKSTR(context->tok.id); + EXPECT_OR(TOKEN_IDENT, false); + advance(context); + vec_add(*path, element); + continue; + } + return true; + } +} /** * param_list * : parameter @@ -68,7 +123,7 @@ inline Expr* parse_constant_expr(Context *context) * * parameter * : expr - * | '[' expr ']' '=' expr + * | param_path '=' expr * ; * */ @@ -78,19 +133,25 @@ bool parse_param_list(Context *context, Expr ***result, bool allow_type, TokenTy while (1) { Expr *expr = NULL; - // Special handling of [123] - if (TOKEN_IS(TOKEN_LBRACKET)) + DesignatorElement **path; + Token current = context->tok; + if (!parse_param_path(context, &path)) return false; + if (path != NULL) { - expr = EXPR_NEW_TOKEN(EXPR_SUBSCRIPT, context->tok); - advance_and_verify(context, TOKEN_LBRACKET); - expr->subscript_expr.index = TRY_EXPR_OR(parse_expr(context), false); - CONSUME_OR(TOKEN_RBRACKET, false); + // Create the parameter expr + expr = EXPR_NEW_TOKEN(EXPR_DESIGNATOR, current); + expr->designator_expr.path = path; RANGE_EXTEND_PREV(expr); - expr = TRY_EXPR_OR(parse_precedence_with_left_side(context, expr, PREC_ASSIGNMENT), false); + + // Expect the '=' after. + CONSUME_OR(TOKEN_EQ, false); + + // Now parse the rest + expr->designator_expr.value = TRY_EXPR_OR(parse_expr_or_initializer_list(context), false); } else { - expr = parse_expr(context); + expr = parse_expr_or_initializer_list(context); } vec_add(*result, expr); if (!try_consume(context, TOKEN_COMMA)) @@ -286,11 +347,11 @@ Expr *parse_initializer(Context *context) Expr *parse_initializer_list(Context *context) { Expr *initializer_list = EXPR_NEW_TOKEN(EXPR_INITIALIZER_LIST, context->tok); - initializer_list->expr_initializer.init_type = INITIALIZER_UNKNOWN; + initializer_list->initializer_expr.init_type = INITIALIZER_UNKNOWN; CONSUME_OR(TOKEN_LBRACE, poisoned_expr); if (!try_consume(context, TOKEN_RBRACE)) { - if (!parse_param_list(context, &initializer_list->expr_initializer.initializer_expr, false, TOKEN_RBRACE)) return poisoned_expr; + if (!parse_param_list(context, &initializer_list->initializer_expr.initializer_expr, false, TOKEN_RBRACE)) return poisoned_expr; CONSUME_OR(TOKEN_RBRACE, poisoned_expr); } return initializer_list; @@ -304,6 +365,7 @@ static Expr *parse_failable(Context *context, Expr *left_side) RANGE_EXTEND_PREV(failable); return failable; } + static Expr *parse_binary(Context *context, Expr *left_side) { assert(left_side && expr_ok(left_side)); @@ -375,6 +437,7 @@ static Expr *parse_subscript_expr(Context *context, Expr *left) { index = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); index->type = type_usize; + index->constant = true; index->resolve_status = RESOLVE_DONE; expr_const_set_int(&index->const_expr, 0, type_usize->canonical->type_kind); } @@ -640,7 +703,6 @@ static Expr *parse_char_lit(Context *context, Expr *left) UNREACHABLE } - expr_int->resolve_status = RESOLVE_DONE; advance(context); return expr_int; } @@ -753,7 +815,6 @@ static Expr *parse_string_literal(Context *context, Expr *left) { assert(!left && "Had left hand side"); Expr *expr_string = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); - expr_string->resolve_status = RESOLVE_DONE; expr_string->type = type_string; char *str = NULL; @@ -799,7 +860,6 @@ static Expr *parse_bool(Context *context, Expr *left) Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); number->const_expr = (ExprConst) { .b = TOKEN_IS(TOKEN_TRUE), .kind = TYPE_BOOL }; number->type = type_bool; - number->resolve_status = RESOLVE_DONE; advance(context); return number; } @@ -816,9 +876,11 @@ static Expr *parse_null(Context *context, Expr *left) Expr *parse_type_compound_literal_expr_after_type(Context *context, TypeInfo *type_info) { + advance_and_verify(context, TOKEN_LPAREN); Expr *expr = expr_new(EXPR_COMPOUND_LITERAL, type_info->span); expr->expr_compound_literal.type_info = type_info; expr->expr_compound_literal.initializer = TRY_EXPR_OR(parse_initializer_list(context), poisoned_expr); + CONSUME_OR(TOKEN_RPAREN, poisoned_expr); RANGE_EXTEND_PREV(expr); return expr; } @@ -850,7 +912,7 @@ Expr *parse_type_expression_with_path(Context *context, Path *path) { type = TRY_TYPE_OR(parse_type(context), poisoned_expr); } - if (TOKEN_IS(TOKEN_LBRACE)) + if (TOKEN_IS(TOKEN_LPAREN) && context->next_tok.type == TOKEN_LBRACE) { return parse_type_compound_literal_expr_after_type(context, type); } @@ -870,8 +932,8 @@ static Expr* parse_expr_block(Context *context, Expr *left) { assert(!left && "Had left hand side"); Expr *expr = EXPR_NEW_TOKEN(EXPR_EXPR_BLOCK, context->tok); - advance_and_verify(context, TOKEN_LPARBRA); - while (!try_consume(context, TOKEN_RPARBRA)) + advance_and_verify(context, TOKEN_LBRAPIPE); + while (!try_consume(context, TOKEN_RBRAPIPE)) { Ast *stmt = parse_stmt(context); if (!ast_ok(stmt)) return poisoned_expr; @@ -906,7 +968,7 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_PLUSPLUS] = { parse_unary_expr, parse_post_unary, PREC_CALL }, [TOKEN_MINUSMINUS] = { parse_unary_expr, parse_post_unary, PREC_CALL }, [TOKEN_LPAREN] = { parse_grouping_expr, parse_call_expr, PREC_CALL }, - [TOKEN_LPARBRA] = { parse_expr_block, NULL, PREC_NONE }, + [TOKEN_LBRAPIPE] = { parse_expr_block, NULL, PREC_NONE }, [TOKEN_CAST] = { parse_cast_expr, NULL, PREC_NONE }, [TOKEN_TYPEOF] = { parse_typeof_expr, NULL, PREC_NONE }, [TOKEN_TRY] = { parse_try_expr, NULL, PREC_NONE }, diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 7ca817e56..909e613e7 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1125,6 +1125,7 @@ bool parse_struct_body(Context *context, Decl *parent) CONSUME_OR(TOKEN_LBRACE, false); assert(decl_is_struct_type(parent)); + MemberIndex index = 0; while (!TOKEN_IS(TOKEN_RBRACE)) { TokenType token_type = context->tok.type; @@ -1152,6 +1153,12 @@ bool parse_struct_body(Context *context, Decl *parent) return false; } vec_add(parent->strukt.members, member); + index++; + if (index > MAX_MEMBERS) + { + SEMA_ERROR(member, "Can't add another member: the count would exceed maximum of %d elements.", MAX_MEMBERS); + return false; + } continue; } TypeInfo *type = TRY_TYPE_OR(parse_type(context), false); @@ -1160,6 +1167,12 @@ bool parse_struct_body(Context *context, Decl *parent) EXPECT_OR(TOKEN_IDENT, false); Decl *member = decl_new_var(context->tok.id, type, VARDECL_MEMBER, parent->visibility); vec_add(parent->strukt.members, member); + index++; + if (index > MAX_MEMBERS) + { + SEMA_ERROR(member, "Can't add another member: the count would exceed maximum of %d elements.", MAX_MEMBERS); + return false; + } advance(context); if (!try_consume(context, TOKEN_COMMA)) break; } diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index f509c2673..d6dd9976d 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -1009,7 +1009,7 @@ Ast *parse_stmt(Context *context) case TOKEN_FALSE: case TOKEN_NULL: case TOKEN_TRUE: - case TOKEN_LPARBRA: + case TOKEN_LBRAPIPE: case TOKEN_TYPEOF: return parse_expr_stmt(context); case TOKEN_ASSERT: @@ -1085,7 +1085,7 @@ Ast *parse_stmt(Context *context) case TOKEN_CT_DEFAULT: case TOKEN_CT_ENDIF: case TOKEN_CT_ENDSWITCH: - case TOKEN_RPARBRA: + case TOKEN_RBRAPIPE: case TOKEN_IN: case TOKEN_BANGBANG: SEMA_TOKEN_ERROR(context->tok, "Unexpected '%s' found when expecting a statement.", token_type_to_string(context->tok.type)); diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index c26310d48..10a2d8eeb 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -106,10 +106,19 @@ static inline bool may_implicitly_cast_ptr_to_ptr(Type *current_type, Type *targ assert(current_type->canonical == current_type); assert(target_type->canonical == target_type); - // Neither is void* or have matching bases: - if (target_type->pointer != type_void && current_type->pointer != type_void && target_type->pointer != current_type->pointer) return false; + // void* converts freely to and from: + if (target_type->pointer == type_void || current_type->pointer == type_void) return true; - return true; + // Pointee is same? Fine! + if (target_type->pointer == current_type->pointer) return true; + + // Special case, does it point to an array, then it's okay if the element is the same. + if (current_type->pointer->type_kind == TYPE_ARRAY && + target_type->pointer == current_type->pointer->array.base) return true; + + // IMPROVE Vector + + return false; } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 060b586ac..25f64defb 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -46,10 +46,9 @@ static inline bool sema_analyse_struct_member(Context *context, Decl *decl) static bool sema_analyse_union_members(Context *context, Decl *decl, Decl **members) { - unsigned max_size = 0; - //unsigned max_size_element = 0; - unsigned max_alignment_element = 0; - unsigned max_alignment = 0; + ByteSize max_size = 0; + MemberIndex max_alignment_element = 0; + AlignSize max_alignment = 0; VECEACH(members, i) { @@ -69,8 +68,8 @@ static bool sema_analyse_union_members(Context *context, Decl *decl, Decl **memb continue; } - size_t member_alignment = type_abi_alignment(member->type); - size_t member_size = type_size(member->type); + AlignSize member_alignment = type_abi_alignment(member->type); + ByteSize member_size = type_size(member->type); // Update max alignment if (member_alignment > max_alignment) @@ -84,10 +83,10 @@ static bool sema_analyse_union_members(Context *context, Decl *decl, Decl **memb //max_size_element = i; max_size = member_size; // If this is bigger than the previous with max - // alignment, pick this as the maximum size element. - if (max_alignment_element != i && max_alignment == member_alignment) + // alignment, pick this as the maximum size field. + if (max_alignment_element != (MemberIndex)i && max_alignment == member_alignment) { - max_alignment_element = i; + max_alignment_element = (MemberIndex)i; } } // Offset is always 0 @@ -103,7 +102,9 @@ static bool sema_analyse_union_members(Context *context, Decl *decl, Decl **memb if (!decl->is_packed) decl->alignment = MAX(decl->alignment, max_alignment); // We're only packed if the max alignment is > 1 - decl->is_packed = max_alignment > 1; + decl->is_packed = decl->is_packed && max_alignment > 1; + + decl->strukt.union_rep = max_alignment_element; // The actual size might be larger than the max size due to alignment. unsigned size = aligned_offset(max_size, decl->alignment); @@ -120,10 +121,10 @@ static bool sema_analyse_union_members(Context *context, Decl *decl, Decl **memb static bool sema_analyse_struct_members(Context *context, Decl *decl, Decl **members) { // Default alignment is 1 even if the it is empty. - size_t natural_alignment = 1; + AlignSize natural_alignment = 1; bool is_unaligned = false; - size_t size = 0; - size_t offset = 0; + ByteSize size = 0; + ByteSize offset = 0; bool is_packed = decl->is_packed; VECEACH(members, i) { @@ -145,7 +146,7 @@ static bool sema_analyse_struct_members(Context *context, Decl *decl, Decl **mem if (!decl_ok(decl)) return false; - size_t member_alignment = type_abi_alignment(member->type); + AlignSize member_alignment = type_abi_alignment(member->type); // If the member alignment is higher than the currently detected alignment, // then we update the natural alignment @@ -614,6 +615,7 @@ static AttributeType sema_analyse_attribute(Context *context, Attr *attr, Attrib SEMA_ERROR(attr->expr, "Alignment must be a power of two."); return ATTRIBUTE_NONE; } + attr->alignment = align; } return type; @@ -757,7 +759,7 @@ static inline bool sema_analyse_global(Context *context, Decl *decl) if (decl->var.init_expr && decl->type) { if (!sema_analyse_expr_of_required_type(context, decl->type, decl->var.init_expr, false)) return false; - if (!expr_is_constant_eval(decl->var.init_expr)) + if (!decl->var.init_expr->constant) { SEMA_ERROR(decl->var.init_expr, "The expression must be a constant value."); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 0b808c110..bd9f52a5f 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -244,6 +244,7 @@ static inline bool sema_type_error_on_binop(Context *context, Expr *expr) static bool expr_cast_to_index(Context *context, Expr *index) { + printf("TODO: consider changing indexing size\n"); if (index->type->canonical->type_kind == type_usize->canonical->type_kind) return true; return cast_implicit(context, index, type_isize); } @@ -460,6 +461,7 @@ static inline bool sema_expr_analyse_identifier_resolve(Context *context, Type * bool expr_is_constant_eval(Expr *expr) { + // TODO rethink this. switch (expr->expr_kind) { case EXPR_CONST: @@ -468,8 +470,8 @@ bool expr_is_constant_eval(Expr *expr) return expr_is_constant_eval(expr->expr_compound_literal.initializer); case EXPR_INITIALIZER_LIST: { - Expr** init_exprs = expr->expr_initializer.initializer_expr; - switch (expr->expr_initializer.init_type) + Expr** init_exprs = expr->initializer_expr.initializer_expr; + switch (expr->initializer_expr.init_type) { case INITIALIZER_NORMAL: { @@ -676,14 +678,20 @@ static inline bool sema_expr_analyse_binary_sub_expr(Context *context, Type *to, return sema_analyse_expr(context, to, left) & sema_analyse_expr(context, to, right); } -static inline int find_index_of_named_parameter(Context *context, Decl** func_params, Expr *expr) +static inline int find_index_of_named_parameter(Decl **func_params, Expr *expr) { - if (expr->expr_kind != EXPR_IDENTIFIER || expr->identifier_expr.path) + if (vec_size(expr->designator_expr.path) != 1) { - SEMA_ERROR(expr, "Expected the name of a function parameter here, enclose the assignment expression in ()."); + SEMA_ERROR(expr, "Expected the name of a function parameter here, this looks like a member path."); return -1; } - const char *name = expr->identifier_expr.identifier; + DesignatorElement *element = expr->designator_expr.path[0]; + if (element->kind != DESIGNATOR_FIELD) + { + SEMA_ERROR(expr, "Expected the name of a function parameter here, this looks like an array path field."); + return -1; + } + const char *name = element->field; VECEACH(func_params, i) { if (func_params[i]->name == name) return (int)i; @@ -790,10 +798,10 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS bool is_implicit = i < struct_args; Expr *arg = is_implicit ? struct_var : args[i - struct_args]; // Named parameters - if (!is_implicit && arg->expr_kind == EXPR_BINARY && arg->binary_expr.operator == BINARYOP_ASSIGN) + if (arg->expr_kind == EXPR_DESIGNATOR) { uses_named_parameters = true; - int index = find_index_of_named_parameter(context, func_params, arg->binary_expr.left); + int index = find_index_of_named_parameter(func_params, arg); if (index < 0) return false; if (actual_args[index]) { @@ -814,6 +822,32 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS return false; } if (!sema_analyse_expr_of_required_type(context, NULL, arg, true)) return false; + // In the case of a compile time variable we cast to c_int / double. + + Type *arg_type = arg->type->canonical; + if (type_is_ct(arg_type)) + { + // Pick double / CInt + Type *target_type = type_is_any_integer(arg_type) ? type_c_int->canonical : type_double; + if (!cast_implicit(context, arg, target_type)) return false; + arg_type = target_type; + } + // bools need explicit promotion. + if (arg_type == type_bool) + { + if (!cast(context, arg, type_c_int->canonical, CAST_TYPE_EXPLICIT)) return false; + } + // Promote any integer to at least CInt + if (type_is_promotable_integer(arg_type) || arg_type == type_bool) + { + if (!cast_implicit(context, arg, type_c_int->canonical)) return false; + arg_type = type_c_int->canonical; + } + if (type_is_promotable_float(arg->type)) + { + if (!cast_implicit(context, arg, type_double)) return false; + arg_type = type_double; + } actual_args[i] = arg; expr->failable |= arg->failable; continue; @@ -1187,8 +1221,17 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr break; case EXPR_MACRO_IDENTIFIER: return sema_expr_analyse_macro_call(context, to, expr, func_expr->identifier_expr.decl); + case EXPR_LEN: + if (func_expr->type == type_void) + { + func_expr->type = type_usize; + expr_replace(expr, func_expr); + return true; + } + FALLTHROUGH; default: - TODO + SEMA_ERROR(expr, "This value cannot be invoked, did you accidentally add ()?"); + return false; } switch (decl->decl_kind) { @@ -1271,7 +1314,7 @@ static bool expr_check_index_in_range(Context *context, Type *type, Expr *index_ index = len - index; } // Checking end can only be done for arrays. - if (end_index && index > len) + 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); return false; @@ -1395,6 +1438,24 @@ static inline bool sema_expr_analyse_slice_after_parent_resolution(Context *cont if (start && end && start->expr_kind == EXPR_CONST && end->expr_kind == EXPR_CONST) { + if (type->type_kind == TYPE_ARRAY) + { + BigInt len; + bigint_init_unsigned(&len, type->array.len); + BigInt result; + if (expr->slice_expr.start_from_back) + { + bigint_sub(&result, &len, &start->const_expr.i); + start->const_expr.i = result; + expr->slice_expr.start_from_back = false; + } + if (expr->slice_expr.end_from_back) + { + bigint_sub(&result, &len, &end->const_expr.i); + end->const_expr.i = result; + expr->slice_expr.end_from_back = false; + } + } if (expr->slice_expr.start_from_back && expr->slice_expr.end_from_back) { if (expr_const_compare(&start->const_expr, &end->const_expr, BINARYOP_LT)) @@ -1403,7 +1464,7 @@ static inline bool sema_expr_analyse_slice_after_parent_resolution(Context *cont return false; } } - else + else if (!expr->slice_expr.start_from_back && !expr->slice_expr.end_from_back) { if (expr_const_compare(&start->const_expr, &end->const_expr, BINARYOP_GT)) { @@ -1834,7 +1895,7 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) { expr->expr_kind = EXPR_LEN; expr->len_expr.inner = parent; - expr->type = type_usize; + expr->type = type_void; expr->resolve_status = RESOLVE_DONE; return true; } @@ -1869,7 +1930,7 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) context_pop_scope(context); if (!member) { - SEMA_ERROR(expr, "There is no element or method '%s.%s'.", decl->name, kw); + SEMA_ERROR(expr, "There is no field or method '%s.%s'.", decl->name, kw); return false; } if (is_pointer) @@ -1885,146 +1946,481 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) return true; } -static DesignatedPath *sema_analyse_init_path(Context *context, DesignatedPath *parent, Expr *expr, bool *has_found_match, bool *has_reported_error); - -static DesignatedPath *sema_analyse_init_identifier_string(Context *context, DesignatedPath *parent_path, const char *string, bool *has_found_match, bool *has_reported_error) +static Decl *sema_resolve_element_for_name(Decl** decls, DesignatorElement **elements, unsigned *index) { - assert(type_is_structlike(parent_path->type)); - Decl **members = parent_path->type->decl->strukt.members; - VECEACH(members, i) + + DesignatorElement *element = elements[*index]; + const char *name = element->field; + unsigned old_index = *index; + VECEACH(decls, i) { - Decl *member = members[i]; - if (!member->name) + Decl *decl = decls[i]; + // The simple case, we have a match. + if (decl->name == name) { - DesignatedPath temp_path; - temp_path.type = member->type; - DesignatedPath *found = sema_analyse_init_identifier_string(context, &temp_path, string, has_found_match, has_reported_error); - if (!found) continue; - DesignatedPath *real_path = malloc_arena(sizeof(DesignatedPath)); - *real_path = temp_path; - real_path->index = i; - real_path->kind = DESIGNATED_IDENT; - parent_path->sub_path = real_path; - *has_found_match = true; - return found; + element->index = i; + return decl; } - if (member->name == string) + if (!decl->name) { - DesignatedPath *sub_path = CALLOCS(DesignatedPath); - sub_path->type = member->type; - sub_path->kind = DESIGNATED_IDENT; - sub_path->index = i; - parent_path->sub_path = sub_path; - sub_path->pure = true; - sub_path->constant = true; - *has_found_match = true; - return sub_path; + assert(type_is_structlike(decl->type)); + // Anonymous struct + Decl *found = sema_resolve_element_for_name(decl->strukt.members, elements, index); + // No match, continue... + if (!found) continue; + + // Special handling, we now need to patch the elements + unsigned current_size = vec_size(elements); + // Add an element at the end. + vec_add(elements, NULL); + // Shift all elements + for (unsigned j = current_size; j > old_index; j--) + { + elements[j] = elements[j - 1]; + } + // Create our anon field. + DesignatorElement *anon_element = CALLOCS(DesignatorElement); + anon_element->kind = DESIGNATOR_FIELD; + anon_element->index = i; + elements[old_index] = anon_element; + // Advance + (*index)++; + return found; } } return NULL; } - -static DesignatedPath *sema_analyse_init_access(Context *context, DesignatedPath *parent, Expr *access_expr, bool *has_found_match, bool *has_reported_error) +static int64_t sema_analyse_designator_index(Context *context, Expr *index) { - DesignatedPath *last_path = sema_analyse_init_path(context, parent, access_expr->access_expr.parent, has_found_match, has_reported_error); - if (!last_path) return NULL; - DesignatedPath *path = sema_analyse_init_identifier_string(context, last_path, - TOKSTR(access_expr->access_expr.sub_element), has_found_match, has_reported_error); - if (path) + if (!sema_analyse_expr_value(context, type_usize, index)) { - path->pure = true; - path->constant = true; - } - if (!path && has_found_match && !has_reported_error) - { - SEMA_TOKID_ERROR(access_expr->access_expr.sub_element, "'%s' is not a valid sub member.", TOKSTR(access_expr->access_expr.sub_element)); - *has_reported_error = true; - } - return path; -} - - -static DesignatedPath *sema_analyse_init_subscript(Context *context, DesignatedPath *parent, Expr *expr, bool *has_found_match, bool *has_reported_error) -{ - assert(expr->expr_kind == EXPR_SUBSCRIPT); - DesignatedPath *path = parent; - if (expr->subscript_expr.expr) - { - path = sema_analyse_init_path(context, parent, expr->subscript_expr.expr, has_found_match, has_reported_error); - } - if (!path) return NULL; - - Type *type = path->type; - if (type->canonical->type_kind == TYPE_POINTER) - { - SEMA_ERROR(expr, "It's not possible to subscript a pointer field in a designated initializer."); - *has_reported_error = true; - return NULL; - } - - Expr *index = expr->subscript_expr.index; - Type *inner_type = type_get_indexed_type(type); - if (!inner_type) - { - SEMA_ERROR(expr, "Not possible to index a value of type '%s'.", type_to_error_string(type)); - *has_reported_error = true; - return NULL; - } - if (!sema_analyse_expr(context, type_isize, index)) - { - *has_reported_error = true; - return NULL; + return -1; } // Unless we already have type_usize, cast to type_isize; if (!expr_cast_to_index(context, index)) { - *has_reported_error = true; - return NULL; + return -1; } - - // Check range - if (!expr_check_index_in_range(context, type->canonical, index, false, false)) + if (index->expr_kind == EXPR_CONST) { - *has_reported_error = true; - return NULL; + if (!bigint_fits_in_bits(&index->const_expr.i, 64, true)) + { + SEMA_ERROR(index, "The value of the index does not fit in a long."); + return -1; + } + int64_t index_val = bigint_as_signed(&index->const_expr.i); + if (index_val < 0) + { + SEMA_ERROR(index, "Negative index values is not allowed."); + return -1; + } + return index_val; } - - DesignatedPath *sub_path = CALLOCS(DesignatedPath); - path->sub_path = sub_path; - sub_path->pure = index->pure; - sub_path->constant = index->constant; - path->constant &= index->pure; - path->pure &= index->pure; - - sub_path->type = inner_type; - sub_path->kind = DESIGNATED_SUBSCRIPT; - sub_path->index_expr = index; - *has_found_match = true; - return sub_path; + return 0; } -static DesignatedPath *sema_analyse_init_path(Context *context, DesignatedPath *parent, Expr *expr, bool *has_found_match, bool *has_reported_error) +static Type *sema_find_type_of_element(Context *context, Type *type, DesignatorElement **elements, unsigned *curr_index, bool *is_constant, bool *did_report_error) { - switch (expr->expr_kind) + Type *type_lowered = type_lowering(type); + DesignatorElement *element = elements[*curr_index]; + if (element->kind == DESIGNATOR_ARRAY || element->kind == DESIGNATOR_RANGE) { - case EXPR_ACCESS: - return sema_analyse_init_access(context, parent, expr, has_found_match, has_reported_error); - case EXPR_IDENTIFIER: - return sema_analyse_init_identifier_string(context, parent, expr->identifier_expr.identifier, has_found_match, has_reported_error); - case EXPR_SUBSCRIPT: - return sema_analyse_init_subscript(context, parent, expr, has_found_match, has_reported_error); + if (type_lowered->type_kind != TYPE_ARRAY) + { + return NULL; + } + int64_t index = sema_analyse_designator_index(context, element->index_expr); + if (index < 0) + { + *did_report_error = true; + return NULL; + } + element->index = index; + if (element->index_expr->expr_kind != EXPR_CONST) *is_constant = false; + + if (element->kind == DESIGNATOR_RANGE) + { + int64_t end_index = sema_analyse_designator_index(context, element->index_end_expr); + if (end_index < 0) + { + *did_report_error = true; + return NULL; + } + if (element->index_end_expr->expr_kind == EXPR_CONST + && element->index_expr->expr_kind == EXPR_CONST + && index > end_index) + { + SEMA_ERROR(element->index_end_expr, "End index must be greater than start index."); + return false; + } + element->index_end = end_index; + } + return type_lowered->array.base; + } + assert(element->kind == DESIGNATOR_FIELD); + if (!type_is_structlike(type_lowered)) + { + return NULL; + } + Decl *member = sema_resolve_element_for_name(type_lowered->decl->strukt.members, elements, curr_index); + if (!member) return NULL; + return member->type; +} + +static Type *sema_expr_analyse_designator(Context *context, Type *current, Expr *expr) +{ + DesignatorElement **path = expr->designator_expr.path; + Expr *value = expr->designator_expr.value; + + // Walk down into this path + bool is_constant = true; + bool did_report_error = false; + for (unsigned i = 0; i < vec_size(path); i++) + { + current = sema_find_type_of_element(context, current, path, &i, &is_constant, &did_report_error); + if (!current) break; + } + if (!current && !did_report_error) + { + SEMA_ERROR(expr, "This is not a valid member of '%s'.", type_to_error_string(current)); + } + expr->constant = is_constant; + expr->pure = is_constant; + return current; +} + +static void sema_update_const_initializer_with_designator(ConstInitializer *const_init, + DesignatorElement **curr, + DesignatorElement **end, + Expr *value); + +static void sema_create_const_initializer_value(ConstInitializer *const_init, Expr *value) +{ + if (value->expr_kind == EXPR_INITIALIZER_LIST && value->initializer_expr.init_type == INITIALIZER_CONST) + { + *const_init = *value->initializer_expr.initializer; + value->initializer_expr.initializer = const_init; + return; + } + const_init->type = value->type->canonical; + const_init->kind = CONST_VALUE; + const_init->value = value; +} + +static bool is_empty_initializer_list(Expr *value) +{ + return value->initializer_expr.init_type == INITIALIZER_CONST && value->initializer_expr.initializer->kind == CONST_INIT_ZERO; +} +static void sema_create_const_initializer(ConstInitializer *const_init, Expr *initializer); + +static inline void sema_update_const_initializer_with_designator_struct(ConstInitializer *const_init, + DesignatorElement **curr, + DesignatorElement **end, + Expr *value) +{ + DesignatorElement *element = curr[0]; + assert(element->kind == DESIGNATOR_FIELD); + DesignatorElement **next_element = curr + 1; + bool at_end = next_element == end; + // Optimize in case this is a zero. + if (at_end && is_empty_initializer_list(value)) + { + const_init->kind = CONST_INIT_ZERO; + return; + } + if (const_init->kind == CONST_INIT_ZERO) + { + Decl **elements = const_init->type->decl->strukt.members; + ConstInitializer **const_inits = MALLOC(sizeof(ConstInitializer *) * vec_size(elements)); + VECEACH(elements, i) + { + ConstInitializer *element_init = CALLOCS(ConstInitializer); + element_init->type = elements[i]->type->canonical; + element_init->kind = CONST_INIT_ZERO; + const_inits[i] = element_init; + } + const_init->elements = const_inits; + const_init->kind = CONST_INIT_EXPANDED; + } + ConstInitializer *sub_element = const_init->elements[element->index]; + if (!at_end) + { + sema_update_const_initializer_with_designator(sub_element, next_element, end, value); + return; + } + sema_create_const_initializer_value(sub_element, value); +} + +static inline void sema_update_const_initializer_with_designator_union(ConstInitializer *const_init, + DesignatorElement **curr, + DesignatorElement **end, + Expr *value) +{ + DesignatorElement *element = curr[0]; + assert(element->kind == DESIGNATOR_FIELD); + ConstInitializer *sub_element = const_init->union_const.element; + // If it's an empty initializer, just clear everything back to CONST_INIT_ZERO + DesignatorElement **next_element = curr + 1; + bool is_at_end = next_element == end; + if (is_at_end && is_empty_initializer_list(value)) + { + const_init->kind = CONST_INIT_ZERO; + return; + } + if (const_init->kind == CONST_INIT_ZERO) + { + sub_element = const_init->union_const.element = CALLOCS(ConstInitializer); + sub_element->kind = CONST_INIT_ZERO; + const_init->union_const.element = sub_element; + } + else if (element->index != const_init->union_const.index) + { + sub_element->kind = CONST_INIT_ZERO; + sub_element->type = value->type; + } + sub_element->type = const_init->type->decl->strukt.members[element->index]->type->canonical; + const_init->union_const.index = element->index; + const_init->kind = CONST_SELECTED; + if (!is_at_end) + { + sema_update_const_initializer_with_designator(sub_element, next_element, end, value); + return; + } + sema_create_const_initializer_value(sub_element, value); +} + + + +static inline ConstInitializer *sema_find_const_init_array_slice(ConstInitializer *const_init, ArrayIndex index) +{ + if (!const_init) return NULL; + switch (const_init->kind) + { + case CONST_INIT_ARRAY_SPLIT: + { + ConstInitializer *found; + if ((found = sema_find_const_init_array_slice(const_init->split_const.low, index))) return found; + if ((found = sema_find_const_init_array_slice(const_init->split_const.mid, index))) return found; + if ((found = sema_find_const_init_array_slice(const_init->split_const.hi, index))) return found; + return false; + } + case CONST_INIT_ARRAY_RANGE_ZERO: + if (const_init->array_range_zero.low > index || const_init->array_range_zero.high < index) return NULL; + return const_init; + case CONST_INIT_ARRAY_VALUE_FRAGMENT: + return const_init->single_array_index.index == index ? const_init : NULL; default: + UNREACHABLE + } +} + +static inline ConstInitializer *sema_split_const_init_array_slice(ConstInitializer *const_init, ArrayIndex index) +{ + switch (const_init->kind) + { + case CONST_INIT_ARRAY_SPLIT: + UNREACHABLE + case CONST_INIT_ARRAY_RANGE_ZERO: + { + // Special case: single element. + if (const_init->array_range_zero.low == const_init->array_range_zero.high) + { + const_init->kind = CONST_INIT_ARRAY_VALUE_FRAGMENT; + const_init->type = const_init->type->array.base; + const_init->single_array_index.index = index; + const_init->single_array_index.element = MALLOC(sizeof(ConstInitializer)); + const_init->single_array_index.element->type = const_init->type->array.base; + const_init->single_array_index.element->kind = CONST_INIT_ZERO; + return const_init; + } + ConstInitializer *low = NULL; + ConstInitializer *hi = NULL; + if (const_init->array_range_zero.low < index) + { + low = MALLOC(sizeof(ConstInitializer)); + low->kind = CONST_INIT_ARRAY_RANGE_ZERO; + low->array_range_zero.low = const_init->array_range_zero.low; + low->array_range_zero.high = index - 1; + low->type = type_get_array(const_init->type->array.base, low->array_range_zero.high - low->array_range_zero.low + 1); + } + if (const_init->array_range_zero.high > index) + { + hi = MALLOC(sizeof(ConstInitializer)); + hi->kind = CONST_INIT_ARRAY_RANGE_ZERO; + hi->array_range_zero.low = index + 1; + hi->array_range_zero.high = const_init->array_range_zero.high; + hi->type = type_get_array(const_init->type->array.base, hi->array_range_zero.high - hi->array_range_zero.low + 1); + } + ConstInitializer *mid = MALLOC(sizeof(ConstInitializer)); + mid->kind = CONST_INIT_ARRAY_VALUE_FRAGMENT; + mid->type = const_init->type->array.base; + mid->single_array_index.index = index; + mid->single_array_index.element = MALLOC(sizeof(ConstInitializer)); + mid->single_array_index.element->type = const_init->type->array.base; + mid->single_array_index.element->kind = CONST_INIT_ZERO; + const_init->split_const.low = low; + const_init->split_const.mid = mid; + const_init->split_const.hi = hi; + const_init->kind = CONST_INIT_ARRAY_SPLIT; + return mid; + } + case CONST_INIT_ARRAY_VALUE_FRAGMENT: + return const_init; + case CONST_INIT_ZERO: + case CONST_INIT_EXPANDED: + case CONST_SELECTED: + case CONST_VALUE: return NULL; } + UNREACHABLE } +static inline void sema_update_const_initializer_with_designator_array(ConstInitializer *const_init, + DesignatorElement **curr, + DesignatorElement **end, + Expr *value) +{ + DesignatorElement *element = curr[0]; + ArrayIndex low_index = element->index; + ArrayIndex high_index = element->kind == DESIGNATOR_RANGE ? element->index_end : element->index; + assert(element->kind == DESIGNATOR_ARRAY || element->kind == DESIGNATOR_RANGE); + if (const_init->kind == CONST_INIT_ZERO) + { + const_init->kind = CONST_INIT_ARRAY_RANGE_ZERO; + const_init->array_range_zero.low = 0; + const_init->array_range_zero.high = const_init->type->array.len - 1; + } + DesignatorElement **next_element = curr + 1; + bool is_direct_update = next_element == end; + for (ArrayIndex index = low_index; index <= high_index; index++) + { + ConstInitializer *sub_element = sema_find_const_init_array_slice(const_init, index); + sub_element = sema_split_const_init_array_slice(sub_element, index)->single_array_index.element; + if (!is_direct_update) + { + sema_update_const_initializer_with_designator(sub_element, next_element, end, value); + continue; + } + sema_create_const_initializer_value(sub_element, value); + } +} + +static inline void sema_update_const_initializer_with_designator( + ConstInitializer *const_init, + DesignatorElement **curr, + DesignatorElement **end, + Expr *value) +{ + switch (const_init->type->type_kind) + { + case TYPE_STRUCT: + case TYPE_ERRTYPE: + sema_update_const_initializer_with_designator_struct(const_init, curr, end, value); + return; + case TYPE_UNION: + sema_update_const_initializer_with_designator_union(const_init, curr, end, value); + return; + case TYPE_ARRAY: + sema_update_const_initializer_with_designator_array(const_init, curr, end, value); + return; + case TYPE_VECTOR: + TODO + default: + UNREACHABLE + } +} + +static void sema_create_const_initializer(ConstInitializer *const_init, Expr *initializer) +{ + const_init->type = initializer->type->canonical; + const_init->kind = CONST_INIT_ZERO; + Expr **init_expressions = initializer->initializer_expr.initializer_expr; + VECEACH(init_expressions, i) + { + Expr *expr = init_expressions[i]; + DesignatorElement **path = expr->designator_expr.path; + Expr *value = expr->designator_expr.value; + sema_update_const_initializer_with_designator(const_init, path, path + vec_size(path), value); + } +} + +static void debug_dump_const_initializer(ConstInitializer *init, const char *name, unsigned indent) +{ + for (unsigned i = 0; i < indent; i++) printf(" "); + if (name) + { + printf("%s : %s", name, init->type->name ?: "WTF"); + } + else + { + printf("%s", init->type->name ?: "WTF"); + } + switch (init->kind) + { + case CONST_INIT_ZERO: + printf(" = 0\n"); + return; + case CONST_SELECTED: + { + printf(" ->\n"); + Decl** members = init->type->decl->strukt.members; + debug_dump_const_initializer(init->union_const.element, members[init->union_const.index]->name, indent + 1); + return; + } + case CONST_INIT_EXPANDED: + { + printf(" ->\n"); + Decl** members = init->type->decl->strukt.members; + unsigned member_count = vec_size(members); + for (unsigned i = 0; i < member_count; i++) + { + debug_dump_const_initializer(init->elements[i], members[i]->name, indent + 1); + } + return; + } + case CONST_INIT_ARRAY_VALUE_FRAGMENT: + { + printf(" [%llu] ->\n", init->single_array_index.index); + debug_dump_const_initializer(init->single_array_index.element, "", indent + 1); + return; + } + case CONST_VALUE: + { + assert(init->value->expr_kind == EXPR_CONST); + printf(" = "); + expr_const_fprint(stdout, &init->value->const_expr); + puts(""); + return; + } + case CONST_INIT_ARRAY_SPLIT: + printf(" [//] ->\n"); + if (init->split_const.low) + { + debug_dump_const_initializer(init->split_const.low, NULL, indent + 1); + } + if (init->split_const.mid) + { + debug_dump_const_initializer(init->split_const.mid, NULL, indent + 1); + } + if (init->split_const.hi) + { + debug_dump_const_initializer(init->split_const.hi, NULL, indent + 1); + } + return; + case CONST_INIT_ARRAY_RANGE_ZERO: + printf(" [%llu .. %llu] = 0\n", init->array_range_zero.low, init->array_range_zero.high); + return; + } + UNREACHABLE +} static bool sema_expr_analyse_designated_initializer(Context *context, Type *assigned, Expr *initializer) { - Expr **init_expressions = initializer->expr_initializer.initializer_expr; + Expr **init_expressions = initializer->initializer_expr.initializer_expr; + Type *original = assigned->canonical; bool is_structlike = type_is_structlike(assigned->canonical); initializer->pure = true; @@ -2032,44 +2428,27 @@ static bool sema_expr_analyse_designated_initializer(Context *context, Type *ass VECEACH(init_expressions, i) { Expr *expr = init_expressions[i]; - // 1. Ensure that're seeing expr = expr on the top level. - if (expr->expr_kind != EXPR_BINARY || expr->binary_expr.operator != BINARYOP_ASSIGN) - { - if (is_structlike) - { - SEMA_ERROR(expr, "Expected an initializer on the format 'foo = 123' here."); - } - else - { - SEMA_ERROR(expr, "Expected an initializer on the format '[1] = 123' here."); - } - return false; - } - Expr *init_expr = expr->binary_expr.left; - DesignatedPath path = { .type = assigned }; - path.pure = true; - path.constant = true; - bool has_reported_error = false; - bool has_found_match = false; - DesignatedPath *last_path = sema_analyse_init_path(context, &path, init_expr, &has_found_match, &has_reported_error); - if (!has_reported_error && !last_path) - { - SEMA_ERROR(expr, "This is not a valid member of '%s'.", type_to_error_string(assigned)); - return false; - } - Expr *value = expr->binary_expr.right; - if (!sema_analyse_expr_of_required_type(context, last_path->type, value, true)) return false; - expr->pure = value->pure & last_path->pure; - expr->constant = value->constant & last_path->constant; - expr->expr_kind = EXPR_DESIGNATED_INITIALIZER; - expr->designated_init_expr.path = path.sub_path; - expr->designated_init_expr.value = value; - expr->failable |= value->failable; + Type *result = sema_expr_analyse_designator(context, original, expr); + if (!result) return false; + if (!sema_analyse_expr_of_required_type(context, result, expr->designator_expr.value, true)) return false; + expr->pure &= expr->designator_expr.value->pure; + expr->constant &= expr->designator_expr.value->constant; + expr->failable |= expr->designator_expr.value->failable; expr->resolve_status = RESOLVE_DONE; initializer->pure &= expr->pure; initializer->constant &= expr->constant; } - initializer->expr_initializer.init_type = INITIALIZER_DESIGNATED; + if (initializer->constant) + { + ConstInitializer *const_init = MALLOC(sizeof(ConstInitializer)); + sema_create_const_initializer(const_init, initializer); + printf("---------------------------------\n"); + debug_dump_const_initializer(const_init, "TOP", 0); + initializer->initializer_expr.init_type = INITIALIZER_CONST; + initializer->initializer_expr.initializer = const_init; + return true; + } + initializer->initializer_expr.init_type = INITIALIZER_DESIGNATED; return true; } @@ -2082,9 +2461,9 @@ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, initializer->pure = true; initializer->constant = true; - Expr **elements = initializer->expr_initializer.initializer_expr; + Expr **elements = initializer->initializer_expr.initializer_expr; Decl **members = assigned->strukt.members; - initializer->expr_initializer.init_type = INITIALIZER_NORMAL; + initializer->initializer_expr.init_type = INITIALIZER_NORMAL; unsigned size = vec_size(elements); unsigned expected_members = vec_size(members); @@ -2122,13 +2501,32 @@ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, initializer->failable |= element->failable; } - // 6. There's the case of too few values as well. Mark the last element as wrong. + // 6. There's the case of too few values as well. Mark the last field as wrong. if (expected_members > size) { SEMA_ERROR(elements[size - 1], "Too few elements in initializer, there should be elements after this one."); return false; } + if (initializer->constant) + { + ConstInitializer *const_init = CALLOCS(ConstInitializer); + const_init->kind = CONST_INIT_EXPANDED; + ConstInitializer **inits = MALLOC(sizeof(ConstInitializer *) * vec_size(elements)); + VECEACH(elements, i) + { + Expr *expr = elements[i]; + if (expr->expr_kind == EXPR_INITIALIZER_LIST) + { + assert(expr->constant); + inits[i] = expr->initializer_expr.initializer; + continue; + } + ConstInitializer *element_init = MALLOC(sizeof(ConstInitializer)); + sema_create_const_initializer_value(element_init, expr); + inits[i] = element_init; + } + } // 7. Done! return true; } @@ -2143,7 +2541,7 @@ static inline bool sema_expr_analyse_array_plain_initializer(Context *context, T initializer->pure = true; initializer->constant = true; - Expr **elements = initializer->expr_initializer.initializer_expr; + Expr **elements = initializer->initializer_expr.initializer_expr; assert(assigned->type_kind == TYPE_ARRAY && "The other types are not done yet."); @@ -2151,7 +2549,7 @@ static inline bool sema_expr_analyse_array_plain_initializer(Context *context, T assert(inner_type); - initializer->expr_initializer.init_type = INITIALIZER_NORMAL; + initializer->initializer_expr.init_type = INITIALIZER_NORMAL; unsigned size = vec_size(elements); unsigned expected_members = assigned->array.len; @@ -2191,18 +2589,24 @@ static inline bool sema_expr_analyse_initializer(Context *context, Type *assigne { expr->type = assigned; - Expr **init_expressions = expr->expr_initializer.initializer_expr; + Expr **init_expressions = expr->initializer_expr.initializer_expr; // 1. Zero size init will initialize to empty. if (vec_size(init_expressions) == 0) { - expr->expr_initializer.init_type = INITIALIZER_ZERO; + ConstInitializer *initializer = CALLOCS(ConstInitializer); + initializer->kind = CONST_INIT_ZERO; + initializer->type = expr->type; + expr->initializer_expr.init_type = INITIALIZER_CONST; + expr->initializer_expr.initializer = initializer; + expr->constant = true; + expr->pure = true; return true; } // 2. Check if we might have a designated initializer // this means that in this case we're actually not resolving macros here. - if (init_expressions[0]->expr_kind == EXPR_BINARY && init_expressions[0]->binary_expr.operator == BINARYOP_ASSIGN) + if (init_expressions[0]->expr_kind == EXPR_DESIGNATOR) { return sema_expr_analyse_designated_initializer(context, assigned, expr); } @@ -2243,7 +2647,7 @@ static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to static inline bool sema_expr_analyse_expr_list(Context *context, Type *to, Expr *expr) { bool success = true; - size_t last = vec_size(expr->expression_list) - 1; + ByteSize last = vec_size(expr->expression_list) - 1; bool constant = true; bool pure = true; VECEACH(expr->expression_list, i) @@ -3007,6 +3411,7 @@ static bool sema_expr_analyse_div(Context *context, Type *to, Expr *expr, Expr * default: UNREACHABLE } + expr->expr_kind = EXPR_CONST; } // 5. Done. @@ -4051,6 +4456,32 @@ static Ast** ast_copy_list_from_macro(Context *context, Ast **to_copy) return result; } +static DesignatorElement** macro_copy_designator_list(Context *context, DesignatorElement **list) +{ + DesignatorElement **result = NULL; + VECEACH(list, i) + { + DesignatorElement *element = MALLOC(sizeof(DesignatorElement)); + DesignatorElement *to_copy = list[i]; + *element = *to_copy; + switch (to_copy->kind) + { + case DESIGNATOR_FIELD: + // Nothing needed + break; + case DESIGNATOR_RANGE: + MACRO_COPY_EXPR(element->index_end_expr); + FALLTHROUGH; + case DESIGNATOR_ARRAY: + MACRO_COPY_EXPR(element->index_expr); + break; + default: + UNREACHABLE + } + vec_add(result, element); + } + return result; +} static Expr *expr_copy_from_macro(Context *context, Expr *source_expr) { @@ -4070,6 +4501,10 @@ static Expr *expr_copy_from_macro(Context *context, Expr *source_expr) case EXPR_HASH_IDENT: // TODO return expr; + case EXPR_DESIGNATOR: + expr->designator_expr.path = macro_copy_designator_list(context, expr->designator_expr.path); + MACRO_COPY_EXPR(expr->designator_expr.value); + return expr; case EXPR_TYPEINFO: MACRO_COPY_TYPE(expr->type_expr); return expr; @@ -4115,9 +4550,6 @@ static Expr *expr_copy_from_macro(Context *context, Expr *source_expr) MACRO_COPY_EXPR(expr->expr_compound_literal.initializer); MACRO_COPY_TYPE(expr->expr_compound_literal.type_info); return expr; - case EXPR_DESIGNATED_INITIALIZER: - // Created during semantic analysis - UNREACHABLE case EXPR_EXPR_BLOCK: MACRO_COPY_AST_LIST(expr->expr_block.stmts); return expr; @@ -4164,7 +4596,7 @@ static Expr *expr_copy_from_macro(Context *context, Expr *source_expr) MACRO_COPY_EXPR(expr->access_expr.parent); return expr; case EXPR_INITIALIZER_LIST: - MACRO_COPY_EXPR_LIST(expr->expr_initializer.initializer_expr); + MACRO_COPY_EXPR_LIST(expr->initializer_expr.initializer_expr); return expr; case EXPR_EXPRESSION_LIST: MACRO_COPY_EXPR_LIST(expr->expression_list); @@ -4439,15 +4871,12 @@ EXIT: } -static inline bool sema_expr_analyse_compound_literal(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_compound_literal(Context *context, Expr *expr) { if (!sema_resolve_type_info(context, expr->expr_compound_literal.type_info)) return false; Type *type = expr->expr_compound_literal.type_info->type; if (!sema_expr_analyse_initializer_list(context, type, expr->expr_compound_literal.initializer)) return false; - expr->pure = expr->expr_compound_literal.initializer->pure; - expr->constant = expr->expr_compound_literal.initializer->constant; - expr->type = type; - expr->failable = expr->expr_compound_literal.initializer->failable; + expr_replace(expr, expr->expr_compound_literal.initializer); return true; } @@ -4517,6 +4946,7 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * case EXPR_UNDEF: case EXPR_ENUM_CONSTANT: case EXPR_MEMBER_ACCESS: + case EXPR_DESIGNATOR: UNREACHABLE case EXPR_HASH_IDENT: return sema_expr_analyse_hash_identifier(context, to, expr); @@ -4528,7 +4958,6 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * case EXPR_POISONED: return false; case EXPR_LEN: - case EXPR_DESIGNATED_INITIALIZER: case EXPR_SLICE_ASSIGN: // Created during semantic analysis UNREACHABLE @@ -4548,7 +4977,7 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * case EXPR_ELSE: return sema_expr_analyse_else(context, to, expr); case EXPR_COMPOUND_LITERAL: - return sema_expr_analyse_compound_literal(context, to, expr); + return sema_expr_analyse_compound_literal(context, expr); case EXPR_EXPR_BLOCK: return sema_expr_analyse_expr_block(context, to, expr); case EXPR_GUARD: @@ -4636,6 +5065,10 @@ static inline bool sema_cast_rvalue(Context *context, Type *to, Expr *expr) } SEMA_ERROR(expr, "A member must be followed by '.' plus a property like 'sizeof'."); return false; + case EXPR_LEN: + if (expr->type != type_void) return true; + SEMA_ERROR(expr, "Expected () after 'len' for subarrays."); + return false; case EXPR_TYPEINFO: SEMA_ERROR(expr, "A type must be followed by either (...) or '.'."); return false; diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index ec494052f..f616bdc96 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -611,7 +611,7 @@ static bool sema_analyse_asm_stmt(Context *context __unused, Ast *statement __un TODO } -static DynamicScope *context_find_scope_by_id(Context *context, unsigned scope_id) +static DynamicScope *context_find_scope_by_id(Context *context, ScopeId scope_id) { DynamicScope *scope = context->current_scope; while (1) diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 4445fab94..0bcefb250 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -90,8 +90,8 @@ const char *token_type_to_string(TokenType type) return ">="; case TOKEN_LESS_EQ: return "<="; - case TOKEN_LPARBRA: - return "({"; + case TOKEN_LBRAPIPE: + return "{|"; case TOKEN_MINUS_ASSIGN: return "-="; case TOKEN_MINUS_MOD: @@ -114,8 +114,8 @@ const char *token_type_to_string(TokenType type) return "+%"; case TOKEN_PLUSPLUS: return "++"; - case TOKEN_RPARBRA: - return "})"; + case TOKEN_RBRAPIPE: + return "|}"; case TOKEN_SCOPE: return "::"; case TOKEN_SHL: diff --git a/src/compiler/types.c b/src/compiler/types.c index 27550894d..658083860 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -129,7 +129,7 @@ const char *type_to_error_string(Type *type) case TYPE_STRING: return "string"; case TYPE_ARRAY: - asprintf(&buffer, "%s[%zu]", type_to_error_string(type->array.base), type->array.len); + asprintf(&buffer, "%s[%llu]", type_to_error_string(type->array.base), (unsigned long long)type->array.len); return buffer; case TYPE_VARARRAY: asprintf(&buffer, "%s[*]", type_to_error_string(type->array.base)); @@ -169,7 +169,7 @@ void type_append_signature_name(Type *type, char *dst, size_t *offset) -size_t type_size(Type *type) +ByteSize type_size(Type *type) { switch (type->type_kind) { @@ -269,7 +269,7 @@ Type *type_abi_find_single_struct_element(Type *type) // Ignore empty arrays if (type_is_empty_field(members[i]->type, true)) continue; - // Already one element found, not single element. + // Already one field found, not single field. if (found) return NULL; Type *field_type = members[i]->type->canonical; @@ -575,7 +575,7 @@ unsigned int type_alloca_alignment(Type *type) Type *type_find_largest_union_element(Type *type) { assert(type->type_kind == TYPE_UNION); - size_t largest = 0; + ByteSize largest = 0; Type *largest_type = NULL; Decl **members = type->decl->strukt.members; VECEACH(members, i) @@ -589,7 +589,7 @@ Type *type_find_largest_union_element(Type *type) return largest_type; } -unsigned int type_abi_alignment(Type *type) +AlignSize type_abi_alignment(Type *type) { switch (type->type_kind) { diff --git a/test/test_suite/abi/literal_load.c3t b/test/test_suite/abi/literal_load.c3t index 6376e3334..20bbad40f 100644 --- a/test/test_suite/abi/literal_load.c3t +++ b/test/test_suite/abi/literal_load.c3t @@ -5,12 +5,14 @@ struct Test int x; } +Test foo = {}; + extern func void blorg(Test t); func Test creator() { - blorg(Test {} ); - return Test {}; + blorg(Test({})); + return Test({}); } // #expect: literal_load.ll diff --git a/test/test_suite/arrays/array_casts.c3t b/test/test_suite/arrays/array_casts.c3t new file mode 100644 index 000000000..7974d4c80 --- /dev/null +++ b/test/test_suite/arrays/array_casts.c3t @@ -0,0 +1,25 @@ +module test; + + +func void test() +{ + int[3] x; + int *y = &x; + int[] z = &x; +} + +// #expect: array_casts.ll + +%"int[]" = type { i32*, i64 } + +%x = alloca [3 x i32], align 4 +%y = alloca i32*, align 8 +%z = alloca %"int[]", align 8 +%0 = bitcast [3 x i32]* %x to i8* +call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 0, i64 12, i1 false) +%ptrptr = bitcast [3 x i32]* %x to i32* +store i32* %ptrptr, i32** %y, align 8 +%1 = bitcast [3 x i32]* %x to i32* +%2 = insertvalue %"int[]" undef, i32* %1, 0 +%3 = insertvalue %"int[]" %2, i64 3, 1 +store %"int[]" %3, %"int[]"* %z, align 8 \ No newline at end of file diff --git a/test/test_suite/arrays/array_invalid_casts.c3 b/test/test_suite/arrays/array_invalid_casts.c3 new file mode 100644 index 000000000..38e43a4cf --- /dev/null +++ b/test/test_suite/arrays/array_invalid_casts.c3 @@ -0,0 +1,12 @@ + +func void test() +{ + int[3] x; + double *y = &x; // #error: Cannot implicitly cast 'int[3]*' to 'double* +} + +func void test2() +{ + int[3] x; + double[] z = &x; // #error: Cannot cast 'int[3]*' to 'double[]' +} \ No newline at end of file diff --git a/test/test_suite/expressions/pointer_access.c3t b/test/test_suite/expressions/pointer_access.c3t index 32d1c7a4d..6c2ab5ef7 100644 --- a/test/test_suite/expressions/pointer_access.c3t +++ b/test/test_suite/expressions/pointer_access.c3t @@ -29,11 +29,12 @@ struct ExtraSimple func void testSimple() { - ExtraSimple a = { c.j = 3.3 }; + ExtraSimple a = { .c.j = 3.3 }; a.c.j = 3.4; printf("a = %d, c.e = %f, c.f = %f, c.j = %f, g = %d, o0 = %f, r = %d, s = %d\n", a.a, a.c.e, a.c.f, a.c.j, a.g, a.o0, a.r, a.s); } +// TODO these may be wrong. // #expect: pointer_access.ll %pointer_access.ExtraSimple = type { i32, i32, %pointer_access.c, %pointer_access.anon, %pointer_access.anon.0, i32 } @@ -45,39 +46,32 @@ func void testSimple() entry: %a = alloca %pointer_access.ExtraSimple, align 8 - %literal = alloca %pointer_access.ExtraSimple, align 8 - %0 = bitcast %pointer_access.ExtraSimple* %literal to i8* - call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 72, i1 false) - %c = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %literal, i32 0, i32 2 - %double = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c, i32 0, i32 4 - store double 3.300000e+00, double* %double - %1 = bitcast %pointer_access.ExtraSimple* %a to i8* - %2 = bitcast %pointer_access.ExtraSimple* %literal to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %1, i8* align 8 %2, i32 72, i1 false) - %c1 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 - %j = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c1, i32 0, i32 4 + %0 = bitcast %pointer_access.ExtraSimple* %a to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %0, i8* align 8 bitcast (%pointer_access.ExtraSimple* @0 to i8*), i32 72, i1 false) + %c = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 + %j = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c, i32 0, i32 4 store double 3.400000e+00, double* %j, align 8 - %a2 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 0 - %3 = load i32, i32* %a2, align 4 + %a1 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 0 + %1 = load i32, i32* %a1, align 4 + %c2 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 + %e = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c2, i32 0, i32 0 + %2 = load double, double* %e, align 8 %c3 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 - %e = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c3, i32 0, i32 0 - %4 = load double, double* %e, align 8 + %f = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c3, i32 0, i32 3 + %3 = load double, double* %f, align 8 %c4 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 - %f = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c4, i32 0, i32 3 - %5 = load double, double* %f, align 8 - %c5 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 - %j6 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c5, i32 0, i32 4 - %6 = load double, double* %j6, align 8 + %j5 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c4, i32 0, i32 4 + %4 = load double, double* %j5, align 8 %g = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 5 - %7 = load i32, i32* %g, align 4 + %5 = load i32, i32* %g, align 4 %anon = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 4 %o0 = bitcast %pointer_access.anon.0* %anon to double* - %8 = load double, double* %o0, align 8 + %6 = load double, double* %o0, align 8 + %anon6 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3 + %r = getelementptr inbounds %pointer_access.anon, %pointer_access.anon* %anon6, i32 0, i32 0 + %7 = load i32, i32* %r, align 4 %anon7 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3 - %r = getelementptr inbounds %pointer_access.anon, %pointer_access.anon* %anon7, i32 0, i32 0 - %9 = load i32, i32* %r, align 4 - %anon8 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3 - %s = getelementptr inbounds %pointer_access.anon, %pointer_access.anon* %anon8, i32 0, i32 1 - %10 = load i32, i32* %s, align 4 - call void (i8*, ...) @printf(i8* getelementptr inbounds ([71 x i8], [71 x i8]* @0, i32 0, i32 0), i32 %3, double %4, double %5, double %6, i32 %7, double %8, i32 %9, i32 %10) + %s = getelementptr inbounds %pointer_access.anon, %pointer_access.anon* %anon7, i32 0, i32 1 + %8 = load i32, i32* %s, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([71 x i8], [71 x i8]* @1, i32 0, i32 0), i32 %1, double %2, double %3, double %4, i32 %5, double %6, i32 %7, i32 %8) ret void \ No newline at end of file diff --git a/test/test_suite/functions/varargs.c3t b/test/test_suite/functions/varargs.c3t new file mode 100644 index 000000000..9d6eb9206 --- /dev/null +++ b/test/test_suite/functions/varargs.c3t @@ -0,0 +1,38 @@ +module test; + +extern func void printf(char* c, ...); + +func void test() +{ + printf("%d\n", true); + printf("%d\n", 123); + printf("%f\n", 12.3); + char x1 = -123; + bool b = false; + float z1 = 12.3; + printf("%d\n", b); + printf("%d\n", x1); + printf("%f\n", z1); +} + +// #expect: varargs.ll + + %x1 = alloca i8, align 1 + %b = alloca i8, align 1 + %z1 = alloca float, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i32 0, i32 0), i32 1) + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @1, i32 0, i32 0), i32 123) + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @2, i32 0, i32 0), double 1.230000e+01) + store i8 -123, i8* %x1, align 1 + store i8 0, i8* %b, align 1 + store float 0x40289999A0000000, float* %z1, align 4 + %0 = load i8, i8* %b, align 1 + %1 = trunc i8 %0 to i1 + %boolsi = zext i1 %1 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @3, i32 0, i32 0), i32 %boolsi) + %2 = load i8, i8* %x1, align 1 + %sisiext = sext i8 %2 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @4, i32 0, i32 0), i32 %sisiext) + %3 = load float, float* %z1, align 4 + %fpfpext = fpext float %3 to double + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @5, i32 0, i32 0), double %fpfpext) \ No newline at end of file diff --git a/test/test_suite/struct/member_access.c3 b/test/test_suite/struct/member_access.c3 index 211e0075c..ee850e06a 100644 --- a/test/test_suite/struct/member_access.c3 +++ b/test/test_suite/struct/member_access.c3 @@ -50,7 +50,7 @@ func void tester() p.uu.e = 8; Point *p2 = &p; p2.bb.b = 3; - p = { a = 1, bb.b = 3, e = 2 }; + p = { .a = 1, .bb.b = 3, .e = 2 }; Point* pp = &p; pp.a = 20; @@ -82,6 +82,6 @@ struct Struct func void myfunc() { Struct s; - s.b = 10; // #error: There is no element or method 'Struct.b' + s.b = 10; // #error: There is no field or method 'Struct.b' } diff --git a/test/test_suite/struct/struct_codegen.c3t b/test/test_suite/struct/struct_codegen.c3t index 8251c54aa..f60f72abb 100644 --- a/test/test_suite/struct/struct_codegen.c3t +++ b/test/test_suite/struct/struct_codegen.c3t @@ -20,5 +20,5 @@ func void test1() entry: %p = alloca %test.Point, align 4 %0 = bitcast %test.Point* %p to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 4 bitcast (%test.Point* @0 to i8*), i32 8, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 8 bitcast (%test.Point* @0 to i8*), i32 8, i1 false) diff --git a/test/test_suite/struct/struct_codegen_empty.c3t b/test/test_suite/struct/struct_codegen_empty.c3t new file mode 100644 index 000000000..d881f322d --- /dev/null +++ b/test/test_suite/struct/struct_codegen_empty.c3t @@ -0,0 +1,39 @@ +struct StructA +{ + int a; +} + +struct StructB +{ + struct b + { + int a; + } +} + +func void test() +{ + StructA a = {}; + StructA a2; + StructB b = {}; + StructB b2; + StructB b3 = { .b = { } }; +} + +// #expect: struct_codegen_empty.ll + + %a = alloca %struct_codegen_empty.StructA, align 4 + %a2 = alloca %struct_codegen_empty.StructA, align 4 + %b = alloca %struct_codegen_empty.StructB, align 4 + %b2 = alloca %struct_codegen_empty.StructB, align 4 + %b3 = alloca %struct_codegen_empty.StructB, align 4 + %0 = bitcast %struct_codegen_empty.StructA* %a to i8* + call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 0, i64 4, i1 false) + %1 = bitcast %struct_codegen_empty.StructA* %a2 to i8* + call void @llvm.memset.p0i8.i64(i8* align 4 %1, i8 0, i64 4, i1 false) + %2 = bitcast %struct_codegen_empty.StructB* %b to i8* + call void @llvm.memset.p0i8.i64(i8* align 4 %2, i8 0, i64 4, i1 false) + %3 = bitcast %struct_codegen_empty.StructB* %b2 to i8* + call void @llvm.memset.p0i8.i64(i8* align 4 %3, i8 0, i64 4, i1 false) + %4 = bitcast %struct_codegen_empty.StructB* %b3 to i8* + call void @llvm.memset.p0i8.i64(i8* align 4 %4, i8 0, i64 4, i1 false) \ No newline at end of file diff --git a/test/test_suite/subarrays/slice_negative_len.c3 b/test/test_suite/subarrays/slice_negative_len.c3 index bd8bd7b15..23455c12a 100644 --- a/test/test_suite/subarrays/slice_negative_len.c3 +++ b/test/test_suite/subarrays/slice_negative_len.c3 @@ -22,7 +22,7 @@ func void test3() func void test4() { int[3] x = { 1, 2, 3 }; - int[] z = x[..^0]; + int[] z = x[..^1]; z = x[..^-1]; // #error: Negative numbers are not allowed when indexing from the end. } @@ -63,4 +63,10 @@ func void test10() int[] w = x[0..]; // #error: Omitting end index is not allowed for pointers. int[] z = x[^2..]; // #error: Indexing from the end is not allowed for pointers. int[] y = x[..^2]; // #error: Indexing from the end is not allowed for pointers. -} \ No newline at end of file +} + +func void test11() +{ + int[3] x = { 1, 2, 3 }; + int[] z = x[..^0]; // #error: Array end index out of bounds, was 3, exceeding array length 3 +} diff --git a/test/test_suite/subarrays/slice_offset.c3t b/test/test_suite/subarrays/slice_offset.c3t index b7d7a0007..ebcd1244a 100644 --- a/test/test_suite/subarrays/slice_offset.c3t +++ b/test/test_suite/subarrays/slice_offset.c3t @@ -13,5 +13,5 @@ func void test() %1 = bitcast [3 x i32]* %x to i32* %offset = getelementptr inbounds i32, i32* %1, i64 1 %2 = insertvalue %"int[]" undef, i32* %offset, 0 - %3 = insertvalue %"int[]" %2, i64 1, 1 + %3 = insertvalue %"int[]" %2, i64 2, 1 store %"int[]" %3, %"int[]"* %y, align 8 diff --git a/test/test_suite/subarrays/slice_offset_neg_end.c3t b/test/test_suite/subarrays/slice_offset_neg_end.c3t index b7f227b60..631b355a8 100644 --- a/test/test_suite/subarrays/slice_offset_neg_end.c3t +++ b/test/test_suite/subarrays/slice_offset_neg_end.c3t @@ -14,6 +14,6 @@ func void test() %1 = bitcast [3 x i32]* %x to i32* %offset = getelementptr inbounds i32, i32* %1, i64 1 %2 = insertvalue %"int[]" undef, i32* %offset, 0 - %3 = insertvalue %"int[]" %2, i64 1, 1 + %3 = insertvalue %"int[]" %2, i64 2, 1 store %"int[]" %3, %"int[]"* %y, align 8 diff --git a/test/test_suite/subarrays/slice_offset_neg_start.c3t b/test/test_suite/subarrays/slice_offset_neg_start.c3t index 536ea1f0e..5347c126b 100644 --- a/test/test_suite/subarrays/slice_offset_neg_start.c3t +++ b/test/test_suite/subarrays/slice_offset_neg_start.c3t @@ -1,7 +1,7 @@ func void test() { int[3] x = { 1, 2, 3 }; - int[] y = x[^2..2]; + int[] y = x[^2..1]; } // #expect: slice_offset_neg_start.ll diff --git a/test/test_suite/subarrays/slice_syntax.c3 b/test/test_suite/subarrays/slice_syntax.c3 index 546d51fa5..44f324fc6 100644 --- a/test/test_suite/subarrays/slice_syntax.c3 +++ b/test/test_suite/subarrays/slice_syntax.c3 @@ -2,9 +2,9 @@ func void test() { int[6] feok2 = { 1, 8, 100, 293, 23982, 34}; int[] feok = &feok2; - int[] flok = feok2[3..6]; + int[] flok = feok2[3..5]; int[] flak = flok[1..2]; - flok = feok2[..6]; + flok = feok2[..5]; flok = feok2[..^2]; flok = feok2[..]; flok = feok2[^3..]; diff --git a/test/test_suite/union/union_codegen_const.c3t b/test/test_suite/union/union_codegen_const.c3t new file mode 100644 index 000000000..36509c722 --- /dev/null +++ b/test/test_suite/union/union_codegen_const.c3t @@ -0,0 +1,20 @@ +module test; + +union Foo +{ + int a; + double b; +} + +Foo f = { .a = 23 }; +Foo g = { .b = 2.3 }; +Foo h = { .a = 23, .b = 2.3 }; +Foo i = { .b = 2.3, .a = 23 }; + +// #expect: union_codegen_const.ll + +@f = protected global { i32, [4 x i8] } { i32 23, [4 x i8] undef }, align 8 +@g = protected global %test.Foo { double 2.300000e+00 }, align 8 +@h = protected global %test.Foo { double 2.300000e+00 }, align 8 +@i = protected global { i32, [4 x i8] } { i32 23, [4 x i8] undef }, align 8 + diff --git a/test/test_suite/union/union_codegen_empty.c3t b/test/test_suite/union/union_codegen_empty.c3t new file mode 100644 index 000000000..6939d14dc --- /dev/null +++ b/test/test_suite/union/union_codegen_empty.c3t @@ -0,0 +1,45 @@ +union UnionA +{ + int a; +} + +union UnionB +{ + struct b + { + int a; + } + int c; + double d; +} + +func void test() +{ + UnionA a = {}; + UnionA a2; + UnionB b = {}; + UnionB b2; + UnionB b3 = { .b = {} }; + UnionB b4 = { .b.a = 23, .c = 4, .d = 0.4, .b = {} }; +} + +// #expect: union_codegen_empty.ll + +%a = alloca %union_codegen_empty.UnionA, align 4 +%a2 = alloca %union_codegen_empty.UnionA, align 4 +%b = alloca %union_codegen_empty.UnionB, align 8 +%b2 = alloca %union_codegen_empty.UnionB, align 8 +%b3 = alloca %union_codegen_empty.UnionB, align 8 +%b4 = alloca %union_codegen_empty.UnionB, align 8 +%0 = bitcast %union_codegen_empty.UnionA* %a to i8* +call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 0, i64 4, i1 false) +%1 = bitcast %union_codegen_empty.UnionA* %a2 to i8* +call void @llvm.memset.p0i8.i64(i8* align 4 %1, i8 0, i64 4, i1 false) +%2 = bitcast %union_codegen_empty.UnionB* %b to i8* +call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false) + %3 = bitcast %union_codegen_empty.UnionB* %b2 to i8* +call void @llvm.memset.p0i8.i64(i8* align 8 %3, i8 0, i64 8, i1 false) +%4 = bitcast %union_codegen_empty.UnionB* %b3 to i8* +call void @llvm.memset.p0i8.i64(i8* align 8 %4, i8 0, i64 8, i1 false) +%5 = bitcast %union_codegen_empty.UnionB* %b4 to i8* +call void @llvm.memset.p0i8.i64(i8* align 8 %5, i8 0, i64 8, i1 false) diff --git a/test/test_suite/union/union_codegen_overwrite_call.c3t b/test/test_suite/union/union_codegen_overwrite_call.c3t new file mode 100644 index 000000000..651e4d60a --- /dev/null +++ b/test/test_suite/union/union_codegen_overwrite_call.c3t @@ -0,0 +1,30 @@ +module test; + +union UnionB +{ + struct b + { + int a; + } + int c; + double d; +} + +extern func int bar(); + +func void test() +{ + UnionB b = { .c = bar(), .b = {} }; +} + +// #expect: union_codegen_overwrite_call.ll + +entry: +%b = alloca %test.UnionB, align 8 +%1 = bitcast %test.UnionB* %b to i32* +%2 = call i32 @bar() +store i32 %2, i32* %1, align 4 +%3 = bitcast %test.UnionB* %b to %test.b* +%4 = bitcast %test.b* %3 to i8* +call void @llvm.memset.p0i8.i64(i8* %4, i8 0, i64 4, i1 false) +ret void