diff --git a/doc/primitives.rst b/doc/primitives.rst
index 8d03ac1f7ac7a6ac0931bbdaa4c296bc7f4ebb33..7442cd11265e9d005006467622068cf2fc0c9f42 100644
--- a/doc/primitives.rst
+++ b/doc/primitives.rst
@@ -65,6 +65,36 @@ Sums, products and such
     :undoc-members:
     :members: mapper_method
 
+Shift operators
+---------------
+
+.. autoclass:: LeftShift
+    :undoc-members:
+    :members: mapper_method
+
+.. autoclass:: RightShift
+    :undoc-members:
+    :members: mapper_method
+
+Bitwise operators
+-----------------
+
+.. autoclass:: BitwiseNot
+    :undoc-members:
+    :members: mapper_method
+
+.. autoclass:: BitwiseOr
+    :undoc-members:
+    :members: mapper_method
+
+.. autoclass:: BitwiseXor
+    :undoc-members:
+    :members: mapper_method
+
+.. autoclass:: BitwiseAnd
+    :undoc-members:
+    :members: mapper_method
+
 Comparisons and logic
 ---------------------
 
diff --git a/pymbolic/mapper/__init__.py b/pymbolic/mapper/__init__.py
index fa54fc8ffacd6761b1555b99b35ae77d07c536a8..cd56ad807b59c50a5339c9bac9b9a22cb2ac7024 100644
--- a/pymbolic/mapper/__init__.py
+++ b/pymbolic/mapper/__init__.py
@@ -186,14 +186,22 @@ class CombineMapper(RecursiveMapper):
                     self.rec(coeff, *args) for exp, coeff in expr.data)
                 )
 
-    def map_logical_and(self, expr, *args):
-        return self.combine(self.rec(child, *args)
-                for child in expr.children)
+    def map_left_shift(self, expr, *args):
+        return self.combine(
+                self.rec(expr.shiftee, *args),
+                self.rec(expr.shift, *args))
 
-    map_logical_or = map_logical_and
+    map_right_shift = map_left_shift
 
-    def map_logical_not(self, expr, *args):
+    def map_bitwise_not(self, expr, *args):
         return self.rec(expr.child, *args)
+    map_bitwise_or = map_sum
+    map_bitwise_xor = map_sum
+    map_bitwise_and = map_sum
+
+    map_logical_not = map_bitwise_not
+    map_logical_and = map_sum
+    map_logical_or = map_sum
 
     def map_comparison(self, expr, *args):
         return self.combine((
@@ -288,16 +296,27 @@ class IdentityMapper(Mapper):
                               ((exp, self.rec(coeff, *args))
                                   for exp, coeff in expr.data))
 
-    def map_logical_and(self, expr, *args):
+    def map_left_shift(self, expr, *args):
+        return type(expr)(
+                self.rec(expr.shiftee, *args),
+                self.rec(expr.shift, *args))
+
+    map_right_shift = map_left_shift
+
+    def map_bitwise_not(self, expr, *args):
+        return type(expr)(
+                self.rec(expr.child, *args))
+
+    def map_bitwise_or(self, expr, *args):
         return type(expr)(tuple(
             self.rec(child, *args) for child in expr.children))
 
-    map_logical_or = map_logical_and
+    map_bitwise_xor = map_bitwise_or
+    map_bitwise_and = map_bitwise_or
 
-    def map_logical_not(self, expr, *args):
-        from pymbolic.primitives import LogicalNot
-        return LogicalNot(
-                self.rec(expr.child, *args))
+    map_logical_not = map_bitwise_not
+    map_logical_or = map_bitwise_or
+    map_logical_and = map_bitwise_or
 
     def map_comparison(self, expr, *args):
         return type(expr)(
@@ -463,19 +482,33 @@ class WalkMapper(RecursiveMapper):
 
         self.rec(expr.child)
 
-    def map_comparison(self, expr):
+    def map_left_shift(self, expr):
         if not self.visit(expr):
             return
 
-        self.rec(expr.left)
-        self.rec(expr.right)
+        self.rec(expr.shift)
+        self.rec(expr.shiftee)
+
+    mrs = map_left_shift
 
-    def map_logical_not(self, expr):
+    def map_bitwise_not(self, expr):
         if not self.visit(expr):
             return
 
         self.rec(expr.child)
 
+    map_bitwise_or = map_sum
+    map_bitwise_xor = map_sum
+    map_bitwise_and = map_sum
+
+    def map_comparison(self, expr):
+        if not self.visit(expr):
+            return
+
+        self.rec(expr.left)
+        self.rec(expr.right)
+
+    map_logical_not = map_bitwise_not
     map_logical_and = map_sum
     map_logical_or = map_sum
 
@@ -537,6 +570,19 @@ class CallbackMapper(RecursiveMapper):
     map_floor_div = map_constant
     map_remainder = map_constant
     map_power = map_constant
+
+    map_left_shift = map_constant
+    map_right_shift = map_constant
+
+    map_bitwise_not = map_constant
+    map_bitwise_or = map_constant
+    map_bitwise_xor = map_constant
+    map_bitwise_and = map_constant
+
+    map_logical_not = map_constant
+    map_logical_or = map_constant
+    map_logical_and = map_constant
+
     map_polynomial = map_constant
     map_list = map_constant
     map_tuple = map_constant
diff --git a/pymbolic/mapper/c_code.py b/pymbolic/mapper/c_code.py
index 61ed0f3218540c496b5a7ff4d9d92d9ef167a3cf..c18d0977fbc3c60b649251570d71879aed5b7027 100644
--- a/pymbolic/mapper/c_code.py
+++ b/pymbolic/mapper/c_code.py
@@ -22,7 +22,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 """
 
-from pymbolic.mapper.stringifier import SimplifyingSortingStringifyMapper
+from pymbolic.mapper.stringifier import (
+        SimplifyingSortingStringifyMapper, PREC_UNARY,
+        PREC_LOGICAL_AND, PREC_LOGICAL_OR)
 
 
 class CCodeMapper(SimplifyingSortingStringifyMapper):
@@ -83,7 +85,8 @@ class CCodeMapper(SimplifyingSortingStringifyMapper):
     def copy_with_mapped_cses(self, cses_and_values):
         return self.copy(self.cse_name_list + cses_and_values)
 
-    # mappings ----------------------------------------------------------------
+    # {{{ mappings
+
     def map_product(self, expr, enclosing_prec):
         from pymbolic.mapper.stringifier import PREC_PRODUCT
         return self.parenthesize_if_needed(
@@ -139,6 +142,21 @@ class CCodeMapper(SimplifyingSortingStringifyMapper):
                     self.rec(expr.numerator, PREC_PRODUCT),
                     self.rec(expr.denominator, PREC_POWER))  # analogous to ^{-1}
 
+    def map_logical_not(self, expr, enclosing_prec):
+        return self.parenthesize_if_needed(
+                "!" + self.rec(expr.child, PREC_UNARY),
+                enclosing_prec, PREC_UNARY)
+
+    def map_logical_and(self, expr, enclosing_prec):
+        return self.parenthesize_if_needed(
+                self.join_rec(" && ", expr.children, PREC_LOGICAL_AND),
+                enclosing_prec, PREC_LOGICAL_AND)
+
+    def map_logical_or(self, expr, enclosing_prec):
+        return self.parenthesize_if_needed(
+                self.join_rec(" || ", expr.children, PREC_LOGICAL_OR),
+                enclosing_prec, PREC_LOGICAL_OR)
+
     def map_common_subexpression(self, expr, enclosing_prec):
         try:
             cse_name = self.cse_to_name[expr.child]
@@ -187,3 +205,4 @@ class CCodeMapper(SimplifyingSortingStringifyMapper):
                 self.rec(expr.then, PREC_NONE),
                 self.rec(expr.else_, PREC_NONE),
                 )
+    # }}}
diff --git a/pymbolic/mapper/evaluator.py b/pymbolic/mapper/evaluator.py
index 5ef2019f5f00b266776c4705f91ab49e64b489b1..c4ef3f64f8c3e3a401239e08db188c020b6ecaa5 100644
--- a/pymbolic/mapper/evaluator.py
+++ b/pymbolic/mapper/evaluator.py
@@ -22,7 +22,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 """
 
+
 from pymbolic.mapper import RecursiveMapper
+import operator as op
 
 
 class UnknownVariableError(Exception):
@@ -90,6 +92,35 @@ class EvaluationMapper(RecursiveMapper):
     def map_power(self, expr):
         return self.rec(expr.base) ** self.rec(expr.exponent)
 
+    def map_left_shift(self, expr):
+        return self.rec(expr.shiftee) << self.rec(expr.shift)
+
+    def map_right_shift(self, expr):
+        return self.rec(expr.shiftee) >> self.rec(expr.shift)
+
+    def map_bitwise_not(self, expr):
+        return ~self.rec(expr.child)
+
+    def map_bitwise_or(self, expr):
+        return reduce(op.or_, (self.rec(ch) for ch in expr.children))
+
+    def map_bitwise_xor(self, expr):
+        return reduce(op.xor, (self.rec(ch) for ch in expr.children))
+
+    def map_bitwise_and(self, expr):
+        return reduce(op.and_, (self.rec(ch) for ch in expr.children))
+
+    def map_logical_not(self, expr):
+        return not self.rec(expr.child)
+
+    def map_logical_or(self, expr):
+        from pytools import any
+        return any(self.rec(ch) for ch in expr.children)
+
+    def map_logical_and(self, expr):
+        from pytools import all
+        return all(self.rec(ch) for ch in expr.children)
+
     def map_polynomial(self, expr):
         # evaluate using Horner's scheme
         result = 0
diff --git a/pymbolic/mapper/stringifier.py b/pymbolic/mapper/stringifier.py
index 4e2f029650e2ff6eeb416b8585094383c231de6f..719869b13956bb1311ce06295aa6863d63a892ce 100644
--- a/pymbolic/mapper/stringifier.py
+++ b/pymbolic/mapper/stringifier.py
@@ -30,9 +30,13 @@ PREC_POWER = 14
 PREC_UNARY = 13
 PREC_PRODUCT = 12
 PREC_SUM = 11
-PREC_COMPARISON = 10
-PREC_LOGICAL_AND = 9
-PREC_LOGICAL_OR = 8
+PREC_SHIFT = 10
+PREC_BITWISE_AND = 9
+PREC_BITWISE_XOR = 8
+PREC_BITWISE_OR = 7
+PREC_COMPARISON = 6
+PREC_LOGICAL_AND = 5
+PREC_LOGICAL_OR = 4
 PREC_NONE = 0
 
 
@@ -167,6 +171,40 @@ class StringifyMapper(pymbolic.mapper.Mapper):
             [coeff*expr.base**exp for exp, coeff in expr.data[::-1]]),
             enclosing_prec)
 
+    def map_left_shift(self, expr, enclosing_prec):
+        return self.parenthesize_if_needed(
+                self.format("%s << %s",
+                    self.rec(expr.shiftee, PREC_SHIFT),
+                    self.rec(expr.shift, PREC_SHIFT)),
+                enclosing_prec, PREC_SHIFT)
+
+    def map_right_shift(self, expr, enclosing_prec):
+        return self.parenthesize_if_needed(
+                self.format("%s >> %s",
+                    self.rec(expr.shiftee, PREC_SHIFT),
+                    self.rec(expr.shift, PREC_SHIFT)),
+                enclosing_prec, PREC_SHIFT)
+
+    def map_bitwise_not(self, expr, enclosing_prec):
+        return self.parenthesize_if_needed(
+                "~" + self.rec(expr.child, PREC_UNARY),
+                enclosing_prec, PREC_UNARY)
+
+    def map_bitwise_or(self, expr, enclosing_prec):
+        return self.parenthesize_if_needed(
+                self.join_rec(" | ", expr.children, PREC_BITWISE_OR),
+                enclosing_prec, PREC_BITWISE_OR)
+
+    def map_bitwise_xor(self, expr, enclosing_prec):
+        return self.parenthesize_if_needed(
+                self.join_rec(" ^ ", expr.children, PREC_BITWISE_XOR),
+                enclosing_prec, PREC_BITWISE_XOR)
+
+    def map_bitwise_and(self, expr, enclosing_prec):
+        return self.parenthesize_if_needed(
+                self.join_rec(" ^ ", expr.children, PREC_BITWISE_AND),
+                enclosing_prec, PREC_BITWISE_AND)
+
     def map_comparison(self, expr, enclosing_prec):
         return self.parenthesize_if_needed(
                 self.format("%s %s %s",
@@ -177,19 +215,19 @@ class StringifyMapper(pymbolic.mapper.Mapper):
 
     def map_logical_not(self, expr, enclosing_prec):
         return self.parenthesize_if_needed(
-                self.rec(expr.child, PREC_UNARY),
+                "not " + self.rec(expr.child, PREC_UNARY),
                 enclosing_prec, PREC_UNARY)
 
-    def map_logical_and(self, expr, enclosing_prec):
-        return self.parenthesize_if_needed(
-                self.join_rec(" && ", expr.children, PREC_LOGICAL_AND),
-                enclosing_prec, PREC_LOGICAL_AND)
-
     def map_logical_or(self, expr, enclosing_prec):
         return self.parenthesize_if_needed(
-                self.join_rec(" || ", expr.children, PREC_LOGICAL_OR),
+                self.join_rec(" or ", expr.children, PREC_LOGICAL_OR),
                 enclosing_prec, PREC_LOGICAL_OR)
 
+    def map_logical_and(self, expr, enclosing_prec):
+        return self.parenthesize_if_needed(
+                self.join_rec(" and ", expr.children, PREC_LOGICAL_AND),
+                enclosing_prec, PREC_LOGICAL_AND)
+
     def map_list(self, expr, enclosing_prec):
         return self.format("[%s]", self.join_rec(", ", expr, PREC_NONE))
 
diff --git a/pymbolic/primitives.py b/pymbolic/primitives.py
index 68a6c291e83d1709c7fa2ccf71b820365c8a7f1c..ab3974178bb1108a452b21ee65f1db40896cd28a 100644
--- a/pymbolic/primitives.py
+++ b/pymbolic/primitives.py
@@ -167,6 +167,49 @@ class Expression(object):
 
     # }}}
 
+    # {{{ shifts
+
+    def __lshift__(self, other):
+        return LeftShift(self, other)
+
+    def __rlshift__(self, other):
+        return LeftShift(other, self)
+
+    def __rshift__(self, other):
+        return RightShift(self, other)
+
+    def __rrshift__(self, other):
+        return RightShift(other, self)
+
+    # }}}
+
+    # {{{ bitwise operators
+
+    def __inv__(self):
+        return BitwiseNot(self)
+
+    def __or__(self, other):
+        return BitwiseOr(self, other)
+
+    def __ror__(self, other):
+        return BitwiseOr(other, self)
+
+    def __xor__(self, other):
+        return BitwiseXor(self, other)
+
+    def __rxor__(self, other):
+        return BitwiseXor(other, self)
+
+    def __and__(self, other):
+        return BitwiseAnd(self, other)
+
+    def __rand__(self, other):
+        return BitwiseAnd(other, self)
+
+    # }}}
+
+    # {{{
+
     def __neg__(self):
         return -1*self
 
@@ -211,6 +254,7 @@ class Expression(object):
         initargs_str = ", ".join(repr(i) for i in self.__getinitargs__())
 
         return "%s(%s)" % (self.__class__.__name__, initargs_str)
+    # }}}
 
     # {{{ hashable interface
 
@@ -396,13 +440,7 @@ class Lookup(AlgebraicLeaf):
 
 # {{{ arithmetic primitives
 
-class Sum(Expression):
-    """
-    .. attribute:: children
-
-        A :class:`tuple`.
-    """
-
+class _MultiChildExpression(Expression):
     def __init__(self, children):
         assert isinstance(children, tuple)
 
@@ -411,6 +449,14 @@ class Sum(Expression):
     def __getinitargs__(self):
         return self.children
 
+
+class Sum(_MultiChildExpression):
+    """
+    .. attribute:: children
+
+        A :class:`tuple`.
+    """
+
     def __add__(self, other):
         if not is_valid_operand(other):
             return NotImplemented
@@ -451,20 +497,13 @@ class Sum(Expression):
     mapper_method = intern("map_sum")
 
 
-class Product(Expression):
+class Product(_MultiChildExpression):
     """
     .. attribute:: children
 
         A :class:`tuple`.
     """
 
-    def __init__(self, children):
-        assert isinstance(children, tuple)
-        self.children = children
-
-    def __getinitargs__(self):
-        return self.children
-
     def __mul__(self, other):
         if not is_valid_operand(other):
             return NotImplemented
@@ -567,6 +606,85 @@ class Power(Expression):
 # }}}
 
 
+# {{{ shift operators
+
+class _ShiftOperator(Expression):
+    def __init__(self, shiftee, shift):
+        self.shiftee = shiftee
+        self.shift = shift
+
+    def __getinitargs__(self):
+        return self.base, self.exponent
+
+
+class LeftShift(Expression):
+    """
+    .. attribute:: shiftee
+    .. attribute:: shift
+    """
+
+    mapper_method = intern("map_left_shift")
+
+
+class RightShift(Expression):
+    """
+    .. attribute:: shiftee
+    .. attribute:: shift
+    """
+
+    mapper_method = intern("map_right_shift")
+
+# }}}
+
+
+# {{{ bitwise operators
+
+class BitwiseNot(Expression):
+    """
+    .. attribute:: child
+    """
+
+    def __init__(self, child):
+        self.child = child
+
+    def __getinitargs__(self):
+        return (self.child,)
+
+    mapper_method = intern("map_bitwise_not")
+
+
+class BitwiseOr(_MultiChildExpression):
+    """
+    .. attribute:: children
+
+        A :class:`tuple`.
+    """
+
+    mapper_method = intern("map_bitwise_or")
+
+
+class BitwiseXor(_MultiChildExpression):
+    """
+    .. attribute:: children
+
+        A :class:`tuple`.
+    """
+
+    mapper_method = intern("map_bitwise_xor")
+
+
+class BitwiseAnd(_MultiChildExpression):
+    """
+    .. attribute:: children
+
+        A :class:`tuple`.
+    """
+
+    mapper_method = intern("map_bitwise_and")
+
+# }}}
+
+
 # {{{ comparisons, logic, conditionals
 
 class ComparisonOperator(Expression):
@@ -598,11 +716,7 @@ class ComparisonOperator(Expression):
     mapper_method = intern("map_comparison")
 
 
-class BooleanExpression(Expression):
-    pass
-
-
-class LogicalNot(BooleanExpression):
+class LogicalNot(Expression):
     """
     .. attribute:: child
     """
@@ -611,44 +725,28 @@ class LogicalNot(BooleanExpression):
         self.child = child
 
     def __getinitargs__(self):
-        return (self.child, self.prefix)
+        return (self.child,)
 
     mapper_method = intern("map_logical_not")
 
 
-class LogicalOr(BooleanExpression):
+class LogicalOr(_MultiChildExpression):
     """
     .. attribute:: children
 
         A :class:`tuple`.
     """
 
-    def __init__(self, children):
-        assert isinstance(children, tuple)
-
-        self.children = children
-
-    def __getinitargs__(self):
-        return self.children
-
     mapper_method = intern("map_logical_or")
 
 
-class LogicalAnd(BooleanExpression):
+class LogicalAnd(_MultiChildExpression):
     """
     .. attribute:: children
 
         A :class:`tuple`.
     """
 
-    def __init__(self, children):
-        assert isinstance(children, tuple)
-
-        self.children = children
-
-    def __getinitargs__(self):
-        return self.children
-
     mapper_method = intern("map_logical_and")