| 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 | 165 | |
| 166 | 166 | ``` |
| 167 | 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 | 169 | x{} |
| 170 | * / % | |
| 171 | + - ++ | |
| 170 | * / % ** *% | |
| 171 | + - ++ +% -% | |
| 172 | 172 | << >> |
| 173 | 173 | & |
| 174 | 174 | ^ |
| 175 | 175 | | |
| 176 | 176 | == != < > <= >= |
| 177 | && | |
| 178 | || | |
| 177 | and | |
| 178 | or | |
| 179 | 179 | ?? %% |
| 180 | 180 | = *= /= %= += -= <<= >>= &= ^= |= |
| 181 | 181 | ``` |
src/all_types.hpp+2-1| ... | ... | @@ -1205,6 +1205,7 @@ enum PanicMsgId { |
| 1205 | 1205 | PanicMsgIdIntegerOverflow, |
| 1206 | 1206 | PanicMsgIdShiftOverflowedBits, |
| 1207 | 1207 | PanicMsgIdDivisionByZero, |
| 1208 | PanicMsgIdRemainderDivisionByZero, | |
| 1208 | 1209 | PanicMsgIdExactDivisionRemainder, |
| 1209 | 1210 | PanicMsgIdSliceWidenRemainder, |
| 1210 | 1211 | PanicMsgIdUnwrapMaybeFail, |
| ... | ... | @@ -1828,7 +1829,7 @@ enum IrBinOp { |
| 1828 | 1829 | IrBinOpMult, |
| 1829 | 1830 | IrBinOpMultWrap, |
| 1830 | 1831 | IrBinOpDiv, |
| 1831 | IrBinOpMod, | |
| 1832 | IrBinOpRem, | |
| 1832 | 1833 | IrBinOpArrayCat, |
| 1833 | 1834 | IrBinOpArrayMult, |
| 1834 | 1835 | }; |
src/bignum.cpp+2-2| ... | ... | @@ -212,7 +212,7 @@ bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2) { |
| 212 | 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 | 216 | assert(op1->kind == op2->kind); |
| 217 | 217 | dest->kind = op1->kind; |
| 218 | 218 | |
| ... | ... | @@ -220,7 +220,7 @@ bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) { |
| 220 | 220 | dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float); |
| 221 | 221 | } else { |
| 222 | 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 | 225 | dest->data.x_uint = op1->data.x_uint % op2->data.x_uint; |
| 226 | 226 | bignum_normalize(dest); |
src/bignum.hpp+1-1| ... | ... | @@ -37,7 +37,7 @@ bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2); |
| 37 | 37 | bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2); |
| 38 | 38 | bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2); |
| 39 | 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 | 42 | bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2); |
| 43 | 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 | 512 | return buf_create_from_str("left shift overflowed bits"); |
| 513 | 513 | case PanicMsgIdDivisionByZero: |
| 514 | 514 | return buf_create_from_str("division by zero"); |
| 515 | case PanicMsgIdRemainderDivisionByZero: | |
| 516 | return buf_create_from_str("remainder division by zero"); | |
| 515 | 517 | case PanicMsgIdExactDivisionRemainder: |
| 516 | 518 | return buf_create_from_str("exact division produced remainder"); |
| 517 | 519 | case PanicMsgIdSliceWidenRemainder: |
| ... | ... | @@ -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 | 1014 | static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 960 | 1015 | IrInstructionBinOp *bin_op_instruction) |
| 961 | 1016 | { |
| ... | ... | @@ -1092,17 +1147,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 1092 | 1147 | } |
| 1093 | 1148 | case IrBinOpDiv: |
| 1094 | 1149 | return gen_div(g, want_debug_safety, op1_value, op2_value, canon_type, false); |
| 1095 | case IrBinOpMod: | |
| 1096 | if (canon_type->id == TypeTableEntryIdFloat) { | |
| 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 | } | |
| 1150 | case IrBinOpRem: | |
| 1151 | return gen_rem(g, want_debug_safety, op1_value, op2_value, canon_type); | |
| 1106 | 1152 | } |
| 1107 | 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 | 3487 | case BinOpTypeAssignDiv: |
| 3488 | 3488 | return ir_gen_assign_op(irb, scope, node, IrBinOpDiv); |
| 3489 | 3489 | case BinOpTypeAssignMod: |
| 3490 | return ir_gen_assign_op(irb, scope, node, IrBinOpMod); | |
| 3490 | return ir_gen_assign_op(irb, scope, node, IrBinOpRem); | |
| 3491 | 3491 | case BinOpTypeAssignPlus: |
| 3492 | 3492 | return ir_gen_assign_op(irb, scope, node, IrBinOpAdd); |
| 3493 | 3493 | case BinOpTypeAssignPlusWrap: |
| ... | ... | @@ -3555,7 +3555,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) |
| 3555 | 3555 | case BinOpTypeDiv: |
| 3556 | 3556 | return ir_gen_bin_op_id(irb, scope, node, IrBinOpDiv); |
| 3557 | 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 | 3559 | case BinOpTypeArrayCat: |
| 3560 | 3560 | return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat); |
| 3561 | 3561 | case BinOpTypeArrayMult: |
| ... | ... | @@ -7394,7 +7394,7 @@ static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val, |
| 7394 | 7394 | { |
| 7395 | 7395 | bool is_int = false; |
| 7396 | 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 | 7398 | if (type->id == TypeTableEntryIdInt || |
| 7399 | 7399 | type->id == TypeTableEntryIdNumLitInt) |
| 7400 | 7400 | { |
| ... | ... | @@ -7480,8 +7480,8 @@ static int ir_eval_math_op(TypeTableEntry *canon_type, ConstExprValue *op1_val, |
| 7480 | 7480 | return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, canon_type, true); |
| 7481 | 7481 | case IrBinOpDiv: |
| 7482 | 7482 | return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, canon_type, false); |
| 7483 | case IrBinOpMod: | |
| 7484 | return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mod, canon_type, false); | |
| 7483 | case IrBinOpRem: | |
| 7484 | return ir_eval_bignum(op1_val, op2_val, out_val, bignum_rem, canon_type, false); | |
| 7485 | 7485 | } |
| 7486 | 7486 | zig_unreachable(); |
| 7487 | 7487 | } |
| ... | ... | @@ -7506,7 +7506,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp |
| 7506 | 7506 | op_id == IrBinOpSub || |
| 7507 | 7507 | op_id == IrBinOpMult || |
| 7508 | 7508 | op_id == IrBinOpDiv || |
| 7509 | op_id == IrBinOpMod)) | |
| 7509 | op_id == IrBinOpRem)) | |
| 7510 | 7510 | { |
| 7511 | 7511 | // float |
| 7512 | 7512 | } else { |
| ... | ... | @@ -7777,7 +7777,7 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi |
| 7777 | 7777 | case IrBinOpMult: |
| 7778 | 7778 | case IrBinOpMultWrap: |
| 7779 | 7779 | case IrBinOpDiv: |
| 7780 | case IrBinOpMod: | |
| 7780 | case IrBinOpRem: | |
| 7781 | 7781 | return ir_analyze_bin_op_math(ira, bin_op_instruction); |
| 7782 | 7782 | case IrBinOpArrayCat: |
| 7783 | 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 | 11211 | } |
| 11212 | 11212 | |
| 11213 | 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 | 11215 | ir_add_error(ira, &instruction->base, buf_sprintf("integer overflow")); |
| 11216 | 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 | 110 | return "*%"; |
| 111 | 111 | case IrBinOpDiv: |
| 112 | 112 | return "/"; |
| 113 | case IrBinOpMod: | |
| 113 | case IrBinOpRem: | |
| 114 | 114 | return "%"; |
| 115 | 115 | case IrBinOpArrayCat: |
| 116 | 116 | return "++"; |