Adding trap on debug builds.

This commit is contained in:
Christoffer Lerno
2020-04-09 13:47:23 +02:00
parent bb806716e4
commit f53b378b0c
10 changed files with 158 additions and 11 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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;

View File

@@ -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;

View File

@@ -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];

View File

@@ -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);

View File

@@ -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);

View File

@@ -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)

View File

@@ -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);

View File

@@ -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)