mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 03:51:18 +00:00
Adding trap on debug builds.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user