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

View File

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

View File

@ -799,7 +799,7 @@ void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2) {
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_shl(&unwrapped, op1, op2);
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_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_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");
case PanicMsgIdIntegerOverflow:
return buf_create_from_str("integer overflow");
case PanicMsgIdShiftOverflowedBits:
case PanicMsgIdShlOverflowedBits:
return buf_create_from_str("left shift overflowed bits");
case PanicMsgIdShrOverflowedBits:
return buf_create_from_str("right shift overflowed bits");
case PanicMsgIdDivisionByZero:
return buf_create_from_str("division by zero");
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,
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
// if the values don't match, we have an overflow
// 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);
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);
return result;
@ -1496,12 +1523,12 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
return LLVMBuildXor(g->builder, op1_value, op2_value, "");
case IrBinOpBinAnd:
return LLVMBuildAnd(g->builder, op1_value, op2_value, "");
case IrBinOpBitShiftLeft:
case IrBinOpBitShiftLeftWrap:
case IrBinOpBitShiftLeftLossy:
case IrBinOpBitShiftLeftExact:
{
assert(type_entry->id == TypeTableEntryIdInt);
bool is_wrapping = (op_id == IrBinOpBitShiftLeftWrap);
if (is_wrapping) {
bool is_sloppy = (op_id == IrBinOpBitShiftLeftLossy);
if (is_sloppy) {
return LLVMBuildShl(g->builder, op1_value, op2_value, "");
} else if (want_debug_safety) {
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, "");
}
}
case IrBinOpBitShiftRight:
assert(type_entry->id == TypeTableEntryIdInt);
if (type_entry->data.integral.is_signed) {
return LLVMBuildAShr(g->builder, op1_value, op2_value, "");
} else {
return LLVMBuildLShr(g->builder, op1_value, op2_value, "");
case IrBinOpBitShiftRightLossy:
case IrBinOpBitShiftRightExact:
{
assert(type_entry->id == TypeTableEntryIdInt);
bool is_sloppy = (op_id == IrBinOpBitShiftRightLossy);
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 IrBinOpSubWrap:
@ -4556,6 +4595,8 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
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) {

View File

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

View File

@ -25,6 +25,7 @@ enum Error {
ErrorUnexpected,
ErrorExactDivRemainder,
ErrorNegativeDenominator,
ErrorShiftedOutOneBits,
};
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:
return ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap);
case BinOpTypeAssignBitShiftLeft:
return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeft);
case BinOpTypeAssignBitShiftLeftWrap:
return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftWrap);
return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy);
case BinOpTypeAssignBitShiftRight:
return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRight);
return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy);
case BinOpTypeAssignBitAnd:
return ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd);
case BinOpTypeAssignBitXor:
@ -3663,11 +3661,9 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
case BinOpTypeBinAnd:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd);
case BinOpTypeBitShiftLeft:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeft);
case BinOpTypeBitShiftLeftWrap:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftWrap);
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy);
case BinOpTypeBitShiftRight:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRight);
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy);
case BinOpTypeAdd:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd);
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);
}
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();
}
@ -8362,16 +8386,27 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val,
assert(is_int);
bigint_and(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
break;
case IrBinOpBitShiftLeft:
case IrBinOpBitShiftLeftExact:
assert(is_int);
bigint_shl(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
break;
case IrBinOpBitShiftLeftWrap:
case IrBinOpBitShiftLeftLossy:
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);
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);
bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
break;
@ -8591,8 +8626,8 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
}
if (resolved_type->id == TypeTableEntryIdNumLitInt) {
if (op_id == IrBinOpBitShiftLeftWrap) {
op_id = IrBinOpBitShiftLeft;
if (op_id == IrBinOpBitShiftLeftLossy) {
op_id = IrBinOpBitShiftLeftExact;
} else if (op_id == IrBinOpAddWrap) {
op_id = IrBinOpAdd;
} else if (op_id == IrBinOpSubWrap) {
@ -8631,6 +8666,9 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
} else if (err == ErrorNegativeDenominator) {
ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("negative denominator"));
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 {
zig_unreachable();
}
@ -8857,9 +8895,10 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi
case IrBinOpBinOr:
case IrBinOpBinXor:
case IrBinOpBinAnd:
case IrBinOpBitShiftLeft:
case IrBinOpBitShiftLeftWrap:
case IrBinOpBitShiftRight:
case IrBinOpBitShiftLeftLossy:
case IrBinOpBitShiftLeftExact:
case IrBinOpBitShiftRightLossy:
case IrBinOpBitShiftRightExact:
case IrBinOpAdd:
case IrBinOpAddWrap:
case IrBinOpSub:

View File

@ -92,12 +92,14 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
return "^";
case IrBinOpBinAnd:
return "&";
case IrBinOpBitShiftLeft:
case IrBinOpBitShiftLeftLossy:
return "<<";
case IrBinOpBitShiftLeftWrap:
return "<<%";
case IrBinOpBitShiftRight:
case IrBinOpBitShiftLeftExact:
return "@shlExact";
case IrBinOpBitShiftRightLossy:
return ">>";
case IrBinOpBitShiftRightExact:
return "@shrExact";
case IrBinOpAdd:
return "+";
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) {
switch (token->id) {
case TokenIdBitShiftLeft: return BinOpTypeBitShiftLeft;
case TokenIdBitShiftLeftPercent: return BinOpTypeBitShiftLeftWrap;
case TokenIdBitShiftRight: return BinOpTypeBitShiftRight;
default: return BinOpTypeInvalid;
}
@ -1909,7 +1908,6 @@ static BinOpType tok_to_ass_op(Token *token) {
case TokenIdMinusEq: return BinOpTypeAssignMinus;
case TokenIdMinusPercentEq: return BinOpTypeAssignMinusWrap;
case TokenIdBitShiftLeftEq: return BinOpTypeAssignBitShiftLeft;
case TokenIdBitShiftLeftPercentEq: return BinOpTypeAssignBitShiftLeftWrap;
case TokenIdBitShiftRightEq: return BinOpTypeAssignBitShiftRight;
case TokenIdBitAndEq: return BinOpTypeAssignBitAnd;
case TokenIdBitXorEq: return BinOpTypeAssignBitXor;

View File

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

View File

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

View File

@ -754,9 +754,22 @@ LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMVa
LLVMValueRef ZigLLVMBuildNUWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
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"
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);
LLVMValueRef ZigLLVMBuildNUWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
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,
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];
out_index += 1;
dest[out_index] = alphabet[((source[i] & 0x3) <<% 4) |
dest[out_index] = alphabet[((source[i] & 0x3) << 4) |
((source[i + 1] & 0xf0) >> 4)];
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)];
out_index += 1;
@ -38,17 +38,17 @@ pub fn encodeWithAlphabet(dest: []u8, source: []const u8, alphabet: []const u8)
out_index += 1;
if (i + 1 == source.len) {
dest[out_index] = alphabet[(source[i] & 0x3) <<% 4];
dest[out_index] = alphabet[(source[i] & 0x3) << 4];
out_index += 1;
dest[out_index] = alphabet[64];
out_index += 1;
} else {
dest[out_index] = alphabet[((source[i] & 0x3) <<% 4) |
dest[out_index] = alphabet[((source[i] & 0x3) << 4) |
((source[i + 1] & 0xf0) >> 4)];
out_index += 1;
dest[out_index] = alphabet[(source[i + 1] & 0xf) <<% 2];
dest[out_index] = alphabet[(source[i + 1] & 0xf) << 2];
out_index += 1;
}
@ -83,15 +83,15 @@ pub fn decodeWithAscii6BitMap(dest: []u8, source: []const u8, ascii6: []const u8
}
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;
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;
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]];
dest_index += 1;
@ -100,17 +100,17 @@ pub fn decodeWithAscii6BitMap(dest: []u8, source: []const u8, ascii6: []const u8
}
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;
dest_index += 1;
}
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;
dest_index += 1;
}
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]];
dest_index += 1;
}

View File

@ -83,7 +83,7 @@ fn exp2_32(x: f32) -> f32 {
const k = i0 / tblsiz;
// 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.
const uk = @bitCast(f64, u64(0x3FF + k) <<% 52);
const uk = @bitCast(f64, u64(0x3FF + k) << 52);
i0 &= tblsiz - 1;
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) {
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) {
var y = x - e + 1.0;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -182,8 +182,8 @@ fn MersenneTwister(
mt.index += 1;
x ^= ((x >> u) & d);
x ^= ((x <<% s) & b);
x ^= ((x <<% t) & c);
x ^= ((x << s) & b);
x ^= ((x << t) & c);
x ^= (x >> l);
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);
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);
if (ux <<% 1 <= uy <<% 1) {
if (ux <<% 1 == uy <<% 1)
if (ux << 1 <= uy << 1) {
if (ux << 1 == uy << 1)
return 0 * x;
return x;
}
// normalize x and y
if (ex == 0) {
i = ux <<% exp_bits;
while (i >> bits_minus_1 == 0) : ({ex -= 1; i <<%= 1}) {}
ux <<%= @bitCast(u32, -ex + 1);
i = ux << exp_bits;
while (i >> bits_minus_1 == 0) : ({ex -= 1; i <<= 1}) {}
ux <<= @bitCast(u32, -ex + 1);
} else {
ux &= @maxValue(uint) >> exp_bits;
ux |= 1 <<% digits;
ux |= 1 << digits;
}
if (ey == 0) {
i = uy <<% exp_bits;
while (i >> bits_minus_1 == 0) : ({ey -= 1; i <<%= 1}) {}
i = uy << exp_bits;
while (i >> bits_minus_1 == 0) : ({ey -= 1; i <<= 1}) {}
uy <<= @bitCast(u32, -ey + 1);
} else {
uy &= @maxValue(uint) >> exp_bits;
uy |= 1 <<% digits;
uy |= 1 << digits;
}
// x mod y
@ -82,7 +82,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) -> T {
return 0 * x;
ux = i;
}
ux <<%= 1;
ux <<= 1;
}
i = ux -% uy;
if (i >> bits_minus_1 == 0) {
@ -90,19 +90,19 @@ fn generic_fmod(comptime T: type, x: T, y: T) -> T {
return 0 * x;
ux = i;
}
while (ux >> digits == 0) : ({ux <<%= 1; ex -= 1}) {}
while (ux >> digits == 0) : ({ux <<= 1; ex -= 1}) {}
// scale result up
if (ex > 0) {
ux -%= 1 <<% digits;
ux |= @bitCast(u32, ex) <<% digits;
ux -%= 1 << digits;
ux |= @bitCast(u32, ex) << digits;
} else {
ux >>= @bitCast(u32, -ex + 1);
}
if (T == f32) {
ux |= sx;
} else {
ux |= uint(sx) <<% bits_minus_1;
ux |= uint(sx) << bits_minus_1;
}
return *@ptrCast(&const T, &ux);
}
@ -111,7 +111,7 @@ fn isNan(comptime T: type, bits: T) -> bool {
if (T == u32) {
return (bits & 0x7fffffff) > 0x7f800000;
} else if (T == u64) {
return (bits & (@maxValue(u64) >> 1)) > (u64(0x7ff) <<% 52);
return (bits & (@maxValue(u64) >> 1)) > (u64(0x7ff) << 52);
} else {
unreachable;
}

View File

@ -168,15 +168,6 @@ fn testNegationWrappingEval(x: i16) {
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_u64_div();
comptime test_u64_div();
@ -257,3 +248,39 @@ test "hex float literal within range" {
const b = 0x0.1p1027;
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'");
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;
\\}
\\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;
\\}
\\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)
\\}
);