diff --git a/missing.txt b/missing.txt index e22a5a02c..597e4dcfa 100644 --- a/missing.txt +++ b/missing.txt @@ -45,6 +45,8 @@ Things missing: * Expressions - Disallow x >= 0 and x < 0 on unsigned types unless in a macro. +- Range check arrays on debug +- Allow negating int if assigned to a larger type. E.g short x = 1; int y = -x; * Switch - String switch diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index 50cd0d882..a2037ca35 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -217,6 +217,7 @@ func int borok() throws func void testNoReturn() { int i = 0; + i = -i; } func int testReturn() @@ -513,7 +514,10 @@ func int testPointers(int x) } func int main(int x) { + printf("Helo!\n"); int efd = 9; + uint fefoek = 1; + long fefoek = -fefoek; int okfe = 1; return 1; switch (int bobe = okfe > 0 ? 1 : 0) diff --git a/src/build/build_options.c b/src/build/build_options.c index 6b9cf9296..d8ac764ea 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -316,6 +316,7 @@ void parse_arguments(int argc, const char *argv[]) build_options.optimization_level = OPTIMIZATION_NOT_SET; build_options.size_optimization_level = SIZE_OPTIMIZATION_NOT_SET; build_options.debug_info = false; + build_options.debug_mode = false; build_options.command = COMMAND_MISSING; build_options.symtab_size = DEFAULT_SYMTAB_SIZE; build_options.files = NULL; diff --git a/src/build/build_options.h b/src/build/build_options.h index 7c1f9645a..14a716b2b 100644 --- a/src/build/build_options.h +++ b/src/build/build_options.h @@ -103,6 +103,7 @@ typedef struct OptimizationLevel optimization_level; SizeOptimizationLevel size_optimization_level; bool debug_info; + bool debug_mode; bool emit_llvm; bool emit_bitcode; } BuildOptions; diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 4561ebcc0..02304d28c 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -95,6 +95,7 @@ void compiler_compile(BuildTarget *target) } if (diagnostics.errors > 0) exit(EXIT_FAILURE); + llvm_codegen_setup(); VECEACH(contexts, i) { Context *context = contexts[i]; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index bccdbd7d0..a1ff2938a 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1005,6 +1005,7 @@ bool cast_to_runtime(Expr *expr); void cast_to_smallest_runtime(Expr *expr); void llvm_codegen(Context *context); +void llvm_codegen_setup(); bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr); diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index aa22ae8d2..49cb4463b 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -155,8 +155,39 @@ static int get_inlining_threshold(void) return 250; } } + + +static inline unsigned lookup_intrinsic(const char *name) +{ + return LLVMLookupIntrinsicID(name, strlen(name)); +} + +static bool intrinsics_setup = false; +unsigned ssub_overflow_intrinsic_id; +unsigned usub_overflow_intrinsic_id; +unsigned sadd_overflow_intrinsic_id; +unsigned uadd_overflow_intrinsic_id; +unsigned smul_overflow_intrinsic_id; +unsigned umul_overflow_intrinsic_id; +unsigned trap_intrinsic_id; + +void llvm_codegen_setup() +{ + assert(intrinsics_setup == false); + ssub_overflow_intrinsic_id = lookup_intrinsic("llvm.ssub.with.overflow"); + usub_overflow_intrinsic_id = lookup_intrinsic("llvm.usub.with.overflow"); + sadd_overflow_intrinsic_id = lookup_intrinsic("llvm.sadd.with.overflow"); + uadd_overflow_intrinsic_id = lookup_intrinsic("llvm.uadd.with.overflow"); + smul_overflow_intrinsic_id = lookup_intrinsic("llvm.smul.with.overflow"); + umul_overflow_intrinsic_id = lookup_intrinsic("llvm.umul.with.overflow"); + trap_intrinsic_id = lookup_intrinsic("llvm.trap"); + + intrinsics_setup = true; +} + void llvm_codegen(Context *context) { + assert(intrinsics_setup); GenContext gen_context; gencontext_init(&gen_context, context); gencontext_begin_module(&gen_context); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 467242eca..6bad86e85 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -13,7 +13,26 @@ static inline LLVMValueRef gencontext_emit_add_int(GenContext *context, Type *ty return LLVMBuildAdd(context->builder, left, right, "add_mod"); } - // TODO insert trap + if (build_options.debug_mode) + { + LLVMTypeRef type_to_use = llvm_type(type->canonical); + LLVMTypeRef types[2] = { type_to_use, type_to_use }; + LLVMValueRef args[2] = { left, right }; + assert(type->canonical == type); + LLVMValueRef add_res; + if (type_is_unsigned(type)) + { + add_res = gencontext_emit_call_intrinsic(context, uadd_overflow_intrinsic_id, types, args, 2); + } + else + { + add_res = gencontext_emit_call_intrinsic(context, sadd_overflow_intrinsic_id, types, args, 2); + } + LLVMValueRef result = LLVMBuildExtractValue(context->builder, add_res, 0, ""); + LLVMValueRef ok = LLVMBuildExtractValue(context->builder, add_res, 1, ""); + gencontext_emit_panic_on_true(context, ok, "Addition overflow"); + return result; + } return type_is_unsigned_integer(type) ? LLVMBuildNUWAdd(context->builder, left, right, "uadd") : LLVMBuildNSWAdd(context->builder, left, right, "add"); @@ -26,7 +45,28 @@ static inline LLVMValueRef gencontext_emit_sub_int(GenContext *context, Type *ty return LLVMBuildSub(context->builder, left, right, "sub_mod"); } - // TODO insert trap + if (build_options.debug_mode) + { + LLVMTypeRef type_to_use = llvm_type(type); + LLVMTypeRef types[2] = { type_to_use, type_to_use }; + LLVMValueRef args[2] = { left, right }; + assert(type->canonical == type); + LLVMValueRef add_res; + if (type_is_unsigned(type)) + { + add_res = gencontext_emit_call_intrinsic(context, usub_overflow_intrinsic_id, types, args, 2); + } + else + { + add_res = gencontext_emit_call_intrinsic(context, ssub_overflow_intrinsic_id, types, args, 2); + } + LLVMValueRef result = LLVMBuildExtractValue(context->builder, add_res, 0, ""); + LLVMValueRef ok = LLVMBuildExtractValue(context->builder, add_res, 1, ""); + gencontext_emit_panic_on_true(context, ok, "Subtraction overflow"); + return result; + } + + return type_is_unsigned_integer(type) ? LLVMBuildNUWSub(context->builder, left, right, "usub") : LLVMBuildNSWSub(context->builder, left, right, "sub"); @@ -211,6 +251,8 @@ static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr * LLVMValueRef rhs = gencontext_emit_expr(context, expr->cast_expr.expr); return gencontext_emit_cast(context, expr->cast_expr.kind, rhs, expr->type->canonical, expr->cast_expr.expr->type->canonical); } + + static inline LLVMValueRef gencontext_emit_designated_initializer(GenContext *context, Type *parent_type, LLVMValueRef parent, Expr *expr) { assert(parent_type == parent_type->canonical); @@ -234,6 +276,12 @@ static inline LLVMValueRef gencontext_emit_designated_initializer(GenContext *co } } +/** + * Emit a Foo { .... } literal. + * + * 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 + */ static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr) { LLVMTypeRef type = llvm_type(expr->type); @@ -343,17 +391,27 @@ LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr) case UNARYOP_NEGMOD: return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "negmod"); case UNARYOP_NEG: - // TODO improve how unsigned numbers are negated. if (type_is_float(type)) { return LLVMBuildFNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "fneg"); } - if (type_is_unsigned(type)) + assert(!type_is_unsigned(type)); { - return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "neg"); + LLVMValueRef to_negate = gencontext_emit_expr(context, expr->unary_expr.expr); + LLVMValueRef zero = LLVMConstInt(llvm_type(expr->unary_expr.expr->type->canonical), 0, false); + if (build_options.debug_mode) + { + LLVMTypeRef type_to_use = llvm_type(type->canonical); + LLVMValueRef args[2] = { zero, to_negate }; + LLVMTypeRef types[2] = { type_to_use, type_to_use }; + LLVMValueRef call_res = gencontext_emit_call_intrinsic(context, ssub_overflow_intrinsic_id, types, args, 2); + LLVMValueRef result = LLVMBuildExtractValue(context->builder, call_res, 0, ""); + LLVMValueRef ok = LLVMBuildExtractValue(context->builder, call_res, 1, ""); + gencontext_emit_panic_on_true(context, ok, "Signed negation overflow"); + return result; + } + return LLVMBuildNSWSub(context->builder, zero, to_negate, "neg"); } - // TODO insert trap - return LLVMBuildNSWNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "neg"); case UNARYOP_ADDR: return gencontext_emit_address(context, expr->unary_expr.expr); case UNARYOP_DEREF: @@ -578,15 +636,33 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM UNREACHABLE case BINARYOP_MULT: if (is_float) return LLVMBuildFMul(context->builder, lhs_value, rhs_value, "fmul"); - // TODO insert trap if (type_is_unsigned_integer(lhs_type)) { + if (build_options.debug_mode) + { + LLVMTypeRef type_to_use = llvm_type(lhs_type); + LLVMValueRef args[2] = { lhs_value, rhs_value }; + LLVMTypeRef types[2] = { type_to_use, type_to_use }; + LLVMValueRef call_res = gencontext_emit_call_intrinsic(context, umul_overflow_intrinsic_id, types, args, 2); + LLVMValueRef result = LLVMBuildExtractValue(context->builder, call_res, 0, ""); + LLVMValueRef ok = LLVMBuildExtractValue(context->builder, call_res, 1, ""); + gencontext_emit_panic_on_true(context, ok, "Unsigned multiplication overflow"); + return result; + } return LLVMBuildNUWMul(context->builder, lhs_value, rhs_value, "umul"); } - else + if (build_options.debug_mode) { - return LLVMBuildNSWMul(context->builder, lhs_value, rhs_value, "mul"); + LLVMTypeRef type_to_use = llvm_type(lhs_type); + LLVMValueRef args[2] = { lhs_value, rhs_value }; + LLVMTypeRef types[2] = { type_to_use, type_to_use }; + LLVMValueRef call_res = gencontext_emit_call_intrinsic(context, smul_overflow_intrinsic_id, types, args, 2); + LLVMValueRef result = LLVMBuildExtractValue(context->builder, call_res, 0, ""); + LLVMValueRef ok = LLVMBuildExtractValue(context->builder, call_res, 1, ""); + gencontext_emit_panic_on_true(context, ok, "Signed multiplication overflow"); + return result; } + return LLVMBuildNSWMul(context->builder, lhs_value, rhs_value, "mul"); case BINARYOP_MULT_MOD: return LLVMBuildMul(context->builder, lhs_value, rhs_value, "mul"); case BINARYOP_SUB: @@ -651,7 +727,6 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM return LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "lt"); case BINARYOP_AND: case BINARYOP_OR: - UNREACHABLE case BINARYOP_ASSIGN: case BINARYOP_MULT_ASSIGN: case BINARYOP_MULT_MOD_ASSIGN: @@ -878,6 +953,15 @@ static inline LLVMValueRef gencontext_emit_expr_block(GenContext *context, Expr return return_out; } +LLVMValueRef gencontext_emit_call_intrinsic(GenContext *context, unsigned intrinsic_id, LLVMTypeRef *types, + LLVMValueRef *values, unsigned arg_count) +{ + LLVMValueRef decl = LLVMGetIntrinsicDeclaration(context->module, intrinsic_id, types, arg_count); + LLVMTypeRef type = LLVMIntrinsicGetType(context->context, intrinsic_id, types, arg_count); + return LLVMBuildCall2(context->builder, type, decl, values, arg_count, ""); +} + + LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) { switch (expr->expr_kind) diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 9e114946e..e460d61b6 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -68,10 +68,20 @@ typedef struct bool did_call_stack_save : 1; } GenContext; +extern unsigned sadd_overflow_intrinsic_id; +extern unsigned uadd_overflow_intrinsic_id; +extern unsigned ssub_overflow_intrinsic_id; +extern unsigned usub_overflow_intrinsic_id; +extern unsigned smul_overflow_intrinsic_id; +extern unsigned umul_overflow_intrinsic_id; +extern unsigned trap_intrinsic_id; void gencontext_begin_module(GenContext *context); void gencontext_end_module(GenContext *context); void gencontext_emit_stmt(GenContext *context, Ast *ast); +LLVMValueRef gencontext_emit_call_intrinsic(GenContext *context, unsigned intrinsic_id, LLVMTypeRef *types, + LLVMValueRef *values, unsigned arg_count); +void gencontext_emit_panic_on_true(GenContext *context, LLVMValueRef value, const char *panic_name); void gencontext_emit_defer(GenContext *context, Ast *defer_start, Ast *defer_end); LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr); LLVMValueRef gencontext_emit_ast_expr(GenContext *context, Ast *expr); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 20da33bab..9f41a100e 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -600,6 +600,18 @@ void gencontext_emit_scoped_stmt(GenContext *context, Ast *ast) gencontext_emit_defer(context, ast->scoped_stmt.defers.start, ast->scoped_stmt.defers.end); } +void gencontext_emit_panic_on_true(GenContext *context, LLVMValueRef value, const char *panic_name) +{ + LLVMBasicBlockRef panic_block = gencontext_create_free_block(context, "panic"); + LLVMBasicBlockRef ok_block = gencontext_create_free_block(context, "checkok"); + gencontext_emit_cond_br(context, value, panic_block, ok_block); + gencontext_emit_block(context, panic_block); + gencontext_emit_call_intrinsic(context, trap_intrinsic_id, NULL, NULL, 0); + gencontext_emit_br(context, ok_block); + gencontext_emit_block(context, ok_block); +} + + void gencontext_emit_stmt(GenContext *context, Ast *ast) { switch (ast->ast_kind)