@@ -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
|
||||
?? %%
|
||||
= *= /= %= += -= <<= >>= &= ^= |=
|
||||
```
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
16
src/ir.cpp
16
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;
|
||||
}
|
||||
|
||||
@@ -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 "++";
|
||||
|
||||
Reference in New Issue
Block a user