From 4536d3a27050cbf8f7d276f85baa9978cd662693 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 6 Apr 2021 18:38:38 +0200 Subject: [PATCH] Fix of missing O3 setting. Added safe/unsafe mode. Updated trapping on negation. Added trap on negative shift & shift exceeding bit width. Added trap on div by zero, rem by zero. Prevent UB on negative shift/shift exceeding bit width. Use ordered FP comparison. --- src/build/build_options.c | 14 +++- src/build/build_options.h | 2 +- src/build/builder.c | 4 ++ src/compiler/llvm_codegen_expr.c | 86 ++++++++++++++--------- test/test_suite/assignment/int_assign.c3t | 28 ++++---- 5 files changed, 86 insertions(+), 48 deletions(-) diff --git a/src/build/build_options.c b/src/build/build_options.c index 4e09a6e98..ed120e659 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -287,7 +287,7 @@ static void parse_option(BuildOptions *options) { options->optimization_setting_override = OPT_SETTING_O2; } - else if (match_shortopt("O2")) + else if (match_shortopt("O3")) { options->optimization_setting_override = OPT_SETTING_O3; } @@ -377,6 +377,16 @@ static void parse_option(BuildOptions *options) options->path = check_dir(next_arg()); return; } + if (match_longopt("safe")) + { + options->safe_mode = 1; + return; + } + if (match_longopt("unsafe")) + { + options->safe_mode = 0; + return; + } if (match_longopt("help")) { break; @@ -407,7 +417,7 @@ BuildOptions parse_arguments(int argc, const char *argv[]) .emit_bitcode = true, .optimization_setting_override = OPT_SETTING_NOT_SET, .debug_info_override = DEBUG_INFO_NOT_SET, - .debug_mode = false, + .safe_mode = -1, .command = COMMAND_MISSING, .files = NULL }; diff --git a/src/build/build_options.h b/src/build/build_options.h index 1e8f0e940..30d7fd5a5 100644 --- a/src/build/build_options.h +++ b/src/build/build_options.h @@ -176,7 +176,7 @@ typedef struct BuildOptions_ OptimizationSetting optimization_setting_override; DebugInfo debug_info_override; ArchOsTarget arch_os_target_override; - bool debug_mode; + int safe_mode; bool emit_llvm; bool emit_bitcode; bool test_mode; diff --git a/src/build/builder.c b/src/build/builder.c index 1d13febaf..5cf523287 100644 --- a/src/build/builder.c +++ b/src/build/builder.c @@ -82,6 +82,10 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions * default: UNREACHABLE } + if (options->safe_mode > -1) + { + target->feature.safe_mode = options->safe_mode == 1; + } if (options->debug_info_override != DEBUG_INFO_NOT_SET) { target->debug_info = options->debug_info_override; diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 256426f27..2b8174635 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -1076,25 +1076,22 @@ static void gencontext_emit_unary_expr(GenContext *c, BEValue *value, Expr *expr } assert(type->canonical != type_bool); assert(!type_is_unsigned(type)); + llvm_emit_expr(c, value, expr->unary_expr.expr); + llvm_value_rvalue(c, value); + if (c->build_target->feature.trap_on_wrap) { LLVMValueRef zero = llvm_get_zero(c, expr->unary_expr.expr->type); - if (c->build_target->feature.trap_on_wrap) - { - // TODO - LLVMTypeRef type_to_use = llvm_get_type(c, type->canonical); - LLVMValueRef args[2] = { zero, value->value }; - LLVMValueRef call_res = llvm_emit_call_intrinsic(c, intrinsic_id_ssub_overflow, - &type_to_use, 1, args, 2); - value->value = LLVMBuildExtractValue(c->builder, call_res, 0, ""); - LLVMValueRef ok = LLVMBuildExtractValue(c->builder, call_res, 1, ""); - llvm_emit_panic_on_true(c, ok, "Signed negation overflow"); - return; - } - llvm_emit_expr(c, value, expr->unary_expr.expr); - llvm_value_rvalue(c, value); - value->value = LLVMBuildNeg(c->builder, value->value, "neg"); + LLVMTypeRef type_to_use = llvm_get_type(c, type->canonical); + LLVMValueRef args[2] = { zero, value->value }; + LLVMValueRef call_res = llvm_emit_call_intrinsic(c, intrinsic_id_ssub_overflow, + &type_to_use, 1, args, 2); + value->value = LLVMBuildExtractValue(c->builder, call_res, 0, ""); + LLVMValueRef ok = LLVMBuildExtractValue(c->builder, call_res, 1, ""); + llvm_emit_panic_on_true(c, ok, "Signed negation overflow"); return; } + value->value = LLVMBuildNeg(c->builder, value->value, "neg"); + return; case UNARYOP_ADDR: llvm_emit_expr(c, value, expr->unary_expr.expr); // Create an addr @@ -1186,6 +1183,34 @@ static void llvm_emit_trap_negative(GenContext *c, Expr *expr, LLVMValueRef valu llvm_emit_panic_on_true(c, ok, error); } +static void llvm_emit_trap_zero(GenContext *c, Type *type, LLVMValueRef value, const char *error) +{ + if (!c->build_target->feature.safe_mode) return; + + LLVMValueRef zero = llvm_get_zero(c, type); + LLVMValueRef ok = type_is_any_integer(type) ? LLVMBuildICmp(c->builder, LLVMIntEQ, value, zero, "zero") : LLVMBuildFCmp(c->builder, LLVMRealUEQ, value, zero, "zero"); + llvm_emit_panic_on_true(c, ok, error); +} + + +static void llvm_emit_trap_invalid_shift(GenContext *c, LLVMValueRef value, Type *type, const char *error) +{ + if (!c->build_target->feature.safe_mode) return; + unsigned type_bit_size = type_size(type) * 8; + LLVMValueRef max = llvm_const_int(c, type, type_bit_size); + if (type_is_unsigned(type)) + { + LLVMValueRef equal_or_greater = LLVMBuildICmp(c->builder, LLVMIntUGE, value, max, "shift_exceeds"); + llvm_emit_panic_on_true(c, equal_or_greater, error); + return; + } + LLVMValueRef zero = llvm_const_int(c, type, 0); + LLVMValueRef negative = LLVMBuildICmp(c->builder, LLVMIntSLT, value, zero, "shift_underflow"); + llvm_emit_panic_on_true(c, negative, error); + LLVMValueRef equal_or_greater = LLVMBuildICmp(c->builder, LLVMIntSGE, value, max, "shift_exceeds"); + llvm_emit_panic_on_true(c, equal_or_greater, error); +} + static void llvm_emit_slice_values(GenContext *c, Expr *slice, Type **parent_type_ref, LLVMValueRef *parent_base_ref, Type **start_type_ref, LLVMValueRef *start_index_ref, Type **end_type_ref, @@ -1636,10 +1661,7 @@ static inline LLVMValueRef llvm_fixup_shift_rhs(GenContext *c, LLVMValueRef left { return LLVMBuildTrunc(c->builder, right, left_type, ""); } - else - { - return LLVMBuildZExt(c->builder, right, left_type, ""); - } + return LLVMBuildZExt(c->builder, right, left_type, ""); } static inline LLVMValueRef llvm_emit_mult_int(GenContext *c, Type *type, LLVMValueRef left, LLVMValueRef right) @@ -1695,13 +1717,7 @@ static void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, EMIT_LOC(c, expr); if (type_is_integer(lhs_type) && binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ) { - llvm_value_set_bool(be_value, - llvm_emit_int_comparison(c, - lhs_type, - rhs_type, - lhs_value, - rhs_value, - binary_op)); + llvm_value_set_bool(be_value, llvm_emit_int_comparison(c, lhs_type, rhs_type, lhs_value, rhs_value, binary_op)); return; } bool is_float = type_is_float(lhs_type); @@ -1752,6 +1768,7 @@ static void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, val = llvm_emit_add_int(c, lhs_type, lhs_value, rhs_value); break; case BINARYOP_DIV: + llvm_emit_trap_zero(c, rhs_type, rhs_value, "% by zero"); if (is_float) { val = LLVMBuildFDiv(c->builder, lhs_value, rhs_value, "fdiv"); @@ -1762,19 +1779,24 @@ static void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, : LLVMBuildSDiv(c->builder, lhs_value, rhs_value, "sdiv"); break; case BINARYOP_MOD: + llvm_emit_trap_zero(c, rhs_type, rhs_value, "% by zero"); val = type_is_unsigned(lhs_type) ? LLVMBuildURem(c->builder, lhs_value, rhs_value, "umod") : LLVMBuildSRem(c->builder, lhs_value, rhs_value, "smod"); break; case BINARYOP_SHR: rhs_value = llvm_fixup_shift_rhs(c, lhs_value, rhs_value); + llvm_emit_trap_invalid_shift(c, rhs_value, lhs_type, "Shift amount out of range."); val = type_is_unsigned(lhs_type) ? LLVMBuildLShr(c->builder, lhs_value, rhs_value, "lshr") : LLVMBuildAShr(c->builder, lhs_value, rhs_value, "ashr"); + val = LLVMBuildFreeze(c->builder, val, ""); break; case BINARYOP_SHL: rhs_value = llvm_fixup_shift_rhs(c, lhs_value, rhs_value); + llvm_emit_trap_invalid_shift(c, rhs_value, lhs_type, "Shift amount out of range."); val = LLVMBuildShl(c->builder, lhs_value, rhs_value, "shl"); + val = LLVMBuildFreeze(c->builder, val, ""); break; case BINARYOP_BIT_AND: val = LLVMBuildAnd(c->builder, lhs_value, rhs_value, "and"); @@ -1788,28 +1810,28 @@ static void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, case BINARYOP_EQ: // Unordered? assert(type_is_float(lhs_type)); - llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealUEQ, lhs_value, rhs_value, "eq")); + llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealOEQ, lhs_value, rhs_value, "eq")); return; case BINARYOP_NE: // Unordered? assert(type_is_float(lhs_type)); - llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealUNE, lhs_value, rhs_value, "neq")); + llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealONE, lhs_value, rhs_value, "neq")); return; case BINARYOP_GE: assert(type_is_float(lhs_type)); - llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealUGE, lhs_value, rhs_value, "ge")); + llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealOGE, lhs_value, rhs_value, "ge")); return; case BINARYOP_GT: assert(type_is_float(lhs_type)); - llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealUGT, lhs_value, rhs_value, "gt")); + llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealOGT, lhs_value, rhs_value, "gt")); return; case BINARYOP_LE: assert(type_is_float(lhs_type)); - llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealULE, lhs_value, rhs_value, "le")); + llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealOLE, lhs_value, rhs_value, "le")); return; case BINARYOP_LT: assert(type_is_float(lhs_type)); - llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealULT, lhs_value, rhs_value, "lt")); + llvm_value_set_bool(be_value, LLVMBuildFCmp(c->builder, LLVMRealOLT, lhs_value, rhs_value, "lt")); return; case BINARYOP_AND: case BINARYOP_OR: diff --git a/test/test_suite/assignment/int_assign.c3t b/test/test_suite/assignment/int_assign.c3t index 6942c95bf..19b698977 100644 --- a/test/test_suite/assignment/int_assign.c3t +++ b/test/test_suite/assignment/int_assign.c3t @@ -47,21 +47,23 @@ func int foo() %10 = load i32, i32* %x, align 4 %11 = load i32, i32* %y, align 4 %shl = shl i32 %10, %11 - store i32 %shl, i32* %x, align 4 - %12 = load i32, i32* %x, align 4 - %13 = load i32, i32* %y, align 4 - %ashr = ashr i32 %12, %13 - store i32 %ashr, i32* %x, align 4 - %14 = load i32, i32* %x, align 4 - %15 = load i32, i32* %y, align 4 - %xor = xor i32 %14, %15 - store i32 %xor, i32* %x, align 4 + %12 = freeze i32 %shl + store i32 %12, i32* %x, align 4 + %13 = load i32, i32* %x, align 4 + %14 = load i32, i32* %y, align 4 + %ashr = ashr i32 %13, %14 + %15 = freeze i32 %ashr + store i32 %15, i32* %x, align 4 %16 = load i32, i32* %x, align 4 %17 = load i32, i32* %y, align 4 - %or = or i32 %16, %17 - store i32 %or, i32* %x, align 4 + %xor = xor i32 %16, %17 + store i32 %xor, i32* %x, align 4 %18 = load i32, i32* %x, align 4 %19 = load i32, i32* %y, align 4 - %and = and i32 %18, %19 - store i32 %and, i32* %x, align 4 + %or = or i32 %18, %19 + store i32 %or, i32* %x, align 4 %20 = load i32, i32* %x, align 4 + %21 = load i32, i32* %y, align 4 + %and = and i32 %20, %21 + store i32 %and, i32* %x, align 4 + %22 = load i32, i32* %x, align 4