| author | |
| committer | |
| log | 8aeea72654b2efbd068abe207b42170c4d27ee03 |
| tree | 34d01fb3e41a7952c00e00088f10e5159d70c976 |
| parent | 6ee63c8f58357a2b97b6039beca90437ba8bf68c |
See #2177 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 |
| 165 | 165 | ||
| 166 | ``` | 166 | ``` |
| 167 | x() x[] x.y | 167 | x() x[] x.y |
| 168 | !x -x ~x *x &x ?x %x %%x | 168 | !x -x -%x ~x *x &x ?x %x %%x ??x |
| 169 | x{} | 169 | x{} |
| 170 | * / % | 170 | * / % ** *% |
| 171 | + - ++ | 171 | + - ++ +% -% |
| 172 | << >> | 172 | << >> |
| 173 | & | 173 | & |
| 174 | ^ | 174 | ^ |
| 175 | | | 175 | | |
| 176 | == != < > <= >= | 176 | == != < > <= >= |
| 177 | && | 177 | and |
| 178 | || | 178 | or |
| 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 | } |
| 214 | 214 | ||
| 215 | bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) { | 215 | bool 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; |
| 218 | 218 | ||
| ... | @@ -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); |
| 37 | bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2); | 37 | bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2); |
| 38 | bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2); | 38 | bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2); |
| 39 | bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2); | 39 | bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2); |
| 40 | bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2); | 40 | bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2); |
| 41 | 41 | ||
| 42 | bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2); | 42 | bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2); |
| 43 | bool bignum_and(BigNum *dest, BigNum *op1, BigNum *op2); | 43 | bool 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 | } |
| 958 | 960 | ||
| 961 | static 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 | |||
| 959 | static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, | 1014 | static 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 | // float | 7511 | // 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 | } |
| 11212 | 11212 | ||
| 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 "++"; |