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.

This commit is contained in:
Christoffer Lerno
2021-04-06 18:38:38 +02:00
committed by Christoffer Lerno
parent 4210f1ccb2
commit 4536d3a270
5 changed files with 86 additions and 48 deletions

View File

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

View File

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

View File

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

View File

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

View File

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