Make negative and too-far shifts have defined behavior.

This commit is contained in:
whitequark 2015-07-23 00:58:41 +03:00
parent bf60978c7b
commit 4cfe4ea148
3 changed files with 24 additions and 6 deletions

View File

@ -900,8 +900,14 @@ class ARTIQIRGenerator(algorithm.Visitor):
def visit_BinOpT(self, node): def visit_BinOpT(self, node):
if builtins.is_numeric(node.type): if builtins.is_numeric(node.type):
# TODO: check for division by zero # TODO: check for division by zero
# TODO: check for shift by too many bits rhs = self.visit(node.right)
return self.append(ir.Arith(node.op, self.visit(node.left), self.visit(node.right))) if isinstance(node.op, (ast.LShift, ast.RShift)):
# Check for negative shift amount.
self._make_check(self.append(ir.Compare(ast.GtE(loc=None), rhs,
ir.Constant(0, rhs.type))),
lambda: self.append(ir.Alloc([], builtins.TValueError())))
return self.append(ir.Arith(node.op, self.visit(node.left), rhs))
elif isinstance(node.op, ast.Add): # list + list, tuple + tuple elif isinstance(node.op, ast.Add): # list + list, tuple + tuple
lhs, rhs = self.visit(node.left), self.visit(node.right) lhs, rhs = self.visit(node.left), self.visit(node.right)
if types.is_tuple(node.left.type) and types.is_tuple(node.right.type): if types.is_tuple(node.left.type) and types.is_tuple(node.right.type):

View File

@ -368,10 +368,20 @@ class LLVMIRGenerator:
return self.llbuilder.fptosi(llvalue, self.llty_of_type(insn.type), return self.llbuilder.fptosi(llvalue, self.llty_of_type(insn.type),
name=insn.name) name=insn.name)
elif isinstance(insn.op, ast.LShift): elif isinstance(insn.op, ast.LShift):
return self.llbuilder.shl(self.map(insn.lhs()), self.map(insn.rhs()), lllhs, llrhs = map(self.map, (insn.lhs(), insn.rhs()))
llrhs_max = ll.Constant(llrhs.type, builtins.get_int_width(insn.lhs().type))
llrhs_overflow = self.llbuilder.icmp_signed('>=', llrhs, llrhs_max)
llvalue_zero = ll.Constant(lllhs.type, 0)
llvalue = self.llbuilder.shl(lllhs, llrhs)
return self.llbuilder.select(llrhs_overflow, llvalue_zero, llvalue,
name=insn.name) name=insn.name)
elif isinstance(insn.op, ast.RShift): elif isinstance(insn.op, ast.RShift):
return self.llbuilder.ashr(self.map(insn.lhs()), self.map(insn.rhs()), lllhs, llrhs = map(self.map, (insn.lhs(), insn.rhs()))
llrhs_max = ll.Constant(llrhs.type, builtins.get_int_width(insn.lhs().type) - 1)
llrhs_overflow = self.llbuilder.icmp_signed('>', llrhs, llrhs_max)
llvalue = self.llbuilder.ashr(lllhs, llrhs)
llvalue_max = self.llbuilder.ashr(lllhs, llrhs_max) # preserve sign bit
return self.llbuilder.select(llrhs_overflow, llvalue_max, llvalue,
name=insn.name) name=insn.name)
elif isinstance(insn.op, ast.BitAnd): elif isinstance(insn.op, ast.BitAnd):
return self.llbuilder.and_(self.map(insn.lhs()), self.map(insn.rhs()), return self.llbuilder.and_(self.map(insn.lhs()), self.map(insn.rhs()),

View File

@ -28,6 +28,8 @@ assert 9.0 ** 0.5 == 3.0
assert 1 << 1 == 2 assert 1 << 1 == 2
assert 2 >> 1 == 1 assert 2 >> 1 == 1
assert -2 >> 1 == -1 assert -2 >> 1 == -1
assert 1 << 32 == 0
assert -1 >> 32 == -1
assert 0x18 & 0x0f == 0x08 assert 0x18 & 0x0f == 0x08
assert 0x18 | 0x0f == 0x1f assert 0x18 | 0x0f == 0x1f
assert 0x18 ^ 0x0f == 0x17 assert 0x18 ^ 0x0f == 0x17