more intuitive left shift and right shift operators

Before:
 * << is left shift, not allowed to shift 1 bits out
 * <<% is left shift, allowed to shift 1 bits out
 * >> is right shift, allowed to shift 1 bits out

After:
 * << is left shift, allowed to shift 1 bits out
 * >> is right shift, allowed to shift 1 bits out
 * @shlExact is left shift, not allowed to shift 1 bits out
 * @shrExact is right shift, not allowed to shift 1 bits out

Closes #413
This commit is contained in:
Andrew Kelley 2017-08-09 10:09:38 -04:00
parent 54675b060a
commit 35d3444e27
28 changed files with 274 additions and 128 deletions

View File

@ -493,7 +493,6 @@ enum BinOpType {
BinOpTypeAssignMinus, BinOpTypeAssignMinus,
BinOpTypeAssignMinusWrap, BinOpTypeAssignMinusWrap,
BinOpTypeAssignBitShiftLeft, BinOpTypeAssignBitShiftLeft,
BinOpTypeAssignBitShiftLeftWrap,
BinOpTypeAssignBitShiftRight, BinOpTypeAssignBitShiftRight,
BinOpTypeAssignBitAnd, BinOpTypeAssignBitAnd,
BinOpTypeAssignBitXor, BinOpTypeAssignBitXor,
@ -512,7 +511,6 @@ enum BinOpType {
BinOpTypeBinXor, BinOpTypeBinXor,
BinOpTypeBinAnd, BinOpTypeBinAnd,
BinOpTypeBitShiftLeft, BinOpTypeBitShiftLeft,
BinOpTypeBitShiftLeftWrap,
BinOpTypeBitShiftRight, BinOpTypeBitShiftRight,
BinOpTypeAdd, BinOpTypeAdd,
BinOpTypeAddWrap, BinOpTypeAddWrap,
@ -1232,6 +1230,8 @@ enum BuiltinFnId {
BuiltinFnIdOffsetOf, BuiltinFnIdOffsetOf,
BuiltinFnIdInlineCall, BuiltinFnIdInlineCall,
BuiltinFnIdTypeId, BuiltinFnIdTypeId,
BuiltinFnIdShlExact,
BuiltinFnIdShrExact,
}; };
struct BuiltinFnEntry { struct BuiltinFnEntry {
@ -1248,7 +1248,8 @@ enum PanicMsgId {
PanicMsgIdCastNegativeToUnsigned, PanicMsgIdCastNegativeToUnsigned,
PanicMsgIdCastTruncatedData, PanicMsgIdCastTruncatedData,
PanicMsgIdIntegerOverflow, PanicMsgIdIntegerOverflow,
PanicMsgIdShiftOverflowedBits, PanicMsgIdShlOverflowedBits,
PanicMsgIdShrOverflowedBits,
PanicMsgIdDivisionByZero, PanicMsgIdDivisionByZero,
PanicMsgIdRemainderDivisionByZero, PanicMsgIdRemainderDivisionByZero,
PanicMsgIdExactDivisionRemainder, PanicMsgIdExactDivisionRemainder,
@ -1930,9 +1931,10 @@ enum IrBinOp {
IrBinOpBinOr, IrBinOpBinOr,
IrBinOpBinXor, IrBinOpBinXor,
IrBinOpBinAnd, IrBinOpBinAnd,
IrBinOpBitShiftLeft, IrBinOpBitShiftLeftLossy,
IrBinOpBitShiftLeftWrap, IrBinOpBitShiftLeftExact,
IrBinOpBitShiftRight, IrBinOpBitShiftRightLossy,
IrBinOpBitShiftRightExact,
IrBinOpAdd, IrBinOpAdd,
IrBinOpAddWrap, IrBinOpAddWrap,
IrBinOpSub, IrBinOpSub,

View File

@ -26,7 +26,6 @@ static const char *bin_op_str(BinOpType bin_op) {
case BinOpTypeBinXor: return "^"; case BinOpTypeBinXor: return "^";
case BinOpTypeBinAnd: return "&"; case BinOpTypeBinAnd: return "&";
case BinOpTypeBitShiftLeft: return "<<"; case BinOpTypeBitShiftLeft: return "<<";
case BinOpTypeBitShiftLeftWrap: return "<<%";
case BinOpTypeBitShiftRight: return ">>"; case BinOpTypeBitShiftRight: return ">>";
case BinOpTypeAdd: return "+"; case BinOpTypeAdd: return "+";
case BinOpTypeAddWrap: return "+%"; case BinOpTypeAddWrap: return "+%";
@ -46,7 +45,6 @@ static const char *bin_op_str(BinOpType bin_op) {
case BinOpTypeAssignMinus: return "-="; case BinOpTypeAssignMinus: return "-=";
case BinOpTypeAssignMinusWrap: return "-%="; case BinOpTypeAssignMinusWrap: return "-%=";
case BinOpTypeAssignBitShiftLeft: return "<<="; case BinOpTypeAssignBitShiftLeft: return "<<=";
case BinOpTypeAssignBitShiftLeftWrap: return "<<%=";
case BinOpTypeAssignBitShiftRight: return ">>="; case BinOpTypeAssignBitShiftRight: return ">>=";
case BinOpTypeAssignBitAnd: return "&="; case BinOpTypeAssignBitAnd: return "&=";
case BinOpTypeAssignBitXor: return "^="; case BinOpTypeAssignBitXor: return "^=";

View File

@ -799,7 +799,7 @@ void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2) {
bigint_normalize(dest); bigint_normalize(dest);
} }
void bigint_shl_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) {
BigInt unwrapped = {0}; BigInt unwrapped = {0};
bigint_shl(&unwrapped, op1, op2); bigint_shl(&unwrapped, op1, op2);
bigint_truncate(dest, &unwrapped, bit_count, is_signed); bigint_truncate(dest, &unwrapped, bit_count, is_signed);

View File

@ -66,7 +66,7 @@ void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2);
void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2); void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2);
void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2); void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2);
void bigint_shl_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed);
void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2); void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2);
void bigint_negate(BigInt *dest, const BigInt *op); void bigint_negate(BigInt *dest, const BigInt *op);

View File

@ -694,8 +694,10 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
return buf_create_from_str("integer cast truncated bits"); return buf_create_from_str("integer cast truncated bits");
case PanicMsgIdIntegerOverflow: case PanicMsgIdIntegerOverflow:
return buf_create_from_str("integer overflow"); return buf_create_from_str("integer overflow");
case PanicMsgIdShiftOverflowedBits: case PanicMsgIdShlOverflowedBits:
return buf_create_from_str("left shift overflowed bits"); return buf_create_from_str("left shift overflowed bits");
case PanicMsgIdShrOverflowedBits:
return buf_create_from_str("right shift overflowed bits");
case PanicMsgIdDivisionByZero: case PanicMsgIdDivisionByZero:
return buf_create_from_str("division by zero"); return buf_create_from_str("division by zero");
case PanicMsgIdRemainderDivisionByZero: case PanicMsgIdRemainderDivisionByZero:
@ -1153,7 +1155,7 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry, static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry,
LLVMValueRef val1, LLVMValueRef val2) LLVMValueRef val1, LLVMValueRef val2)
{ {
// for unsigned left shifting, we do the wrapping shift, then logically shift // for unsigned left shifting, we do the lossy shift, then logically shift
// right the same number of bits // right the same number of bits
// if the values don't match, we have an overflow // if the values don't match, we have an overflow
// for signed left shifting we do the same except arithmetic shift right // for signed left shifting we do the same except arithmetic shift right
@ -1174,7 +1176,32 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry,
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
LLVMPositionBuilderAtEnd(g->builder, fail_block); LLVMPositionBuilderAtEnd(g->builder, fail_block);
gen_debug_safety_crash(g, PanicMsgIdShiftOverflowedBits); gen_debug_safety_crash(g, PanicMsgIdShlOverflowedBits);
LLVMPositionBuilderAtEnd(g->builder, ok_block);
return result;
}
static LLVMValueRef gen_overflow_shr_op(CodeGen *g, TypeTableEntry *type_entry,
LLVMValueRef val1, LLVMValueRef val2)
{
assert(type_entry->id == TypeTableEntryIdInt);
LLVMValueRef result;
if (type_entry->data.integral.is_signed) {
result = LLVMBuildAShr(g->builder, val1, val2, "");
} else {
result = LLVMBuildLShr(g->builder, val1, val2, "");
}
LLVMValueRef orig_val = LLVMBuildShl(g->builder, result, val2, "");
LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, orig_val, "");
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk");
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail");
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
LLVMPositionBuilderAtEnd(g->builder, fail_block);
gen_debug_safety_crash(g, PanicMsgIdShrOverflowedBits);
LLVMPositionBuilderAtEnd(g->builder, ok_block); LLVMPositionBuilderAtEnd(g->builder, ok_block);
return result; return result;
@ -1496,12 +1523,12 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
return LLVMBuildXor(g->builder, op1_value, op2_value, ""); return LLVMBuildXor(g->builder, op1_value, op2_value, "");
case IrBinOpBinAnd: case IrBinOpBinAnd:
return LLVMBuildAnd(g->builder, op1_value, op2_value, ""); return LLVMBuildAnd(g->builder, op1_value, op2_value, "");
case IrBinOpBitShiftLeft: case IrBinOpBitShiftLeftLossy:
case IrBinOpBitShiftLeftWrap: case IrBinOpBitShiftLeftExact:
{ {
assert(type_entry->id == TypeTableEntryIdInt); assert(type_entry->id == TypeTableEntryIdInt);
bool is_wrapping = (op_id == IrBinOpBitShiftLeftWrap); bool is_sloppy = (op_id == IrBinOpBitShiftLeftLossy);
if (is_wrapping) { if (is_sloppy) {
return LLVMBuildShl(g->builder, op1_value, op2_value, ""); return LLVMBuildShl(g->builder, op1_value, op2_value, "");
} else if (want_debug_safety) { } else if (want_debug_safety) {
return gen_overflow_shl_op(g, type_entry, op1_value, op2_value); return gen_overflow_shl_op(g, type_entry, op1_value, op2_value);
@ -1511,12 +1538,24 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
return ZigLLVMBuildNUWShl(g->builder, op1_value, op2_value, ""); return ZigLLVMBuildNUWShl(g->builder, op1_value, op2_value, "");
} }
} }
case IrBinOpBitShiftRight: case IrBinOpBitShiftRightLossy:
assert(type_entry->id == TypeTableEntryIdInt); case IrBinOpBitShiftRightExact:
if (type_entry->data.integral.is_signed) { {
return LLVMBuildAShr(g->builder, op1_value, op2_value, ""); assert(type_entry->id == TypeTableEntryIdInt);
} else { bool is_sloppy = (op_id == IrBinOpBitShiftRightLossy);
return LLVMBuildLShr(g->builder, op1_value, op2_value, ""); if (is_sloppy) {
if (type_entry->data.integral.is_signed) {
return LLVMBuildAShr(g->builder, op1_value, op2_value, "");
} else {
return LLVMBuildLShr(g->builder, op1_value, op2_value, "");
}
} else if (want_debug_safety) {
return gen_overflow_shr_op(g, type_entry, op1_value, op2_value);
} else if (type_entry->data.integral.is_signed) {
return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_value, "");
} else {
return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_value, "");
}
} }
case IrBinOpSub: case IrBinOpSub:
case IrBinOpSubWrap: case IrBinOpSubWrap:
@ -4556,6 +4595,8 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdMod, "mod", 2); create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1); create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);
create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2);
create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2);
} }
static const char *bool_to_str(bool b) { static const char *bool_to_str(bool b) {

View File

@ -25,6 +25,7 @@ const char *err_str(int err) {
case ErrorUnexpected: return "unexpected error"; case ErrorUnexpected: return "unexpected error";
case ErrorExactDivRemainder: return "exact division had a remainder"; case ErrorExactDivRemainder: return "exact division had a remainder";
case ErrorNegativeDenominator: return "negative denominator"; case ErrorNegativeDenominator: return "negative denominator";
case ErrorShiftedOutOneBits: return "exact shift shifted out one bits";
} }
return "(invalid error)"; return "(invalid error)";
} }

View File

@ -25,6 +25,7 @@ enum Error {
ErrorUnexpected, ErrorUnexpected,
ErrorExactDivRemainder, ErrorExactDivRemainder,
ErrorNegativeDenominator, ErrorNegativeDenominator,
ErrorShiftedOutOneBits,
}; };
const char *err_str(int err); const char *err_str(int err);

View File

@ -3625,11 +3625,9 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
case BinOpTypeAssignMinusWrap: case BinOpTypeAssignMinusWrap:
return ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap); return ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap);
case BinOpTypeAssignBitShiftLeft: case BinOpTypeAssignBitShiftLeft:
return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeft); return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy);
case BinOpTypeAssignBitShiftLeftWrap:
return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftWrap);
case BinOpTypeAssignBitShiftRight: case BinOpTypeAssignBitShiftRight:
return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRight); return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy);
case BinOpTypeAssignBitAnd: case BinOpTypeAssignBitAnd:
return ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd); return ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd);
case BinOpTypeAssignBitXor: case BinOpTypeAssignBitXor:
@ -3663,11 +3661,9 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
case BinOpTypeBinAnd: case BinOpTypeBinAnd:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd); return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd);
case BinOpTypeBitShiftLeft: case BinOpTypeBitShiftLeft:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeft); return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy);
case BinOpTypeBitShiftLeftWrap:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftWrap);
case BinOpTypeBitShiftRight: case BinOpTypeBitShiftRight:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRight); return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy);
case BinOpTypeAdd: case BinOpTypeAdd:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd); return ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd);
case BinOpTypeAddWrap: case BinOpTypeAddWrap:
@ -4457,6 +4453,34 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
return ir_build_type_id(irb, scope, node, arg0_value); return ir_build_type_id(irb, scope, node, arg0_value);
} }
case BuiltinFnIdShlExact:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
return ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true);
}
case BuiltinFnIdShrExact:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
return ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true);
}
} }
zig_unreachable(); zig_unreachable();
} }
@ -8362,16 +8386,27 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val,
assert(is_int); assert(is_int);
bigint_and(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); bigint_and(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
break; break;
case IrBinOpBitShiftLeft: case IrBinOpBitShiftLeftExact:
assert(is_int); assert(is_int);
bigint_shl(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); bigint_shl(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
break; break;
case IrBinOpBitShiftLeftWrap: case IrBinOpBitShiftLeftLossy:
assert(type_entry->id == TypeTableEntryIdInt); assert(type_entry->id == TypeTableEntryIdInt);
bigint_shl_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, bigint_shl_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint,
type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
break; break;
case IrBinOpBitShiftRight: case IrBinOpBitShiftRightExact:
{
assert(is_int);
bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
BigInt orig_bigint;
bigint_shl(&orig_bigint, &out_val->data.x_bigint, &op2_val->data.x_bigint);
if (bigint_cmp(&op1_val->data.x_bigint, &orig_bigint) != CmpEQ) {
return ErrorShiftedOutOneBits;
}
break;
}
case IrBinOpBitShiftRightLossy:
assert(is_int); assert(is_int);
bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
break; break;
@ -8591,8 +8626,8 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
} }
if (resolved_type->id == TypeTableEntryIdNumLitInt) { if (resolved_type->id == TypeTableEntryIdNumLitInt) {
if (op_id == IrBinOpBitShiftLeftWrap) { if (op_id == IrBinOpBitShiftLeftLossy) {
op_id = IrBinOpBitShiftLeft; op_id = IrBinOpBitShiftLeftExact;
} else if (op_id == IrBinOpAddWrap) { } else if (op_id == IrBinOpAddWrap) {
op_id = IrBinOpAdd; op_id = IrBinOpAdd;
} else if (op_id == IrBinOpSubWrap) { } else if (op_id == IrBinOpSubWrap) {
@ -8631,6 +8666,9 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
} else if (err == ErrorNegativeDenominator) { } else if (err == ErrorNegativeDenominator) {
ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("negative denominator")); ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("negative denominator"));
return ira->codegen->builtin_types.entry_invalid; return ira->codegen->builtin_types.entry_invalid;
} else if (err == ErrorShiftedOutOneBits) {
ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("exact shift shifted out 1 bits"));
return ira->codegen->builtin_types.entry_invalid;
} else { } else {
zig_unreachable(); zig_unreachable();
} }
@ -8857,9 +8895,10 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi
case IrBinOpBinOr: case IrBinOpBinOr:
case IrBinOpBinXor: case IrBinOpBinXor:
case IrBinOpBinAnd: case IrBinOpBinAnd:
case IrBinOpBitShiftLeft: case IrBinOpBitShiftLeftLossy:
case IrBinOpBitShiftLeftWrap: case IrBinOpBitShiftLeftExact:
case IrBinOpBitShiftRight: case IrBinOpBitShiftRightLossy:
case IrBinOpBitShiftRightExact:
case IrBinOpAdd: case IrBinOpAdd:
case IrBinOpAddWrap: case IrBinOpAddWrap:
case IrBinOpSub: case IrBinOpSub:

View File

@ -92,12 +92,14 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
return "^"; return "^";
case IrBinOpBinAnd: case IrBinOpBinAnd:
return "&"; return "&";
case IrBinOpBitShiftLeft: case IrBinOpBitShiftLeftLossy:
return "<<"; return "<<";
case IrBinOpBitShiftLeftWrap: case IrBinOpBitShiftLeftExact:
return "<<%"; return "@shlExact";
case IrBinOpBitShiftRight: case IrBinOpBitShiftRightLossy:
return ">>"; return ">>";
case IrBinOpBitShiftRightExact:
return "@shrExact";
case IrBinOpAdd: case IrBinOpAdd:
return "+"; return "+";
case IrBinOpAddWrap: case IrBinOpAddWrap:

View File

@ -1131,7 +1131,6 @@ static AstNode *ast_parse_add_expr(ParseContext *pc, size_t *token_index, bool m
static BinOpType tok_to_bit_shift_op(Token *token) { static BinOpType tok_to_bit_shift_op(Token *token) {
switch (token->id) { switch (token->id) {
case TokenIdBitShiftLeft: return BinOpTypeBitShiftLeft; case TokenIdBitShiftLeft: return BinOpTypeBitShiftLeft;
case TokenIdBitShiftLeftPercent: return BinOpTypeBitShiftLeftWrap;
case TokenIdBitShiftRight: return BinOpTypeBitShiftRight; case TokenIdBitShiftRight: return BinOpTypeBitShiftRight;
default: return BinOpTypeInvalid; default: return BinOpTypeInvalid;
} }
@ -1909,7 +1908,6 @@ static BinOpType tok_to_ass_op(Token *token) {
case TokenIdMinusEq: return BinOpTypeAssignMinus; case TokenIdMinusEq: return BinOpTypeAssignMinus;
case TokenIdMinusPercentEq: return BinOpTypeAssignMinusWrap; case TokenIdMinusPercentEq: return BinOpTypeAssignMinusWrap;
case TokenIdBitShiftLeftEq: return BinOpTypeAssignBitShiftLeft; case TokenIdBitShiftLeftEq: return BinOpTypeAssignBitShiftLeft;
case TokenIdBitShiftLeftPercentEq: return BinOpTypeAssignBitShiftLeftWrap;
case TokenIdBitShiftRightEq: return BinOpTypeAssignBitShiftRight; case TokenIdBitShiftRightEq: return BinOpTypeAssignBitShiftRight;
case TokenIdBitAndEq: return BinOpTypeAssignBitAnd; case TokenIdBitAndEq: return BinOpTypeAssignBitAnd;
case TokenIdBitXorEq: return BinOpTypeAssignBitXor; case TokenIdBitXorEq: return BinOpTypeAssignBitXor;

View File

@ -201,7 +201,6 @@ enum TokenizeState {
TokenizeStateSawBang, TokenizeStateSawBang,
TokenizeStateSawLessThan, TokenizeStateSawLessThan,
TokenizeStateSawLessThanLessThan, TokenizeStateSawLessThanLessThan,
TokenizeStateSawShiftLeftPercent,
TokenizeStateSawGreaterThan, TokenizeStateSawGreaterThan,
TokenizeStateSawGreaterThanGreaterThan, TokenizeStateSawGreaterThanGreaterThan,
TokenizeStateSawDot, TokenizeStateSawDot,
@ -673,24 +672,6 @@ void tokenize(Buf *buf, Tokenization *out) {
end_token(&t); end_token(&t);
t.state = TokenizeStateStart; t.state = TokenizeStateStart;
break; break;
case '%':
set_token_id(&t, t.cur_tok, TokenIdBitShiftLeftPercent);
t.state = TokenizeStateSawShiftLeftPercent;
break;
default:
t.pos -= 1;
end_token(&t);
t.state = TokenizeStateStart;
continue;
}
break;
case TokenizeStateSawShiftLeftPercent:
switch (c) {
case '=':
set_token_id(&t, t.cur_tok, TokenIdBitShiftLeftPercentEq);
end_token(&t);
t.state = TokenizeStateStart;
break;
default: default:
t.pos -= 1; t.pos -= 1;
end_token(&t); end_token(&t);
@ -1410,7 +1391,6 @@ void tokenize(Buf *buf, Tokenization *out) {
case TokenizeStateSawStarPercent: case TokenizeStateSawStarPercent:
case TokenizeStateSawPlusPercent: case TokenizeStateSawPlusPercent:
case TokenizeStateSawMinusPercent: case TokenizeStateSawMinusPercent:
case TokenizeStateSawShiftLeftPercent:
case TokenizeStateLineString: case TokenizeStateLineString:
case TokenizeStateLineStringEnd: case TokenizeStateLineStringEnd:
end_token(&t); end_token(&t);
@ -1451,8 +1431,6 @@ const char * token_name(TokenId id) {
case TokenIdBitOrEq: return "|="; case TokenIdBitOrEq: return "|=";
case TokenIdBitShiftLeft: return "<<"; case TokenIdBitShiftLeft: return "<<";
case TokenIdBitShiftLeftEq: return "<<="; case TokenIdBitShiftLeftEq: return "<<=";
case TokenIdBitShiftLeftPercent: return "<<%";
case TokenIdBitShiftLeftPercentEq: return "<<%=";
case TokenIdBitShiftRight: return ">>"; case TokenIdBitShiftRight: return ">>";
case TokenIdBitShiftRightEq: return ">>="; case TokenIdBitShiftRightEq: return ">>=";
case TokenIdBitXorEq: return "^="; case TokenIdBitXorEq: return "^=";

View File

@ -23,8 +23,6 @@ enum TokenId {
TokenIdBitOrEq, TokenIdBitOrEq,
TokenIdBitShiftLeft, TokenIdBitShiftLeft,
TokenIdBitShiftLeftEq, TokenIdBitShiftLeftEq,
TokenIdBitShiftLeftPercent,
TokenIdBitShiftLeftPercentEq,
TokenIdBitShiftRight, TokenIdBitShiftRight,
TokenIdBitShiftRightEq, TokenIdBitShiftRightEq,
TokenIdBitXorEq, TokenIdBitXorEq,

View File

@ -754,9 +754,22 @@ LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMVa
LLVMValueRef ZigLLVMBuildNUWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, LLVMValueRef ZigLLVMBuildNUWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
const char *name) const char *name)
{ {
return wrap(unwrap(builder)->CreateShl(unwrap(LHS), unwrap(RHS), name, false, true)); return wrap(unwrap(builder)->CreateShl(unwrap(LHS), unwrap(RHS), name, true, false));
} }
LLVMValueRef ZigLLVMBuildLShrExact(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
const char *name)
{
return wrap(unwrap(builder)->CreateLShr(unwrap(LHS), unwrap(RHS), name, true));
}
LLVMValueRef ZigLLVMBuildAShrExact(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
const char *name)
{
return wrap(unwrap(builder)->CreateAShr(unwrap(LHS), unwrap(RHS), name, true));
}
#include "buffer.hpp" #include "buffer.hpp"
bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count, Buf *diag_buf) { bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count, Buf *diag_buf) {

View File

@ -48,6 +48,10 @@ LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMVa
const char *name); const char *name);
LLVMValueRef ZigLLVMBuildNUWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, LLVMValueRef ZigLLVMBuildNUWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
const char *name); const char *name);
LLVMValueRef ZigLLVMBuildLShrExact(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
const char *name);
LLVMValueRef ZigLLVMBuildAShrExact(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
const char *name);
ZigLLVMDIType *ZigLLVMCreateDebugPointerType(ZigLLVMDIBuilder *dibuilder, ZigLLVMDIType *pointee_type, ZigLLVMDIType *ZigLLVMCreateDebugPointerType(ZigLLVMDIBuilder *dibuilder, ZigLLVMDIType *pointee_type,
uint64_t size_in_bits, uint64_t align_in_bits, const char *name); uint64_t size_in_bits, uint64_t align_in_bits, const char *name);

View File

@ -21,11 +21,11 @@ pub fn encodeWithAlphabet(dest: []u8, source: []const u8, alphabet: []const u8)
dest[out_index] = alphabet[(source[i] >> 2) & 0x3f]; dest[out_index] = alphabet[(source[i] >> 2) & 0x3f];
out_index += 1; out_index += 1;
dest[out_index] = alphabet[((source[i] & 0x3) <<% 4) | dest[out_index] = alphabet[((source[i] & 0x3) << 4) |
((source[i + 1] & 0xf0) >> 4)]; ((source[i + 1] & 0xf0) >> 4)];
out_index += 1; out_index += 1;
dest[out_index] = alphabet[((source[i + 1] & 0xf) <<% 2) | dest[out_index] = alphabet[((source[i + 1] & 0xf) << 2) |
((source[i + 2] & 0xc0) >> 6)]; ((source[i + 2] & 0xc0) >> 6)];
out_index += 1; out_index += 1;
@ -38,17 +38,17 @@ pub fn encodeWithAlphabet(dest: []u8, source: []const u8, alphabet: []const u8)
out_index += 1; out_index += 1;
if (i + 1 == source.len) { if (i + 1 == source.len) {
dest[out_index] = alphabet[(source[i] & 0x3) <<% 4]; dest[out_index] = alphabet[(source[i] & 0x3) << 4];
out_index += 1; out_index += 1;
dest[out_index] = alphabet[64]; dest[out_index] = alphabet[64];
out_index += 1; out_index += 1;
} else { } else {
dest[out_index] = alphabet[((source[i] & 0x3) <<% 4) | dest[out_index] = alphabet[((source[i] & 0x3) << 4) |
((source[i + 1] & 0xf0) >> 4)]; ((source[i + 1] & 0xf0) >> 4)];
out_index += 1; out_index += 1;
dest[out_index] = alphabet[(source[i + 1] & 0xf) <<% 2]; dest[out_index] = alphabet[(source[i + 1] & 0xf) << 2];
out_index += 1; out_index += 1;
} }
@ -83,15 +83,15 @@ pub fn decodeWithAscii6BitMap(dest: []u8, source: []const u8, ascii6: []const u8
} }
while (in_buf_len > 4) { while (in_buf_len > 4) {
dest[dest_index] = ascii6[source[src_index + 0]] <<% 2 | dest[dest_index] = ascii6[source[src_index + 0]] << 2 |
ascii6[source[src_index + 1]] >> 4; ascii6[source[src_index + 1]] >> 4;
dest_index += 1; dest_index += 1;
dest[dest_index] = ascii6[source[src_index + 1]] <<% 4 | dest[dest_index] = ascii6[source[src_index + 1]] << 4 |
ascii6[source[src_index + 2]] >> 2; ascii6[source[src_index + 2]] >> 2;
dest_index += 1; dest_index += 1;
dest[dest_index] = ascii6[source[src_index + 2]] <<% 6 | dest[dest_index] = ascii6[source[src_index + 2]] << 6 |
ascii6[source[src_index + 3]]; ascii6[source[src_index + 3]];
dest_index += 1; dest_index += 1;
@ -100,17 +100,17 @@ pub fn decodeWithAscii6BitMap(dest: []u8, source: []const u8, ascii6: []const u8
} }
if (in_buf_len > 1) { if (in_buf_len > 1) {
dest[dest_index] = ascii6[source[src_index + 0]] <<% 2 | dest[dest_index] = ascii6[source[src_index + 0]] << 2 |
ascii6[source[src_index + 1]] >> 4; ascii6[source[src_index + 1]] >> 4;
dest_index += 1; dest_index += 1;
} }
if (in_buf_len > 2) { if (in_buf_len > 2) {
dest[dest_index] = ascii6[source[src_index + 1]] <<% 4 | dest[dest_index] = ascii6[source[src_index + 1]] << 4 |
ascii6[source[src_index + 2]] >> 2; ascii6[source[src_index + 2]] >> 2;
dest_index += 1; dest_index += 1;
} }
if (in_buf_len > 3) { if (in_buf_len > 3) {
dest[dest_index] = ascii6[source[src_index + 2]] <<% 6 | dest[dest_index] = ascii6[source[src_index + 2]] << 6 |
ascii6[source[src_index + 3]]; ascii6[source[src_index + 3]];
dest_index += 1; dest_index += 1;
} }

View File

@ -83,7 +83,7 @@ fn exp2_32(x: f32) -> f32 {
const k = i0 / tblsiz; const k = i0 / tblsiz;
// NOTE: musl relies on undefined overflow shift behaviour. Appears that this produces the // NOTE: musl relies on undefined overflow shift behaviour. Appears that this produces the
// intended result but should confirm how GCC/Clang handle this to ensure. // intended result but should confirm how GCC/Clang handle this to ensure.
const uk = @bitCast(f64, u64(0x3FF + k) <<% 52); const uk = @bitCast(f64, u64(0x3FF + k) << 52);
i0 &= tblsiz - 1; i0 &= tblsiz - 1;
uf -= redux; uf -= redux;

View File

@ -124,7 +124,7 @@ fn expm1_32(x_: f32) -> f32 {
} }
} }
const twopk = @bitCast(f32, u32((0x7F + k) <<% 23)); const twopk = @bitCast(f32, u32((0x7F + k) << 23));
if (k < 0 or k > 56) { if (k < 0 or k > 56) {
var y = x - e + 1.0; var y = x - e + 1.0;
@ -253,7 +253,7 @@ fn expm1_64(x_: f64) -> f64 {
} }
} }
const twopk = @bitCast(f64, u64(0x3FF + k) <<% 52); const twopk = @bitCast(f64, u64(0x3FF + k) << 52);
if (k < 0 or k > 56) { if (k < 0 or k > 56) {
var y = x - e + 1.0; var y = x - e + 1.0;

View File

@ -49,7 +49,7 @@ fn ilogb32(x: f32) -> i32 {
if (e == 0xFF) { if (e == 0xFF) {
math.raiseInvalid(); math.raiseInvalid();
if (u <<% 9 != 0) { if (u << 9 != 0) {
return fp_ilogbnan; return fp_ilogbnan;
} else { } else {
return @maxValue(i32); return @maxValue(i32);
@ -84,7 +84,7 @@ fn ilogb64(x: f64) -> i32 {
if (e == 0x7FF) { if (e == 0x7FF) {
math.raiseInvalid(); math.raiseInvalid();
if (u <<% 12 != 0) { if (u << 12 != 0) {
return fp_ilogbnan; return fp_ilogbnan;
} else { } else {
return @maxValue(i32); return @maxValue(i32);

View File

@ -36,7 +36,7 @@ fn lnf(x_: f32) -> f32 {
// x < 2^(-126) // x < 2^(-126)
if (ix < 0x00800000 or ix >> 31 != 0) { if (ix < 0x00800000 or ix >> 31 != 0) {
// log(+-0) = -inf // log(+-0) = -inf
if (ix <<% 1 == 0) { if (ix << 1 == 0) {
return -math.inf(f32); return -math.inf(f32);
} }
// log(-#) = nan // log(-#) = nan
@ -91,7 +91,7 @@ fn lnd(x_: f64) -> f64 {
if (hx < 0x00100000 or hx >> 31 != 0) { if (hx < 0x00100000 or hx >> 31 != 0) {
// log(+-0) = -inf // log(+-0) = -inf
if (ix <<% 1 == 0) { if (ix << 1 == 0) {
return -math.inf(f64); return -math.inf(f64);
} }
// log(-#) = nan // log(-#) = nan

View File

@ -38,7 +38,7 @@ fn log10_32(x_: f32) -> f32 {
// x < 2^(-126) // x < 2^(-126)
if (ix < 0x00800000 or ix >> 31 != 0) { if (ix < 0x00800000 or ix >> 31 != 0) {
// log(+-0) = -inf // log(+-0) = -inf
if (ix <<% 1 == 0) { if (ix << 1 == 0) {
return -math.inf(f32); return -math.inf(f32);
} }
// log(-#) = nan // log(-#) = nan
@ -100,7 +100,7 @@ fn log10_64(x_: f64) -> f64 {
if (hx < 0x00100000 or hx >> 31 != 0) { if (hx < 0x00100000 or hx >> 31 != 0) {
// log(+-0) = -inf // log(+-0) = -inf
if (ix <<% 1 == 0) { if (ix << 1 == 0) {
return -math.inf(f32); return -math.inf(f32);
} }
// log(-#) = nan // log(-#) = nan
@ -139,7 +139,7 @@ fn log10_64(x_: f64) -> f64 {
// hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f) // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f)
var hi = f - hfsq; var hi = f - hfsq;
var hii = @bitCast(u64, hi); var hii = @bitCast(u64, hi);
hii &= u64(@maxValue(u64)) <<% 32; hii &= u64(@maxValue(u64)) << 32;
hi = @bitCast(f64, hii); hi = @bitCast(f64, hii);
const lo = f - hi - hfsq + s * (hfsq + R); const lo = f - hi - hfsq + s * (hfsq + R);

View File

@ -49,7 +49,7 @@ fn log1p_32(x: f32) -> f32 {
} }
} }
// |x| < 2^(-24) // |x| < 2^(-24)
if ((ix <<% 1) < (0x33800000 << 1)) { if ((ix << 1) < (0x33800000 << 1)) {
// underflow if subnormal // underflow if subnormal
if (ix & 0x7F800000 == 0) { if (ix & 0x7F800000 == 0) {
math.forceEval(x * x); math.forceEval(x * x);
@ -128,7 +128,7 @@ fn log1p_64(x: f64) -> f64 {
} }
} }
// |x| < 2^(-53) // |x| < 2^(-53)
if ((hx <<% 1) < (0x3CA00000 << 1)) { if ((hx << 1) < (0x3CA00000 << 1)) {
if ((hx & 0x7FF00000) == 0) { if ((hx & 0x7FF00000) == 0) {
math.raiseUnderflow(); math.raiseUnderflow();
} }

View File

@ -36,7 +36,7 @@ fn log2_32(x_: f32) -> f32 {
// x < 2^(-126) // x < 2^(-126)
if (ix < 0x00800000 or ix >> 31 != 0) { if (ix < 0x00800000 or ix >> 31 != 0) {
// log(+-0) = -inf // log(+-0) = -inf
if (ix <<% 1 == 0) { if (ix << 1 == 0) {
return -math.inf(f32); return -math.inf(f32);
} }
// log(-#) = nan // log(-#) = nan
@ -94,7 +94,7 @@ fn log2_64(x_: f64) -> f64 {
if (hx < 0x00100000 or hx >> 31 != 0) { if (hx < 0x00100000 or hx >> 31 != 0) {
// log(+-0) = -inf // log(+-0) = -inf
if (ix <<% 1 == 0) { if (ix << 1 == 0) {
return -math.inf(f64); return -math.inf(f64);
} }
// log(-#) = nan // log(-#) = nan
@ -133,7 +133,7 @@ fn log2_64(x_: f64) -> f64 {
// hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f) // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f)
var hi = f - hfsq; var hi = f - hfsq;
var hii = @bitCast(u64, hi); var hii = @bitCast(u64, hi);
hii &= u64(@maxValue(u64)) <<% 32; hii &= u64(@maxValue(u64)) << 32;
hi = @bitCast(f64, hii); hi = @bitCast(f64, hii);
const lo = f - hi - hfsq + s * (hfsq + R); const lo = f - hi - hfsq + s * (hfsq + R);

View File

@ -44,7 +44,7 @@ fn modf32(x: f32) -> modf32_result {
// no fractional part // no fractional part
if (e >= 23) { if (e >= 23) {
result.ipart = x; result.ipart = x;
if (e == 0x80 and u <<% 9 != 0) { // nan if (e == 0x80 and u << 9 != 0) { // nan
result.fpart = x; result.fpart = x;
} else { } else {
result.fpart = @bitCast(f32, us); result.fpart = @bitCast(f32, us);
@ -88,7 +88,7 @@ fn modf64(x: f64) -> modf64_result {
// no fractional part // no fractional part
if (e >= 52) { if (e >= 52) {
result.ipart = x; result.ipart = x;
if (e == 0x400 and u <<% 12 != 0) { // nan if (e == 0x400 and u << 12 != 0) { // nan
result.fpart = x; result.fpart = x;
} else { } else {
result.fpart = @bitCast(f64, us); result.fpart = @bitCast(f64, us);

View File

@ -182,8 +182,8 @@ fn MersenneTwister(
mt.index += 1; mt.index += 1;
x ^= ((x >> u) & d); x ^= ((x >> u) & d);
x ^= ((x <<% s) & b); x ^= ((x << s) & b);
x ^= ((x <<% t) & c); x ^= ((x << t) & c);
x ^= (x >> l); x ^= (x >> l);
return x; return x;

View File

@ -47,31 +47,31 @@ fn generic_fmod(comptime T: type, x: T, y: T) -> T {
const sx = if (T == f32) u32(ux & 0x80000000) else i32(ux >> bits_minus_1); const sx = if (T == f32) u32(ux & 0x80000000) else i32(ux >> bits_minus_1);
var i: uint = undefined; var i: uint = undefined;
if (uy <<% 1 == 0 or isNan(uint, uy) or ex == mask) if (uy << 1 == 0 or isNan(uint, uy) or ex == mask)
return (x * y) / (x * y); return (x * y) / (x * y);
if (ux <<% 1 <= uy <<% 1) { if (ux << 1 <= uy << 1) {
if (ux <<% 1 == uy <<% 1) if (ux << 1 == uy << 1)
return 0 * x; return 0 * x;
return x; return x;
} }
// normalize x and y // normalize x and y
if (ex == 0) { if (ex == 0) {
i = ux <<% exp_bits; i = ux << exp_bits;
while (i >> bits_minus_1 == 0) : ({ex -= 1; i <<%= 1}) {} while (i >> bits_minus_1 == 0) : ({ex -= 1; i <<= 1}) {}
ux <<%= @bitCast(u32, -ex + 1); ux <<= @bitCast(u32, -ex + 1);
} else { } else {
ux &= @maxValue(uint) >> exp_bits; ux &= @maxValue(uint) >> exp_bits;
ux |= 1 <<% digits; ux |= 1 << digits;
} }
if (ey == 0) { if (ey == 0) {
i = uy <<% exp_bits; i = uy << exp_bits;
while (i >> bits_minus_1 == 0) : ({ey -= 1; i <<%= 1}) {} while (i >> bits_minus_1 == 0) : ({ey -= 1; i <<= 1}) {}
uy <<= @bitCast(u32, -ey + 1); uy <<= @bitCast(u32, -ey + 1);
} else { } else {
uy &= @maxValue(uint) >> exp_bits; uy &= @maxValue(uint) >> exp_bits;
uy |= 1 <<% digits; uy |= 1 << digits;
} }
// x mod y // x mod y
@ -82,7 +82,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) -> T {
return 0 * x; return 0 * x;
ux = i; ux = i;
} }
ux <<%= 1; ux <<= 1;
} }
i = ux -% uy; i = ux -% uy;
if (i >> bits_minus_1 == 0) { if (i >> bits_minus_1 == 0) {
@ -90,19 +90,19 @@ fn generic_fmod(comptime T: type, x: T, y: T) -> T {
return 0 * x; return 0 * x;
ux = i; ux = i;
} }
while (ux >> digits == 0) : ({ux <<%= 1; ex -= 1}) {} while (ux >> digits == 0) : ({ux <<= 1; ex -= 1}) {}
// scale result up // scale result up
if (ex > 0) { if (ex > 0) {
ux -%= 1 <<% digits; ux -%= 1 << digits;
ux |= @bitCast(u32, ex) <<% digits; ux |= @bitCast(u32, ex) << digits;
} else { } else {
ux >>= @bitCast(u32, -ex + 1); ux >>= @bitCast(u32, -ex + 1);
} }
if (T == f32) { if (T == f32) {
ux |= sx; ux |= sx;
} else { } else {
ux |= uint(sx) <<% bits_minus_1; ux |= uint(sx) << bits_minus_1;
} }
return *@ptrCast(&const T, &ux); return *@ptrCast(&const T, &ux);
} }
@ -111,7 +111,7 @@ fn isNan(comptime T: type, bits: T) -> bool {
if (T == u32) { if (T == u32) {
return (bits & 0x7fffffff) > 0x7f800000; return (bits & 0x7fffffff) > 0x7f800000;
} else if (T == u64) { } else if (T == u64) {
return (bits & (@maxValue(u64) >> 1)) > (u64(0x7ff) <<% 52); return (bits & (@maxValue(u64) >> 1)) > (u64(0x7ff) << 52);
} else { } else {
unreachable; unreachable;
} }

View File

@ -168,15 +168,6 @@ fn testNegationWrappingEval(x: i16) {
assert(neg == -32768); assert(neg == -32768);
} }
test "shift left wrapping" {
testShlWrappingEval(@maxValue(u16));
comptime testShlWrappingEval(@maxValue(u16));
}
fn testShlWrappingEval(x: u16) {
const shifted = x <<% 1;
assert(shifted == 65534);
}
test "unsigned 64-bit division" { test "unsigned 64-bit division" {
test_u64_div(); test_u64_div();
comptime test_u64_div(); comptime test_u64_div();
@ -257,3 +248,39 @@ test "hex float literal within range" {
const b = 0x0.1p1027; const b = 0x0.1p1027;
const c = 0x1.0p-1022; const c = 0x1.0p-1022;
} }
test "truncating shift left" {
testShlTrunc(@maxValue(u16));
comptime testShlTrunc(@maxValue(u16));
}
fn testShlTrunc(x: u16) {
const shifted = x << 1;
assert(shifted == 65534);
}
test "truncating shift right" {
testShrTrunc(@maxValue(u16));
comptime testShrTrunc(@maxValue(u16));
}
fn testShrTrunc(x: u16) {
const shifted = x >> 1;
assert(shifted == 32767);
}
test "exact shift left" {
testShlExact(0b00110101);
comptime testShlExact(0b00110101);
}
fn testShlExact(x: u8) {
const shifted = @shlExact(x, 2);
assert(shifted == 0b11010100);
}
test "exact shift right" {
testShrExact(0b10110100);
comptime testShrExact(0b10110100);
}
fn testShrExact(x: u8) {
const shifted = @shrExact(x, 2);
assert(shifted == 0b00101101);
}

View File

@ -1959,4 +1959,18 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\} \\}
, ,
".tmp_source.zig:2:15: error: expected pointer, found 'i32'"); ".tmp_source.zig:2:15: error: expected pointer, found 'i32'");
cases.add("@shlExact shifts out 1 bits",
\\comptime {
\\ const x = @shlExact(u8(0b01010101), 2);
\\}
,
".tmp_source.zig:2:15: error: operation caused overflow");
cases.add("@shrExact shifts out 1 bits",
\\comptime {
\\ const x = @shrExact(u8(0b10101010), 2);
\\}
,
".tmp_source.zig:2:15: error: exact shift shifted out 1 bits");
} }

View File

@ -112,7 +112,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ if (x == 0) return error.Whatever; \\ if (x == 0) return error.Whatever;
\\} \\}
\\fn shl(a: i16, b: i16) -> i16 { \\fn shl(a: i16, b: i16) -> i16 {
\\ a << b \\ @shlExact(a, b)
\\} \\}
); );
@ -127,7 +127,37 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ if (x == 0) return error.Whatever; \\ if (x == 0) return error.Whatever;
\\} \\}
\\fn shl(a: u16, b: u16) -> u16 { \\fn shl(a: u16, b: u16) -> u16 {
\\ a << b \\ @shlExact(a, b)
\\}
);
cases.addDebugSafety("signed shift right overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\error Whatever;
\\pub fn main() -> %void {
\\ const x = shr(-16385, 1);
\\ if (x == 0) return error.Whatever;
\\}
\\fn shr(a: i16, b: i16) -> i16 {
\\ @shrExact(a, b)
\\}
);
cases.addDebugSafety("unsigned shift right overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\ @breakpoint();
\\ while (true) {}
\\}
\\error Whatever;
\\pub fn main() -> %void {
\\ const x = shr(0b0010111111111111, 3);
\\ if (x == 0) return error.Whatever;
\\}
\\fn shr(a: u16, b: u16) -> u16 {
\\ @shrExact(a, b)
\\} \\}
); );