Example #1
0
 def new_member_val(cls, node, name, dest_reg):
     """ Extract value of an already initialized member while initializing some other member. """
     cls, obj_ptr = cls.instantiating_class
     m_offset = cls.get_member_idx(name) * CC.var_size
     node.add_instr(CC.MOV, src=Loc.const(m_offset), dest=dest_reg)
     node.add_instr(CC.ADD, lhs=obj_ptr, rhs=dest_reg)
     node.add_instr(CC.MOV, src=Loc.mem(str(dest_reg)), dest=dest_reg)
Example #2
0
 def _gen_code_boolop(self, **kwargs):
     self.label_true = kwargs.get('on_true', CC.new_label())
     self.label_false = kwargs.get('on_false', CC.new_label())
     self.label_right = CC.new_label(
     )  # additional label to jump to the right operand
     for case in switch(self.type.type):
         if case(LP.AND):
             self.add_child_by_idx(0,
                                   on_true=self.label_right,
                                   on_false=self.label_false)
             self.add_instr(CC.LABEL, label=self.label_right)
             self.add_child_by_idx(1,
                                   on_true=self.label_true,
                                   on_false=self.label_false)
             break
         if case(LP.OR):
             self.add_child_by_idx(0,
                                   on_true=self.label_true,
                                   on_false=self.label_right)
             self.add_instr(CC.LABEL, label=self.label_right)
             self.add_child_by_idx(1,
                                   on_true=self.label_true,
                                   on_false=self.label_false)
             break
         if case():
             raise InternalError('wrong bool op type %s' % str(self.type))
     # if no jump keywords were given, the result will be used as a value -- push it
     if not self.has_jump_codes(kwargs):
         self.label_after = CC.new_label()
         self.add_instr(CC.LABEL, label=self.label_true)
         self.add_instr(CC.PUSH, src=Loc.const(1))
         self.add_instr(CC.JUMP, label=self.label_after)
         self.add_instr(CC.LABEL, label=self.label_false)
         self.add_instr(CC.PUSH, src=Loc.const(0))
         self.add_instr(CC.LABEL, label=self.label_after)
Example #3
0
 def gen_code(self, **kwargs):
     for case in switch(self.type.type):
         if case(LP.RETURN):
             if self.children:
                 # Evaluate the expression and pop the result to eax for returning.
                 self.add_child_by_idx(0)
                 self.add_instr(CC.POP, dest=Loc.reg('a'))
             # Jump to the return section
             self.add_instr(CC.JUMP, label=self.get_cur_fun().ret_label)
             break
         if case(LP.IF):
             # children: cond, (then-block)?, (else-block)?
             self.add_child_by_idx(0,
                                   on_true=self.label_then,
                                   on_false=self.label_else)
             if len(self.children) > 1:  # there is a then-block
                 self.add_instr(CC.LABEL, label=self.label_then)
                 self.add_child_by_idx(1)
             if len(self.children) > 2:  # there is an else-block
                 self.add_instr(
                     CC.JUMP,
                     label=self.label_after)  # jump out of then-block
                 self.add_instr(CC.LABEL, label=self.label_else)
                 self.add_child_by_idx(2)
             self.add_instr(CC.LABEL, label=self.label_after)
             break
         if case(LP.WHILE):
             # children: cond, (block)?
             if len(self.children) > 1:  # there is a loop block
                 self.add_instr(CC.JUMP, label=self.label_cond)
                 self.add_instr(CC.LABEL, label=self.label_block)
                 self.add_child_by_idx(1)
             self.add_instr(CC.LABEL, label=self.label_cond)
             self.add_child_by_idx(0,
                                   on_true=self.label_block,
                                   on_false=self.label_after)
             self.add_instr(CC.LABEL, label=self.label_after)
             break
         if case(LP.ASSIGN):
             # compute assigned value on stack
             self.add_child_by_idx(1)
             # compute the destination address, if needed
             self.add_child_by_idx(0, addr_only=True)
             # put the value into destination address
             self.add_instr(CC.POP, dest=Loc.reg('a'))
             var_loc = self.children[0].get_loc()
             self.add_instr(CC.MOV, src=Loc.reg('a'), dest=var_loc)
             break
         if case(LP.INCR, LP.DECR):
             op = CC.ADD if self.type.type == LP.INCR else CC.SUB
             # compute the destination address, if needed
             self.add_child_by_idx(0, addr_only=True)
             var_loc = self.children[0].get_loc()
             self.add_instr(op, lhs=Loc.const(1), rhs=var_loc)
             break
         if case():
             raise NotImplementedError('unknown statement type: %s' %
                                       str(self.type))
Example #4
0
 def check_unused_result(self):
     """ Drop result pushed on stack which wouldn't be used. Both this add and the push itself
     will be optimized out later. """
     if self.tree.unused_result:
         debug('POP UNUSED RESULT', self.tree.pos)
         self.add_instr(CC.ADD,
                        lhs=Loc.const(CC.var_size),
                        rhs=Loc.reg('top'),
                        comment=CC.S_UNUSED_RESULT)
Example #5
0
 def get_loc(self):
     """ Return where the variable is located after the generated code is executed. """
     for case in switch(self.type.type):
         if case(LP.IDENT):
             return Loc.sym(self.tree.symbol(self.value))
         if case(LP.ATTR):
             return Loc.mem(Loc.reg_d)
         if case(LP.ELEM):
             return Loc.mem(Loc.reg_d)
Example #6
0
 def _gen_code_as_value(self, **kwargs):
     for case in switch(self.type.type):
         if case(LP.BOOLEAN, LP.INT, LP.OBJECT, LP.ARRAY):
             self.add_instr(CC.PUSH, src=Loc.const(self.value))
             break
         if case(LP.STRING):
             self.add_instr(CC.PUSH, src=Loc.stringlit(self))
             break
         if case():
             raise InternalError('invalid literal type %s' %
                                 str(self.type.type))
Example #7
0
 def _gen_code_load_member(self, dest_reg, addr_only=False):
     cls = self.tree.cls
     m_idx = cls.get_member_idx(self.value)
     self.add_child_by_idx(0)  # evaluate the object's base address
     self.add_instr(CC.POP, dest=Loc.reg('a'))
     self.add_instr(CC.MOV, src=Loc.const(m_idx),
                    dest=Loc.reg('d'))  # idx must be a register
     self._gen_code_load_memory(dest_reg,
                                Loc.reg_a,
                                idx=Loc.reg('d'),
                                mult=CC.var_size,
                                addr_only=addr_only)
Example #8
0
 def _gen_code_load_array_elem(self, dest_reg, addr_only=False):
     self.add_child_by_idx(0)  # evaluate the array address
     self.add_child_by_idx(1)  # evaluate the array index
     self.add_instr(CC.POP, dest=Loc.reg('a'))
     self.add_instr(CC.POP, dest=Loc.reg('d'))
     # Calculate the elem address -- with offset +1 because of array size stored at 0.
     self._gen_code_load_memory(dest_reg,
                                Loc.reg_d,
                                offset=CC.var_size,
                                idx=Loc.reg_a,
                                mult=CC.var_size,
                                addr_only=addr_only)
Example #9
0
 def default_asm_value(type):
     """ Return Loc of a value that should be assigned to allocated but unset objects. """
     for case in switch(type):
         if case(LP.INT, LP.BOOLEAN, LP.ARRAY, LP.OBJECT):
             return Loc.const(
                 0)  # False=0 for boolean, NULL=0 for array and object.
         if case(LP.STRING):
             return Loc.stringlit(LiteralTree(
                 LP.STRING, '""'))  # Pointer to "" constant.
         if case():
             raise InternalError('no default asm value for type %s' %
                                 str(type))
Example #10
0
 def gen_code(self, **kwargs):
     for case in switch(self.type.type):
         if case(LP.NEG):  # integer negation
             self.add_child_by_idx(0)
             self.add_instr(CC.POP, dest=Loc.reg('a'))
             self.add_instr(CC.NEG, rhs=Loc.reg('a'))
             self.add_instr(CC.PUSH, src=Loc.reg('a'))
             break
         if case(LP.NOT):  # logical not
             if self.has_jump_codes(kwargs):
                 # called as part of condition evaluation -- just forward the jump labels
                 self.add_child_by_idx(0,
                                       on_true=kwargs['on_false'],
                                       on_false=kwargs['on_true'])
                 return
             # otherwise -- evaluate the boolean value: arg == false (0)
             self.add_child_by_idx(0)
             self.add_instr(CC.POP, dest=Loc.reg('a'))
             self.add_instr(CC.BOOL_OP,
                            lhs=Loc.const(0),
                            rhs=Loc.reg('a'),
                            op='sete',
                            dest=Loc.reg('a'))
             self.add_instr(CC.PUSH, src=Loc.reg('a'))
             break
         if case():
             raise InternalError('wrong unop value type')
     self.check_unused_result()
Example #11
0
 def gen_code(self, **kwargs):
     fun = self.get_cur_fun()
     block = self.get_cur_block()
     # For each declared item, compute its address on stack (and assign the value if needed).
     for item in self.items:
         addr = Loc.var_addr(fun.next_var_num())
         sym = Symbol(item.name, self.decl_type.type, addr)
         if item.expr:
             self.add_child_by_idx(item.expr_child_idx)
             self.add_instr(CC.POP, dest=Loc.reg('a'))
             self.add_instr(CC.MOV, src=Loc.reg('a'), dest=Loc.sym(sym))
         # Important: we add symbol containing the new var's address *after* assigned expression
         # is evaluated (because of e.g. int i = i+7); but still inside the loop -- so next
         # declarations use the new symbol.
         block.tree.add_symbol(sym)
Example #12
0
 def scan_labels(self):
     """ A function that indexes the labels and jumps in the given codes. """
     self.labels = {}
     self.jumps = {}
     for i in self.matcher.code_iter():
         code = self.codes[i]
         if self.matcher.match(code, type=CC.LABEL):
             self.labels[code['label']] = i
         else:
             if self.matcher.match(code, type=AnyOf(CC.JUMP, CC.IF_JUMP, CC.CALL)):
                 # functions begin with labels -- collect their calls, it might be useful later
                 label = code['label']
             elif self.matcher.match(code, type=AnyOf(CC.PUSH, CC.MOV),
                                     src=Loc.stringlit(Loc.ANY)):
                 # collect uses of string constants
                 label = code['src'].value
             else:
                 continue
             if label in self.jumps:
                 self.jumps[label].append(i)
             else:
                 self.jumps[label] = [i]
     debug('--- label maps ---')
     for label in self.labels:
         debug('[%d]' % self.labels[label], label, ': ', str(self.jumps.get(label, None)))
Example #13
0
 def _gen_code_in_cond(self, **kwargs):
     # [0]: load the bool value into a register.
     for case in switch(self.type.type):
         if case(LP.IDENT) and self.tree.symbol(
                 self.value).type == LP.BOOLEAN:
             # A variable referenced while instantiating is surely a class member.
             if NewCode.instantiating_class:
                 NewCode.new_member_val(self, self.value, Loc.reg('a'))
             else:
                 self.add_instr(CC.MOV,
                                src=Loc.sym(self.tree.symbol(self.value)),
                                dest=Loc.reg('a'))
             break
         if case(LP.ELEM) and self.tree.value_type.type == LP.BOOLEAN:
             self._gen_code_load_array_elem(dest_reg=Loc.reg('a'))
             break
         if case(LP.ATTR) and self.tree.value_type.type == LP.BOOLEAN:
             self._gen_code_load_member(dest_reg=Loc.reg('a'))
         if case():
             raise InternalError(
                 'jump-expr codes for non-bool %s expression at %s!' %
                 (str(self.type), self.tree.pos))
     # [1]: Compare and jump (note: comparing with 0, so on equality jump to false!)
     self.add_instr(CC.IF_JUMP,
                    lhs=Loc.const(0),
                    rhs=Loc.reg('a'),
                    op='je',
                    label=kwargs['on_false'])
     self.add_instr(CC.JUMP, label=kwargs['on_true'])
Example #14
0
 def _gen_code_load_memory(self,
                           dest_reg,
                           base_ptr,
                           offset=None,
                           idx=None,
                           mult=None,
                           addr_only=False):
     """ Evaluate element/member address and get its value. """
     self.add_instr(CC.LEA,
                    src=Loc.mem(base_ptr, offset=offset, idx=idx,
                                mult=mult),
                    dest=dest_reg,
                    drop_reg1=Loc.reg('a'),
                    drop_reg2=Loc.reg('d'))
     if addr_only:
         return
     self.add_instr(CC.MOV, src=Loc.mem(Loc.reg_d),
                    dest=dest_reg)  # load element
Example #15
0
 def _cp_save_to_pocket(self, pos, code):
     """ Constant propagation: when moving const to a register, stow it in the pocket instead."""
     if (self.matcher.match(code, type=CC.MOV, src=self.CONST_LOCS,
                            dest=Loc.reg(Loc.ANY)) and
             not self.matcher.match(code, comment=CC.S_PROPAGATED)):
         debug('mov const %s -> reg %s found at %d' % (code['src'].value,
                                                       code['dest'].value, pos))
         self.pocket[code['dest']] = code['src']
         self.mark_deleted(pos, comment=CC.S_PROPAGATED)
Example #16
0
 def _cp_empty_pocket_if_needed(self, pos, code):
     """ Constant propagation: empty the pocket in case of calls or jumps. """
     # [1] On function call, empty the pocket.
     if len(self.pocket) and code['type'] == CC.CALL:
         debug('function call at %d, emptying pocket' % pos)
         self.pocket = {}
     # [2] On function exit, drop %eax and empty pocket.
     if len(self.pocket) and code['type'] == CC.ENDFUNC:
         if Loc.reg('a') in self.pocket.keys():
             self._add_to_apply_pocket(pos, {Loc.reg('a'): self.pocket[Loc.reg('a')]})
             debug('ENDFUNC at %d, dropping reg a' % pos)
         self.pocket = {}
     # [3] On jump instructions (both in-/out-bound) assign the pocket values anyway.
     if len(self.pocket) and code['type'] in [CC.JUMP, CC.IF_JUMP, CC.LABEL]:
         debug('%s at %d, reassigning pocket values' % (CC._code_name(code['type']),
                                                        pos))
         # We can't insert into a list while iterating, so save the pocket for now
         # TODO we could later skip at least some of pocket's values, if we check that
         # a value is the same at label and all jumps to it, or the register is not live.
         self._add_to_apply_pocket(pos, self.pocket.copy())
         self.pocket = {}
Example #17
0
 def __init__(self, tree, **kwargs):
     super(FunCode, self).__init__(tree, **kwargs)
     self.ret_type = tree.ret_type
     self.name = tree.name
     self.args = tree.args
     self.add_child(BlockCode(tree.children[0]))
     self.ret_label = CC.new_label()
     for i in xrange(len(self.args)):
         arg = self.args[i]
         self.tree.add_symbol(Symbol(arg.name, arg.type, Loc.arg_addr(i)))
     self.var_count = self.children[0].count_local_vars()
     debug('fun ', self.name, 'VAR COUNT: ', self.var_count)
     self.used_vars = 0  # how many local variables are currently allocated
Example #18
0
 def _cp_two_const_operator(self, pos, code):
     """ Constant propagation for operators: if both operands are consts, calculate the result
     and propagate it instead."""
     # 'lhs' and 'rhs' are both registers with values stored in pocket, or only 'rhs' is and
     # 'lhs' is an actual constant (e.g. propagated there in previous loop iteration, when 'rhs'
     # was not yet propagated).
     if (self.matcher.match(
             code, type=self.BIN_OPS, lhs=self.CONST_OR_REG, rhs=Loc.reg(Loc.ANY)) and
             (code['lhs'].is_constant() or code['lhs'] in self.pocket) and
             code['rhs'] in self.pocket):
         debug('two-const operator %s at %d' % (CC._code_name(code['type']), pos))
         if code['type'] == CC.BOOL_OP:
             cmp_fun = {
                 'sete': operator.eq, 'setne': operator.ne,
                 'setg': operator.gt, 'setge': operator.ge,
                 'setl': operator.lt, 'setle': operator.le,
             }[code['op']]
             # The operator.* bool functions return bool, we want an int to push.
             op_fun = lambda x, y: int(cmp_fun(x, y))
         else:
             op_fun = {
                 CC.ADD: operator.add, CC.SUB: operator.sub,
                 CC.MUL: operator.mul, CC.DIV: operator.floordiv, CC.MOD: c_modulo
             }[code['type']]
         arg1 = int(self.pocket[code['rhs']].value)
         if code['lhs'].is_constant():
             arg2 = int(code['lhs'].value)
         else:
             arg2 = int(self.pocket[code['lhs']].value)
         res_val = op_fun(arg1, arg2)
         debug('   -> args %d, %d res %d' % (arg1, arg2, res_val))
         res_reg = code['dest'] if code['type'] in [CC.DIV, CC.MOD] else code['rhs']
         # Delete and re-insert value, to maintain hash properties.
         if res_reg in self.pocket:
             del self.pocket[res_reg]
         self.pocket[res_reg] = Loc.const(res_val)
         self.mark_deleted(pos, comment=CC.S_PROPAGATED, value=res_val)
         self.prop_consts += 1
         return True
Example #19
0
 def _cp_overwrite_pocket_values(self, pos, code):
     """ Constant propagation: if a register is being assigned, delete its entry in pocket."""
     # Only attrs modifying their location are 'dest', 'rhs'.
     # Note: rhs needs to be reviewed first, otherwise if rhs and dest are the same we
     # would forget the pocket value at dest, while it is still needed by rhs.
     for attr in [a for a in ['rhs', 'dest'] if a in code.keys()]:
         if not code[attr].is_reg() or code[attr] not in self.pocket:
             continue
         value = self.pocket[code[attr]].value
         # NEG is a special case here: 'dest' is both source and destination -- but the
         # value remains constant, so remove the code and adjust value in pocket.
         if code['type'] == CC.NEG:
             new_value = value[1:] if '-' in value else '-' + value
             debug('NEG reg %s with const at %d, adjusting pocket value to %s' % (
                 code[attr].value, pos, new_value))
             # Delete and re-insert value, to maintain hash properties.
             loc = self.pocket[code[attr]]
             del self.pocket[code[attr]]
             loc.value = new_value
             self.pocket[code[attr]] = loc
             self.mark_deleted(pos, comment=CC.S_PROPAGATED)
             self.prop_consts += 1
             return True
         else:  # otherwise, just forget the register's value from pocket.
             # but the right operand for cmpl also needs to be dropped.
             debug('reg %s is %s at %d, forgetting from pocket' % (
                 code[attr].value, attr, pos))
             if attr == 'rhs':
                 debug('   ^ but applying reg %s' % code['rhs'].value)
                 self._add_to_apply_pocket(pos, {code['rhs']: self.pocket[code['rhs']]})
             del self.pocket[code[attr]]
     # Special case: division -- invalidate %eax and %edx values in pocket as idivl stores
     # results there.
     if code['type'] in [CC.DIV, CC.MOD]:
         for reg in [Loc.reg('a'), Loc.reg('d')]:
             if reg in self.pocket:
                 del self.pocket[reg]
Example #20
0
 def _gen_code_relop(self, **kwargs):
     self.add_child_by_idx(0)
     self.add_child_by_idx(1)
     self.add_instr(CC.POP, dest=Loc.reg('d'))
     self.add_instr(CC.POP, dest=Loc.reg('a'))
     try:
         if self.has_jump_codes(kwargs):
             # part of condition evaluation -- select the conditional jump instruction
             self.label_true = kwargs['on_true']
             self.label_false = kwargs['on_false']
             jmp_code = {
                 LP.EQ: 'je',
                 LP.NEQ: 'jne',
                 LP.GT: 'jg',
                 LP.GEQ: 'jge',
                 LP.LT: 'jl',
                 LP.LEQ: 'jle',
             }[self.type.type.id]
             self.add_instr(CC.IF_JUMP,
                            lhs=Loc.reg('d'),
                            rhs=Loc.reg('a'),
                            op=jmp_code,
                            label=self.label_true)
             self.add_instr(CC.JUMP, label=self.label_false)
         else:
             # expression returning bool -- select the comparision set instruction
             set_code = {
                 LP.EQ: 'sete',
                 LP.NEQ: 'setne',
                 LP.GT: 'setg',
                 LP.GEQ: 'setge',
                 LP.LT: 'setl',
                 LP.LEQ: 'setle',
             }[self.type.type.id]
             self.add_instr(CC.BOOL_OP,
                            lhs=Loc.reg('d'),
                            rhs=Loc.reg('a'),
                            op=set_code,
                            dest=Loc.reg('a'))
             self.add_instr(CC.PUSH, src=Loc.reg('a'))
     except KeyError:
         raise InternalError('wrong rel op type %s' % str(self.type))
Example #21
0
 def _gen_code_stringop(self):
     if self.type.type != LP.PLUS:
         raise InternalError('wrong string op type %s' % str(self.type))
     # only + (concatenation) for now
     # If at least one operand is a constant or a variable, we can safely just push them in
     # reversed order (for call to 'concatString' library function). Otherwise we need to
     # evaluate them in the right order and then reverse them on stack.
     if isinstance(self.children[0], LiteralCode) or isinstance(
             self.children[1], LiteralCode):
         self.add_child_by_idx(1)
         self.add_child_by_idx(0)
     else:
         self.add_child_by_idx(0)
         self.add_child_by_idx(1)
         self.add_instr(CC.POP, dest=Loc.reg('a'))
         self.add_instr(CC.POP, dest=Loc.reg('d'))
         self.add_instr(CC.PUSH, src=Loc.reg('a'))
         self.add_instr(CC.PUSH, src=Loc.reg('d'))
     self.add_instr(CC.CALL, label=Builtins.STRCAT_FUNCTION)
     self.add_instr(CC.ADD,
                    lhs=Loc.const(2 * CC.var_size),
                    rhs=Loc.reg('top'))
     self.add_instr(CC.PUSH, src=Loc.reg('a'))
Example #22
0
 def _add_nonzero_inits(self, cls):
     """ Init all class fields which have nonzero values. Start with subclass members, as they
     are first in the memory block. """
     if cls.base:
         # Init the superclass part. `instantiating_class` can remain the same, as the base
         # pointer is the same and the offsets are calculated properly in get_member_idx().
         self._add_nonzero_inits(cls.base)
     for decl in cls.decls():
         dtype = decl.decl_type.type
         for item in decl.items:
             expr_code = ExprFactory(item.expr)
             if expr_code.is_constant() and expr_code.value == 0:
                 continue  # Skip assignments with 0, as the memory is zero-filled.
             self.add_child_code(expr_code)  # Evaluate the assigned value.
             self.add_instr(CC.POP, dest=Loc.reg('d'))
             # Compute member address -- object base pointer is still in %ebx.
             m_offset = cls.get_member_idx(item.name) * CC.var_size
             self.add_instr(CC.MOV,
                            src=Loc.const(m_offset),
                            dest=Loc.reg('a'))
             self.add_instr(CC.ADD, lhs=Loc.reg('b'), rhs=Loc.reg('a'))
             self.add_instr(CC.MOV,
                            src=Loc.reg('d'),
                            dest=Loc.mem(Loc.reg_a))
Example #23
0
 def _gen_code_as_value(self, addr_only=False, **kwargs):
     # If addr_only is set, don't push the value, stop after its location is computed.
     for case in switch(self.type.type):
         if case(LP.IDENT):
             if addr_only:
                 return
             # A variable referenced while instantiating is surely a class member.
             if NewCode.instantiating_class:
                 NewCode.new_member_val(self, self.value, Loc.reg('a'))
             else:
                 self.add_instr(CC.MOV,
                                src=Loc.sym(self.tree.symbol(self.value)),
                                dest=Loc.reg('a'))
             self.add_instr(CC.PUSH, src=Loc.reg('a'))
             break
         if case(LP.ATTR):
             if self.tree.obj_type.type == LP.ARRAY and self.value == Builtins.LENGTH:
                 # Array length is stored in first element of its memory block.
                 self.add_child_by_idx(0)
                 self.add_instr(CC.POP, dest=Loc.reg('d'))
                 if addr_only:
                     return
                 self.add_instr(CC.MOV,
                                src=Loc.mem(Loc.reg_d),
                                dest=Loc.reg('d'))
                 self.add_instr(CC.PUSH, src=Loc.reg('d'))
             elif self.tree.obj_type.type == LP.OBJECT:
                 self._gen_code_load_member(dest_reg=Loc.reg('d'),
                                            addr_only=addr_only)
                 if addr_only:
                     return
                 self.add_instr(CC.PUSH, src=Loc.reg('d'))
             else:
                 raise InternalError('invalid attr `%s` for type `%s`' %
                                     (self.value, str(self.tree.obj_type)))
             break
         if case(LP.ELEM):
             self._gen_code_load_array_elem(dest_reg=Loc.reg('d'),
                                            addr_only=addr_only)
             if addr_only:
                 return
             self.add_instr(CC.PUSH, src=Loc.reg('d'))
             break
         if case():
             raise InternalError('invalid variable type %s' %
                                 str(self.type.type))
Example #24
0
 def _gen_code_intop(self):
     self.add_child_by_idx(0)
     self.add_child_by_idx(1)
     for case in switch(self.type.type):
         if case(LP.PLUS, LP.MINUS, LP.MULT):
             op = {
                 LP.PLUS: CC.ADD,
                 LP.MINUS: CC.SUB,
                 LP.MULT: CC.MUL
             }[self.type.type.id]
             self.add_instr(CC.POP, dest=Loc.reg('d'))
             self.add_instr(CC.POP, dest=Loc.reg('a'))
             self.add_instr(op, lhs=Loc.reg('d'), rhs=Loc.reg('a'))
             self.add_instr(CC.PUSH, src=Loc.reg('a'))
             break
         if case(LP.DIV, LP.MOD):
             self.add_instr(CC.POP, dest=Loc.reg('c'))
             self.add_instr(CC.POP, dest=Loc.reg('a'))
             # quotient in eax, remainder in edx
             result = {
                 LP.DIV: Loc.reg('a'),
                 LP.MOD: Loc.reg('d')
             }[self.type.type.id]
             code = {LP.DIV: CC.DIV, LP.MOD: CC.MOD}[self.type.type.id]
             self.add_instr(code,
                            lhs=Loc.reg('c'),
                            rhs=Loc.reg('a'),
                            dest=result)
             self.add_instr(CC.PUSH, src=result)
             break
         if case():
             raise InternalError('wrong int op type %s' % str(self.type))
Example #25
0
class LatteOptimizer(object):

    INF_PASSES = 100  # number of passes considered 'sufficiently infinite'
    # Codes that 'don't do anything', e.g. if there are no other codes between a jump and its
    # label, the jump can be safely deleted.
    NOOPS = AnyOf(CC.LABEL, CC.EMPTY, CC.DELETED, CC.SCOPE, CC.ENDSCOPE)
    # Codes that do an operation but no flow control or stack operations, so in [push, <op>, pop]
    # push and pop can be combined into mov if the operation's arguments are unrelated.
    NO_STACK_OPS = AnyOf(CC.MOV, CC.ADD, CC.SUB, CC.MUL, CC.NEG)
    # All locations considered constant.
    CONST_LOCS = AnyOf(Loc.const(Loc.ANY), Loc.stringlit(Loc.ANY))
    # Binary operators that have a result.
    BIN_OPS = AnyOf(CC.ADD, CC.SUB, CC.MUL, CC.DIV, CC.MOD, CC.BOOL_OP)
    # Const matcher for constant propagation.
    CONST_OR_REG = AnyOf(Loc.const(Loc.ANY), Loc.reg(Loc.ANY))

    def __init__(self, codes):
        self.codes = codes
        self.matcher = CodeMatcher(self)
        self.labels = {}  # Map from label name to position in codes.
        self.jumps = {}  # Map from label name to positions which jump to it.
        self.print_codes()
        self.scan_labels()
        self.opt_counters = {}

    def run_all(self, max_passes):
        """ Optimizer main function, which runs the implemented optimizations on the codes. """
        if max_passes == 0:
            debug('optimizer disabled')
            return
        self.run_opt(self.del_unused_results)  # no need to run this one multiple times
        for count in xrange(max_passes):
            debug('------------- global optimizer pass %d (of max %d) -------------' % (
                count+1, max_passes))
            sum_counters = sum(self.opt_counters.values())
            self.run_opt(self.del_jumps_to_next, max_passes=self.INF_PASSES)
            self.run_opt(self.del_unused_labels)
            self.run_opt(self.reduce_push_pop, max_passes=self.INF_PASSES)
            self.run_opt(self.propagate_constants)
            #self.run_opt(self.clear_deleted_codes)
            if sum(self.opt_counters.values()) == sum_counters:
                debug('------------------ all optimizations returned finish -----------------')
                break
        # TODO don't assign dead vars
        # TODO free string memory
        # TODO [mov mem regA, mov regA regB]
        # TODO [mov regA memX, mov memX regB]
        if Flags.optimizer_summary:
            Status.add_note(LatteError('optimizer case counters:'))
            for name, count in self.opt_counters.iteritems():
                Status.add_note(LatteError(name + ': ' + str(count)))

    def run_opt(self, function, max_passes=1, **kwargs):
        """ A function to run a single optimization, possibly with many passes in a row.

        An optimization is ran again if it returned a non-zero value and it has been started less
        than max_passes times.
        Other keyword arguments are passed to the optimization function. """
        for count in xrange(max_passes):
            name = function.__name__
            debug('--- OPT:', name, 'pass %d (of max %d)' % (count+1, max_passes))
            ret = function(**kwargs)
            # sum the optimization results, assuming value returned is number of cases resolved
            self.opt_counters[name] = self.opt_counters.get(name, 0) + ret
            debug('--- OPT:', name, 'returned', ret or 'finish')
            if not ret:
                break

    def print_codes(self):
        """ Debug: print the current codes list. """
        for i in xrange(len(self.codes)):
            code = self.codes[i]
            if (code['type'] == CC.EMPTY):
                debug('\n', no_hdr=True)
                continue
            d = code.copy()
            del d['type']
            debug('[%d]' % i, CC._code_name(code['type']) + '\t' + CC._str_code(d), no_hdr=True)

    def scan_labels(self):
        """ A function that indexes the labels and jumps in the given codes. """
        self.labels = {}
        self.jumps = {}
        for i in self.matcher.code_iter():
            code = self.codes[i]
            if self.matcher.match(code, type=CC.LABEL):
                self.labels[code['label']] = i
            else:
                if self.matcher.match(code, type=AnyOf(CC.JUMP, CC.IF_JUMP, CC.CALL)):
                    # functions begin with labels -- collect their calls, it might be useful later
                    label = code['label']
                elif self.matcher.match(code, type=AnyOf(CC.PUSH, CC.MOV),
                                        src=Loc.stringlit(Loc.ANY)):
                    # collect uses of string constants
                    label = code['src'].value
                else:
                    continue
                if label in self.jumps:
                    self.jumps[label].append(i)
                else:
                    self.jumps[label] = [i]
        debug('--- label maps ---')
        for label in self.labels:
            debug('[%d]' % self.labels[label], label, ': ', str(self.jumps.get(label, None)))

    def find_jump_before(self, label, pos):
        """ Return position of last code jumping to a label *before* position pos. """
        ret = bisect_left(self.jumps.get(label, []), pos) - 1
        return self.jumps[label][ret] if ret >= 0 else None

    def mark_deleted(self, pos_or_iter, **kwargs):
        """ Mark code for deletion, at a single index or whole iterable. """
        if isinstance(pos_or_iter, int):
            self.codes[pos_or_iter]['_type'] = CC._code_name(self.codes[pos_or_iter]['type'])
            self.codes[pos_or_iter].update(kwargs)  # just for debugging, to put more indicators
            self.codes[pos_or_iter]['type'] = CC.DELETED
        else:
            for pos in pos_or_iter:
                self.mark_deleted(pos)

    def del_unused_results(self, **kwargs):
        """ Find the stack pops that are marked as popping an unused result, and delete them along
        with the push that causes them. """
        result = 0
        for pos in self.matcher.code_iter():
            if self.matcher.match_at(pos, type=CC.ADD, comment=CC.S_UNUSED_RESULT):
                # Found an unused result, trace back to all pushes that might lead to it.
                # We don't need to trace arbitrary jump sequences that lead there, as for now
                # the sequence should be either just [push, pop] or, for bool expr evaluation:
                # [push 1, jump after, ..., push 0, label after, pop]
                debug('unused result at', pos)
                self.mark_deleted(pos)
                push_off = -1
                # first, try the two-push case: find the one before the jump
                if self.matcher.match_at(pos-1, type=CC.LABEL):
                    debug('   found label', self.codes[pos-1]['label'], 'before pop')
                    push_off = -2
                    jump_pos = self.find_jump_before(self.codes[pos-1]['label'], pos)
                    if jump_pos is None:
                        debug('   jump to label not found, ignoring')
                    elif self.matcher.match_at(jump_pos-1, type=CC.PUSH):
                        debug('   found jumped push at', jump_pos-1)
                        self.mark_deleted(jump_pos-1)
                        result += 1
                    else:
                        debug('   code at', jump_pos-1, 'is not a push, ignoring')
                # find the other push (or the only one if there was no label)
                if self.matcher.match_at(pos+push_off, type=CC.PUSH):
                    debug('   found push at', pos+push_off)
                    self.mark_deleted(pos+push_off)
                    result += 1
                else:
                    debug('   code at', pos+push_off, 'is not a push, ignoring')
        return result

    def clear_deleted_codes(self, **kwargs):
        """ Really delete the codes marked DELETED. """
        old_len = len(self.codes)
        self.codes = filter(lambda code: not self.matcher.match(code, type=CC.DELETED), self.codes)
        debug('pruned %d deleted codes' % (old_len - len(self.codes)))
        # label maps need to be recalculated after deleting
        self.scan_labels()
        return old_len - len(self.codes)

    def del_jumps_to_next(self, **kwargs):
        """ Delete jump codes that can be safely omitted (passed through). If the jump is
        conditional, the comparision itself is also deleted. """
        result = 0
        start_pos = 0
        for pos in self.matcher.gen_next(start_pos, type=AnyOf(CC.JUMP, CC.IF_JUMP)):
            start_pos = pos
            label = self.codes[pos]['label']
            # check if there is any 'non-noop' code between the jump and its label
            # (keep in mind that the label may be before the jump)
            start = min(pos, self.labels[label])
            stop = max(pos, self.labels[label])
            try:
                self.matcher.gen_next(start, stop, negate=True, type=self.NOOPS).next()
            except StopIteration:
                debug('skipping', self.codes[pos].get('op', 'jmp'), 'to', label, 'at', pos)
                result += 1
                self.mark_deleted(pos)
        return result

    def del_unused_labels(self, **kwargs):
        """ Delete unnecessary labels (ones without jumps to them). """
        # First, rescan labels to remove deleted jumps from map
        self.scan_labels()
        result = 0
        for label, pos in self.labels.iteritems():
            # don't consider labels starting function -- unused function should already be deleted
            if label[0] == '.' and label not in self.jumps:
                debug('deleting unused label', label, 'at', pos)
                self.mark_deleted(pos)
                result += 1
        return result

    def reduce_push_pop(self, **kwargs):
        """ Optimize push-pop sequences. Detailed cases:

        * delete sequences [push X, pop X]
        * combine [push X, pop Y] into [mov X Y] -- the pop destination is always a register """
        result = 0
        for indexes in self.matcher.gen_seq([code_spec(type=CC.PUSH),
                                             code_spec(type=CC.POP)]):
            debug('push-pop sequence:', str(indexes))
            result += self._do_push_pop_reduction(indexes)
        for indexes in self.matcher.gen_seq([code_spec(type=CC.PUSH),
                                             code_spec(type=self.NO_STACK_OPS),
                                             code_spec(type=CC.POP)]):
            debug('push-op-pop sequence:', str(indexes))
            p_push, p_op, p_pop = indexes
            src, dest = self.codes[p_push]['src'], self.codes[p_pop]['dest']
            # do the reduction only if op's arguments do nothing to src and dest locations
            if ((src.is_constant() or src not in self.codes[p_op].values()) and
                    dest not in self.codes[p_op].values()):
                result += self._do_push_pop_reduction([p_push, p_pop])
        return result

    def _do_push_pop_reduction(self, indexes):
        """ Child function of reduce_push_pop, that does the actual reduction. Separate function
        just for code reuse. `indexes` should be a two-element position list."""
        p_push, p_pop = indexes
        src, dest = self.codes[p_push]['src'], self.codes[p_pop]['dest']
        if src == dest:
            debug('   deleting push-pop with same attrs at', str(indexes))
            self.mark_deleted(indexes)
            return 1
        else:
            debug('   combining [push, pop] to mov at', str(indexes))
            self.mark_deleted(p_push, _type='[push,pop]', dest=dest)
            self.codes[p_pop] = CC.mkcode(CC.MOV, src=src, dest=dest,
                                          comment='combined from [push,pop]')
            return 1
        return 0

    def propagate_constants(self, **kwargs):
        """ Propagate constants moved to registers to the place where they're used. """
        # Note: some codes, e.g. division, require arguments in registers.
        # After deleting a [mov const, reg], hold the const value in pocket and paste it into all
        # reg occurences until it's assigned something else.
        # Note: remember to empty your pockets when jumping :) (and calling)
        # In particular: before a label or jump assign the value to the register anyway.
        # TODO this can be sometimes avoided if we consider the whole jump graph and live vars...
        # Also remember that division requires both arguments in registers.
        # For result, count when a register is replaced with a value from pocket.
        # TODO another level: propagate if_jumps if both operands are constants.
        self.prop_consts = 0
        self.pocket = {}
        self.apply_needed = False
        for pos in self.matcher.code_iter():
            code = self.codes[pos]
            if len(self.pocket):
                # For readability, the subsequent cases of constant propagation are in separate
                # functions. Any of those functions can return True to indicate that the code no
                # longer needs to be considered for propagation (e.g. was deleted), and the
                # iteration will step over to the next code.
                # TODO two const in IF_JUMP
                if (self._cp_two_const_operator(pos, code) or
                        self._cp_apply_value_from_pocket(pos, code) or
                        self._cp_drop_marked_registers(pos, code) or
                        self._cp_overwrite_pocket_values(pos, code) or
                        self._cp_empty_pocket_if_needed(pos, code)):
                    continue
            self._cp_save_to_pocket(pos, code)
        # Turn the pocket indicators into assignments
        if self.apply_needed:
            self._insert_apply_pockets()
        return self.prop_consts

    def _add_to_apply_pocket(self, pos, a_pocket):
        debug('  _add_to_apply_pocket at %d:' % pos)
        for reg, val in a_pocket.iteritems():
            debug('\t', reg.value, '->', val.value)
        if 'apply_pocket' in self.codes[pos]:
            self.codes[pos]['apply_pocket'].update(a_pocket)
        else:
            self.codes[pos]['apply_pocket'] = a_pocket.copy()
        self.apply_needed = True

    def _insert_apply_pockets(self):
        """ Child function of propagate_constants, used to insert assignments before pocket
        indicators, because they can't be inserted before when iterating through the list. """
        start_pos = 0
        debug('APPLY POCKETS')
        for pos in self.matcher.gen_next(start_pos, attrlist=['apply_pocket']):
            # Generate the move instructions and insert them inside codes list.
            moves = map(lambda (reg, val): CC.mkcode(CC.MOV, src=val, dest=reg,
                                                     comment=CC.S_PROPAGATED),
                        self.codes[pos]['apply_pocket'].iteritems())
            debug('apply pocket: insert %d moves at %d' % (len(moves), pos))
            del self.codes[pos]['apply_pocket']
            self.codes[pos:pos] = moves
            start_pos = pos + len(moves) - 1
        # rebuild the jump maps
        self.scan_labels()

    def _cp_two_const_operator(self, pos, code):
        """ Constant propagation for operators: if both operands are consts, calculate the result
        and propagate it instead."""
        # 'lhs' and 'rhs' are both registers with values stored in pocket, or only 'rhs' is and
        # 'lhs' is an actual constant (e.g. propagated there in previous loop iteration, when 'rhs'
        # was not yet propagated).
        if (self.matcher.match(
                code, type=self.BIN_OPS, lhs=self.CONST_OR_REG, rhs=Loc.reg(Loc.ANY)) and
                (code['lhs'].is_constant() or code['lhs'] in self.pocket) and
                code['rhs'] in self.pocket):
            debug('two-const operator %s at %d' % (CC._code_name(code['type']), pos))
            if code['type'] == CC.BOOL_OP:
                cmp_fun = {
                    'sete': operator.eq, 'setne': operator.ne,
                    'setg': operator.gt, 'setge': operator.ge,
                    'setl': operator.lt, 'setle': operator.le,
                }[code['op']]
                # The operator.* bool functions return bool, we want an int to push.
                op_fun = lambda x, y: int(cmp_fun(x, y))
            else:
                op_fun = {
                    CC.ADD: operator.add, CC.SUB: operator.sub,
                    CC.MUL: operator.mul, CC.DIV: operator.floordiv, CC.MOD: c_modulo
                }[code['type']]
            arg1 = int(self.pocket[code['rhs']].value)
            if code['lhs'].is_constant():
                arg2 = int(code['lhs'].value)
            else:
                arg2 = int(self.pocket[code['lhs']].value)
            res_val = op_fun(arg1, arg2)
            debug('   -> args %d, %d res %d' % (arg1, arg2, res_val))
            res_reg = code['dest'] if code['type'] in [CC.DIV, CC.MOD] else code['rhs']
            # Delete and re-insert value, to maintain hash properties.
            if res_reg in self.pocket:
                del self.pocket[res_reg]
            self.pocket[res_reg] = Loc.const(res_val)
            self.mark_deleted(pos, comment=CC.S_PROPAGATED, value=res_val)
            self.prop_consts += 1
            return True

    def _cp_apply_value_from_pocket(self, pos, code):
        """ Constant propagation: apply values from pocket. To be used before assigning new values,
        in case of e.g. [mov $1 %eax, mov %eax %edx]."""
        # Only attrs 'src', 'lhs' can carry a const value.
        for attr in [a for a in ['src', 'lhs'] if a in code.keys()]:
            if not code[attr].is_reg() or code[attr] not in self.pocket:
                continue
            debug('attr %s is reg %s at %d, applying %s from pocket' % (
                attr, code[attr].value, pos, self.pocket[code[attr]].value))
            code[attr] = self.pocket[code[attr]]
            self.prop_consts += 1

    def _cp_drop_marked_registers(self, pos, code):
        """ Constant propagation: drop each register passed in drop_reg* attribute -- to be used
        when a code uses that register indirectly or just needs the value to be in register. """
        to_apply = {}
        for attr, value in code.iteritems():
            if attr.startswith('drop_reg') and value in self.pocket:
                to_apply[value] = self.pocket[value]
        if len(to_apply):
            self._add_to_apply_pocket(pos, to_apply)

    def _cp_overwrite_pocket_values(self, pos, code):
        """ Constant propagation: if a register is being assigned, delete its entry in pocket."""
        # Only attrs modifying their location are 'dest', 'rhs'.
        # Note: rhs needs to be reviewed first, otherwise if rhs and dest are the same we
        # would forget the pocket value at dest, while it is still needed by rhs.
        for attr in [a for a in ['rhs', 'dest'] if a in code.keys()]:
            if not code[attr].is_reg() or code[attr] not in self.pocket:
                continue
            value = self.pocket[code[attr]].value
            # NEG is a special case here: 'dest' is both source and destination -- but the
            # value remains constant, so remove the code and adjust value in pocket.
            if code['type'] == CC.NEG:
                new_value = value[1:] if '-' in value else '-' + value
                debug('NEG reg %s with const at %d, adjusting pocket value to %s' % (
                    code[attr].value, pos, new_value))
                # Delete and re-insert value, to maintain hash properties.
                loc = self.pocket[code[attr]]
                del self.pocket[code[attr]]
                loc.value = new_value
                self.pocket[code[attr]] = loc
                self.mark_deleted(pos, comment=CC.S_PROPAGATED)
                self.prop_consts += 1
                return True
            else:  # otherwise, just forget the register's value from pocket.
                # but the right operand for cmpl also needs to be dropped.
                debug('reg %s is %s at %d, forgetting from pocket' % (
                    code[attr].value, attr, pos))
                if attr == 'rhs':
                    debug('   ^ but applying reg %s' % code['rhs'].value)
                    self._add_to_apply_pocket(pos, {code['rhs']: self.pocket[code['rhs']]})
                del self.pocket[code[attr]]
        # Special case: division -- invalidate %eax and %edx values in pocket as idivl stores
        # results there.
        if code['type'] in [CC.DIV, CC.MOD]:
            for reg in [Loc.reg('a'), Loc.reg('d')]:
                if reg in self.pocket:
                    del self.pocket[reg]

    def _cp_empty_pocket_if_needed(self, pos, code):
        """ Constant propagation: empty the pocket in case of calls or jumps. """
        # [1] On function call, empty the pocket.
        if len(self.pocket) and code['type'] == CC.CALL:
            debug('function call at %d, emptying pocket' % pos)
            self.pocket = {}
        # [2] On function exit, drop %eax and empty pocket.
        if len(self.pocket) and code['type'] == CC.ENDFUNC:
            if Loc.reg('a') in self.pocket.keys():
                self._add_to_apply_pocket(pos, {Loc.reg('a'): self.pocket[Loc.reg('a')]})
                debug('ENDFUNC at %d, dropping reg a' % pos)
            self.pocket = {}
        # [3] On jump instructions (both in-/out-bound) assign the pocket values anyway.
        if len(self.pocket) and code['type'] in [CC.JUMP, CC.IF_JUMP, CC.LABEL]:
            debug('%s at %d, reassigning pocket values' % (CC._code_name(code['type']),
                                                           pos))
            # We can't insert into a list while iterating, so save the pocket for now
            # TODO we could later skip at least some of pocket's values, if we check that
            # a value is the same at label and all jumps to it, or the register is not live.
            self._add_to_apply_pocket(pos, self.pocket.copy())
            self.pocket = {}

    def _cp_save_to_pocket(self, pos, code):
        """ Constant propagation: when moving const to a register, stow it in the pocket instead."""
        if (self.matcher.match(code, type=CC.MOV, src=self.CONST_LOCS,
                               dest=Loc.reg(Loc.ANY)) and
                not self.matcher.match(code, comment=CC.S_PROPAGATED)):
            debug('mov const %s -> reg %s found at %d' % (code['src'].value,
                                                          code['dest'].value, pos))
            self.pocket[code['dest']] = code['src']
            self.mark_deleted(pos, comment=CC.S_PROPAGATED)
Example #26
0
 def gen_code(self, **kwargs):
     if self.fsym.cls:  # calling a method, the called expr must be an ATTR
         for case in switch(self.children[0].type.type):
             if case(LP.ATTR):
                 obj_expr = self.children[0].children[0]
                 break
             if case(LP.IDENT):
                 obj_expr = None
                 break
             if case():
                 raise InternalError('expr of invalid type `%s` to call' %
                                     str(self.children[0].type))
     else:
         obj_expr = None
     # [1] Compute memory usage for arguments.
     argmem = CC.var_size * self.arg_count
     total_argmem = argmem + (CC.var_size if obj_expr else 0)
     # [2] Push arguments.
     # Arguments need to be pushed in reverse order, but evaluated in normal order -- hence
     # we first make enough stack space for all of them and move them in the right place after
     # evaluation.
     if self.arg_count > 1:
         self.add_instr(CC.SUB, lhs=Loc.const(argmem), rhs=Loc.reg('top'))
         for i in xrange(len(self.children) -
                         1):  # One less -- [0] is the function!
             self.add_child_by_idx(i + 1)  # Leaves the value on stack.
             self.add_instr(CC.POP, dest=Loc.reg('a'))
             # i-th argument should be placed in 4*i(%esp)
             self.add_instr(CC.MOV,
                            src=Loc.reg('a'),
                            dest=Loc.mem(Loc.top, i * CC.var_size))
     elif self.arg_count > 0:
         self.add_child_by_idx(
             1)  # With only one argument we can just push it on stack.
     # [2a] When calling a method, push reference to `self` (first function's argument).
     if obj_expr:
         self.add_child_code(obj_expr)
     # [3] Call and pop arguments.
     self.add_instr(CC.CALL, label=self.fsym.call_name())
     if total_argmem > 0:
         self.add_instr(CC.ADD,
                        lhs=Loc.const(total_argmem),
                        rhs=Loc.reg('top'))
     # [4] finish depending on how we were called:
     if self.has_jump_codes(kwargs):
         if self.fsym.ret_type.type != LP.BOOLEAN:
             raise InternalError(
                 'jump-expr codes for non-bool function %s %s at %s!' %
                 (self.fsym.full_name(), str(self.fsym), self.tree.pos))
         # [4a] bool function as part of condition evaluation -- jump basing on the result
         # note: comparing with 0, so on equality jump to false!
         self.add_instr(CC.IF_JUMP,
                        lhs=Loc.const(0),
                        rhs=Loc.reg('a'),
                        op='je',
                        label=kwargs['on_false'])
         self.add_instr(CC.JUMP, label=kwargs['on_true'])
     else:
         # [4b] normal expression -- push the return value on stack if needed
         if self.fsym.ret_type.type != LP.VOID:
             self.add_instr(CC.PUSH, src=Loc.reg('a'))
         self.check_unused_result()
Example #27
0
 def gen_code(self, **kwargs):
     for case in switch(self.value_type.type):
         if case(LP.ARRAY):
             self.add_child_by_idx(0)  # evaluate array size
             self.add_instr(CC.POP, dest=Loc.reg('a'))
             self.add_instr(
                 CC.PUSH,
                 src=Loc.reg('a'))  # the value needs to be saved later
             # Convention: array of size N is a block of memory for (N+1) variables, and the
             # first variable will contain array's size ( = N)
             self.add_instr(CC.LEA,
                            src=Loc.mem('',
                                        offset=CC.var_size,
                                        idx=Loc.reg_a,
                                        mult=CC.var_size),
                            dest=Loc.reg('a'),
                            drop_reg1=Loc.reg('a'))  # calc memory size
             init_value = self.default_asm_value(
                 self.value_type.type.subtype)
             debug('init value for %s:' % self.value_type, str(init_value))
             self.add_instr(CC.PUSH, src=init_value)
             self.add_instr(CC.PUSH, src=Loc.reg('a'))
             self.add_instr(CC.CALL, label=Builtins.MALLOC_FUNCTION)
             self.add_instr(CC.ADD,
                            lhs=Loc.const(2 * CC.var_size),
                            rhs=Loc.reg('top'))
             # Write the array size into the first index.
             self.add_instr(
                 CC.POP,
                 dest=Loc.reg('d'))  # load the array size saved earlier
             self.add_instr(CC.MOV,
                            src=Loc.reg('d'),
                            dest=Loc.mem(Loc.reg_a))
             # Push the memory pointer as expression result.
             self.add_instr(CC.PUSH, src=Loc.reg('a'))
             break
         if case(LP.OBJECT):
             # Allocate required space.
             self.add_instr(CC.PUSH,
                            src=Loc.const(0))  # Fill the memory with 0.
             # Allocate space for the whole object, along with superclass members.
             self.add_instr(CC.PUSH,
                            src=Loc.const(self.cls.total_var_count *
                                          CC.var_size))
             self.add_instr(CC.CALL, label=Builtins.MALLOC_FUNCTION)
             self.add_instr(CC.ADD,
                            lhs=Loc.const(2 * CC.var_size),
                            rhs=Loc.reg('top'))
             if self.cls.has_nonzero_initializers():
                 # Save %ebx to store the class base pointer there.
                 self.add_instr(CC.PUSH, src=Loc.reg('b'))
                 self.add_instr(CC.MOV, src=Loc.reg('a'), dest=Loc.reg('b'))
                 # Assign the non-0 default values and specified initializations.
                 old_instantiating_class = NewCode.instantiating_class
                 NewCode.instantiating_class = (self.cls, Loc.reg('b'))
                 self._add_nonzero_inits(self.cls)
                 NewCode.instantiating_class = old_instantiating_class
                 # Restore %ebx and push the object memory pointer as expression result.
                 self.add_instr(CC.MOV, src=Loc.reg('b'), dest=Loc.reg('a'))
                 self.add_instr(CC.POP, dest=Loc.reg('b'))
             self.add_instr(CC.PUSH, src=Loc.reg('a'))
             break
         if case():
             raise InternalError('invalid type for new operator: ' +
                                 str(self.value_type))
     self.check_unused_result()