mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Optimized and improved aggregate initialization. Compound literal updated to Foo({ 1, 2 })-style. ".name = x" style initialization for named arguments and designated initializers. Added runtime messages on panics. subarrays convert implictly to pointers. len/len() functions. Fix taking slice of pointer. Vararg fixes
Consistent length typedef.
First stab at initializers.
Change compound literal to Foo({ 1, 2 }) style.
Fixed up some tests.
Optimize the zero struct codegen.
Optimize union empty initializer.
Fix issues with unions. Added alignment to globals. Added some union tests.
Use puts to emit error messages during runtime. Fixup of int[] -> int* style conversions.
Fix implicit conversion of int[3]* -> int*
Fix int[] size. Use () to invoke the length of a subarray. Fix taking a slice of a pointer. Limit the number of members in a struct.
Fixes to vararg using debug and cleanups to slices.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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, // >>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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.");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
25
test/test_suite/arrays/array_casts.c3t
Normal file
25
test/test_suite/arrays/array_casts.c3t
Normal file
@@ -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
|
||||
12
test/test_suite/arrays/array_invalid_casts.c3
Normal file
12
test/test_suite/arrays/array_invalid_casts.c3
Normal file
@@ -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[]'
|
||||
}
|
||||
@@ -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
|
||||
38
test/test_suite/functions/varargs.c3t
Normal file
38
test/test_suite/functions/varargs.c3t
Normal file
@@ -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)
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
39
test/test_suite/struct/struct_codegen_empty.c3t
Normal file
39
test/test_suite/struct/struct_codegen_empty.c3t
Normal file
@@ -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)
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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..];
|
||||
|
||||
20
test/test_suite/union/union_codegen_const.c3t
Normal file
20
test/test_suite/union/union_codegen_const.c3t
Normal file
@@ -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
|
||||
|
||||
45
test/test_suite/union/union_codegen_empty.c3t
Normal file
45
test/test_suite/union/union_codegen_empty.c3t
Normal file
@@ -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)
|
||||
30
test/test_suite/union/union_codegen_overwrite_call.c3t
Normal file
30
test/test_suite/union/union_codegen_overwrite_call.c3t
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user