def vcall(name, args, ci): """ \return the IR for a non-static Babel virtual method call """ try: cdecl = ci.epv.find_method(name) epv_type = ci.epv.get_type() epv = ir.Get_struct_item(ci.obj, ir.Deref(args[0]), ir.Struct_item(epv_type, 'd_epv')) except: if False: # FIXME no_contracts and no_hooks: return ir.Call('_'.join(ci.co.qualified_name + [name]), args) else: cdecl = ci.epv.find_static_method(name) epv_type = ci.epv.get_sepv_type() epv = '_getSEPV()' # this is part of an ugly hack to make sure that self is # dereferenced as self->d_object (by setting attr of self to the # unused value of 'pure') if ci.co.is_interface() and args: _, attrs, type_, id_, arguments, doc = cdecl _, attrs0, mode0, type0, name0 = arguments[0] arguments = [ir.Arg([ir.pure], mode0, type0, name0)] + arguments[1:] cdecl = ir.Fn_decl(attrs, type_, id_, arguments, doc) return ir.Call( ir.Deref( ir.Get_struct_item( epv_type, ir.Deref(epv), ir.Struct_item(ir.Pointer_type(cdecl), 'f_' + name))), args)
def rewrite_pow(expr): coeff = expr.right base = expr.left if coeff == ir.Zero: return ir.One elif base == ir.Zero: # checking for weird errors more than anything if coeff.constant: if operator.lt(coeff.value, 0): # this isn't intended to catch all edge cases, just an obvious # one that may come up after folding msg = f"raises 0 to a negative power {expr}." raise CompilerError(msg) else: return ir.Zero elif coeff == ir.One: return expr.left elif coeff == ir.IntConst(-1): op = "/=" if expr.in_place else "/" return ir.BinOp(ir.One, expr.left, op) elif coeff == ir.IntConst(-2): op = "/=" if expr.in_place else "/" return ir.BinOp(ir.One, ir.BinOp(expr.left, expr.left, "*"), op) elif coeff == ir.IntConst(2): op = "*=" if expr.in_place else "*" return ir.BinOp(expr.left, expr.left, op) elif coeff == ir.FloatConst(0.5): return ir.Call("sqrt", (expr.left,), ()) else: return expr
def visit_call_expression(self, node, target, *args): this = self.get_id() arg_ids = [self.get_id() for _ in node.args] compute_this = linearize([self.visit(node.obj, ir.EXPR, this)]) compute_args = [ linearize([self.visit(arg, ir.EXPR, trg)]) for arg, trg in zip(node.args, arg_ids) ] if all_local(compute_this): compute_args = [compute_this] + compute_args compute_this = [] compute_args.sort(key=total_complexity, reverse=True) if target == ir.EXPR: return compute_this + compute_args + [ ir.Call(node.method_owner + '.' + node.method, [this] + arg_ids, args[0]), ] else: val = self.get_id() return compute_this + compute_args + [ ir.Call(node.method_owner + '.' + node.method, [this] + arg_ids, val), ir.CJumpBool(val, args[0], args[1]), ]
def visit_Call( self, node: ast.Call) -> typing.Union[ir.ValueRef, ir.NameRef, ir.Call]: if isinstance(node.func, ast.Name): func_name = ir.NameRef(node.func.id) else: func_name = self.visit(node.func) args = tuple(self.visit(arg) for arg in node.args) keywords = tuple( (kw.arg, self.visit(kw.value)) for kw in node.keywords) # replace call should handle folding of casts call_ = ir.Call(func_name, args, keywords) # Todo: need a way to identify array creation repl = replace_call(call_) return repl
def build_function_call(ci, cdecl, static): ''' Build an IR expression that consists of the EPV lookup for a possibly virtual function call using Babel IOR. ''' #if final: # final : Final methods are the opposite of virtual. While # they may still be inherited by child classes, they # cannot be overridden. # static call #The problem with this is that e.g. C++ symbols usere different names #We should modify Babel to generate __attribute__ ((weak, alias ("__entry"))) # callee = '_'.join(['impl']+symbol_table.prefix+[ci.epv.name,Name]) if static: # static : Static methods are sometimes called "class # methods" because they are part of a class, but do not # depend on an object instance. In non-OO languages, this # means that the typical first argument of an instance is # removed. In OO languages, these are mapped directly to # an Java or C++ static method. epv_type = ci.epv.get_type() obj_type = ci.obj callee = ir.Get_struct_item( epv_type, ir.Deref(ir.Call('_getSEPV', [])), ir.Struct_item(ir.Pointer_type(cdecl), 'f_' + ir.fn_decl_id(cdecl))) else: # dynamic virtual method call epv_type = ci.epv.get_type() obj_type = ci.obj callee = ir.Deref( ir.Get_struct_item( epv_type, ir.Deref( ir.Get_struct_item(obj_type, ir.Deref('self'), ir.Struct_item(epv_type, 'd_epv'))), ir.Struct_item(ir.Pointer_type(cdecl), 'f_' + ir.fn_decl_id(cdecl)))) return callee
def generate(self, node, scope=ChapelFile()): """ This code generator is a bit unusual in that it accepts a hybrid of \c sidl and \c ir nodes. """ def gen(node): return self.generate(node, scope) def new_def(s): return scope.new_def(s) def new_header_def(s): return scope.new_header_def(s) @accepts(str, list, str) def new_scope(prefix, body, suffix='\n'): '''used for things like if, while, ...''' comp_stmt = ChapelFile(parent=scope, relative_indent=4) s = str(self.generate(body, comp_stmt)) return new_def(scope._sep.join(['', prefix + s, suffix])) @accepts(str, str, str) def new_scope1(prefix, body, suffix): '''used for things like enumerator''' return scope.new_header_def(''.join([prefix, body, suffix]) + ';') def gen_comma_sep(defs): return self.gen_in_scope( defs, scope.sub_scope(relative_indent=1, separator=',')) def gen_semicolon_sep(defs): return self.gen_in_scope( defs, scope.sub_scope(relative_indent=2, separator=';\n')) + ';' def gen_ws_sep(defs): return self.gen_in_scope( defs, scope.sub_scope(relative_indent=0, separator=' ')) def gen_dot_sep(defs): return self.gen_in_scope( defs, scope.sub_scope(relative_indent=0, separator='.')) def tmap(f, l): return tuple(map(f, l)) def gen_comment(DocComment): return gen_doc_comment(DocComment, scope) def gen_attrs(attrs): return sep_by(' ', attrs) def drop_this(args): if args <> []: if args[0] == (ir.arg, [], ir.in_, ir.void_ptr, '_this'): return args[1:] return args cbool = 'chpl_bool' int32 = 'int32_t' int64 = 'int64_t' fcomplex = '_complex64' dcomplex = '_complex128' val = self.generate_non_tuple(node, scope) if val <> None: return val with match(node): if (ir.fn_defn, Attrs, (ir.primitive_type, 'void'), Name, Args, Body, DocComment): new_scope( '%s%sproc %s(%s) {' % (gen_comment(DocComment), gen_attrs(Attrs), gen(Name), gen_comma_sep(drop_this(Args))), Body, '}') new_def('') elif (ir.fn_defn, Attrs, Type, Name, Args, Body, DocComment): new_scope( '%s%sproc %s(%s): %s {' % (gen_comment(DocComment), gen_attrs(Attrs), gen(Name), gen_comma_sep(drop_this(Args)), gen(Type)), Body, '}') new_def('') elif (ir.fn_decl, Attrs, (ir.primitive_type, 'void'), Name, Args, DocComment): attrs = 'extern ' if 'extern' in Attrs else '' new_def('%sproc %s(%s)' % (attrs, gen(Name), gen_comma_sep(Args))) elif (ir.fn_decl, Attrs, Type, Name, Args, DocComment): attrs = 'extern ' if 'extern' in Attrs else '' new_def('%sproc %s(%s): %s' % (attrs, gen(Name), gen_comma_sep(Args), gen(Type))) elif (ir.call, (ir.deref, (ir.get_struct_item, S, _, (ir.struct_item, _, Name))), Args): # We can't do a function pointer call in Chapel # Emit a C stub for that import pdb pdb.set_trace() _, s_id, _, _ = S retval_arg = generate_method_stub(scope, node, s_id) stubname = '_'.join( [epv_qname(s_id), re.sub('^f_', '', Name), 'stub']) if retval_arg: scope.pre_def(gen(ir.Var_decl(retval_arg, '_retval'))) scope.pre_def( gen( ir.Assignment('_retval', ir.Call(stubname, Args + retval_arg)))) return '_retval' else: return gen(ir.Call(stubname, Args)) elif (ir.call, (ir.get_struct_item, S, _, (ir.struct_item, _, Name)), Args): # We can't do a function pointer call in Chapel # Emit a C stub for that import pdb pdb.set_trace() _, s_id, _, _ = S retval_arg = generate_method_stub(scope, node, s_id) stubname = '_'.join( [epv_qname(s_id), re.sub('^f_', '', Name), 'stub']) if retval_arg: scope.pre_def(gen(ir.Var_decl(retval_arg, '_retval'))) scope.pre_def( gen( ir.Assignment('_retval', ir.Call(stubname, Args + retval_arg)))) return '_retval' else: return gen(ir.Call(stubname, Args)) elif (ir.new, Type, Args): return 'new %s(%s)' % (gen(Type), gen_comma_sep(Args)) elif (ir.const, Type): return '/*FIXME: CONST*/' + gen(Type) # Special handling of rarray types elif (ir.arg, Attrs, Mode, (ir.rarray, Scalar_type, Dimension, Extents), Name): (arg_mode, arg_name) = (gen(Mode), gen(Name)) # rarray type will include a new domain variable definition arg_type = '[?%s_dom] %s' % (Name, gen(Scalar_type)) return '%s %s: %s' % (arg_mode, arg_name, arg_type) elif (ir.arg, Attrs, ir.inout, Type, '_ex'): return '%s %s: opaque /*%s*/' % (gen(Mode), gen('_ex'), gen(Type)) elif (ir.arg, Attrs, Mode, Type, Name): return '%s %s: %s' % (gen(Mode), gen(Name), gen(Type)) elif (sidlir.class_, (ir.scoped_id, Prefix, Name, Ext), Extends, Implements, Invariants, Methods, DocComment): return '%sclass %s' % (gen_comment(DocComment), '.'.join( Prefix + ['_'.join(Prefix + [Name + Ext])])) elif (sidlir.array, [], [], []): print '** WARNING: deprecated rule, use a sidl__array struct instead' return 'sidl.Array(opaque, sidl__array) /* DEPRECATED */' elif (sidlir.array, Scalar_type, Dimension, Orientation): print '** WARNING: deprecated rule, use a sidl_*__array struct instead' if ir.is_scoped_id(Scalar_type): ctype = 'BaseInterface' else: ctype = Scalar_type[1] #scope.cstub.optional.add('#include <sidl_%s_IOR.h>'%ctype) return 'sidl.Array(%s, sidl_%s__array) /* DEPRECATED */' % ( gen(Scalar_type), ctype) elif (ir.pointer_type, (ir.const, (ir.primitive_type, ir.char))): return "string" elif (ir.pointer_type, (ir.primitive_type, ir.void)): return "opaque" elif (ir.pointer_type, Type): # ignore wrongfully introduced pointers # -> actually I should fix generate_method_stub instead return gen(Type) elif (ir.typedef_type, cbool): return "bool" elif (ir.typedef_type, 'sidl_bool'): return "bool" elif (ir.typedef_type, int32): return "int(32)" elif (ir.typedef_type, int64): return "int(64)" elif (ir.typedef_type, fcomplex): return "complex(64)" elif (ir.typedef_type, dcomplex): return "complex(128)" elif (ir.struct, (ir.scoped_id, Prefix, Name, Ext), Items, DocComment): #if Name[:4] =='sidl':import pdb; pdb.set_trace() #print 'prefix %s, name %s, ext %s' %(Prefix, Name, Ext) return '.'.join( list(Prefix) + ['_'.join(list(Prefix) + [Name + Ext])]) elif (ir.struct, Name, Items, DocComment): # special rule for handling SIDL arrays scalar_t = self.is_sidl_array(Name) if scalar_t: #scope.cstub.optional.add('#include <sidl_%s_IOR.h>'%ctype) return 'sidl.Array(%s, %s)' % (scalar_t, Name) if Name == 'sidl__array': # Generic array We # need to use opaque as the Chapel representation # because otherwise Chapel would start copying # arguments and thus mess with the invariant that # genarr.d_metadata == genarr return 'opaque /* array< > */' # some other struct if Name[0] == '_' and Name[1] == '_': # by convention, Chapel generates structs as # struct __foo { ... } foo; return Name[2:] return Name elif (ir.get_struct_item, _, StructName, (ir.struct_item, _, Item)): return "%s.%s" % (gen(StructName), gen(Item)) elif (ir.set_struct_item, _, (ir.deref, StructName), (ir.struct_item, _, Item), Value): return gen(StructName) + '.' + gen(Item) + ' = ' + gen(Value) elif (ir.struct_item, (sidlir.array, Scalar_type, Dimension, Extents), Name): return '%s: []%s' % (gen(Name), gen(Scalar_type)) elif (ir.type_decl, (ir.struct, Name, Items, DocComment)): itemdecls = gen_semicolon_sep( map(lambda i: ir.Var_decl(i[1], i[2]), Items)) return gen_comment(DocComment) + str( new_scope1('record %s {\n' % gen(Name), itemdecls, '\n}')) elif (ir.var_decl, Type, Name): return scope.new_header_def('var %s: %s' % (gen(Name), gen(Type))) elif (ir.var_decl_init, (ir.typedef_type, "inferred_type"), Name, Initializer): return scope.new_header_def('var %s = %s' % (gen(Name), gen(Initializer))) elif (ir.var_decl_init, Type, Name, Initializer): return scope.new_header_def( 'var %s: %s = %s' % (gen(Name), gen(Type), gen(Initializer))) elif (ir.enum, Name, Items, DocComment): return unscope(scope, Name) elif (ir.cast, Type, Expr): return '%s:%s' % (gen(Expr), gen(Type)) elif (ir.type_decl, (ir.enum, Name, Items, DocComment)): # Manually transform the Items enum_transformed = False used_states = [] for loop_item in Items: if (len(loop_item) == 3): used_states.append(loop_item[2]) else: enum_transformed = True items_to_use = Items if enum_transformed: # Explicitly initialize the enum values, since # Chapel enums start at 1 new_items = [] avail_state = 0 for loop_item in Items: if (len(loop_item) == 3): new_items.append(loop_item) else: while avail_state in used_states: avail_state = avail_state + 1 new_items.append( ir.Enumerator_value(loop_item[1], avail_state)) used_states.append(avail_state) items_to_use = new_items return new_scope1('enum %s {' % gen(Name), gen_comma_sep(items_to_use), '}') elif (ir.import_, Name): new_def('use %s;' % Name) # ignore these two -- Chapel's mode attribute handling # should automatically take care of doing (de)referencing elif (ir.deref, (ir.pointer_expr, Name)): return gen(Name) elif (ir.pointer_expr, (ir.deref, Name)): return gen(Name) elif (ir.pointer_expr, Name): return gen(Name) elif (ir.deref, Name): return gen(Name) elif (ir.float, N): return str(N) + ':real(32)' elif (ir.double, N): return str(N) + ':real(64)' elif (sidlir.custom_attribute, Id): return gen(Id) elif (sidlir.method_name, Id, Extension): return gen(Id) + gen(Extension) elif (sidlir.scoped_id, Prefix, Name, Ext): return '.'.join(Prefix + [Name]) elif (ir.typedef_type, 'sidl_enum'): return 'int(64)' else: return super(ChapelCodeGenerator, self).generate(node, scope) return scope
def generate_server_method(self, symbol_table, method, ci): """ Generate server code for a method interface. This function generates a C-callable skeleton for the method and generates a Skeleton of Chapel code complete with splicer blocks for the user to fill in. \param method s-expression of the method's SIDL declaration \param symbol_table the symbol table of the SIDL file \param ci a ClassInfo object """ def convert_arg((arg, attrs, mode, typ, name)): """ Extract name and generate argument conversions """ iorname = name return iorname, (arg, attrs, mode, typ, name) # Chapel skeleton (Method, Type, (MName, Name, Extension), Attrs, Args, Except, From, Requires, Ensures, DocComment) = method #ior_args = drop_rarray_ext_args(Args) # ci.epv.add_method((Method, Type, (MName, Name, Extension), Attrs, ior_args, # Except, From, Requires, Ensures, DocComment)) abstract = member_chk(sidlir.abstract, Attrs) static = member_chk(sidlir.static, Attrs) #final = member_chk(sidlir.static, Attrs) if abstract: # nothing to be done for an abstract function return decls = [] pre_call = [] call_args = [] post_call = [] ior_args = babel.lower_ir(symbol_table, Args, lower_scoped_ids=False) ctype = babel.lower_ir(symbol_table, Type, lower_scoped_ids=False) return_stmt = [] skel = ci.chpl_skel opt = skel.cstub.optional qname = '_'.join(ci.co.qualified_name + [Name]) callee = qname + '_impl' # Argument conversions # --------------------- # self this_arg = [] if static else [ir.Arg([], ir.in_, ir.void_ptr, '_this')] # IN map( lambda (arg, attr, mode, typ, name): conv.codegen( (strip(typ), deref(mode, typ, name)), ('chpl', strip(typ)), pre_call, skel, '_CHPL_' + name, typ), filter(incoming, ior_args)) # OUT map( lambda (arg, attr, mode, typ, name): conv.codegen( (('chpl', strip(typ)), '_CHPL_' + name), strip( typ), post_call, skel, '(*%s)' % name, typ), filter(outgoing, ior_args)) # RETURN value type conversion -- treated just like an OUT argument rarg = (ir.arg, [], ir.out, ctype, '_retval') conv.codegen((('chpl', strip(ctype)), '_CHPL__retval'), strip(ctype), post_call, skel, '_retval', ctype) chpl_rarg = conv.ir_arg_to_chpl(rarg) _, _, _, chpltype, _ = chpl_rarg if Type <> sidlir.void: decls.append(ir.Stmt(ir.Var_decl(ctype, '_retval'))) # def pointerize_struct((arg, attr, mode, typ, name)): # # FIXME: this is borked.. instead we should remove this # # _and_ the code in codegenerator that strips the # # pointer_type again # if typ[0] == ir.struct: # return (arg, attr, mode, (ir.pointer_type, typ), name) # else: return (arg, attr, mode, typ, name) # chpl_args = map(pointerize_struct, map(conv.ir_arg_to_chpl, ior_args)) chpl_args = map(conv.ir_arg_to_chpl, ior_args) # Proxy declarations / revised names of call arguments is_retval = True for (_,attrs,mode,chpl_t,name), (_,_,_,c_t,_) \ in zip([chpl_rarg]+chpl_args, [rarg]+ior_args): if chpl_t <> c_t: is_struct = False proxy_t = chpl_t if c_t[0] == ir.pointer_type and c_t[1][0] == ir.struct: # inefficient!!! opt.add(str(c_gen(ir.Type_decl(chpl_t[1])))) c_t = c_t[1] is_struct = True proxy_t = chpl_t[1] # FIXME see comment in chpl_to_ior name = '_CHPL_' + name decls.append(ir.Stmt(ir.Var_decl(proxy_t, name))) if (mode <> sidlir.in_ or is_struct # TODO this should be handled by a conversion rule or (mode == sidlir.in_ and (c_t == ir.pt_fcomplex or c_t == ir.pt_dcomplex))): name = ir.Pointer_expr(name) if name == 'self' and member_chk(ir.pure, attrs): # part of the hack for self dereferencing upcast = ( '({0}*)(((struct sidl_BaseInterface__object*)self)->d_object)' .format(c_gen(c_t[1]))) call_args.append(upcast) else: if is_retval: is_retval = False else: call_args.append(name) call_args.append('_ex') if not static: call_args = ['self->d_data'] + call_args # The actual function call if Type == sidlir.void: Type = ir.pt_void call = [ir.Stmt(ir.Call(callee, call_args))] else: if post_call: call = [ ir.Stmt( ir.Assignment('_CHPL__retval', ir.Call(callee, call_args))) ] return_stmt = [ir.Stmt(ir.Return('_retval'))] else: call = [ir.Stmt(ir.Return(ir.Call(callee, call_args)))] #TODO: ior_args = drop_rarray_ext_args(Args) skeldefn = (ir.fn_defn, [], ctype, qname + '_skel', babel.epv_args(Attrs, Args, ci.epv.symbol_table, ci.epv.name), decls + pre_call + call + post_call + return_stmt, DocComment) def skel_args((arg, attr, mode, typ, name)): # lower array args if typ[0] == sidlir.array: return arg, attr, mode, ir.pt_void, name # complex is always passed as a pointer since chpl 1.5 elif mode == ir.in_ and typ[0] == ir.typedef_type and ( typ[1] == '_complex64' or typ[1] == '_complex128'): return arg, attr, mode, ir.Pointer_type(typ), name else: return arg, attr, mode, typ, name ex_arg = [ir.Arg([], ir.inout, babel.ir_baseinterface_type(), '_ex')] impl_args = this_arg + map(skel_args, chpl_args) + ex_arg impldecl = (ir.fn_decl, [], chpltype, callee, impl_args, DocComment) splicer = '.'.join(ci.epv.symbol_table.prefix + [ci.epv.name, Name]) impldefn = (ir.fn_defn, ['export ' + callee], chpltype, Name, impl_args, [ 'set_to_null(_ex);', '// DO-NOT-DELETE splicer.begin(%s)' % splicer, '// DO-NOT-DELETE splicer.end(%s)' % splicer ], DocComment) c_gen(skeldefn, ci.stub) c_gen(impldecl, ci.stub) upc_gen(impldefn, ci.impl)
def generate_client_method(self, symbol_table, method, ci, has_impl): """ Generate client code for a method interface. \param method s-expression of the method's SIDL declaration \param symbol_table the symbol table of the SIDL file \param ci a ClassInfo object This is currently a no-op, since the UPC calling convention is identical to the IOR. """ (Method, Type, (MName, Name, Extension), Attrs, Args, Except, From, Requires, Ensures, DocComment) = method abstract = member_chk(sidlir.abstract, Attrs) #final = member_chk(sidlir.final, Attrs) static = member_chk(sidlir.static, Attrs) attrs = [] if abstract: # we still need to output a stub for an abstract function, # since it might me a virtual function call through an # abstract interface pass if static: attrs.append(ir.static) ior_type = babel.lower_ir(symbol_table, Type) ior_args = babel.epv_args(Attrs, Args, symbol_table, ci.epv.name) call_args = map(lambda arg: ir.arg_id(arg), ior_args) cdecl = ir.Fn_decl(attrs, ior_type, Name + Extension, ior_args, DocComment) qname = '_'.join(ci.co.qualified_name + [Name]) + Extension if self.server and has_impl: # if we are generating server code we can take a shortcut # and directly invoke the implementation modname = '_'.join(ci.co.symbol_table.prefix + ['Impl']) if not static: qname = '_'.join(ci.co.qualified_name + ['Impl']) # FIXME! callee = '_'.join([modname, ir.fn_decl_id(cdecl)]) else: callee = babel.build_function_call(ci, cdecl, static) if Type == sidlir.void: call = [ir.Stmt(ir.Call(callee, call_args))] else: call = [ir.Stmt(ir.Return(ir.Call(callee, call_args)))] cdecl = ir.Fn_decl(attrs, ior_type, qname, ior_args, DocComment) cdefn = ir.Fn_defn(attrs, ior_type, qname, ior_args, call, DocComment) if static: # TODO: [performance] we would only need to put the # _externals variable into the _Stub.c, not necessarily # all the function definitions ci.stub.gen(cdecl) ci.stub.new_def('#pragma weak ' + qname) ci.stub.gen(cdefn) else: # FIXME: can't UPC handle the inline keyword?? ci.stub.new_header_def('static inline') ci.stub.genh(cdefn)