diff --git a/doc/langref.md b/doc/langref.md index a70503a137..73b456701e 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -165,17 +165,17 @@ ContainerDecl = option("extern" | "packed") ("struct" | "enum" | "union") "{" ma ``` x() x[] x.y -!x -x ~x *x &x ?x %x %%x +!x -x -%x ~x *x &x ?x %x %%x ??x x{} -* / % -+ - ++ +* / % ** *% ++ - ++ +% -% << >> & ^ | == != < > <= >= -&& -|| +and +or ?? %% = *= /= %= += -= <<= >>= &= ^= |= ``` diff --git a/src/all_types.hpp b/src/all_types.hpp index 732381b2d0..48fa3fb9f0 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1205,6 +1205,7 @@ enum PanicMsgId { PanicMsgIdIntegerOverflow, PanicMsgIdShiftOverflowedBits, PanicMsgIdDivisionByZero, + PanicMsgIdRemainderDivisionByZero, PanicMsgIdExactDivisionRemainder, PanicMsgIdSliceWidenRemainder, PanicMsgIdUnwrapMaybeFail, @@ -1828,7 +1829,7 @@ enum IrBinOp { IrBinOpMult, IrBinOpMultWrap, IrBinOpDiv, - IrBinOpMod, + IrBinOpRem, IrBinOpArrayCat, IrBinOpArrayMult, }; diff --git a/src/bignum.cpp b/src/bignum.cpp index 9723b50994..1aea38e296 100644 --- a/src/bignum.cpp +++ b/src/bignum.cpp @@ -212,7 +212,7 @@ bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2) { return false; } -bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) { +bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2) { assert(op1->kind == op2->kind); dest->kind = op1->kind; @@ -220,7 +220,7 @@ bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) { dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float); } else { if (op1->is_negative || op2->is_negative) { - zig_panic("TODO handle mod with negative numbers"); + zig_panic("TODO handle remainder division with negative numbers"); } dest->data.x_uint = op1->data.x_uint % op2->data.x_uint; bignum_normalize(dest); diff --git a/src/bignum.hpp b/src/bignum.hpp index 935ffda74f..f8d960c490 100644 --- a/src/bignum.hpp +++ b/src/bignum.hpp @@ -37,7 +37,7 @@ bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2); bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2); bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2); bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2); -bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2); +bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2); bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2); bool bignum_and(BigNum *dest, BigNum *op1, BigNum *op2); diff --git a/src/codegen.cpp b/src/codegen.cpp index 4a99a543b7..f6af44b4ed 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -512,6 +512,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { return buf_create_from_str("left shift overflowed bits"); case PanicMsgIdDivisionByZero: return buf_create_from_str("division by zero"); + case PanicMsgIdRemainderDivisionByZero: + return buf_create_from_str("remainder division by zero"); case PanicMsgIdExactDivisionRemainder: return buf_create_from_str("exact division produced remainder"); case PanicMsgIdSliceWidenRemainder: @@ -956,6 +958,59 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, LLVMValueRef val } } +static LLVMValueRef gen_rem(CodeGen *g, bool want_debug_safety, LLVMValueRef val1, LLVMValueRef val2, + TypeTableEntry *type_entry) +{ + + if (want_debug_safety) { + LLVMValueRef zero = LLVMConstNull(type_entry->type_ref); + LLVMValueRef is_zero_bit; + if (type_entry->id == TypeTableEntryIdInt) { + is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, ""); + } else if (type_entry->id == TypeTableEntryIdFloat) { + is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); + } else { + zig_unreachable(); + } + LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk"); + LLVMBasicBlockRef rem_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroFail"); + LLVMBuildCondBr(g->builder, is_zero_bit, rem_zero_fail_block, rem_zero_ok_block); + + LLVMPositionBuilderAtEnd(g->builder, rem_zero_fail_block); + gen_debug_safety_crash(g, PanicMsgIdRemainderDivisionByZero); + + LLVMPositionBuilderAtEnd(g->builder, rem_zero_ok_block); + + if (type_entry->id == TypeTableEntryIdInt && type_entry->data.integral.is_signed) { + LLVMValueRef neg_1_value = LLVMConstInt(type_entry->type_ref, -1, true); + LLVMValueRef int_min_value = LLVMConstInt(type_entry->type_ref, min_signed_val(type_entry), true); + LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemOverflowOk"); + LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemOverflowFail"); + LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, ""); + LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, ""); + LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, ""); + LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block); + + LLVMPositionBuilderAtEnd(g->builder, overflow_fail_block); + gen_debug_safety_crash(g, PanicMsgIdIntegerOverflow); + + LLVMPositionBuilderAtEnd(g->builder, overflow_ok_block); + } + } + + if (type_entry->id == TypeTableEntryIdFloat) { + return LLVMBuildFRem(g->builder, val1, val2, ""); + } else { + assert(type_entry->id == TypeTableEntryIdInt); + if (type_entry->data.integral.is_signed) { + return LLVMBuildSRem(g->builder, val1, val2, ""); + } else { + return LLVMBuildURem(g->builder, val1, val2, ""); + } + } + +} + static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, IrInstructionBinOp *bin_op_instruction) { @@ -1092,17 +1147,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, } case IrBinOpDiv: return gen_div(g, want_debug_safety, op1_value, op2_value, canon_type, false); - case IrBinOpMod: - if (canon_type->id == TypeTableEntryIdFloat) { - return LLVMBuildFRem(g->builder, op1_value, op2_value, ""); - } else { - assert(canon_type->id == TypeTableEntryIdInt); - if (canon_type->data.integral.is_signed) { - return LLVMBuildSRem(g->builder, op1_value, op2_value, ""); - } else { - return LLVMBuildURem(g->builder, op1_value, op2_value, ""); - } - } + case IrBinOpRem: + return gen_rem(g, want_debug_safety, op1_value, op2_value, canon_type); } zig_unreachable(); } diff --git a/src/ir.cpp b/src/ir.cpp index 94d3fed315..326c7223f1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3487,7 +3487,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) case BinOpTypeAssignDiv: return ir_gen_assign_op(irb, scope, node, IrBinOpDiv); case BinOpTypeAssignMod: - return ir_gen_assign_op(irb, scope, node, IrBinOpMod); + return ir_gen_assign_op(irb, scope, node, IrBinOpRem); case BinOpTypeAssignPlus: return ir_gen_assign_op(irb, scope, node, IrBinOpAdd); case BinOpTypeAssignPlusWrap: @@ -3555,7 +3555,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) case BinOpTypeDiv: return ir_gen_bin_op_id(irb, scope, node, IrBinOpDiv); case BinOpTypeMod: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpMod); + return ir_gen_bin_op_id(irb, scope, node, IrBinOpRem); case BinOpTypeArrayCat: return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat); case BinOpTypeArrayMult: @@ -7394,7 +7394,7 @@ static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val, { bool is_int = false; bool is_float = false; - if (bignum_fn == bignum_div || bignum_fn == bignum_mod) { + if (bignum_fn == bignum_div || bignum_fn == bignum_rem) { if (type->id == TypeTableEntryIdInt || type->id == TypeTableEntryIdNumLitInt) { @@ -7480,8 +7480,8 @@ static int ir_eval_math_op(TypeTableEntry *canon_type, ConstExprValue *op1_val, return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, canon_type, true); case IrBinOpDiv: return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, canon_type, false); - case IrBinOpMod: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mod, canon_type, false); + case IrBinOpRem: + return ir_eval_bignum(op1_val, op2_val, out_val, bignum_rem, canon_type, false); } zig_unreachable(); } @@ -7506,7 +7506,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp op_id == IrBinOpSub || op_id == IrBinOpMult || op_id == IrBinOpDiv || - op_id == IrBinOpMod)) + op_id == IrBinOpRem)) { // float } else { @@ -7777,7 +7777,7 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi case IrBinOpMult: case IrBinOpMultWrap: case IrBinOpDiv: - case IrBinOpMod: + case IrBinOpRem: return ir_analyze_bin_op_math(ira, bin_op_instruction); case IrBinOpArrayCat: return ir_analyze_array_cat(ira, bin_op_instruction); @@ -11211,7 +11211,7 @@ static TypeTableEntry *ir_analyze_instruction_div_exact(IrAnalyze *ira, IrInstru } BigNum remainder; - if (bignum_mod(&remainder, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) { + if (bignum_rem(&remainder, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) { ir_add_error(ira, &instruction->base, buf_sprintf("integer overflow")); return ira->codegen->builtin_types.entry_invalid; } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 8365aec050..b0e5fdc6c0 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -110,7 +110,7 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) { return "*%"; case IrBinOpDiv: return "/"; - case IrBinOpMod: + case IrBinOpRem: return "%"; case IrBinOpArrayCat: return "++";