authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-03-26 15:06:43-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-03-26 15:06:43-04:00
log8aeea72654b2efbd068abe207b42170c4d27ee03
tree34d01fb3e41a7952c00e00088f10e5159d70c976
parent6ee63c8f58357a2b97b6039beca90437ba8bf68c

add debug safety checks for remainder division

See #217

7 files changed, 76 insertions(+), 29 deletions(-)

doc/langref.md+5-5
...@@ -165,17 +165,17 @@ ContainerDecl = option("extern" | "packed") ("struct" | "enum" | "union") "{" ma...@@ -165,17 +165,17 @@ ContainerDecl = option("extern" | "packed") ("struct" | "enum" | "union") "{" ma
165165
166```166```
167x() x[] x.y167x() x[] x.y
168!x -x ~x *x &x ?x %x %%x168!x -x -%x ~x *x &x ?x %x %%x ??x
169x{}169x{}
170* / %170* / % ** *%
171+ - ++171+ - ++ +% -%
172<< >>172<< >>
173&173&
174^174^
175|175|
176== != < > <= >=176== != < > <= >=
177&&177and
178||178or
179?? %%179?? %%
180= *= /= %= += -= <<= >>= &= ^= |=180= *= /= %= += -= <<= >>= &= ^= |=
181```181```
src/all_types.hpp+2-1
...@@ -1205,6 +1205,7 @@ enum PanicMsgId {...@@ -1205,6 +1205,7 @@ enum PanicMsgId {
1205 PanicMsgIdIntegerOverflow,1205 PanicMsgIdIntegerOverflow,
1206 PanicMsgIdShiftOverflowedBits,1206 PanicMsgIdShiftOverflowedBits,
1207 PanicMsgIdDivisionByZero,1207 PanicMsgIdDivisionByZero,
1208 PanicMsgIdRemainderDivisionByZero,
1208 PanicMsgIdExactDivisionRemainder,1209 PanicMsgIdExactDivisionRemainder,
1209 PanicMsgIdSliceWidenRemainder,1210 PanicMsgIdSliceWidenRemainder,
1210 PanicMsgIdUnwrapMaybeFail,1211 PanicMsgIdUnwrapMaybeFail,
...@@ -1828,7 +1829,7 @@ enum IrBinOp {...@@ -1828,7 +1829,7 @@ enum IrBinOp {
1828 IrBinOpMult,1829 IrBinOpMult,
1829 IrBinOpMultWrap,1830 IrBinOpMultWrap,
1830 IrBinOpDiv,1831 IrBinOpDiv,
1831 IrBinOpMod,1832 IrBinOpRem,
1832 IrBinOpArrayCat,1833 IrBinOpArrayCat,
1833 IrBinOpArrayMult,1834 IrBinOpArrayMult,
1834};1835};
src/bignum.cpp+2-2
...@@ -212,7 +212,7 @@ bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2) {...@@ -212,7 +212,7 @@ bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2) {
212 return false;212 return false;
213}213}
214214
215bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) {215bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2) {
216 assert(op1->kind == op2->kind);216 assert(op1->kind == op2->kind);
217 dest->kind = op1->kind;217 dest->kind = op1->kind;
218218
...@@ -220,7 +220,7 @@ bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) {...@@ -220,7 +220,7 @@ bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) {
220 dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float);220 dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float);
221 } else {221 } else {
222 if (op1->is_negative || op2->is_negative) {222 if (op1->is_negative || op2->is_negative) {
223 zig_panic("TODO handle mod with negative numbers");223 zig_panic("TODO handle remainder division with negative numbers");
224 }224 }
225 dest->data.x_uint = op1->data.x_uint % op2->data.x_uint;225 dest->data.x_uint = op1->data.x_uint % op2->data.x_uint;
226 bignum_normalize(dest);226 bignum_normalize(dest);
src/bignum.hpp+1-1
...@@ -37,7 +37,7 @@ bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2);...@@ -37,7 +37,7 @@ bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2);
37bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2);37bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2);
38bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2);38bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2);
39bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2);39bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2);
40bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2);40bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2);
4141
42bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2);42bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2);
43bool bignum_and(BigNum *dest, BigNum *op1, BigNum *op2);43bool bignum_and(BigNum *dest, BigNum *op1, BigNum *op2);
src/codegen.cpp+57-11
...@@ -512,6 +512,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {...@@ -512,6 +512,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
512 return buf_create_from_str("left shift overflowed bits");512 return buf_create_from_str("left shift overflowed bits");
513 case PanicMsgIdDivisionByZero:513 case PanicMsgIdDivisionByZero:
514 return buf_create_from_str("division by zero");514 return buf_create_from_str("division by zero");
515 case PanicMsgIdRemainderDivisionByZero:
516 return buf_create_from_str("remainder division by zero");
515 case PanicMsgIdExactDivisionRemainder:517 case PanicMsgIdExactDivisionRemainder:
516 return buf_create_from_str("exact division produced remainder");518 return buf_create_from_str("exact division produced remainder");
517 case PanicMsgIdSliceWidenRemainder:519 case PanicMsgIdSliceWidenRemainder:
...@@ -956,6 +958,59 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, LLVMValueRef val...@@ -956,6 +958,59 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, LLVMValueRef val
956 }958 }
957}959}
958960
961static LLVMValueRef gen_rem(CodeGen *g, bool want_debug_safety, LLVMValueRef val1, LLVMValueRef val2,
962 TypeTableEntry *type_entry)
963{
964
965 if (want_debug_safety) {
966 LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
967 LLVMValueRef is_zero_bit;
968 if (type_entry->id == TypeTableEntryIdInt) {
969 is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
970 } else if (type_entry->id == TypeTableEntryIdFloat) {
971 is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, "");
972 } else {
973 zig_unreachable();
974 }
975 LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk");
976 LLVMBasicBlockRef rem_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroFail");
977 LLVMBuildCondBr(g->builder, is_zero_bit, rem_zero_fail_block, rem_zero_ok_block);
978
979 LLVMPositionBuilderAtEnd(g->builder, rem_zero_fail_block);
980 gen_debug_safety_crash(g, PanicMsgIdRemainderDivisionByZero);
981
982 LLVMPositionBuilderAtEnd(g->builder, rem_zero_ok_block);
983
984 if (type_entry->id == TypeTableEntryIdInt && type_entry->data.integral.is_signed) {
985 LLVMValueRef neg_1_value = LLVMConstInt(type_entry->type_ref, -1, true);
986 LLVMValueRef int_min_value = LLVMConstInt(type_entry->type_ref, min_signed_val(type_entry), true);
987 LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemOverflowOk");
988 LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemOverflowFail");
989 LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, "");
990 LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, "");
991 LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, "");
992 LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block);
993
994 LLVMPositionBuilderAtEnd(g->builder, overflow_fail_block);
995 gen_debug_safety_crash(g, PanicMsgIdIntegerOverflow);
996
997 LLVMPositionBuilderAtEnd(g->builder, overflow_ok_block);
998 }
999 }
1000
1001 if (type_entry->id == TypeTableEntryIdFloat) {
1002 return LLVMBuildFRem(g->builder, val1, val2, "");
1003 } else {
1004 assert(type_entry->id == TypeTableEntryIdInt);
1005 if (type_entry->data.integral.is_signed) {
1006 return LLVMBuildSRem(g->builder, val1, val2, "");
1007 } else {
1008 return LLVMBuildURem(g->builder, val1, val2, "");
1009 }
1010 }
1011
1012}
1013
959static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,1014static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
960 IrInstructionBinOp *bin_op_instruction)1015 IrInstructionBinOp *bin_op_instruction)
961{1016{
...@@ -1092,17 +1147,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,...@@ -1092,17 +1147,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
1092 }1147 }
1093 case IrBinOpDiv:1148 case IrBinOpDiv:
1094 return gen_div(g, want_debug_safety, op1_value, op2_value, canon_type, false);1149 return gen_div(g, want_debug_safety, op1_value, op2_value, canon_type, false);
1095 case IrBinOpMod:1150 case IrBinOpRem:
1096 if (canon_type->id == TypeTableEntryIdFloat) {1151 return gen_rem(g, want_debug_safety, op1_value, op2_value, canon_type);
1097 return LLVMBuildFRem(g->builder, op1_value, op2_value, "");
1098 } else {
1099 assert(canon_type->id == TypeTableEntryIdInt);
1100 if (canon_type->data.integral.is_signed) {
1101 return LLVMBuildSRem(g->builder, op1_value, op2_value, "");
1102 } else {
1103 return LLVMBuildURem(g->builder, op1_value, op2_value, "");
1104 }
1105 }
1106 }1152 }
1107 zig_unreachable();1153 zig_unreachable();
1108}1154}
src/ir.cpp+8-8
...@@ -3487,7 +3487,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)...@@ -3487,7 +3487,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
3487 case BinOpTypeAssignDiv:3487 case BinOpTypeAssignDiv:
3488 return ir_gen_assign_op(irb, scope, node, IrBinOpDiv);3488 return ir_gen_assign_op(irb, scope, node, IrBinOpDiv);
3489 case BinOpTypeAssignMod:3489 case BinOpTypeAssignMod:
3490 return ir_gen_assign_op(irb, scope, node, IrBinOpMod);3490 return ir_gen_assign_op(irb, scope, node, IrBinOpRem);
3491 case BinOpTypeAssignPlus:3491 case BinOpTypeAssignPlus:
3492 return ir_gen_assign_op(irb, scope, node, IrBinOpAdd);3492 return ir_gen_assign_op(irb, scope, node, IrBinOpAdd);
3493 case BinOpTypeAssignPlusWrap:3493 case BinOpTypeAssignPlusWrap:
...@@ -3555,7 +3555,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)...@@ -3555,7 +3555,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
3555 case BinOpTypeDiv:3555 case BinOpTypeDiv:
3556 return ir_gen_bin_op_id(irb, scope, node, IrBinOpDiv);3556 return ir_gen_bin_op_id(irb, scope, node, IrBinOpDiv);
3557 case BinOpTypeMod:3557 case BinOpTypeMod:
3558 return ir_gen_bin_op_id(irb, scope, node, IrBinOpMod);3558 return ir_gen_bin_op_id(irb, scope, node, IrBinOpRem);
3559 case BinOpTypeArrayCat:3559 case BinOpTypeArrayCat:
3560 return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat);3560 return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat);
3561 case BinOpTypeArrayMult:3561 case BinOpTypeArrayMult:
...@@ -7394,7 +7394,7 @@ static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val,...@@ -7394,7 +7394,7 @@ static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val,
7394{7394{
7395 bool is_int = false;7395 bool is_int = false;
7396 bool is_float = false;7396 bool is_float = false;
7397 if (bignum_fn == bignum_div || bignum_fn == bignum_mod) {7397 if (bignum_fn == bignum_div || bignum_fn == bignum_rem) {
7398 if (type->id == TypeTableEntryIdInt ||7398 if (type->id == TypeTableEntryIdInt ||
7399 type->id == TypeTableEntryIdNumLitInt)7399 type->id == TypeTableEntryIdNumLitInt)
7400 {7400 {
...@@ -7480,8 +7480,8 @@ static int ir_eval_math_op(TypeTableEntry *canon_type, ConstExprValue *op1_val,...@@ -7480,8 +7480,8 @@ static int ir_eval_math_op(TypeTableEntry *canon_type, ConstExprValue *op1_val,
7480 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, canon_type, true);7480 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, canon_type, true);
7481 case IrBinOpDiv:7481 case IrBinOpDiv:
7482 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, canon_type, false);7482 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, canon_type, false);
7483 case IrBinOpMod:7483 case IrBinOpRem:
7484 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mod, canon_type, false);7484 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_rem, canon_type, false);
7485 }7485 }
7486 zig_unreachable();7486 zig_unreachable();
7487}7487}
...@@ -7506,7 +7506,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp...@@ -7506,7 +7506,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
7506 op_id == IrBinOpSub ||7506 op_id == IrBinOpSub ||
7507 op_id == IrBinOpMult ||7507 op_id == IrBinOpMult ||
7508 op_id == IrBinOpDiv ||7508 op_id == IrBinOpDiv ||
7509 op_id == IrBinOpMod))7509 op_id == IrBinOpRem))
7510 {7510 {
7511 // float7511 // float
7512 } else {7512 } else {
...@@ -7777,7 +7777,7 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi...@@ -7777,7 +7777,7 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi
7777 case IrBinOpMult:7777 case IrBinOpMult:
7778 case IrBinOpMultWrap:7778 case IrBinOpMultWrap:
7779 case IrBinOpDiv:7779 case IrBinOpDiv:
7780 case IrBinOpMod:7780 case IrBinOpRem:
7781 return ir_analyze_bin_op_math(ira, bin_op_instruction);7781 return ir_analyze_bin_op_math(ira, bin_op_instruction);
7782 case IrBinOpArrayCat:7782 case IrBinOpArrayCat:
7783 return ir_analyze_array_cat(ira, bin_op_instruction);7783 return ir_analyze_array_cat(ira, bin_op_instruction);
...@@ -11211,7 +11211,7 @@ static TypeTableEntry *ir_analyze_instruction_div_exact(IrAnalyze *ira, IrInstru...@@ -11211,7 +11211,7 @@ static TypeTableEntry *ir_analyze_instruction_div_exact(IrAnalyze *ira, IrInstru
11211 }11211 }
1121211212
11213 BigNum remainder;11213 BigNum remainder;
11214 if (bignum_mod(&remainder, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) {11214 if (bignum_rem(&remainder, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) {
11215 ir_add_error(ira, &instruction->base, buf_sprintf("integer overflow"));11215 ir_add_error(ira, &instruction->base, buf_sprintf("integer overflow"));
11216 return ira->codegen->builtin_types.entry_invalid;11216 return ira->codegen->builtin_types.entry_invalid;
11217 }11217 }
src/ir_print.cpp+1-1
...@@ -110,7 +110,7 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {...@@ -110,7 +110,7 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
110 return "*%";110 return "*%";
111 case IrBinOpDiv:111 case IrBinOpDiv:
112 return "/";112 return "/";
113 case IrBinOpMod:113 case IrBinOpRem:
114 return "%";114 return "%";
115 case IrBinOpArrayCat:115 case IrBinOpArrayCat:
116 return "++";116 return "++";