diff --git a/artiq/compiler/ir.py b/artiq/compiler/ir.py index c7a40235a..dd89fe3ec 100644 --- a/artiq/compiler/ir.py +++ b/artiq/compiler/ir.py @@ -647,38 +647,6 @@ class SetLocal(Instruction): def value(self): return self.operands[1] -class GetConstructor(Instruction): - """ - An intruction that loads a local variable with the given type - from an environment, possibly going through multiple levels of indirection. - - :ivar var_name: (string) variable name - """ - - """ - :param env: (:class:`Value`) local environment - :param var_name: (string) local variable name - :param var_type: (:class:`types.Type`) local variable type - """ - def __init__(self, env, var_name, var_type, name=""): - assert isinstance(env, Value) - assert isinstance(env.type, TEnvironment) - assert isinstance(var_name, str) - assert isinstance(var_type, types.Type) - super().__init__([env], var_type, name) - self.var_name = var_name - - def copy(self, mapper): - self_copy = super().copy(mapper) - self_copy.var_name = self.var_name - return self_copy - - def opcode(self): - return "getconstructor({})".format(repr(self.var_name)) - - def environment(self): - return self.operands[0] - class GetAttr(Instruction): """ An intruction that loads an attribute from an object, @@ -697,8 +665,12 @@ class GetAttr(Instruction): if isinstance(attr, int): assert isinstance(obj.type, types.TTuple) typ = obj.type.elts[attr] - else: + elif attr in obj.type.attributes: typ = obj.type.attributes[attr] + else: + typ = obj.type.constructor.attributes[attr] + if types.is_function(typ): + typ = types.TMethod(obj.type, typ) super().__init__([obj], typ, name) self.attr = attr diff --git a/artiq/compiler/transforms/artiq_ir_generator.py b/artiq/compiler/transforms/artiq_ir_generator.py index 6d97f0d2b..90b8e7bd0 100644 --- a/artiq/compiler/transforms/artiq_ir_generator.py +++ b/artiq/compiler/transforms/artiq_ir_generator.py @@ -806,8 +806,8 @@ class ARTIQIRGenerator(algorithm.Visitor): self.append(ir.Builtin("watchdog_clear", [watchdog_id], builtins.TNone()))) else: # user-defined context manager context_mgr = self.visit(context_expr_node) - enter_fn = self._get_attribute(context_mgr, '__enter__') - exit_fn = self._get_attribute(context_mgr, '__exit__') + enter_fn = self.append(ir.GetAttr(context_mgr, '__enter__')) + exit_fn = self.append(ir.GetAttr(context_mgr, '__exit__')) try: self.current_assign = self._user_call(enter_fn, [], {}) @@ -900,26 +900,6 @@ class ARTIQIRGenerator(algorithm.Visitor): else: return self._set_local(node.id, self.current_assign) - def _get_attribute(self, obj, attr_name): - if attr_name not in obj.type.find().attributes: - # A class attribute. Get the constructor (class object) and - # extract the attribute from it. - constr_type = obj.type.constructor - constr = self.append(ir.GetConstructor(self._env_for(constr_type.name), - constr_type.name, constr_type, - name="constructor." + constr_type.name)) - - if types.is_function(constr.type.attributes[attr_name]): - # A method. Construct a method object instead. - func = self.append(ir.GetAttr(constr, attr_name)) - return self.append(ir.Alloc([func, obj], - types.TMethod(obj.type, func.type))) - else: - obj = constr - - return self.append(ir.GetAttr(obj, attr_name, - name="{}.{}".format(_readable_name(obj), attr_name))) - def visit_AttributeT(self, node): try: old_assign, self.current_assign = self.current_assign, None @@ -928,7 +908,8 @@ class ARTIQIRGenerator(algorithm.Visitor): self.current_assign = old_assign if self.current_assign is None: - return self._get_attribute(obj, node.attr) + return self.append(ir.GetAttr(obj, node.attr, + name="{}.{}".format(_readable_name(obj), node.attr))) elif types.is_rpc_function(self.current_assign.type): # RPC functions are just type-level markers return self.append(ir.Builtin("nop", [], builtins.TNone())) diff --git a/artiq/compiler/transforms/dead_code_eliminator.py b/artiq/compiler/transforms/dead_code_eliminator.py index 23eaf57bb..eb6d1a191 100644 --- a/artiq/compiler/transforms/dead_code_eliminator.py +++ b/artiq/compiler/transforms/dead_code_eliminator.py @@ -32,9 +32,8 @@ class DeadCodeEliminator: # a diagnostic for reads of uninitialized locals, and # it also has to run after the interleaver, but interleaver # doesn't like to work with IR before DCE. - if isinstance(insn, (ir.Phi, ir.Alloc, ir.GetConstructor, - ir.GetAttr, ir.GetElem, ir.Coerce, ir.Arith, - ir.Compare, ir.Closure, ir.Select, ir.Quote)) \ + if isinstance(insn, (ir.Phi, ir.Alloc, ir.GetAttr, ir.GetElem, ir.Coerce, + ir.Arith, ir.Compare, ir.Closure, ir.Select, ir.Quote)) \ and not any(insn.uses): insn.erase() modified = True diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index e830235c1..4b06970bc 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -646,13 +646,6 @@ class LLVMIRGenerator: llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name) return self.llbuilder.load(llptr) - def process_GetConstructor(self, insn): - env = insn.environment() - llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name, insn.type) - llconstr = self.llbuilder.load(llptr) - llconstr.metadata['invariant.load'] = self.empty_metadata - return llconstr - def process_SetLocal(self, insn): env = insn.environment() llvalue = self.map(insn.value()) @@ -670,26 +663,67 @@ class LLVMIRGenerator: llvalue = self.llbuilder.bitcast(llvalue, llptr.type.pointee) return self.llbuilder.store(llvalue, llptr) - def attr_index(self, insn): - return list(insn.object().type.attributes.keys()).index(insn.attr) + def attr_index(self, typ, attr): + return list(typ.attributes.keys()).index(attr) + + def get_class(self, typ): + assert types.is_constructor(typ) + name = "class.{}".format(typ.name) + if name in self.llmodule.globals: + llglobal = self.llmodule.get_global(name) + else: + llty = self.llty_of_type(typ) + llglobal = ll.GlobalVariable(self.llmodule, llty.pointee, name) + return llglobal def process_GetAttr(self, insn): - if types.is_tuple(insn.object().type): - return self.llbuilder.extract_value(self.map(insn.object()), insn.attr, + typ, attr = insn.object().type, insn.attr + if types.is_tuple(typ): + return self.llbuilder.extract_value(self.map(insn.object()), attr, name=insn.name) - elif not builtins.is_allocated(insn.object().type): - return self.llbuilder.extract_value(self.map(insn.object()), self.attr_index(insn), + elif not builtins.is_allocated(typ): + return self.llbuilder.extract_value(self.map(insn.object()), + self.attr_index(typ, attr), name=insn.name) else: - llptr = self.llbuilder.gep(self.map(insn.object()), - [self.llindex(0), self.llindex(self.attr_index(insn))], + if attr in typ.attributes: + index = self.attr_index(typ, attr) + obj = self.map(insn.object()) + elif attr in typ.constructor.attributes: + index = self.attr_index(typ.constructor, attr) + obj = self.get_class(typ.constructor) + else: + assert False + + llptr = self.llbuilder.gep(obj, [self.llindex(0), self.llindex(index)], inbounds=True, name=insn.name) - return self.llbuilder.load(llptr) + llclosure = self.llbuilder.load(llptr) + + if types.is_method(insn.type) and attr not in typ.attributes: + llmethodty = self.llty_of_type(insn.type) + llmethod = ll.Constant(llmethodty, ll.Undefined) + llmethod = self.llbuilder.insert_value(llmethod, llclosure, + self.attr_index(insn.type, '__func__')) + llmethod = self.llbuilder.insert_value(llmethod, self.map(insn.object()), + self.attr_index(insn.type, '__self__')) + return llmethod + else: + return llclosure def process_SetAttr(self, insn): - assert builtins.is_allocated(insn.object().type) - llptr = self.llbuilder.gep(self.map(insn.object()), - [self.llindex(0), self.llindex(self.attr_index(insn))], + typ, attr = insn.object().type, insn.attr + assert builtins.is_allocated(typ) + + if attr in typ.attributes: + obj = self.map(insn.object()) + elif attr in typ.constructor.attributes: + typ = typ.constructor + obj = self.get_class(typ) + else: + assert False + + llptr = self.llbuilder.gep(obj, [self.llindex(0), + self.llindex(self.attr_index(typ, attr))], inbounds=True, name=insn.name) return self.llbuilder.store(self.map(insn.value()), llptr) @@ -1161,9 +1195,7 @@ class LLVMIRGenerator: if value_id in self.llobject_map: return self.llobject_map[value_id] - global_name = "" llty = self.llty_of_type(typ) - if types.is_constructor(typ) or types.is_instance(typ): llglobal = None llfields = [] @@ -1173,8 +1205,12 @@ class LLVMIRGenerator: llfields.append(ll.Constant(lli32, objectid)) assert llglobal is None - llglobal = ll.GlobalVariable(self.llmodule, llty.pointee, - name="object.{}".format(objectid)) + if types.is_constructor(typ): + llglobal = self.get_class(typ) + else: + llglobal = ll.GlobalVariable(self.llmodule, llty.pointee, + name="object.{}".format(objectid)) + self.llobject_map[value_id] = llglobal else: llfields.append(self._quote(getattr(value, attr), typ.attributes[attr],