Commit 35d3444e27
Changed files (28)
src
std
src/all_types.hpp
@@ -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,
src/ast_render.cpp
@@ -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 "^=";
src/bigint.cpp
@@ -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);
src/bigint.hpp
@@ -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);
src/codegen.cpp
@@ -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) {
src/error.cpp
@@ -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)";
}
src/error.hpp
@@ -25,6 +25,7 @@ enum Error {
ErrorUnexpected,
ErrorExactDivRemainder,
ErrorNegativeDenominator,
+ ErrorShiftedOutOneBits,
};
const char *err_str(int err);
src/ir.cpp
@@ -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:
src/ir_print.cpp
@@ -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:
src/parser.cpp
@@ -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;
src/tokenizer.cpp
@@ -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 "^=";
src/tokenizer.hpp
@@ -23,8 +23,6 @@ enum TokenId {
TokenIdBitOrEq,
TokenIdBitShiftLeft,
TokenIdBitShiftLeftEq,
- TokenIdBitShiftLeftPercent,
- TokenIdBitShiftLeftPercentEq,
TokenIdBitShiftRight,
TokenIdBitShiftRightEq,
TokenIdBitXorEq,
src/zig_llvm.cpp
@@ -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) {
src/zig_llvm.hpp
@@ -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);
std/math/exp2.zig
@@ -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;
std/math/expm1.zig
@@ -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;
std/math/ilogb.zig
@@ -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);
std/math/ln.zig
@@ -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
std/math/log10.zig
@@ -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);
std/math/log1p.zig
@@ -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();
}
std/math/log2.zig
@@ -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);
std/math/modf.zig
@@ -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);
std/special/builtin.zig
@@ -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;
}
std/base64.zig
@@ -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;
}
std/rand.zig
@@ -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;
test/cases/math.zig
@@ -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);
+}
test/compile_errors.zig
@@ -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");
}
test/debug_safety.zig
@@ -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)
\\}
);