1import re
2import logging
3from typing import List, Optional
4
5
6from . import VEXObject
7from archinfo import RegisterOffset, TmpVar
8from .enums import IRCallee, IRRegArray, get_int_from_enum, get_enum_from_int
9from .const import get_type_size, U8, U16, U32, U64
10
11l = logging.getLogger("pyvex.expr")
12
13
14
15class IRExpr(VEXObject):
16    """
17    IR expressions in VEX represent operations without side effects.
18    """
19
20    __slots__ = [ ]
21
22    tag = None # type: Optional[str]
23    tag_int = 0  # set automatically at bottom of file
24
25    def pp(self):
26        print(self.__str__())
27
28    @property
29    def child_expressions(self) -> List['IRExpr']:
30        """
31        A list of all of the expressions that this expression ends up evaluating.
32        """
33        expressions = [ ]
34        for k in self.__slots__:
35            v = getattr(self, k)
36            if isinstance(v, IRExpr):
37                expressions.append(v)
38                expressions.extend(v.child_expressions)
39        return expressions
40
41    @property
42    def constants(self):
43        """
44        A list of all of the constants that this expression ends up using.
45        """
46        constants = [ ]
47        for k in self.__slots__:
48            v = getattr(self, k)
49            if isinstance(v, IRExpr):
50                constants.extend(v.constants)
51            elif isinstance(v, IRConst):
52                constants.append(v)
53        return constants
54
55    def result_size(self, tyenv):
56        return get_type_size(self.result_type(tyenv))
57
58    def result_type(self, tyenv):
59        raise NotImplementedError()
60
61    def replace_expression(self, expr, replacement):
62        """
63        Replace child expressions in-place.
64
65        :param IRExpr expr:         The expression to look for.
66        :param IRExpr replacement:  The expression to replace with.
67        :return:                    None
68        """
69
70        for k in self.__slots__:
71            v = getattr(self, k)
72            if v is expr:
73                setattr(self, k, replacement)
74            elif type(v) is list:
75                # Replace the instance in the list
76                for i, expr_ in enumerate(v):
77                    if expr_ is expr:
78                        v[i] = replacement
79            elif type(v) is tuple:
80                # Rebuild the tuple
81                _lst = [ ]
82                replaced = False
83                for i, expr_ in enumerate(v):
84                    if expr_ is expr:
85                        _lst.append(replacement)
86                        replaced = True
87                    else:
88                        _lst.append(expr_)
89                if replaced:
90                    setattr(self, k, tuple(_lst))
91            elif isinstance(v, IRExpr):
92                v.replace_expression(expr, replacement)
93
94    @staticmethod
95    def _from_c(c_expr) -> 'IRExpr':
96        if c_expr == ffi.NULL or c_expr[0] == ffi.NULL:
97            return None
98
99        try:
100            return enum_to_expr_class(c_expr.tag)._from_c(c_expr)
101        except KeyError:
102            raise PyVEXError('Unknown/unsupported IRExprTag %s\n' % get_enum_from_int(c_expr.tag))
103    _translate = _from_c
104
105    @staticmethod
106    def _to_c(expr):
107        try:
108            return tag_to_expr_class(expr.tag)._to_c(expr)
109        except KeyError:
110            raise PyVEXError('Unknown/unsupported IRExprTag %s\n' % expr.tag)
111
112    def typecheck(self, tyenv):
113        return self.result_type(tyenv)
114
115
116class Binder(IRExpr):
117    """
118    Used only in pattern matching within Vex. Should not be seen outside of Vex.
119    """
120
121    __slots__ = ['binder']
122
123    tag = 'Iex_Binder'
124
125    def __init__(self, binder):
126        self.binder = binder
127
128    def __str__(self):
129        return "Binder"
130
131    @staticmethod
132    def _from_c(c_expr):
133        return Binder(c_expr.iex.Binder.binder)
134
135    @staticmethod
136    def _to_c(expr):
137        return pvc.IRExpr_Binder(expr.binder)
138
139    def result_type(self, tyenv):
140        return 'Ity_INVALID'
141
142
143class VECRET(IRExpr):
144
145    tag = 'Iex_VECRET'
146
147    __slots__ = [ ]
148
149    def __str__(self):
150        return "VECRET"
151
152    @staticmethod
153    def _from_c(c_expr):
154        return VECRET()
155
156    @staticmethod
157    def _to_c(expr):
158        return pvc.IRExpr_VECRET()
159
160    def result_type(self, tyenv):
161        return 'Ity_INVALID'
162
163
164class GSPTR(IRExpr):
165
166    __slots__ = [ ]
167
168    tag = 'Iex_GSPTR'
169
170    def __str__(self):
171        return "GSPTR"
172
173    @staticmethod
174    def _from_c(c_expr):
175        return GSPTR()
176
177    @staticmethod
178    def _to_c(expr):
179        return pvc.IRExpr_GSPTR()
180
181    def result_type(self, tyenv):
182        return 'Ity_INVALID'
183
184
185class GetI(IRExpr):
186    """
187    Read a guest register at a non-fixed offset in the guest state.
188    """
189
190    __slots__ = ['descr', 'ix', 'bias']
191
192    tag = 'Iex_GetI'
193
194    def __init__(self, descr, ix, bias):
195        self.descr = descr
196        self.ix = ix
197        self.bias = bias
198
199    @property
200    def description(self):
201        return self.descr
202
203    @property
204    def index(self):
205        return self.ix
206
207    def __str__(self):
208        return "GetI(%s)[%s,%s]" % (self.descr, self.ix, self.bias)
209
210    @staticmethod
211    def _from_c(c_expr):
212        descr = IRRegArray._from_c(c_expr.Iex.GetI.descr)
213        ix = IRExpr._from_c(c_expr.Iex.GetI.ix)
214        bias = c_expr.Iex.GetI.bias
215        return GetI(descr, ix, bias)
216
217    @staticmethod
218    def _to_c(expr):
219        return pvc.IRExpr_GetI(IRRegArray._to_c(expr.descr),
220                               IRExpr._to_c(expr.ix),
221                               expr.bias)
222
223    def result_type(self, tyenv):
224        return self.descr.elemTy
225
226
227class RdTmp(IRExpr):
228    """
229    Read the value held by a temporary.
230    """
231
232    __slots__ = ['_tmp']
233
234    tag = 'Iex_RdTmp'
235
236    def __init__(self, tmp: TmpVar):
237        self._tmp = tmp
238
239    def __str__(self):
240        return "t%d" % self.tmp
241
242    @property
243    def tmp(self) -> TmpVar:
244        return self._tmp
245
246    @staticmethod
247    def _from_c(c_expr):
248        tmp = c_expr.Iex.RdTmp.tmp
249        return RdTmp.get_instance(tmp)
250
251    @staticmethod
252    def _to_c(expr):
253        return pvc.IRExpr_RdTmp(expr.tmp)
254
255    @staticmethod
256    def get_instance(tmp):
257        if tmp < 1024:
258            # for small tmp reads, they are cached and are only created once globally
259            return _RDTMP_POOL[tmp]
260        return RdTmp(tmp)
261
262    def replace_expression(self, expr, replacement):
263        # RdTmp is one of the terminal IRExprs, which cannot be replaced.
264        pass
265
266    def result_type(self, tyenv):
267        return tyenv.lookup(self.tmp)
268
269
270_RDTMP_POOL = list(RdTmp(i) for i in range(0, 1024))
271
272
273class Get(IRExpr):
274    """
275    Read a guest register, at a fixed offset in the guest state.
276    """
277
278    __slots__ = ['offset', 'ty']
279
280    tag = 'Iex_Get'
281
282    def __init__(self, offset: RegisterOffset, ty: str):
283        self.offset = offset
284        self.ty = ty
285
286    @property
287    def type(self):
288        return self.ty
289
290    def __str__(self, reg_name=None):
291        if reg_name:
292            return "GET:%s(%s)" % (self.ty[4:], reg_name)
293        else:
294            return "GET:%s(offset=%s)" % (self.ty[4:], self.offset)
295
296    @staticmethod
297    def _from_c(c_expr):
298        return Get(c_expr.Iex.Get.offset,
299                   get_enum_from_int(c_expr.Iex.Get.ty))
300
301    @staticmethod
302    def _to_c(expr):
303        return pvc.IRExpr_Get(expr.offset,
304                              get_int_from_enum(expr.ty))
305
306    def result_type(self, tyenv):
307        return self.ty
308
309
310class Qop(IRExpr):
311    """
312    A quaternary operation (4 arguments).
313    """
314
315    __slots__ = ['op', 'args']
316
317    tag = 'Iex_Qop'
318
319    def __init__(self, op, args):
320        self.op = op
321        self.args = args
322
323    def __str__(self):
324        return "%s(%s)" % (self.op[4:], ','.join(str(a) for a in self.args))
325
326    @property
327    def child_expressions(self):
328        expressions = sum((a.child_expressions for a in self.args), [ ])
329        expressions.extend(self.args)
330        return expressions
331
332    @staticmethod
333    def _from_c(c_expr):
334        return Qop(get_enum_from_int(c_expr.Iex.Qop.details.op),
335                   [IRExpr._from_c(arg)
336                    for arg in [c_expr.Iex.Qop.details.arg1,
337                                c_expr.Iex.Qop.details.arg2,
338                                c_expr.Iex.Qop.details.arg3,
339                                c_expr.Iex.Qop.details.arg4]])
340
341    @staticmethod
342    def _to_c(expr):
343        return pvc.IRExpr_Qop(get_int_from_enum(expr.op),
344                              *[IRExpr._to_c(arg)
345                                for arg in expr.args])
346
347    def result_type(self, tyenv):
348        return get_op_retty(self.op)
349
350    def typecheck(self, tyenv): # TODO change all this to use PyvexTypeErrorException
351        resty, (arg1ty, arg2ty, arg3ty, arg4ty) = op_arg_types(self.op)
352        arg1ty_real = self.args[0].typecheck(tyenv)
353        arg2ty_real = self.args[1].typecheck(tyenv)
354        arg3ty_real = self.args[2].typecheck(tyenv)
355        arg4ty_real = self.args[3].typecheck(tyenv)
356        if arg1ty_real is None or arg2ty_real is None or arg3ty_real is None or arg4ty_real is None:
357            return None
358
359        if arg1ty_real != arg1ty:
360            l.debug("First arg of %s must be %s", self.op, arg1ty)
361            return None
362        if arg2ty_real != arg2ty:
363            l.debug("Second arg of %s must be %s", self.op, arg2ty)
364            return None
365        if arg3ty_real != arg3ty:
366            l.debug("Third arg of %s must be %s", self.op, arg3ty)
367            return None
368        if arg4ty_real != arg4ty:
369            l.debug("Fourth arg of %s must be %s", self.op, arg4ty)
370            return None
371
372        return resty
373
374
375class Triop(IRExpr):
376    """
377    A ternary operation (3 arguments)
378    """
379
380    __slots__ = ['op', 'args']
381
382    tag = 'Iex_Triop'
383
384    def __init__(self, op, args):
385        self.op = op
386        self.args = args
387
388    def __str__(self):
389        return "%s(%s)" % (self.op[4:], ','.join(str(a) for a in self.args))
390
391    @property
392    def child_expressions(self):
393        expressions = sum((a.child_expressions for a in self.args), [ ])
394        expressions.extend(self.args)
395        return expressions
396
397    @staticmethod
398    def _from_c(c_expr):
399        return Triop(get_enum_from_int(c_expr.Iex.Triop.details.op),
400                     [IRExpr._from_c(arg)
401                      for arg in [c_expr.Iex.Triop.details.arg1,
402                                  c_expr.Iex.Triop.details.arg2,
403                                  c_expr.Iex.Triop.details.arg3]])
404
405    @staticmethod
406    def _to_c(expr):
407        return pvc.IRExpr_Triop(get_int_from_enum(expr.op),
408                                *[IRExpr._to_c(arg)
409                                  for arg in expr.args])
410
411    def result_type(self, tyenv):
412        return get_op_retty(self.op)
413
414    def typecheck(self, tyenv):
415        resty, (arg1ty, arg2ty, arg3ty) = op_arg_types(self.op)
416        arg1ty_real = self.args[0].typecheck(tyenv)
417        arg2ty_real = self.args[1].typecheck(tyenv)
418        arg3ty_real = self.args[2].typecheck(tyenv)
419        if arg1ty_real is None or arg2ty_real is None or arg3ty_real is None:
420            return None
421
422        if arg1ty_real != arg1ty:
423            l.debug("First arg of %s must be %s", self.op, arg1ty)
424            return None
425        if arg2ty_real != arg2ty:
426            l.debug("Second arg of %s must be %s", self.op, arg2ty)
427            return None
428        if arg3ty_real != arg3ty:
429            l.debug("Third arg of %s must be %s", self.op, arg3ty)
430            return None
431
432        return resty
433
434
435class Binop(IRExpr):
436    """
437    A binary operation (2 arguments).
438    """
439
440    __slots__ = ['_op', 'op_int', 'args']
441
442    tag = 'Iex_Binop'
443
444    def __init__(self, op, args, op_int=None):
445        self.op_int = op_int
446        self.args = args
447        self._op = op if op is not None else None
448
449    def __str__(self):
450        return "%s(%s)" % (self.op[4:], ','.join(str(a) for a in self.args))
451
452    @property
453    def op(self):
454        if self._op is None:
455            self._op = get_enum_from_int(self.op_int)
456        return self._op
457
458    @property
459    def child_expressions(self):
460        expressions = sum((a.child_expressions for a in self.args), [ ])
461        expressions.extend(self.args)
462        return expressions
463
464    @staticmethod
465    def _from_c(c_expr):
466        return Binop(None,
467                     [IRExpr._from_c(arg)
468                      for arg in [c_expr.Iex.Binop.arg1,
469                                  c_expr.Iex.Binop.arg2]],
470                     op_int=c_expr.Iex.Binop.op)
471
472    @staticmethod
473    def _to_c(expr):
474        return pvc.IRExpr_Binop(get_int_from_enum(expr.op),
475                                *[IRExpr._to_c(arg)
476                                  for arg in expr.args])
477
478    def result_type(self, tyenv):
479        return get_op_retty(self.op)
480
481    def typecheck(self, tyenv):
482        arg1ty_real = self.args[0].typecheck(tyenv)
483        arg2ty_real = self.args[1].typecheck(tyenv)
484
485        resty, (arg1ty, arg2ty) = op_arg_types(self.op)
486        if arg1ty_real is None or arg2ty_real is None:
487            return None
488
489        if arg1ty_real != arg1ty:
490            l.debug("First arg of %s must be %s", self.op, arg1ty)
491            return None
492        if arg2ty_real != arg2ty:
493            l.debug("Second arg of %s must be %s", self.op, arg2ty)
494            return None
495
496        return resty
497
498
499class Unop(IRExpr):
500    """
501    A unary operation (1 argument).
502    """
503
504    __slots__ = ['op', 'args']
505
506    tag = 'Iex_Unop'
507
508    def __init__(self, op, args):
509        self.op = op
510        self.args = args
511
512    def __str__(self):
513        return "%s(%s)" % (self.op[4:], ','.join(str(a) for a in self.args))
514
515    @property
516    def child_expressions(self):
517        expressions = sum((a.child_expressions for a in self.args), [ ])
518        expressions.extend(self.args)
519        return expressions
520
521    @staticmethod
522    def _from_c(c_expr):
523        return Unop(get_enum_from_int(c_expr.Iex.Unop.op),
524                    [IRExpr._from_c(c_expr.Iex.Unop.arg)])
525
526    @staticmethod
527    def _to_c(expr):
528        return pvc.IRExpr_Unop(get_int_from_enum(expr.op),
529                               IRExpr._to_c(expr.args[0]))
530
531    def result_type(self, tyenv):
532        return get_op_retty(self.op)
533
534    def typecheck(self, tyenv):
535        resty, (arg1ty,) = op_arg_types(self.op)
536        arg1ty_real = self.args[0].typecheck(tyenv)
537        if arg1ty_real is None:
538            return None
539
540        if arg1ty_real != arg1ty:
541            l.debug("First arg of %s must be %s", self.op, arg1ty)
542            return None
543
544        return resty
545
546
547class Load(IRExpr):
548    """
549    A load from memory.
550    """
551
552    __slots__ = ['end', 'ty', 'addr']
553
554    tag = 'Iex_Load'
555
556    def __init__(self, end, ty, addr):
557        self.end = end
558        self.ty = ty
559        self.addr = addr
560
561    @property
562    def endness(self):
563        return self.end
564
565    @property
566    def type(self):
567        return self.ty
568
569    def __str__(self):
570        return "LD%s:%s(%s)" % (self.end[-2:].lower(), self.ty[4:], self.addr)
571
572    @staticmethod
573    def _from_c(c_expr):
574        return Load(get_enum_from_int(c_expr.Iex.Load.end),
575                    get_enum_from_int(c_expr.Iex.Load.ty),
576                    IRExpr._from_c(c_expr.Iex.Load.addr))
577
578    @staticmethod
579    def _to_c(expr):
580        return pvc.IRExpr_Load(get_int_from_enum(expr.end),
581                               get_int_from_enum(expr.ty),
582                               IRExpr._to_c(expr.addr))
583
584    def result_type(self, tyenv):
585        return self.ty
586
587    def typecheck(self, tyenv):
588        addrty = self.addr.typecheck(tyenv)
589        if addrty is None:
590            return None
591        if addrty != tyenv.wordty:
592            l.debug("Address must be word-sized")
593            return None
594        return self.ty
595
596
597class Const(IRExpr):
598    """
599    A constant expression.
600    """
601
602    __slots__ = ['_con']
603
604    tag = 'Iex_Const'
605
606    def __init__(self, con: 'IRConst'):
607        self._con = con
608
609    def __str__(self):
610        return str(self.con)
611
612    @property
613    def con(self) -> 'IRConst':
614        return self._con
615
616    @staticmethod
617    def _from_c(c_expr):
618        con = IRConst._from_c(c_expr.Iex.Const.con)
619        return Const.get_instance(con)
620
621    @staticmethod
622    def _to_c(expr):
623        return pvc.IRExpr_Const(IRConst._to_c(expr.con))
624
625    @staticmethod
626    def get_instance(con):
627        if con.value < 1024 and con.__class__ in _CONST_POOL:
628            return _CONST_POOL[con.__class__][con.value]
629        return Const(con)
630
631    def result_type(self, tyenv):
632        return self.con.type
633
634
635_CONST_POOL = {
636    U8: [Const(U8(i)) for i in range(0, 1024)],
637    U16: [Const(U16(i)) for i in range(0, 1024)],
638    U32: [Const(U32(i)) for i in range(0, 1024)],
639    U64: [Const(U64(i)) for i in range(0, 1024)],
640}
641
642
643class ITE(IRExpr):
644    """
645    An if-then-else expression.
646    """
647
648    __slots__ = ['cond', 'iffalse', 'iftrue']
649
650    tag = 'Iex_ITE'
651
652    def __init__(self, cond, iffalse, iftrue):
653        self.cond = cond
654        self.iffalse = iffalse
655        self.iftrue = iftrue
656
657    def __str__(self):
658        return "ITE(%s,%s,%s)" % (self.cond, self.iftrue, self.iffalse)
659
660    @staticmethod
661    def _from_c(c_expr):
662        return ITE(IRExpr._from_c(c_expr.Iex.ITE.cond),
663                   IRExpr._from_c(c_expr.Iex.ITE.iffalse),
664                   IRExpr._from_c(c_expr.Iex.ITE.iftrue))
665
666    @staticmethod
667    def _to_c(expr):
668        return pvc.IRExpr_ITE(IRExpr._to_c(expr.cond),
669                              IRExpr._to_c(expr.iftrue),
670                              IRExpr._to_c(expr.iffalse))
671
672    def result_type(self, tyenv):
673        return self.iftrue.result_type(tyenv)
674
675    def typecheck(self, tyenv):
676        condty = self.cond.typecheck(tyenv)
677        falsety = self.iffalse.typecheck(tyenv)
678        truety = self.iftrue.typecheck(tyenv)
679
680        if condty is None or falsety is None or truety is None:
681            return None
682
683        if condty != 'Ity_I1':
684            l.debug("guard must be Ity_I1")
685            return None
686
687        if falsety != truety:
688            l.debug("false condition must be same type as true condition")
689            return None
690
691        return falsety
692
693
694class CCall(IRExpr):
695    """
696    A call to a pure (no side-effects) helper C function.
697    """
698
699    __slots__ = ['retty', 'cee', 'args']
700
701    tag = 'Iex_CCall'
702
703    def __init__(self, retty, cee, args):
704        self.retty = retty
705        self.cee = cee
706        self.args = tuple(args)
707
708    @property
709    def ret_type(self):
710        return self.retty
711
712    @property
713    def callee(self):
714        return self.cee
715
716    def __str__(self):
717        return "%s(%s):%s" % (self.cee, ','.join(str(a) for a in self.args), self.retty)
718
719    @property
720    def child_expressions(self):
721        expressions = sum((a.child_expressions for a in self.args), [ ])
722        expressions.extend(self.args)
723        return expressions
724
725    @staticmethod
726    def _from_c(c_expr):
727        i = 0
728        args = []
729        while True:
730            arg = c_expr.Iex.CCall.args[i]
731            if arg == ffi.NULL:
732                break
733            args.append(IRExpr._from_c(arg))
734            i += 1
735
736        return CCall(get_enum_from_int(c_expr.Iex.CCall.retty),
737                     IRCallee._from_c(c_expr.Iex.CCall.cee),
738                     tuple(args))
739
740    @staticmethod
741    def _to_c(expr):
742        args = [IRExpr._to_c(arg) for arg in expr.args]
743        mkIRExprVec = getattr(pvc, 'mkIRExprVec_%d' % len(args))
744        return pvc.IRExpr_CCall(IRCallee._to_c(expr.cee),
745                                get_int_from_enum(expr.retty),
746                                mkIRExprVec(*args))
747
748    def result_type(self, tyenv):
749        return self.retty
750
751
752def get_op_retty(op):
753    return op_arg_types(op)[0]
754
755
756op_signatures = {}
757def _request_op_type_from_cache(op):
758    return op_signatures[op]
759
760def _request_op_type_from_libvex(op):
761    Ity_INVALID = 0x1100  # as defined in enum IRType in VEX
762
763    res_ty = ffi.new('IRType *')
764    arg_tys = [ffi.new('IRType *') for _ in range(4)]
765    # initialize all IRTypes to Ity_INVALID
766    for arg in arg_tys:
767        arg[0] = Ity_INVALID
768    pvc.typeOfPrimop(get_int_from_enum(op), res_ty, *arg_tys)
769    arg_ty_vals = [a[0] for a in arg_tys]
770
771    try:
772        numargs = arg_ty_vals.index(Ity_INVALID)
773    except ValueError:
774        numargs = 4
775    args_tys_list = [get_enum_from_int(arg_ty_vals[i]) for i in range(numargs)]
776
777    op_ty_sig = (get_enum_from_int(res_ty[0]), tuple(args_tys_list))
778    op_signatures[op] = op_ty_sig
779    return op_ty_sig
780
781class PyvexOpMatchException(Exception):
782    pass
783
784class PyvexTypeErrorException(Exception):
785    pass
786
787def int_type_for_size(size):
788    return 'Ity_I%d' % size
789
790# precompiled regexes
791unop_signature_re = re.compile(r'Iop_(Not|Ctz|Clz)(?P<size>\d+)$')
792binop_signature_re = re.compile(r'Iop_(Add|Sub|Mul|Xor|Or|And|Div[SU]|Mod)(?P<size>\d+)$')
793shift_signature_re = re.compile(r'Iop_(Shl|Shr|Sar)(?P<size>\d+)$')
794cmp_signature_re_1 = re.compile(r'Iop_Cmp(EQ|NE)(?P<size>\d+)$')
795cmp_signature_re_2 = re.compile(r'Iop_Cmp(GT|GE|LT|LE)(?P<size>\d+)[SU]$')
796mull_signature_re = re.compile(r'Iop_Mull[SU](?P<size>\d+)$')
797half_signature_re = re.compile(r'Iop_DivMod[SU](?P<fullsize>\d+)to(?P<halfsize>\d+)$')
798cast_signature_re = re.compile(r'Iop_(?P<srcsize>\d+)(U|S|HI|HL)?to(?P<dstsize>\d+)')
799
800
801def unop_signature(op):
802    m = unop_signature_re.match(op)
803    if m is None:
804        raise PyvexOpMatchException()
805    size = int(m.group('size'))
806    size_type = int_type_for_size(size)
807    return size_type, (size_type,)
808
809def binop_signature(op):
810    m = binop_signature_re.match(op)
811    if m is None:
812        raise PyvexOpMatchException()
813    size = int(m.group('size'))
814    size_type = int_type_for_size(size)
815    return (size_type, (size_type, size_type))
816
817def shift_signature(op):
818    m = shift_signature_re.match(op)
819    if m is None:
820        raise PyvexOpMatchException()
821    size = int(m.group('size'))
822    if size > 255:
823        raise PyvexTypeErrorException('Cannot apply shift operation to %d size int because shift index is 8-bit' % size)
824    size_type = int_type_for_size(size)
825    return (size_type, (size_type, int_type_for_size(8)))
826
827def cmp_signature(op):
828    m = cmp_signature_re_1.match(op)
829    m2 = cmp_signature_re_2.match(op)
830    if (m is None) == (m2 is None):
831        raise PyvexOpMatchException()
832    mfound = m if m is not None else m2
833    size = int(mfound.group('size'))
834    size_type = int_type_for_size(size)
835    return (int_type_for_size(1), (size_type, size_type))
836
837def mull_signature(op):
838    m = mull_signature_re.match(op)
839    if m is None:
840        raise PyvexOpMatchException()
841    size = int(m.group('size'))
842    size_type = int_type_for_size(size)
843    doubled_size_type = int_type_for_size(2 * size)
844    return (doubled_size_type, (size_type, size_type))
845
846def half_signature(op):
847    m = half_signature_re.match(op)
848    if m is None:
849        raise PyvexOpMatchException()
850    fullsize = int(m.group('fullsize'))
851    halfsize = int(m.group('halfsize'))
852    if halfsize * 2 != fullsize:
853        raise PyvexTypeErrorException('Invalid Instruction %s: Type 1 must be twice the size of type 2' % op)
854    fullsize_type = int_type_for_size(fullsize)
855    halfsize_type = int_type_for_size(halfsize)
856    return (fullsize_type, (fullsize_type, halfsize_type))
857
858def cast_signature(op):
859    m = cast_signature_re.match(op)
860    if m is None:
861        raise PyvexOpMatchException()
862    src_type = int_type_for_size(int(m.group('srcsize')))
863    dst_type = int_type_for_size(int(m.group('dstsize')))
864    return (dst_type, (src_type,))
865
866polymorphic_op_processors = [ unop_signature,
867                              binop_signature,
868                              shift_signature,
869                              cmp_signature,
870                              mull_signature,
871                              half_signature,
872                              cast_signature ]
873
874def _request_polymorphic_op_type(op):
875    for polymorphic_signature in polymorphic_op_processors:
876        try:
877            op_ty_sig = polymorphic_signature(op)
878            break
879        except PyvexOpMatchException:
880            continue
881    else:
882        raise PyvexOpMatchException('Op %s not recognized' % op)
883    return op_ty_sig
884
885_request_funcs = [_request_op_type_from_cache,
886                  _request_op_type_from_libvex,
887                  _request_polymorphic_op_type]
888
889def op_arg_types(op):
890    for _request_func in _request_funcs:
891        try:
892           return _request_func(op)
893        except KeyError:
894            continue
895    raise ValueError("Cannot find type of op %s" % op)
896
897
898_globals = globals().copy()
899#
900# Mapping from tag strings/enums to IRExpr classes
901#
902tag_to_expr_mapping = { }
903enum_to_expr_mapping = { }
904tag_count = 0
905cls = None
906for cls in _globals.values():
907    if type(cls) is type and issubclass(cls, IRExpr) and cls is not IRExpr:
908        tag_to_expr_mapping[cls.tag] = cls
909        enum_to_expr_mapping[get_int_from_enum(cls.tag)] = cls
910        cls.tag_int = tag_count
911        tag_count += 1
912del cls
913
914def tag_to_expr_class(tag):
915    """
916    Convert a tag string to the corresponding IRExpr class type.
917
918    :param str tag: The tag string.
919    :return:        A class.
920    :rtype:         type
921    """
922
923    try:
924        return tag_to_expr_mapping[tag]
925    except KeyError:
926        raise KeyError('Cannot find expression class for type %s.' % tag)
927
928
929def enum_to_expr_class(tag_enum):
930    """
931    Convert a tag enum to the corresponding IRExpr class.
932
933    :param int tag_enum: The tag enum.
934    :return:             A class.
935    :rtype:              type
936    """
937
938    try:
939        return enum_to_expr_mapping[tag_enum]
940    except KeyError:
941        raise KeyError("Cannot find expression class for type %s." % get_enum_from_int(tag_enum))
942
943
944from .const import IRConst
945from .errors import PyVEXError
946from . import ffi, pvc
947