def create_indirect_read_recvs(ctxt: InvocationContext, builder: GraphTypeBuilder): for (ai, arg) in ctxt.indirect_reads: with builder.subst(set=ctxt.stat.iter_set.id, index=ai, dat=arg.dat.id): props = {"index": DataType(numpy.uint8, shape=())} builder.add_input_pin( "set_{set}", "{invocation}_arg{index}_read_recv", "dat_{dat}", props, None, """ assert(deviceState->{invocation}_read_recv_count < {invocation}_{set}_read_recv_total); // Standard edge trigger for start of invocation if(!deviceState->{invocation}_in_progress){{ deviceState->{invocation}_in_progress=1; deviceState->{invocation}_read_send_mask = {invocation}_{set}_read_send_mask_all; }} deviceState->{invocation}_read_recv_count++; """) if arg.index < 0: builder.extend_input_pin_handler( "set_{set}", "{invocation}_arg{index}_read_recv", """ copy_value(deviceState->{invocation}_arg{index}_buffer[edgeProperties->index], message->value); """) else: builder.extend_input_pin_handler( "set_{set}", "{invocation}_arg{index}_read_recv", """ copy_value(deviceState->{invocation}_arg{index}_buffer, message->value); """)
def create_indirect_write_recvs(ctxt: InvocationContext, builder: GraphTypeBuilder): for (ai, arg) in ctxt.indirect_writes: with builder.subst(set=arg.to_set.id, index=ai, dat=arg.dat.id, arity=arg.map.arity): if arg.index >= 0: props = None message_type = "dat_{dat}" else: props = {"index": scalar_uint32} message_type = "dat_{dat}_x{arity}" handler = """ assert(deviceState->{invocation}_write_recv_count < deviceProperties->{invocation}_write_recv_total); // Standard edge trigger for start of invocation if(!deviceState->{invocation}_in_progress){{ deviceState->{invocation}_in_progress=1; deviceState->{invocation}_read_send_mask = {invocation}_{set}_read_send_mask_all; }} deviceState->{invocation}_write_recv_count++; """ if arg.access_mode == AccessMode.WRITE or arg.access_mode == AccessMode.RW: if arg.index >= 0: handler += """ copy_value(deviceState->dat_{dat}, message->value); """ else: handler += """ copy_value(deviceState->dat_{dat}, message->value[edgeProperties->index]); """ elif arg.access_mode == AccessMode.INC: if arg.index >= 0: handler += """ inc_value(deviceState->dat_{dat}, message->value); """ else: handler += """ inc_value(deviceState->dat_{dat}, message->value[edgeProperties->index]); """ else: raise RuntimeError("Unexpected access mode {}".format( arg.access_mode)) builder.add_input_pin("set_{set}", "{invocation}_arg{index}_write_recv", message_type, props, None, handler)
def create_invocation_begin(ctxt: InvocationContext, builder: GraphTypeBuilder): for set in ctxt.get_all_involved_sets(): with builder.subst(set=set.id): handler = """ // Standard edge trigger for start of invocation if(!deviceState->{invocation}_in_progress){{ deviceState->{invocation}_in_progress=1; deviceState->{invocation}_read_send_mask = {invocation}_{set}_read_send_mask_all; }} deviceState->{invocation}_read_recv_count++; """ if set == ctxt.stat.iter_set: for (ai, arg) in ctxt.mutable_global_reads: handler += """ copy_value(deviceState->global_{}, message->global_{}); """.format(arg.global_.id, arg.global_.id) builder.add_input_pin("set_{set}", "{invocation}_begin", "{invocation}_begin", None, None, handler)
def create_invocation_controller(ctxt: InvocationContext, builder: GraphTypeBuilder): handler = """ assert(deviceState->end_received==0); assert(deviceState->invocation_index==RTS_INDEX_controller_{invocation}_begin); deviceState->end_received=0; deviceState->rts=0; """ for (ai, arg) in ctxt.mutable_global_reads: handler += builder.s(""" copy_value(message->global_{name}, deviceState->global_{name}); """, name=arg.global_.id) builder.add_output_pin("controller", "{invocation}_begin", "{invocation}_begin", handler) handler = """ assert(deviceState->invocation_index==RTS_INDEX_controller_{invocation}_begin); assert(deviceState->end_received < graphProperties->{invocation}_total_responding_devices); deviceState->end_received++; """ # Collect any inc's to global values for (ai, arg) in ctxt.global_writes: assert arg.access_mode == AccessMode.INC handler += builder.s(""" inc_value(deviceState->global_{name}, message->global_{name}); """, name=arg.global_.id) # Check whether we have finished handler += """ if(deviceState->end_received == graphProperties->{invocation}_total_responding_devices){{ """ handler += """ deviceState->invocation_index=-1; deviceState->end_received=0; deviceState->rts=RTS_FLAG_controller_control; }} """ builder.add_input_pin("controller", "{invocation}_end", "{invocation}_end", None, None, handler)
def compile_global_controller(gi: str, spec: SystemSpecification, builder: GraphTypeBuilder, code: Statement): create_controller_states( code) # Make sure every statement has an entry state builder.add_device_state(gi, "rts", scalar_uint32) builder.add_device_state(gi, "state", scalar_uint32) # This will be used as a hidden global, and captures the value # of the condition for If and While assert "_cond_" in spec.globals start_state = get_statement_state(code) finish_state = make_state() builder.add_rts_clause(gi, "*readyToSend = deviceState->rts;\n") with builder.subst(start_state=start_state): handler = """ deviceState->rts=RTS_FLAG_control; deviceState->state={start_state}; handler_log(4, "rts=%x, state=%d", deviceState->rts, deviceState->state); """ for mg in spec.globals.values(): if isinstance(mg, MutableGlobal): handler += """ copy_value(deviceState->global_{global_}, graphProperties->init_global_{global_}); """.format(global_=mg.id) builder.add_input_pin(gi, "__init__", "__init__", None, None, handler) handler = raw(""" handler_log(4, "rts=%x, state=%d", deviceState->rts, deviceState->state); *doSend=0; // disable this send... deviceState->rts=RTS_FLAG_control; // ... but say that we want to send again (by default) switch(deviceState->state){ """) handler += render_controller_statement(code, finish_state) handler += """ case {finish_state}: handler_log(4, "Hit finish state."); handler_exit(0); break; default: handler_log(3, "Unknown state id %d.", deviceState->state); assert(0); }} """.format(finish_state=finish_state) builder.create_message_type("control", {}) builder.add_output_pin(gi, "control", "control", handler) for user_code in find_scalars_in_code(code): name = user_code.id assert user_code.ast src = mini_op2.framework.kernel_translator.scalar_to_c( user_code.ast, name) builder.add_device_shared_code("controller", raw(src)) for k in code.all_statements(): if isinstance(k, While): name = k.id src = mini_op2.framework.kernel_translator.scalar_to_c( k.expr_ast, name) builder.add_device_shared_code("controller", raw(src))
def create_invocation_tester(testIndex: int, isLast: bool, ctxt: InvocationContext, builder: GraphTypeBuilder): with builder.subst(testIndex=testIndex, isLast=int(isLast)): handler = """ assert(deviceState->end_received==0); assert(deviceState->test_state==2*{testIndex}); deviceState->test_state++; // Odd value means we are waiting for the return deviceState->end_received=0; """ #for (ai,arg) in ctxt.mutable_global_reads: # handler+=builder.s(""" # copy_value(message->global_{name}, graphProperties->test_{invocation}_{name}_in); # """,name=arg.global_.id) builder.add_output_pin("tester", "{invocation}_begin", "{invocation}_begin", handler) handler = """ assert(deviceState->test_state==2*{testIndex}+1); assert(deviceState->end_received < graphProperties->{invocation}_total_responding_devices); deviceState->end_received++; """ # Collect any inc's to global values for (ai, arg) in ctxt.global_writes: assert arg.access_mode == AccessMode.INC handler += builder.s(""" inc_value(deviceState->global_{name}, message->global_{name}); """, name=arg.global_.id) # Check whether we have finished handler += """ if(deviceState->end_received == graphProperties->{invocation}_total_responding_devices){{ """ # Remove for now - not clear how to do this. if False: # ... and if so, try to check the results are "right" for (ai, arg) in ctxt.global_writes: handler += builder.s(""" check_value(deviceState->global_{name}, graphProperties->test_{invocation}_{name}_out); """, name=arg.global_.id) handler += """ if( {isLast} ){{ handler_exit(0); }}else{{ deviceState->test_state++; // start the next invocation deviceState->end_received=0; }} }} """ builder.add_input_pin("tester", "{invocation}_end", "{invocation}_end", None, None, handler) builder.add_rts_clause( "tester", """ if(deviceState->test_state==2*{testIndex}){{ *readyToSend = RTS_FLAG_{invocation}_begin; }} """)
def sync_compiler(spec: SystemSpecification, code: Statement): builder = GraphTypeBuilder("op2_inst") builder.add_shared_code_raw(r""" #include <cmath> #include <cstdio> #include <cstdarg> void fprintf_stderr(const char *msg, ...) { va_list v; va_start(v,msg); vfprintf(stderr, msg, v); fprintf(stderr, "\n"); va_end(v); } template<class T,unsigned N> void copy_value(T (&x)[N], const T (&y)[N]){ for(unsigned i=0; i<N; i++){ x[i]=y[i]; } } template<class T,unsigned N,unsigned M> void copy_value(T (&x)[N][M], const T (&y)[N][M]){ for(unsigned i=0; i<N; i++){ for(unsigned j=0; j<M; j++){ x[i][j]=y[i][j]; } } } /*template<class T> void copy_value(T &x, const T (&y)[1]){ x[0]=y[0]; }*/ template<class T,unsigned N> void inc_value(T (&x)[N], const T (&y)[N]){ for(unsigned i=0; i<N; i++){ x[i]+=y[i]; } } template<class T,unsigned N> void zero_value(T (&x)[N]){ for(unsigned i=0; i<N; i++){ x[i]=0; } } /* Mainly for debug. Used to roughly check that a calculated value is correct, based on "known-good" pre-calculated values. Leaves a lot to be desired... */ template<class T,unsigned N> void check_value(T (&got)[N], T (&ref)[N] ){ for(unsigned i=0; i<N; i++){ auto diff=std::abs( got[i] - ref[i] ); assert( diff < 1e-6 ); // Bleh... } } """) builder.create_message_type("executeMsgType", {}) # Support two kinds of global. Only one can be wired into an instance. builder.create_device_type( "controller") # This runs the actual program logic builder.create_device_type( "tester") # This solely tests each invocation in turn builder.add_device_state("tester", "test_state", DataType(shape=(), dtype=numpy.uint32)) builder.add_device_state("tester", "end_received", DataType(shape=(), dtype=numpy.uint32)) builder.add_device_state("controller", "end_received", DataType(shape=(), dtype=numpy.uint32)) builder.add_device_state("controller", "invocation_index", DataType(shape=(), dtype=numpy.uint32)) for global_ in spec.globals.values(): if isinstance(global_, MutableGlobal): builder.add_device_state("controller", "global_{}".format(global_.id), global_.data_type) builder.add_device_state("tester", "global_{}".format(global_.id), global_.data_type) builder.add_graph_property("init_global_{}".format(global_.id), global_.data_type) elif isinstance(global_, ConstGlobal): builder.add_graph_property("global_{}".format(global_.id), global_.data_type) else: raise RuntimeError("Unexpected global type : {}", type(global_)) builder.merge_message_type("__init__", {}) for s in spec.sets.values(): with builder.subst(set="set_" + s.id): builder.create_device_type("{set}") init_handler = "" for dat in s.dats.values(): with builder.subst(dat=dat.id): builder.add_device_property("{set}", "init_dat_{dat}", dat.data_type) builder.add_device_state("{set}", "dat_{dat}", dat.data_type) init_handler += builder.s( " copy_value(deviceState->dat_{dat}, deviceProperties->init_dat_{dat});\n" ) builder.add_input_pin("{set}", "__init__", "__init__", None, None, init_handler) kernels = find_kernels_in_code(code) emitted_kernels = set() for (i, stat) in enumerate(kernels): ctxt = InvocationContext(spec, stat) with builder.subst(invocation=ctxt.invocation): compile_invocation(spec, builder, ctxt, emitted_kernels) create_invocation_tester(i, i + 1 == len(kernels), ctxt, builder) create_invocation_controller(ctxt, builder) compile_global_controller("controller", spec, builder, code) return builder