More correct alignment. Foreach added. Removed dynamic designated array initialization. Use 16 byte alignment on arrays >= 16 for x64

This commit is contained in:
Christoffer Lerno
2021-01-27 00:09:09 +01:00
committed by Christoffer Lerno
parent 26d25e3f74
commit 45c4f205bb
29 changed files with 1078 additions and 203 deletions

View File

@@ -90,7 +90,7 @@ the r/ProgrammingLanguages Discord: https://discord.gg/cfu4wdk
- [x] `next` statement
- [x] Expression blocks
- [x] Do-without-while
- [ ] Foreach statement
- [x] Foreach statement
- [ ] All attributes
- [ ] Associative array literals
- [ ] CT type constants
@@ -117,10 +117,10 @@ the r/ProgrammingLanguages Discord: https://discord.gg/cfu4wdk
- [ ] String functions
- [ ] Vararrays e.g. `int[*]`
- [ ] Compile time incremental arrays
- [-] Complete C ABI conformance *in progress*
- [ ] Complete C ABI conformance *in progress*
- [ ] Generic functions
- [-] Debug info *in progress*
- [ ] Simd vectors
- [ ] Debug info *in progress*
- [ ] Simd vector types
- [ ] Complex types
#### What can you help with?

View File

@@ -46,11 +46,6 @@ static TypeInfo poison_type_info = { .kind = TYPE_INFO_POISON };
Type *poisoned_type = &poison_type;
TypeInfo *poisoned_type_info = &poison_type_info;
AlignSize decl_abi_alignment(Decl *decl)
{
return decl->alignment ?: type_abi_alignment(decl->type);
}
void decl_set_external_name(Decl *decl)
{
if (decl->visibility == VISIBLE_EXTERN)
@@ -1108,6 +1103,13 @@ static void fprint_ast_recursive(Context *context, FILE *file, Ast *ast, int ind
}
DUMPAST(ast->for_stmt.body);
DUMPEND();
case AST_FOREACH_STMT:
DUMP("(foreach");
DUMPDECL(ast->foreach_stmt.index);
DUMPDECL(ast->foreach_stmt.variable);
DUMPEXPR(ast->foreach_stmt.enumeration);
DUMPAST(ast->foreach_stmt.body);
DUMPEND();
case AST_IF_STMT:
DUMP("(if");
DUMPEXPR(ast->if_stmt.cond);

View File

@@ -908,6 +908,20 @@ typedef struct
void *exit_block;
} AstForStmt;
typedef struct
{
FlowCommon flow;
bool index_by_ref : 1;
bool value_by_ref : 1;
CastKind cast;
Decl *index;
Decl *variable;
Expr *enumeration;
Ast *body;
void *continue_block;
void *exit_block;
} AstForeachStmt;
typedef struct
{
AstId prev_defer;
@@ -1066,6 +1080,7 @@ typedef struct _Ast
AstNextStmt next_stmt; // 16
AstCatchStmt catch_stmt; // 32
AstForStmt for_stmt; // 32
AstForeachStmt foreach_stmt;
AstCtIfStmt ct_if_stmt; // 24
AstCtIfStmt ct_elif_stmt; // 24
Ast *ct_else_stmt; // 8
@@ -1435,7 +1450,6 @@ 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);
AlignSize decl_abi_alignment(Decl *decl);
static inline DeclKind decl_from_token(TokenType type);
#pragma mark --- Diag functions
@@ -1528,6 +1542,7 @@ bool sema_unwrap_var(Context *context, Decl *decl);
bool sema_rewrap_var(Context *context, Decl *decl);
bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr, bool may_be_failable);
ArrayIndex sema_get_initializer_const_array_size(Context *context, Expr *initializer, bool *may_be_array, bool *is_const_size);
bool sema_analyse_expr(Context *context, Type *to, Expr *expr);
bool sema_analyse_decl(Context *context, Decl *decl);
bool sema_analyse_ct_assert_stmt(Context *context, Ast *statement);
@@ -1648,7 +1663,7 @@ static inline Type *type_reduced_from_expr(Expr *expr)
static inline bool type_is_integer(Type *type)
{
assert(type == type->canonical);
return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_U64;
return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_U128;
}
@@ -1661,19 +1676,19 @@ static inline bool type_is_any_integer(Type *type)
static inline bool type_is_integer_signed(Type *type)
{
assert(type == type->canonical);
return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_I64;
return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_I128;
}
static inline bool type_is_integer_kind(Type *type)
{
assert(type == type->canonical);
return type->type_kind >= TYPE_BOOL && type->type_kind <= TYPE_U64;
return type->type_kind >= TYPE_BOOL && type->type_kind <= TYPE_U128;
}
static inline bool type_is_integer_unsigned(Type *type)
{
assert(type == type->canonical);
return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_U64;
return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_U128;
}
static inline bool type_info_poison(TypeInfo *type)

View File

@@ -72,6 +72,7 @@ typedef enum
AST_EXPR_STMT,
AST_TRY_STMT,
AST_FOR_STMT,
AST_FOREACH_STMT,
AST_IF_STMT,
AST_NOP_STMT,
AST_RETURN_STMT,
@@ -410,6 +411,7 @@ typedef enum
TOKEN_EXTERN,
TOKEN_FALSE,
TOKEN_FOR,
TOKEN_FOREACH,
TOKEN_FUNC,
TOKEN_GENERIC,
TOKEN_IF,

View File

@@ -5,10 +5,6 @@
#include "llvm_codegen_internal.h"
static inline void llvm_set_alignment(LLVMValueRef alloca, LLVMTypeRef type, AlignSize alignment)
{
LLVMSetAlignment(alloca, alignment ?: llvm_abi_alignment(type));
}
static int get_inlining_threshold(void);
static void diagnostics_handler(LLVMDiagnosticInfoRef ref, void *context)
@@ -292,7 +288,6 @@ static void gencontext_emit_global_variable_definition(GenContext *c, Decl *decl
if (!decl->type) return;
decl->backend_ref = LLVMAddGlobal(c->module, llvm_get_type(c, decl->type), "tempglobal");
}
static void gencontext_emit_global_variable_init(GenContext *c, Decl *decl)
@@ -305,7 +300,7 @@ static void gencontext_emit_global_variable_init(GenContext *c, Decl *decl)
bool modified = false;
LLVMValueRef init_value;
ByteSize alignment = type_abi_alignment(decl->type);
ByteSize alignment = type_alloca_alignment(decl->type);
if (decl->var.init_expr)
{
@@ -329,7 +324,7 @@ static void gencontext_emit_global_variable_init(GenContext *c, Decl *decl)
// TODO fix name
LLVMValueRef old = decl->backend_ref;
decl->backend_ref = LLVMAddGlobal(c->module, LLVMTypeOf(init_value), decl->name);
LLVMSetAlignment(decl->backend_ref, alignment);
llvm_set_alignment(decl->backend_ref, alignment);
if (decl->visibility != VISIBLE_EXTERN)
{
LLVMSetInitializer(decl->backend_ref, init_value);
@@ -407,10 +402,11 @@ void gencontext_print_llvm_ir(GenContext *context)
LLVMValueRef llvm_emit_alloca(GenContext *context, LLVMTypeRef type, unsigned alignment, const char *name)
{
assert(alignment > 0);
LLVMBasicBlockRef current_block = LLVMGetInsertBlock(context->builder);
LLVMPositionBuilderBefore(context->builder, context->alloca_point);
LLVMValueRef alloca = LLVMBuildAlloca(context->builder, type, name);
llvm_set_alignment(alloca, type, alignment);
llvm_set_alignment(alloca, alignment);
LLVMPositionBuilderAtEnd(context->builder, current_block);
return alloca;
}
@@ -420,13 +416,20 @@ LLVMValueRef llvm_emit_alloca_aligned(GenContext *c, Type *type, const char *nam
return llvm_emit_alloca(c, llvm_get_type(c, type), type_alloca_alignment(type), name);
}
LLVMValueRef llvm_emit_decl_alloca(GenContext *c, Decl *decl)
void llvm_emit_and_set_decl_alloca(GenContext *c, Decl *decl)
{
LLVMTypeRef type = llvm_get_type(c, decl->type);
return llvm_emit_alloca(c,
type,
decl->alignment ?: type_alloca_alignment(decl->type),
decl->name ?: "anon");
decl->backend_ref = llvm_emit_alloca(c, type, decl->alignment, decl->name ?: "anon");
}
void llvm_emit_local_var_alloca(GenContext *c, Decl *decl)
{
llvm_emit_and_set_decl_alloca(c, decl);
if (llvm_use_debug(c))
{
llvm_emit_debug_local_var(c, decl);
}
}
/**
@@ -697,6 +700,18 @@ void llvm_value_set_address_align(BEValue *value, LLVMValueRef llvm_value, Type
value->kind = BE_ADDRESS;
value->type = type_flatten(type);
}
void llvm_value_set_decl_address(BEValue *value, Decl *decl)
{
llvm_value_set_address(value, decl_ref(decl), decl->type);
value->alignment = decl->alignment;
if (decl->decl_kind == DECL_VAR && decl->var.failable)
{
value->kind = BE_ADDRESS_FAILABLE;
value->failable = decl->var.failable_ref;
}
}
void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type)
{
llvm_value_set_address_align(value, llvm_value, type_flatten(type), type_abi_alignment(type));
@@ -1015,7 +1030,7 @@ void llvm_store_self_aligned(GenContext *context, LLVMValueRef pointer, LLVMValu
void llvm_store_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment)
{
LLVMValueRef ref = LLVMBuildStore(context->builder, value, pointer);
if (alignment) LLVMSetAlignment(ref, alignment);
if (alignment) llvm_set_alignment(ref, alignment);
}
void llvm_store_aligned_decl(GenContext *context, Decl *decl, LLVMValueRef value)
@@ -1037,14 +1052,13 @@ void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLV
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);
llvm_emit_memcpy(c, decl->backend_ref, decl->alignment ?: type_abi_alignment(decl->type),
source, source_alignment, type_size(decl->type));
llvm_emit_memcpy(c, decl->backend_ref, decl->alignment, source, source_alignment, type_size(decl->type));
}
LLVMValueRef llvm_emit_load_aligned(GenContext *context, LLVMTypeRef type, LLVMValueRef pointer, unsigned alignment, const char *name)
LLVMValueRef llvm_emit_load_aligned(GenContext *context, LLVMTypeRef type, LLVMValueRef pointer, AlignSize alignment, const char *name)
{
LLVMValueRef value = LLVMBuildLoad2(context->builder, type, pointer, name);
llvm_set_alignment(value, type, alignment);
llvm_set_alignment(value, alignment ?: llvm_abi_alignment(type));
return value;
}
@@ -1052,3 +1066,15 @@ unsigned llvm_store_size(LLVMTypeRef type)
{
return LLVMStoreSizeOfType(target_data_layout(), type);
}
void llvm_set_error_exit(GenContext *c, LLVMBasicBlockRef block)
{
c->catch_block = block;
c->error_var = NULL;
}
void llvm_set_error_exit_and_value(GenContext *c, LLVMBasicBlockRef block, LLVMValueRef ref)
{
c->catch_block = block;
c->error_var = ref;
}

View File

@@ -6,7 +6,6 @@
#include "compiler_internal.h"
#include "bigint.h"
static LLVMValueRef llvm_emit_int_comparison(GenContext *c, Type *lhs_type, Type *rhs_type, LLVMValueRef lhs_value, LLVMValueRef rhs_value, BinaryOp binary_op);
static void gencontext_emit_unary_expr(GenContext *context, BEValue *value, Expr *expr);
static inline void llvm_emit_post_inc_dec(GenContext *c, BEValue *value, Expr *expr, int diff, bool use_mod);
static inline void llvm_emit_pre_inc_dec(GenContext *c, BEValue *value, Expr *expr, int diff, bool use_mod);
@@ -354,18 +353,6 @@ static void gencontext_emit_scoped_expr(GenContext *context, BEValue *value, Exp
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)
{
llvm_value_set_address(value, decl_ref(decl), decl->type);
value->alignment = decl_abi_alignment(decl);
if (decl->decl_kind == DECL_VAR && decl->var.failable)
{
value->kind = BE_ADDRESS_FAILABLE;
value->failable = decl->var.failable_ref;
}
}
/**
* Here we are converting an array to a subarray.
@@ -617,8 +604,8 @@ static inline void llvm_emit_initialize_reference_temporary_const(GenContext *c,
LLVMValueRef global_copy = LLVMAddGlobal(c->module, type, "");
// Set a nice alignment
unsigned alignment = LLVMPreferredAlignmentOfGlobal(target_data_layout(), global_copy);
LLVMSetAlignment(global_copy, alignment);
unsigned alignment = type_alloca_alignment(expr->type);
llvm_set_alignment(global_copy, alignment);
// Set the value and make it constant
LLVMSetInitializer(global_copy, value);
@@ -829,57 +816,6 @@ static void llvm_emit_initialize_designated_const_range(GenContext *c, BEValue *
}
}
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)
{
@@ -909,7 +845,7 @@ static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, uint64_
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);
unsigned decl_alignment = decl->alignment;
if (ref->type->type_kind == TYPE_UNION)
{
llvm_value_set_address_align(&value, llvm_emit_bitcast(c, ref->value, type_get_ptr(type)), type, type_min_alignment(offset, decl_alignment));
@@ -925,34 +861,17 @@ static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, uint64_
{
Type *type = ref->type->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);
}
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);
LLVMValueRef ptr = LLVMBuildInBoundsGEP2(c->builder, llvm_get_type(c, ref->type), ref->value, indices, 2, "");
llvm_value_set_address_align(&value, ptr, type, type_min_alignment(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);
}
llvm_emit_initialize_designated_const_range(c, ref, offset, current, last, expr, emitted_value);
break;
default:
UNREACHABLE
@@ -1206,23 +1125,20 @@ static void gencontext_emit_unary_expr(GenContext *c, BEValue *value, Expr *expr
UNREACHABLE
}
static void gencontext_emit_len(GenContext *c, BEValue *be_value, Expr *expr)
void llvm_emit_len_for_expr(GenContext *c, BEValue *be_value, BEValue *expr_to_len)
{
Expr *inner = expr->len_expr.inner;
llvm_emit_expr(c, be_value, inner);
llvm_value_addr(c, be_value);
Type *type = inner->type;
switch (type->canonical->type_kind)
llvm_value_addr(c, expr_to_len);
switch (expr_to_len->type->type_kind)
{
case TYPE_SUBARRAY:
{
LLVMTypeRef subarray_type = llvm_get_type(c, type);
LLVMValueRef len_addr = LLVMBuildStructGEP2(c->builder, subarray_type, be_value->value, 1, "len");
LLVMTypeRef subarray_type = llvm_get_type(c, expr_to_len->type);
LLVMValueRef len_addr = LLVMBuildStructGEP2(c->builder, subarray_type, expr_to_len->value, 1, "len");
llvm_value_set_address(be_value, len_addr, type_usize);
break;
}
case TYPE_ARRAY:
llvm_value_set(be_value, llvm_const_int(c, type_usize, type->array.len), type_usize);
llvm_value_set(be_value, llvm_const_int(c, type_usize, expr_to_len->type->array.len), type_usize);
break;
case TYPE_VARARRAY:
case TYPE_STRING:
@@ -1231,6 +1147,11 @@ static void gencontext_emit_len(GenContext *c, BEValue *be_value, Expr *expr)
UNREACHABLE
}
}
static void llvm_emit_len(GenContext *c, BEValue *be_value, Expr *expr)
{
llvm_emit_expr(c, be_value, expr->len_expr.inner);
llvm_emit_len_for_expr(c, be_value, be_value);
}
static void gencontext_emit_trap_negative(GenContext *context, Expr *expr, LLVMValueRef value, const char *error)
{
@@ -1561,7 +1482,7 @@ static void gencontext_emit_logical_and_or(GenContext *c, BEValue *be_value, Exp
static LLVMValueRef llvm_emit_int_comparison(GenContext *c, Type *lhs_type, Type *rhs_type, LLVMValueRef lhs_value, LLVMValueRef rhs_value, BinaryOp binary_op)
LLVMValueRef llvm_emit_int_comparison(GenContext *c, Type *lhs_type, Type *rhs_type, LLVMValueRef lhs_value, LLVMValueRef rhs_value, BinaryOp binary_op)
{
bool lhs_signed = type_is_signed(lhs_type);
bool rhs_signed = type_is_signed(rhs_type);
@@ -2122,8 +2043,7 @@ static void gencontext_emit_binary_expr(GenContext *context, BEValue *be_value,
{
failable_ref = decl_failable_ref(expr->binary_expr.left->identifier_expr.decl);
}
LLVMValueRef result = llvm_emit_assign_expr(context, be_value->value, expr->binary_expr.right, failable_ref);
llvm_value_set(be_value, result, expr->type);
*be_value = llvm_emit_assign_expr(context, be_value, expr->binary_expr.right, failable_ref);
return;
}
@@ -2594,7 +2514,7 @@ void llvm_emit_call_expr(GenContext *context, BEValue *be_value, Expr *expr)
vec_add(values, return_param);
if (ret_info->indirect.realignment)
{
LLVMSetAlignment(return_param, ret_info->indirect.realignment);
llvm_set_alignment(return_param, ret_info->indirect.realignment);
}
break;
case ABI_ARG_EXPAND:
@@ -2834,7 +2754,7 @@ static inline void gencontext_emit_macro_block(GenContext *context, BEValue *be_
case VARDECL_PARAM:
break;
}
decl->backend_ref = llvm_emit_decl_alloca(context, decl);
llvm_emit_and_set_decl_alloca(context, decl);
BEValue value;
llvm_emit_expr(context, &value, expr->macro_block.args[i]);
printf("TODO: unoptimized use of BEValue\n");
@@ -2871,8 +2791,9 @@ LLVMValueRef llvm_emit_call_intrinsic(GenContext *context, unsigned intrinsic_id
return LLVMBuildCall2(context->builder, type, decl, values, arg_count, "");
}
LLVMValueRef llvm_emit_assign_expr(GenContext *c, LLVMValueRef ref, Expr *expr, LLVMValueRef failable)
BEValue llvm_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, LLVMValueRef failable)
{
llvm_value_addr(c, ref);
LLVMBasicBlockRef assign_block = NULL;
PUSH_ERROR();
@@ -2895,16 +2816,13 @@ LLVMValueRef llvm_emit_assign_expr(GenContext *c, LLVMValueRef ref, Expr *expr,
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);
llvm_emit_initialize_reference(c, ref, expr);
value = *ref;
}
else
{
llvm_emit_expr(c, &value, expr);
printf("TODO: // Optimize store \n");
llvm_store_bevalue_aligned(c, ref, &value, 0);
llvm_store_bevalue(c, ref, &value);
}
if (failable)
@@ -2919,7 +2837,7 @@ LLVMValueRef llvm_emit_assign_expr(GenContext *c, LLVMValueRef ref, Expr *expr,
llvm_emit_block(c, assign_block);
}
return value.value;
return value;
}
@@ -2978,7 +2896,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr)
gencontext_emit_slice(c, value, expr);
return;
case EXPR_LEN:
gencontext_emit_len(c, value, expr);
llvm_emit_len(c, value, expr);
return;
case EXPR_FAILABLE:
gencontext_emit_failable(c, value, expr);
@@ -3030,7 +2948,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr)
UNREACHABLE
case EXPR_IDENTIFIER:
case EXPR_CONST_IDENTIFIER:
gencontext_emit_identifier(c, value, expr->identifier_expr.decl);
llvm_value_set_decl_address(value, expr->identifier_expr.decl);
return;
case EXPR_SUBSCRIPT:
gencontext_emit_subscript(c, value, expr);

View File

@@ -156,7 +156,7 @@ 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);
AlignSize decl_alignment = decl_abi_alignment(decl);
AlignSize decl_alignment = decl->alignment;
// Cast to { lo, hi }
LLVMValueRef cast = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(struct_type, 0), "pair");
// Point to the lo value.
@@ -181,7 +181,6 @@ 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");
AlignSize decl_alignment = decl_abi_alignment(decl);
// If we're not flattening, we simply do a store.
if (!abi_info_should_flatten(info))
@@ -200,7 +199,7 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, unsig
LLVMValueRef element_ptr = LLVMBuildStructGEP2(c->builder, coerce_type, cast, idx, "");
LLVMValueRef value = llvm_get_next_param(c, index);
llvm_store_aligned(c, element_ptr, value, MIN(llvm_abi_alignment(element_type), decl_alignment));
llvm_store_aligned(c, element_ptr, value, MIN(llvm_abi_alignment(element_type), decl->alignment));
}
return;
}
@@ -220,7 +219,7 @@ static inline void llvm_emit_parameter(GenContext *context, Decl *decl, unsigned
assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_PARAM);
// Allocate room on stack, but do not copy.
decl->backend_ref = llvm_emit_decl_alloca(context, decl);
llvm_emit_and_set_decl_alloca(context, decl);
llvm_process_parameter_value(context, decl, index);
if (llvm_use_debug(context))
{
@@ -571,7 +570,7 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl)
}
if (decl->alignment)
{
LLVMSetAlignment(function, decl->alignment);
llvm_set_alignment(function, decl->alignment);
}
if (decl->section)
{

View File

@@ -187,6 +187,7 @@ void llvm_value_set_bool(BEValue *value, LLVMValueRef llvm_value);
void llvm_value_set(BEValue *value, LLVMValueRef llvm_value, Type *type);
void llvm_value_set_address_align(BEValue *value, LLVMValueRef llvm_value, Type *type, unsigned alignment);
void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type);
void llvm_value_set_decl_address(BEValue *value, Decl *decl);
void llvm_value_fold_failable(GenContext *c, BEValue *value);
void llvm_value_struct_gep(GenContext *c, BEValue *element, BEValue *struct_pointer, unsigned index);
@@ -205,13 +206,13 @@ LLVMValueRef llvm_emit_const_padding(GenContext *c, ByteSize size);
LLVMTypeRef llvm_const_padding_type(GenContext *c, ByteSize size);
LLVMValueRef llvm_emit_alloca(GenContext *context, LLVMTypeRef type, unsigned alignment, const char *name);
LLVMValueRef llvm_emit_alloca_aligned(GenContext *c, Type *type, const char *name);
LLVMValueRef llvm_emit_assign_expr(GenContext *context, LLVMValueRef ref, Expr *expr, LLVMValueRef failable);
BEValue llvm_emit_assign_expr(GenContext *context, BEValue *ref, Expr *expr, LLVMValueRef failable);
static inline LLVMValueRef llvm_emit_bitcast(GenContext *context, LLVMValueRef value, Type *type);
void llvm_emit_block(GenContext *c, LLVMBasicBlockRef next_block);
void llvm_emit_br(GenContext *c, LLVMBasicBlockRef next_block);
void llvm_emit_compound_stmt(GenContext *context, Ast *ast);
void llvm_emit_and_set_decl_alloca(GenContext *c, Decl *decl);
LLVMValueRef llvm_emit_convert_value_from_coerced(GenContext *context, LLVMTypeRef coerced, LLVMValueRef value, Type *original_type);
LLVMValueRef llvm_emit_decl_alloca(GenContext *c, Decl *decl);
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);
@@ -224,9 +225,13 @@ void llvm_emit_debug_local_var(GenContext *c, Decl *var);
void llvm_emit_debug_global_var(GenContext *c, Decl *global);
void llvm_emit_defer(GenContext *c, AstId defer_start, AstId defer_end);
void llvm_emit_extern_decl(GenContext *context, Decl *decl);
LLVMValueRef llvm_emit_const_aggregate(GenContext *c, Expr *expr, bool *modified);
LLVMValueRef llvm_emit_int_comparison(GenContext *c, Type *lhs_type, Type *rhs_type, LLVMValueRef lhs_value, LLVMValueRef rhs_value, BinaryOp binary_op);
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_len_for_expr(GenContext *c, BEValue *be_value, BEValue *expr_to_len);
LLVMValueRef llvm_emit_load_aligned(GenContext *context, LLVMTypeRef type, LLVMValueRef pointer, AlignSize alignment, const char *name);
void llvm_emit_local_var_alloca(GenContext *c, Decl *decl);
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, BEValue *ref);
@@ -392,6 +397,13 @@ static inline LLVMValueRef llvm_const_int(GenContext *c, Type *type, uint64_t va
return LLVMConstInt(llvm_get_type(c, type), val, type_is_integer_signed(type));
}
static inline void llvm_set_alignment(LLVMValueRef alloca, AlignSize alignment)
{
assert(alignment > 0);
LLVMSetAlignment(alloca, alignment);
}
void llvm_set_error_exit(GenContext *c, LLVMBasicBlockRef block);
void llvm_set_error_exit_and_value(GenContext *c, LLVMBasicBlockRef block, LLVMValueRef value);
#define EMIT_LOC(c, x) do { if (c->debug.builder) llvm_emit_debug_location(c, x->span); } while (0);

View File

@@ -39,29 +39,26 @@ void gencontext_emit_ct_compound_stmt(GenContext *context, Ast *ast)
}
}
static LLVMValueRef llvm_emit_decl(GenContext *c, Ast *ast)
{
Decl *decl = ast->declare_stmt;
LLVMTypeRef alloc_type = llvm_get_type(c, type_lowering(decl->type));
decl->backend_ref = llvm_emit_decl_alloca(c, decl);
llvm_emit_local_var_alloca(c, decl);
if (decl->var.failable)
{
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(c))
{
llvm_emit_debug_local_var(c, decl);
}
Expr *init = decl->var.init_expr;
if (init)
{
// If we don't have undef, then make an assign.
if (init->expr_kind != EXPR_UNDEF)
{
llvm_emit_assign_expr(c, decl->backend_ref, decl->var.init_expr, decl->var.failable_ref);
BEValue value;
llvm_value_set_decl_address(&value, decl);
llvm_emit_assign_expr(c, &value, decl->var.init_expr, decl->var.failable_ref);
}
// TODO trap on undef in debug mode.
}
@@ -75,7 +72,7 @@ static LLVMValueRef llvm_emit_decl(GenContext *c, Ast *ast)
}
else
{
llvm_emit_memclear_size_align(c, decl->backend_ref, type_size(decl->type), decl_abi_alignment(decl), true);
llvm_emit_memclear_size_align(c, decl->backend_ref, type_size(decl->type), decl->alignment, true);
}
}
return decl->backend_ref;
@@ -333,7 +330,6 @@ void gencontext_emit_for_stmt(GenContext *c, Ast *ast)
ast->for_stmt.continue_block = continue_block;
ast->for_stmt.exit_block = exit_block;
LLVMValueRef value = NULL;
LLVMBasicBlockRef loopback_block = cond_block;
if (cond_block)
@@ -404,6 +400,137 @@ void gencontext_emit_for_stmt(GenContext *c, Ast *ast)
llvm_emit_block(c, exit_block);
}
void llvm_emit_foreach_stmt(GenContext *c, Ast *ast)
{
// First we generate an exit.
LLVMBasicBlockRef exit_block = llvm_basic_block_new(c, "foreach.exit");
PUSH_ERROR();
llvm_set_error_exit(c, exit_block);
// First evaluate the enumerated collection
BEValue enum_value;
llvm_emit_expr(c, &enum_value, ast->foreach_stmt.enumeration);
// Get the length
BEValue len;
llvm_emit_len_for_expr(c, &len, &enum_value);
LLVMValueRef len_value = llvm_value_rvalue_store(c, &len);
// We pop the error here.
POP_ERROR();
// Create the index and optionally the index var
LLVMTypeRef real_index_type = llvm_get_type(c, type_isize);
BEValue index_var = {};
LLVMTypeRef index_type = ast->foreach_stmt.index ? llvm_get_type(c, ast->foreach_stmt.index->type) : NULL;
BEValue index = {};
bool extend_bits = false;
// In case we have an index, set it up.
if (ast->foreach_stmt.index)
{
llvm_emit_local_var_alloca(c, ast->foreach_stmt.index);
llvm_value_set_address(&index_var, ast->foreach_stmt.index->backend_ref, ast->foreach_stmt.index->type);
// And set it to zero.
llvm_store_bevalue_raw(c, &index_var, llvm_get_zero(c, index_var.type));
index = index_var;
extend_bits = type_size(index_var.type) > type_size(type_isize);
}
// If types don't match (either index has a different type or it doesn't exist)
// then create the address for the internal index and set it to zero.
if (index_type != real_index_type)
{
llvm_value_set_address(&index, llvm_emit_alloca(c, real_index_type, type_abi_alignment(type_isize), "idx"), type_isize);
llvm_store_bevalue_raw(c, &index, llvm_get_zero(c, index.type));
}
Type *actual_type = type_get_indexed_type(ast->foreach_stmt.enumeration->type);
LLVMTypeRef actual_type_llvm = llvm_get_type(c, actual_type);
llvm_emit_local_var_alloca(c, ast->foreach_stmt.variable);
Type *var_type = type_flatten(ast->foreach_stmt.variable->type);
LLVMTypeRef var_type_llvm = llvm_get_type(c, var_type);
BEValue var;
llvm_value_set_address(&var, ast->foreach_stmt.variable->backend_ref, var_type);
LLVMBasicBlockRef inc_block = llvm_basic_block_new(c, "foreach.inc");
LLVMBasicBlockRef body_block = llvm_basic_block_new(c, "foreach.body");
LLVMBasicBlockRef cond_block = llvm_basic_block_new(c, "foreach.cond");
ast->foreach_stmt.continue_block = inc_block;
ast->foreach_stmt.exit_block = exit_block;
// Emit cond
llvm_emit_br(c, cond_block);
llvm_emit_block(c, cond_block);
LLVMValueRef index_value = llvm_value_rvalue_store(c, &index);
LLVMValueRef may_loop = llvm_emit_int_comparison(c, type_isize, type_isize, index_value, len_value, BINARYOP_LT);
BEValue b;
llvm_value_set_bool(&b, may_loop);
llvm_emit_cond_br(c, &b, body_block, exit_block);
llvm_emit_block(c, body_block);
// In the case where we have an index that is smaller, we need to do a cast.
if (index_var.value && index.value != index_var.value)
{
LLVMValueRef stored_value;
// Note that the case of index_var being usize and index being isize
// does not occur here, since they are considered the same LLVM type.
if (extend_bits)
{
// Note that we zero extend. We never deal in negative indices.
stored_value = LLVMBuildZExt(c->builder, index_value, index_type, "");
}
else
{
stored_value = LLVMBuildTrunc(c->builder, index_value, index_type, "");
}
llvm_store_bevalue_raw(c, &index_var, stored_value);
}
assert(llvm_value_is_addr(&enum_value));
LLVMValueRef ref_to_element = LLVMBuildInBoundsGEP2(c->builder, actual_type_llvm, enum_value.value, &index_value, 1, "");
BEValue result;
if (ast->foreach_stmt.value_by_ref)
{
llvm_value_set(&result, ref_to_element, type_get_ptr(actual_type));
LLVMTypeRef pointer_llvm = llvm_get_ptr_type(c, actual_type);
if (pointer_llvm != var_type_llvm)
{
llvm_emit_cast(c, ast->foreach_stmt.cast, &result, var_type, result.type);
}
}
else
{
llvm_value_set_address(&result, ref_to_element, actual_type);
if (var_type_llvm != actual_type_llvm)
{
llvm_emit_cast(c, ast->foreach_stmt.cast, &result, var_type, actual_type);
}
}
llvm_store_bevalue(c, &var, &result);
llvm_emit_stmt(c, ast->foreach_stmt.body);
llvm_emit_br(c, inc_block);
llvm_emit_block(c, inc_block);
index_value = llvm_value_rvalue_store(c, &index);
index_value = LLVMBuildAdd(c->builder, index_value, llvm_const_int(c, type_isize, 1), "");
llvm_store_bevalue_raw(c, &index, index_value);
// Loop back.
llvm_emit_br(c, cond_block);
// And insert exit block
llvm_emit_block(c, exit_block);
}
void gencontext_emit_while_stmt(GenContext *context, Ast *ast)
{
// First, emit all inits.
@@ -736,6 +863,9 @@ void gencontext_emit_break(GenContext *context, Ast *ast)
case AST_WHILE_STMT:
jump = jump_target->while_stmt.break_block;
break;
case AST_FOREACH_STMT:
jump = jump_target->foreach_stmt.exit_block;
break;
case AST_FOR_STMT:
jump = jump_target->for_stmt.exit_block;
break;
@@ -764,13 +894,15 @@ void gencontext_emit_continue(GenContext *context, Ast *ast)
case AST_IF_STMT:
case AST_SWITCH_STMT:
UNREACHABLE
break;
case AST_WHILE_STMT:
jump = jump_target->while_stmt.continue_block;
break;
case AST_DO_STMT:
jump = jump_target->do_stmt.continue_block;
break;
case AST_FOREACH_STMT:
jump = jump_target->foreach_stmt.continue_block;
break;
case AST_FOR_STMT:
jump = jump_target->for_stmt.continue_block;
break;
@@ -1082,6 +1214,9 @@ void llvm_emit_stmt(GenContext *c, Ast *ast)
case AST_FOR_STMT:
gencontext_emit_for_stmt(c, ast);
break;
case AST_FOREACH_STMT:
llvm_emit_foreach_stmt(c, ast);
break;
case AST_WHILE_STMT:
gencontext_emit_while_stmt(c, ast);
break;

View File

@@ -354,6 +354,7 @@ Expr *parse_initializer_list(Context *context)
if (!parse_param_list(context, &initializer_list->initializer_expr.initializer_expr, false, TOKEN_RBRACE)) return poisoned_expr;
CONSUME_OR(TOKEN_RBRACE, poisoned_expr);
}
RANGE_EXTEND_PREV(initializer_list);
return initializer_list;
}

View File

@@ -477,6 +477,77 @@ static inline Ast* parse_for_stmt(Context *context)
return ast;
}
static inline bool parse_foreach_var(Context *context, Ast *foreach)
{
TypeInfo *type = NULL;
bool failable = false;
// If we don't get foreach (foo ... or foreach (*foo ... then a type is expected.
if (!TOKEN_IS(TOKEN_IDENT) && !TOKEN_IS(TOKEN_AMP))
{
type = TRY_TYPE_OR(parse_type(context), false);
failable = try_consume(context, TOKEN_BANG);
// Add the failable to the type for nicer error reporting.
RANGE_EXTEND_PREV(type);
}
if (try_consume(context, TOKEN_AMP))
{
foreach->foreach_stmt.value_by_ref = true;
}
if (!try_consume(context, TOKEN_IDENT))
{
if (type)
{
SEMA_TOKEN_ERROR(context->tok, "Expected an identifier after the type.");
return false;
}
SEMA_TOKEN_ERROR(context->tok, "Expected an identifier or type.");
return false;
}
Decl *var = decl_new_var(context->prev_tok, type, VARDECL_LOCAL, VISIBLE_LOCAL);
var->var.failable = failable;
foreach->foreach_stmt.variable = var;
return true;
}
/**
* foreach_statement
* : FOREACH (CONST_IDENT ':')? '(' type? '*'? IDENT (',' type? '*'? IDENT) ':' expression ')' statement
* ;
*/
static inline Ast* parse_foreach_stmt(Context *context)
{
Ast *ast = AST_NEW_TOKEN(AST_FOREACH_STMT, context->tok);
advance_and_verify(context, TOKEN_FOREACH);
ast->foreach_stmt.flow.label = TRY_DECL_OR(parse_optional_label(context, ast), poisoned_ast);
CONSUME_OR(TOKEN_LPAREN, poisoned_ast);
// Parse the first variable.
if (!parse_foreach_var(context, ast)) return poisoned_ast;
// Do we have a second variable?
if (try_consume(context, TOKEN_COMMA))
{
// Copy the first variable to "index"
ast->foreach_stmt.index = ast->foreach_stmt.variable;
ast->foreach_stmt.index_by_ref = ast->foreach_stmt.value_by_ref;
ast->foreach_stmt.value_by_ref = false;
// Parse the second variable
if (!parse_foreach_var(context, ast)) return poisoned_ast;
}
CONSUME_OR(TOKEN_COLON, poisoned_ast);
ast->foreach_stmt.enumeration = TRY_EXPR_OR(parse_initializer(context), poisoned_ast);
CONSUME_OR(TOKEN_RPAREN, poisoned_ast);
extend_ast_with_prev_token(context, ast);
ast->foreach_stmt.body = TRY_AST(parse_stmt(context));
return ast;
}
/**
* continue_stmt
* : CONTINUE
@@ -948,6 +1019,8 @@ Ast *parse_stmt(Context *context)
return parse_do_stmt(context);
case TOKEN_FOR:
return parse_for_stmt(context);
case TOKEN_FOREACH:
return parse_foreach_stmt(context);
case TOKEN_CATCH:
return parse_catch_stmt(context);
case TOKEN_CONTINUE:

View File

@@ -348,6 +348,7 @@ static inline bool sema_analyse_function_param(Context *context, Decl *param, bo
}
*has_default = true;
}
param->alignment = type_abi_alignment(param->type);
return true;
}
@@ -853,6 +854,7 @@ static inline bool sema_analyse_global(Context *context, Decl *decl)
if (decl->type)
{
decl->resolve_status = RESOLVE_DONE;
if (!decl->alignment) decl->alignment = type_alloca_alignment(decl->type);
}
// Check the initializer.

View File

@@ -2070,22 +2070,23 @@ static int64_t sema_analyse_designator_index(Context *context, Expr *index)
{
return -1;
}
if (index->expr_kind == EXPR_CONST)
if (index->expr_kind != EXPR_CONST)
{
if (!bigint_fits_in_bits(&index->const_expr.i, 64, true))
{
SEMA_ERROR(index, "The value of the index does not fit in a long.");
return -1;
}
int64_t index_val = bigint_as_signed(&index->const_expr.i);
if (index_val < 0)
{
SEMA_ERROR(index, "Negative index values is not allowed.");
return -1;
}
return index_val;
SEMA_ERROR(index, "The index must be a constant value.");
return -1;
}
return 0;
if (!bigint_fits_in_bits(&index->const_expr.i, 64, true))
{
SEMA_ERROR(index, "The value of the index does not fit in a long.");
return -1;
}
int64_t index_val = bigint_as_signed(&index->const_expr.i);
if (index_val < 0)
{
SEMA_ERROR(index, "Negative index values is not allowed.");
return -1;
}
return index_val;
}
static Type *sema_find_type_of_element(Context *context, Type *type, DesignatorElement **elements, unsigned *curr_index, bool *is_constant, bool *did_report_error)
@@ -2112,7 +2113,6 @@ static Type *sema_find_type_of_element(Context *context, Type *type, DesignatorE
return NULL;
}
element->index = index;
if (element->index_expr->expr_kind != EXPR_CONST) *is_constant = false;
if (element->kind == DESIGNATOR_RANGE)
{
@@ -2122,9 +2122,7 @@ static Type *sema_find_type_of_element(Context *context, Type *type, DesignatorE
*did_report_error = true;
return NULL;
}
if (element->index_end_expr->expr_kind == EXPR_CONST
&& element->index_expr->expr_kind == EXPR_CONST
&& index > end_index)
if (index > end_index)
{
SEMA_ERROR(element->index_end_expr, "End index must be greater than start index.");
*did_report_error = true;
@@ -2136,7 +2134,6 @@ static Type *sema_find_type_of_element(Context *context, Type *type, DesignatorE
SEMA_ERROR(element->index_expr, "The index may must be less than the array length (which was %llu).", (unsigned long long)len);
return NULL;
}
element->index_end = end_index;
}
return type_lowered->array.base;
@@ -4921,6 +4918,13 @@ static Ast *ast_copy_from_macro(Context *context, Ast *source)
MACRO_COPY_AST(ast->for_stmt.body);
MACRO_COPY_EXPR(ast->for_stmt.init);
return ast;
case AST_FOREACH_STMT:
copy_flow(context, ast);
MACRO_COPY_DECL(ast->foreach_stmt.index);
MACRO_COPY_DECL(ast->foreach_stmt.variable);
MACRO_COPY_EXPR(ast->foreach_stmt.enumeration);
MACRO_COPY_AST(ast->for_stmt.body);
return ast;
case AST_IF_STMT:
copy_flow(context, ast);
MACRO_COPY_EXPR(ast->if_stmt.cond);
@@ -5208,7 +5212,7 @@ bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr,
if (expr->failable && !may_be_failable)
{
if (!to) to = expr->type;
SEMA_ERROR(expr, "'%s!' cannot be implicitly cast to '%s'.", type_to_error_string(expr->type), type_to_error_string(to));
SEMA_ERROR(expr, "'%s!' cannot be converted into '%s'.", type_to_error_string(expr->type), type_to_error_string(to));
return false;
}
return cast_implicit(context, expr, to);
@@ -5289,6 +5293,62 @@ bool sema_analyse_expr_value(Context *context, Type *to, Expr *expr)
}
}
ArrayIndex sema_get_initializer_const_array_size(Context *context, Expr *initializer, bool *may_be_array, bool *is_const_size)
{
assert(initializer->expr_kind == EXPR_INITIALIZER_LIST);
Expr **initializers = initializer->initializer_expr.initializer_expr;
*may_be_array = true;
*is_const_size = true;
unsigned element_count = vec_size(initializers);
// If it's empty or the first element is not a designator, we assume an array list
// with that many elements.
if (!element_count || initializers[0]->expr_kind != EXPR_DESIGNATOR) return element_count;
ArrayIndex size = 0;
// Otherwise we assume everything's a designator.
VECEACH(initializers, i)
{
Expr *sub_initializer = initializers[i];
if (sub_initializer->expr_kind != EXPR_DESIGNATOR)
{
// Simply messed up: a mix of designators and regular ones.
return -1;
}
DesignatorElement *element = sub_initializer->designator_expr.path[0];
switch (element->kind)
{
case DESIGNATOR_FIELD:
// Struct, abandon!
*may_be_array = false;
return -1;
case DESIGNATOR_ARRAY:
{
ArrayIndex index = sema_analyse_designator_index(context, element->index_expr);
if (index < 0 || element->index_expr->expr_kind != EXPR_CONST)
{
*is_const_size = false;
return -1;
}
size = MAX(size, index + 1);
break;
}
case DESIGNATOR_RANGE:
{
ArrayIndex index = sema_analyse_designator_index(context, element->index_end_expr);
if (index < 0 || element->index_end_expr->expr_kind != EXPR_CONST)
{
*is_const_size = false;
return -1;
}
size = MAX(size, index + 1);
break;
}
default:
UNREACHABLE
}
}
return size;
}
bool sema_analyse_expr(Context *context, Type *to, Expr *expr)
{
return sema_analyse_expr_value(context, to, expr) && sema_cast_rvalue(context, to, expr);

View File

@@ -10,6 +10,7 @@
int sema_check_comp_time_bool(Context *context, Expr *expr);
bool sema_analyse_function_body(Context *context, Decl *func);
void context_pop_scope(Context *context);
void context_pop_scope_error(Context *context);
void context_push_scope_with_flags(Context *context, ScopeFlags flags);
AstId context_start_defer(Context *context);
static inline void context_push_scope(Context *context)

View File

@@ -75,6 +75,12 @@ void context_pop_scope(Context *context)
context->current_scope--;
}
void context_pop_scope_error(Context *context)
{
context->current_scope->defer_last = context_start_defer(context);
context_pop_scope(context);
}
static Expr *context_pop_defers_and_wrap_expr(Context *context, Expr *expr)
{
DeferList defers = { 0, 0 };
@@ -326,9 +332,8 @@ static inline bool sema_analyse_do_stmt(Context *context, Ast *statement)
}
static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement)
static inline bool sema_analyse_local_decl(Context *context, Decl *decl)
{
Decl *decl = statement->declare_stmt;
assert(decl->decl_kind == DECL_VAR);
if (!sema_add_local(context, decl)) return decl_poison(decl);
if (decl->var.kind == VARDECL_CONST)
@@ -353,6 +358,7 @@ static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement)
}
if (!sema_resolve_type_info(context, decl->var.type_info)) return decl_poison(decl);
decl->type = decl->var.type_info->type;
if (!decl->alignment) decl->alignment = type_alloca_alignment(decl->type);
if (decl->var.init_expr)
{
Expr *init = decl->var.init_expr;
@@ -374,6 +380,11 @@ static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement)
return true;
}
static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement)
{
return sema_analyse_local_decl(context, statement->declare_stmt);
}
static inline bool sema_analyse_define_stmt(Context *context, Ast *statement)
{
Decl *decl = statement->declare_stmt;
@@ -478,11 +489,13 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement)
// Enter for scope
context_push_scope(context);
bool is_infinite = statement->for_stmt.cond == NULL;
if (statement->for_stmt.init)
{
success = sema_analyse_decl_expr_list(context, statement->for_stmt.init);
}
if (success && statement->for_stmt.cond)
{
// Conditional scope start
@@ -490,7 +503,7 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement)
Expr *cond = statement->for_stmt.cond;
success = sema_analyse_expr_of_required_type(context, type_bool, cond, false);
statement->for_stmt.cond = context_pop_defers_and_wrap_expr(context, cond);
// If this is const true, then set this to infinte and remove the expression.
// If this is const true, then set this to infinite and remove the expression.
if (statement->for_stmt.cond->expr_kind == EXPR_CONST && statement->for_stmt.cond->const_expr.b)
{
statement->for_stmt.cond = NULL;
@@ -511,7 +524,7 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement)
}
if (!success)
{
context_pop_scope(context);
context_pop_scope_error(context);
return false;
}
@@ -535,6 +548,172 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement)
}
static inline bool sema_analyse_foreach_stmt(Context *context, Ast *statement)
{
// Pull out the relevant data.
Decl *index = statement->foreach_stmt.index;
Decl *var = statement->foreach_stmt.variable;
Expr *enumerator = statement->foreach_stmt.enumeration;
Ast *body = statement->foreach_stmt.body;
// First fold the enumerator expression, removing any () around it.
while (enumerator->expr_kind == EXPR_GROUP) enumerator = enumerator->group_expr;
// First analyse the enumerator.
// Conditional scope start
context_push_scope_with_flags(context, SCOPE_COND);
// In the case of foreach (int x : { 1, 2, 3 }) we will infer the int[] type, so pick out the number of elements.
Type *inferred_type = NULL;
// We may have an initializer list, in this case we rely on an inferred type.
if (enumerator->expr_kind == EXPR_INITIALIZER_LIST)
{
bool may_be_array;
bool is_const_size;
ArrayIndex size = sema_get_initializer_const_array_size(context, enumerator, &may_be_array, &is_const_size);
if (!may_be_array)
{
SEMA_ERROR(enumerator, "This initializer appears to be a struct initializer when, an array initializer was expected.");
goto EXIT_FAIL;
}
if (!is_const_size)
{
SEMA_ERROR(enumerator, "Only constant sized initializers may be implicitly initialized.");
goto EXIT_FAIL;
}
if (size < 0)
{
SEMA_ERROR(enumerator, "The initializer mixes designated initialization with array initialization.");
goto EXIT_FAIL;
}
assert(size >= 0);
TypeInfo *variable_type_info = var->var.type_info;
if (!variable_type_info)
{
SEMA_ERROR(var, "Add the type of your variable here if you want to iterate over an initializer list.");
goto EXIT_FAIL;
}
// First infer the type of the variable.
if (!sema_resolve_type_info(context, variable_type_info)) return false;
// And create the inferred type:
inferred_type = type_get_array(var->var.type_info->type, size);
}
// because we don't want the index + variable to move into the internal scope
if (!sema_analyse_expr(context, inferred_type, enumerator))
{
// Exit early here, because semantic checking might be messed up otherwise.
goto EXIT_FAIL;
}
// Check that we can even index this expression.
Type *indexed_type = type_get_indexed_type(enumerator->type);
if (!indexed_type)
{
SEMA_ERROR(enumerator, "It's not possible to enumerate an expression of type '%s'.", type_to_error_string(enumerator->type));
goto EXIT_FAIL;
}
// Pop any possible defers.
enumerator = context_pop_defers_and_wrap_expr(context, enumerator);
// And pop the cond scope.
context_pop_scope(context);
// Enter foreach scope, to put index + variable into the internal scope.
// the foreach may be labelled.
context_push_scope_with_label(context, statement->foreach_stmt.flow.label);
if (index)
{
if (statement->foreach_stmt.index_by_ref)
{
SEMA_ERROR(index, "The index cannot be held by reference, did you accidentally add a '&'?");
goto EXIT_FAIL;
}
// Set type to isize if missing.
if (!index->var.type_info)
{
index->var.type_info = type_info_new_base(type_isize, index->span);
}
// Analyse the declaration.
if (!sema_analyse_local_decl(context, index)) goto EXIT_FAIL;
// Make sure that the index is an integer.
if (!type_is_integer(type_flatten(index->type)))
{
SEMA_ERROR(index->var.type_info, "Index must be an integer type, '%s' is not valid.", type_to_error_string(index->type));
goto EXIT_FAIL;
}
if (index->var.failable)
{
SEMA_ERROR(index->var.type_info, "The index may not be a failable.");
goto EXIT_FAIL;
}
}
// Find the expected type for the value.
Type *expected_var_type = indexed_type;
// If by ref, then the pointer.
if (statement->foreach_stmt.value_by_ref) expected_var_type = type_get_ptr(expected_var_type);
// If we type infer, then the expected type is the same as the indexed type.
if (!var->var.type_info)
{
var->var.type_info = type_info_new_base(expected_var_type, var->span);
}
// Analyse the value declaration.
if (!sema_analyse_local_decl(context, var)) goto EXIT_FAIL;
if (var->var.failable)
{
SEMA_ERROR(var->var.type_info, "The variable may not be a failable.");
goto EXIT_FAIL;
}
// TODO consider failables.
// We may have an implicit cast happening.
statement->foreach_stmt.cast = CAST_ERROR;
// If the type is different, attempt an implicit cast.
if (var->type != expected_var_type)
{
// This is hackish, replace when cast is refactored.
Expr dummy = { .resolve_status = RESOLVE_DONE, .span = { var->var.type_info->span.loc, var->span.end_loc }, .expr_kind = EXPR_IDENTIFIER, .type = expected_var_type };
if (!cast_implicit(context, &dummy, var->type)) goto EXIT_FAIL;
assert(dummy.expr_kind == EXPR_CAST);
statement->foreach_stmt.cast = dummy.cast_expr.kind;
}
// Push the statement on the break/continue stack.
PUSH_BREAKCONT(statement);
bool success = sema_analyse_statement(context, body);
statement->foreach_stmt.flow.no_exit = context->current_scope->jump_end;
// Pop the stack.
POP_BREAKCONT();
// End foreach scope
context_pop_defers_and_replace_ast(context, body);
context_pop_scope(context);
return success;
EXIT_FAIL:
context_pop_scope_error(context);
return false;
}
static inline bool sema_analyse_if_stmt(Context *context, Ast *statement)
@@ -1234,8 +1413,15 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement)
bool success = sema_analyse_switch_body(context, statement, cond->span,
switch_type->canonical,
statement->switch_stmt.cases);
if (success) context_pop_defers_and_replace_ast(context, statement);
context_pop_scope(context);
if (success)
{
context_pop_defers_and_replace_ast(context, statement);
context_pop_scope(context);
}
else
{
context_pop_scope_error(context);
}
if (statement->flow.no_exit && !statement->flow.has_break)
{
context->current_scope->jump_end = true;
@@ -1333,8 +1519,15 @@ static bool sema_analyse_catch_stmt(Context *context, Ast *statement)
success = sema_analyse_statement(context, statement->catch_stmt.body);
if (context->current_scope->jump_end) statement->flow.no_exit = true;
}
if (success) context_pop_defers_and_replace_ast(context, statement);
context_pop_scope(context);
if (success)
{
context_pop_defers_and_replace_ast(context, statement);
context_pop_scope(context);
}
else
{
context_pop_scope_error(context);
}
EXIT:
if (success)
{
@@ -1391,7 +1584,7 @@ static bool sema_analyse_try_stmt(Context *context, Ast *stmt)
return true;
ERR:
context_pop_scope(context);
context_pop_scope_error(context);
return false;
}
@@ -1530,6 +1723,8 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement
return sema_analyse_do_stmt(context, statement);
case AST_EXPR_STMT:
return sema_analyse_expr_stmt(context, statement);
case AST_FOREACH_STMT:
return sema_analyse_foreach_stmt(context, statement);
case AST_FOR_STMT:
return sema_analyse_for_stmt(context, statement);
case AST_TRY_STMT:

View File

@@ -224,6 +224,8 @@ const char *token_type_to_string(TokenType type)
return "false";
case TOKEN_FOR:
return "for";
case TOKEN_FOREACH:
return "foreach";
case TOKEN_FUNC:
return "func";
case TOKEN_GENERIC:

View File

@@ -576,6 +576,11 @@ bool type_is_homogenous_aggregate(Type *type, Type **base, unsigned *elements)
AlignSize type_alloca_alignment(Type *type)
{
if (build_target.abi == ABI_X64)
{
type = type_flatten(type);
if (type->type_kind == TYPE_ARRAY && type_size(type) >= 16) return 16;
}
return type_abi_alignment(type);
}

View File

@@ -1,3 +1,4 @@
// #target: x64_darwin
module testing;
func double test(uint x)
@@ -13,14 +14,14 @@ func double test(uint x)
// #expect: array_literal.ll
@0 = constant [30 x double] [double 0.000000e+00, double 1.270600e+01, double 4.303000e+00, double 3.182000e+00, double 2.776000e+00, double 2.571000e+00, double 2.447000e+00, double 2.365000e+00, double 2.306000e+00, double 2.262000e+00, double 2.228000e+00, double 2.201000e+00, double 2.179000e+00, double 2.160000e+00, double 2.145000e+00, double 2.131000e+00, double 2.120000e+00, double 2.110000e+00, double 2.101000e+00, double 2.093000e+00, double 2.086000e+00, double 2.080000e+00, double 2.074000e+00, double 2.069000e+00, double 2.064000e+00, double 2.060000e+00, double 2.056000e+00, double 2.052000e+00, double 2.048000e+00, double 2.045000e+00], align 8
@0 = constant [30 x double] [double 0.000000e+00, double 1.270600e+01, double 4.303000e+00, double 3.182000e+00, double 2.776000e+00, double 2.571000e+00, double 2.447000e+00, double 2.365000e+00, double 2.306000e+00, double 2.262000e+00, double 2.228000e+00, double 2.201000e+00, double 2.179000e+00, double 2.160000e+00, double 2.145000e+00, double 2.131000e+00, double 2.120000e+00, double 2.110000e+00, double 2.101000e+00, double 2.093000e+00, double 2.086000e+00, double 2.080000e+00, double 2.074000e+00, double 2.069000e+00, double 2.064000e+00, double 2.060000e+00, double 2.056000e+00, double 2.052000e+00, double 2.048000e+00, double 2.045000e+00], align 16
entry:
%x = alloca i32, align 4
%student_t = alloca [30 x double], align 8
%student_t = alloca [30 x double], align 16
store i32 %0, i32* %x, align 4
%1 = bitcast [30 x double]* %student_t to i8*
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %1, i8* align 8 bitcast ([30 x double]* @0 to i8*), i32 240, i1 false)
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 16 %1, i8* align 16 bitcast ([30 x double]* @0 to i8*), i32 240, i1 false)
%2 = load i32, i32* %x, align 4
%arridx = getelementptr inbounds [30 x double], [30 x double]* %student_t, i32 0, i32 %2
%3 = load double, double* %arridx, align 8

View File

@@ -1,3 +1,4 @@
// #target: x64_darwin
module test;
struct Foo
@@ -9,4 +10,4 @@ Foo[10] array;
// #expect: array_struct.ll
@array = protected global [10 x %test.Foo] zeroinitializer, align 4
@array = protected global [10 x %test.Foo] zeroinitializer, align 16

View File

@@ -1,3 +1,4 @@
// #target: x64_darwin
module test;
struct Connection
@@ -17,4 +18,4 @@ Connection[3] link
@0 = internal constant [6 x i8] c"link1\00"
@1 = internal constant [6 x i8] c"link2\00"
@2 = internal constant [6 x i8] c"link3\00"
@link = protected global [3 x %test.Connection] [%test.Connection { i64 1, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i32 0, i32 0), i64 10 }, %test.Connection { i64 2, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i32 0, i32 0), i64 20 }, %test.Connection { i64 3, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i32 0, i32 0), i64 30 }], align 8
@link = protected global [3 x %test.Connection] [%test.Connection { i64 1, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i32 0, i32 0), i64 10 }, %test.Connection { i64 2, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i32 0, i32 0), i64 20 }, %test.Connection { i64 3, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i32 0, i32 0), i64 30 }], align 16

View File

@@ -3,9 +3,9 @@ func void syntaxErrors()
int! i = 0;
while (i + 1) {} // #error: 'int!' cannot be converted into 'bool'
if (i + 1) {} // #error: 'int!' cannot be converted into 'bool'
for (int x = i;;) {} // #error: 'int!' cannot be implicitly cast to 'int'.
for (int x = 0; x < i + 1;) {} // #error: 'bool!' cannot be implicitly cast to 'bool'
for (int x = 0; x < 10; x += i + 1) {} // #error: 'int!' cannot be implicitly cast to 'int'
for (int x = i;;) {} // #error: 'int!' cannot be converted into 'int'.
for (int x = 0; x < i + 1;) {} // #error: 'bool!' cannot be converted into 'bool'
for (int x = 0; x < 10; x += i + 1) {} // #error: 'int!' cannot be converted into 'int'
switch (i + 1) // #error: 'int!' cannot be converted into 'int'
{
default:

View File

@@ -106,7 +106,7 @@ void @test.setInternalFPFZero(%test.InternalFPF* %0)
store %test.InternalFPF* %0, %test.InternalFPF** %dest, align 8
%1 = load %test.InternalFPF*, %test.InternalFPF** %dest, align 8
%2 = getelementptr inbounds %test.InternalFPF, %test.InternalFPF* %1, i32 0, i32 0
store i8 0, i8* %2, align 1
store i8 0, i8* %2, align 8
ret void
void @test.denormalize(%test.InternalFPF* %0)

View File

@@ -1,3 +1,5 @@
// #target: x64_darwin
module test;
double foo = 17;
@@ -11,4 +13,4 @@ void*[3] data = { &foo, &bar, &xx };
@foo = protected global double 1.700000e+01, align 8
@bar = protected global double 1.200000e+01, align 8
@xx = protected global float 1.200000e+01, align 4
@data = protected global [3 x i8*] [i8* bitcast (double* @foo to i8*), i8* bitcast (double* @bar to i8*), i8* bitcast (float* @xx to i8*)], align 8
@data = protected global [3 x i8*] [i8* bitcast (double* @foo to i8*), i8* bitcast (double* @bar to i8*), i8* bitcast (float* @xx to i8*)], align 16

View File

@@ -0,0 +1,51 @@
module test;
func void test()
{
int[3] x;
int g = 0;
foreach (z : x)
{
if (z > 0) break;
if (z == 1) continue;
g += z;
}
}
// #expect: foreach_break.ll
store i32 0, i32* %g, align 4
store i64 0, i64* %idx, align 8
br label %foreach.cond
foreach.cond:
%1 = load i64, i64* %idx, align 8
%lt = icmp ult i64 %1, 3
br i1 %lt, label %foreach.body, label %foreach.exit
foreach.body:
%2 = getelementptr inbounds i32, [3 x i32]* %x, i64 %1
%3 = load i32, i32* %2, align 4
store i32 %3, i32* %z, align 4
%4 = load i32, i32* %z, align 4
%gt = icmp sgt i32 %4, 0
br i1 %gt, label %if.then, label %if.exit
if.then:
br label %foreach.exit
if.exit:
%5 = load i32, i32* %z, align 4
%eq = icmp eq i32 %5, 1
br i1 %eq, label %if.then1, label %if.exit2
if.then1:
br label %foreach.inc
if.exit2:
%6 = load i32, i32* %g, align 4
%7 = load i32, i32* %z, align 4
%add = add nsw i32 %6, %7
store i32 %add, i32* %g, align 4
br label %foreach.inc
foreach.inc:
%8 = load i64, i64* %idx, align 8
%9 = add i64 %8, 1
store i64 %9, i64* %idx, align 8
br label %foreach.cond
foreach.exit:

View File

@@ -0,0 +1,210 @@
module test;
extern func void printf(char*, ...);
func void main()
{
float[3] foo = { 2, 4.5, 8 };
foreach (a : foo)
{
printf("Value: %f\n", a);
}
foreach (float* &a : foo)
{
*a *= 2;
printf("Value: %f\n", *a);
}
foreach (void* &a : foo)
{
printf("Value: %f\n", *(cast(a as float*)));
}
foreach (i, a : foo)
{
printf("Value[%d]: %f\n", i, a);
}
foreach (byte i, double a : foo)
{
printf("Value2[%d]: %f\n", i, a);
}
foreach (double a : foo)
{
printf("Value3: %f\n", a);
}
}
// #expect: foreach_common.ll
entry:
%foo = alloca [3 x float], align 4
%idx = alloca i64, align 8
%a = alloca float, align 4
%idx1 = alloca i64, align 8
%a2 = alloca float*, align 8
%idx9 = alloca i64, align 8
%a10 = alloca i8*, align 8
%i = alloca i64, align 8
%a18 = alloca float, align 4
%i25 = alloca i8, align 1
%idx26 = alloca i64, align 8
%a27 = alloca double, align 8
%idx34 = alloca i64, align 8
%a35 = alloca double, align 8
%0 = bitcast [3 x float]* %foo to i8*
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 4 bitcast ([3 x float]* @0 to i8*), i32 12, i1 false)
store i64 0, i64* %idx, align 8
br label %foreach.cond
foreach.cond:
%1 = load i64, i64* %idx, align 8
%lt = icmp ult i64 %1, 3
br i1 %lt, label %foreach.body, label %foreach.exit
foreach.body:
%2 = getelementptr inbounds float, [3 x float]* %foo, i64 %1
%3 = load float, float* %2, align 4
store float %3, float* %a, align 4
%4 = load float, float* %a, align 4
%fpfpext = fpext float %4 to double
call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @1, i32 0, i32 0), double %fpfpext)
br label %foreach.inc
foreach.inc:
%5 = load i64, i64* %idx, align 8
%6 = add i64 %5, 1
store i64 %6, i64* %idx, align 8
br label %foreach.cond
foreach.exit:
store i64 0, i64* %idx1, align 8
br label %foreach.cond3
foreach.cond3:
%7 = load i64, i64* %idx1, align 8
%lt4 = icmp ult i64 %7, 3
br i1 %lt4, label %foreach.body5, label %foreach.exit8
foreach.body5:
%8 = getelementptr inbounds float, [3 x float]* %foo, i64 %7
store float* %8, float** %a2, align 8
%9 = load float*, float** %a2, align 8
%10 = load float, float* %9, align 8
%fmul = fmul float %10, 2.000000e+00
store float %fmul, float* %9, align 8
%11 = load float*, float** %a2, align 8
%12 = load float, float* %11, align 8
%fpfpext6 = fpext float %12 to double
call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @2, i32 0, i32 0), double %fpfpext6)
br label %foreach.inc7
foreach.inc7:
%13 = load i64, i64* %idx1, align 8
%14 = add i64 %13, 1
store i64 %14, i64* %idx1, align 8
br label %foreach.cond3
foreach.exit8:
store i64 0, i64* %idx9, align 8
br label %foreach.cond11
foreach.cond11:
%15 = load i64, i64* %idx9, align 8
%lt12 = icmp ult i64 %15, 3
br i1 %lt12, label %foreach.body13, label %foreach.exit17
foreach.body13:
%16 = getelementptr inbounds float, [3 x float]* %foo, i64 %15
%ptrptr = bitcast float* %16 to i8*
store i8* %ptrptr, i8** %a10, align 8
%17 = load i8*, i8** %a10, align 8
%ptrptr14 = bitcast i8* %17 to float*
%18 = load float, float* %ptrptr14, align 8
%fpfpext15 = fpext float %18 to double
call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @3, i32 0, i32 0), double %fpfpext15)
br label %foreach.inc16
foreach.inc16:
%19 = load i64, i64* %idx9, align 8
%20 = add i64 %19, 1
store i64 %20, i64* %idx9, align 8
br label %foreach.cond11
foreach.exit17:
store i64 0, i64* %i, align 8
br label %foreach.cond19
foreach.cond19:
%21 = load i64, i64* %i, align 8
%lt20 = icmp ult i64 %21, 3
br i1 %lt20, label %foreach.body21, label %foreach.exit24
foreach.body21:
%22 = getelementptr inbounds float, [3 x float]* %foo, i64 %21
%23 = load float, float* %22, align 4
store float %23, float* %a18, align 4
%24 = load i64, i64* %i, align 8
%25 = load float, float* %a18, align 4
%fpfpext22 = fpext float %25 to double
call void (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @4, i32 0, i32 0), i64 %24, double %fpfpext22)
br label %foreach.inc23
foreach.inc23:
%26 = load i64, i64* %i, align 8
%27 = add i64 %26, 1
store i64 %27, i64* %i, align 8
br label %foreach.cond19
foreach.exit24:
store i8 0, i8* %i25, align 1
store i64 0, i64* %idx26, align 8
br label %foreach.cond28
foreach.cond28:
%28 = load i64, i64* %idx26, align 8
%lt29 = icmp ult i64 %28, 3
br i1 %lt29, label %foreach.body30, label %foreach.exit33
foreach.body30:
%29 = trunc i64 %28 to i8
store i8 %29, i8* %i25, align 1
%30 = getelementptr inbounds float, [3 x float]* %foo, i64 %28
%31 = load float, float* %30, align 4
%fpfpext31 = fpext float %31 to double
store double %fpfpext31, double* %a27, align 8
%32 = load i8, i8* %i25, align 1
%uisiext = zext i8 %32 to i32
%33 = load double, double* %a27, align 8
call void (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* @5, i32 0, i32 0), i32 %uisiext, double %33)
br label %foreach.inc32
foreach.inc32:
%34 = load i64, i64* %idx26, align 8
%35 = add i64 %34, 1
store i64 %35, i64* %idx26, align 8
br label %foreach.cond28
foreach.exit33:
store i64 0, i64* %idx34, align 8
br label %foreach.cond36
foreach.cond36:
%36 = load i64, i64* %idx34, align 8
%lt37 = icmp ult i64 %36, 3
br i1 %lt37, label %foreach.body38, label %foreach.exit41
foreach.body38:
%37 = getelementptr inbounds float, [3 x float]* %foo, i64 %36
%38 = load float, float* %37, align 4
%fpfpext39 = fpext float %38 to double
store double %fpfpext39, double* %a35, align 8
%39 = load double, double* %a35, align 8
call void (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @6, i32 0, i32 0), double %39)
br label %foreach.inc40
foreach.inc40:
%40 = load i64, i64* %idx34, align 8
%41 = add i64 %40, 1
store i64 %41, i64* %idx34, align 8
br label %foreach.cond36
foreach.exit41:
ret void

View File

@@ -0,0 +1,59 @@
module test;
extern func void foo();
int[3] z;
func void test1()
{
int x;
foreach (a : x) // #error: It's not possible to enumerate an expression of type 'int'.
{
foo();
}
}
func void test2()
{
foreach (a : z) foo();
foreach (i, a : z) foo();
foreach (double i, a : z); // #error: Index must be an integer type, 'double' is not valid.
}
func void test3()
{
foreach (&a : z) foo();
foreach (&i, &a : z) foo(); // #error: The index cannot be held by reference, did you accidentally add a '&'?
}
func void test4()
{
foreach (&a : z) foo();
foreach (&i, a : z) foo(); // #error: The index cannot be held by reference, did you accidentally add a '&'?
}
func void test5()
{
foreach (int! y : z) foo(); // #error: The variable may not be a failable.
}
func void test6()
{
foreach (int! i, y : z) foo(); // #error: The index may not be a failable.
}
func void test7()
{
foreach (int a : { 1, 2, 3 }) foo();
foreach (a : { 1, 2, 3 }) foo(); // #error: Add the type of your variable here if you want to iterate over
}
func void test8()
{
foreach (int a : { z }) foo(); // #error: Cannot implicitly cast 'int[3]' to 'int'
}
func void test9()
{
foreach (int a : { [2] = 1 }) foo();
foreach (int a : { [2] = 2, 1 }) foo(); // #error: The initializer mixes designated initialization with array initialization
}

View File

@@ -0,0 +1,68 @@
module test;
func void test()
{
int[3]! x;
int g;
foreach (z : x)
{
g += z;
x[0] = 1;
}
}
// #expect: foreach_with_error.ll
entry:
%x = alloca [3 x i32], align 4
%x1 = alloca { i64, i64 }, align 8
%g = alloca i32, align 4
%idx = alloca i64, align 8
%z = alloca i32, align 4
store { i64, i64 } zeroinitializer, { i64, i64 }* %x1, 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)
store i32 0, i32* %g, align 4
%1 = load { i64, i64 }, { i64, i64 }* %x1, align 8
%2 = extractvalue { i64, i64 } %1, 0
%noerr = icmp eq i64 %2, 0
br i1 %noerr, label %after_check, label %foreach.exit
after_check:
store i64 0, i64* %idx, align 8
br label %foreach.cond
foreach.cond:
%3 = load i64, i64* %idx, align 8
%lt = icmp ult i64 %3, 3
br i1 %lt, label %foreach.body, label %foreach.exit
foreach.body:
%4 = getelementptr inbounds i32, [3 x i32]* %x, i64 %3
%5 = load i32, i32* %4, align 4
store i32 %5, i32* %z, align 4
%6 = load i32, i32* %g, align 4
%7 = load i32, i32* %z, align 4
%add = add nsw i32 %6, %7
store i32 %add, i32* %g, align 4
%8 = load { i64, i64 }, { i64, i64 }* %x1, align 8
%9 = extractvalue { i64, i64 } %8, 0
%noerr2 = icmp eq i64 %9, 0
br i1 %noerr2, label %after_check3, label %voiderr
after_check3:
%arridx = getelementptr inbounds [3 x i32], [3 x i32]* %x, i32 0, i32 0
store i32 1, i32* %arridx, align 4
br label %voiderr
voiderr:
br label %foreach.inc
foreach.inc:
%10 = load i64, i64* %idx, align 8
%11 = add i64 %10, 1
store i64 %11, i64* %idx, align 8
br label %foreach.cond
foreach.exit:
ret void

View File

@@ -0,0 +1,34 @@
module test;
func void test1()
{
bool! x = 0;
if (x) // #error: 'bool!' cannot be converted into 'bool'
{
x = 100;
}
}
func void test2()
{
bool! x = 0;
while (x) // #error: 'bool!' cannot be converted into 'bool'
{
x = false;
}
}
func void test3()
{
bool! x = 0;
double y = 1;
do
{
y = y + 1;
} while (y);
do
{
x = !x;
}
while (x); // #error: 'bool!' cannot be converted into 'bool'
}

View File

@@ -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 8 bitcast (%test.Point* @0 to i8*), i32 8, i1 false)
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 4 bitcast (%test.Point* @0 to i8*), i32 8, i1 false)