UNPKG

costreamjs

Version:

A high-performance streaming programming language for parallel architecture. This repo (js-version) is created for better using & reading & debugging.

853 lines (793 loc) 32.3 kB
import { COStreamJS, setTop } from "../FrontEnd/global" import { declareNode, function_definition, compositeNode, strdclNode, blockNode, fileReaderNode } from "../ast/node"; import { getIOHandlerStrings } from "./IOHandler" import { FlatNode } from "../FrontEnd/FlatNode" import { StaticStreamGraph } from "../FrontEnd/StaticStreamGraph"; import { Partition } from "./Partition" import Plugins from "../plugins" export class X86CodeGeneration { constructor(nCpucore, ssg, mp) { this.nCpucore = nCpucore /**@type {StaticStreamGraph} */ this.ssg = ssg /**@type {Partition} */ this.mp = mp /** @type {Map<string,bufferSpace>} 字符串到对应的缓冲区的映射 */ this.bufferMatch = new Map() /** @type {Map<string,number>} 缓冲区到对应缓冲区类型的映射,通过这个来判断调用consumer和producer哪种方法 */ this.bufferType = new Map() /** @type {Map<number,Set<number>} 处理器编号到 阶段号集合 的对应关系, 例如 0号核上有 0,2 两个阶段*/ this.mapNum2Stage = new Map() //构造每个线程上的stage集合mapNum2Stage for (let i = 0; i < nCpucore; i++) { let stageNums = new Set() //使用Set来对阶段号做"数组去重"操作 this.mp.PartitonNum2FlatNode.get(i).forEach(flat => stageNums.add(flat.stageNum)) this.mapNum2Stage.set(i, stageNums) //Set 转回数组 } //头节点执行一次work所需读入的数据量 this.workLen = 0 } } class bufferSpace { constructor(original, instance, buffersize, buffertype, copySize = 0, copyStartPos = 0) { /** @type {string} 原始缓冲区的名称 */ this.original = original /** @type {string} 实际对应的缓冲区名称 */ this.instance = instance /** @type {int} 分配缓冲区的大小 */ this.buffersize = buffersize /** @type {int} 分配缓冲区的类型,是否可复用,0代表未分配,1代表不可复用,2代表可复用 */ this.buffertype = buffertype /** FIXME用来标识流的类型,未完成 */ this.classification = 'int_x' this.copySize = copySize this.copyStartPos = copyStartPos } } X86CodeGeneration.prototype.CGMakefile = function () { /** Makefile 要求左边必须靠边, 在左边的空白字符用 \t 而不能用空格 */ var buf = ` PROGRAM := a.out SOURCES := $(wildcard ./*.cpp) SOURCES += $(wildcard ./src/*.cpp) OBJS := $(patsubst %.cpp,%.o,$(SOURCES)) CXX := g++ CPPFLAGS := -DNDEBUG -Wall -std=c++11 -Ofast -march=native INCLUDE := -I . LIB := -lpthread -ldl .PHONY: clean install $(PROGRAM): $(OBJS) \t$(CXX) -o $@ $^ $(LIB) $(CPPFLAGS) %.o: %.c \t$(CXX) -o $@ -c $< $(CPPFLAGS) $(INCLUDE) clean: \trm -f $(OBJS) $(PROGRAM) install: $(PROGRAM) \tcp $(PROGRAM) ./bin/ ` COStreamJS.files['Makefile'] = buf } X86CodeGeneration.prototype.CGGlobalvar = function () { var buf = `#include "GlobalVar.h" \n`; for (let node of COStreamJS.ast) { if (node instanceof declareNode) { buf += node.toString() + ';\n' } } buf = Plugins.after('CGGlobalvar', buf, COStreamJS.ast) COStreamJS.files['GlobalVar.cpp'] = buf.beautify() } X86CodeGeneration.prototype.CGGlobalvarHeader = function () { var buf = `#ifndef GLOBALVAL_H\n` buf += `#define GLOBALVAL_H\n`; for (let node of COStreamJS.ast) { if (node instanceof declareNode) { let str = node.toString().replace(/=\s*\{[^}]*}/g, '') //去除 a[3] = {1,2,3} 的赋值部分 str = sliceStringFromComma(str) //去除 a = 2 的赋值部分 buf += "extern " + str + ';\n' } } buf = Plugins.after('CGGlobalvarHeader', buf) COStreamJS.files['GlobalVar.h'] = (buf + `#endif`).beautify(); //截取一个字符串 = 号后(括号匹配最外层的)逗号前的字符串 function sliceStringFromComma(str){ let res = '' let paren_depth = 0 let hasPassedEqual = false for(let i of str){ if(i === '=') hasPassedEqual = true if(paren_depth === 0 && i === ',') return res if(['[','{','('].includes(i)) paren_depth++ if([']', '}', ')'].includes(i)) paren_depth-- if(!hasPassedEqual) res+=i } return res } } /** * 生成stream流类型和全局数据流缓存区的声明 * @description 边的命名规则:A_B,其中A->B */ X86CodeGeneration.prototype.CGGlobalHeader = function () { var buf = ` #ifndef _GLOBAL_H #define _GLOBAL_H #include "Buffer.h" #include <math.h> #include <string> using namespace std; ` //遍历所有compositeNode的streamType,找到流中所有包含的数据类型,作为结构体streamData中的数据 //FIXME: 目前符号表未完成, 所以暂时假设所有的流都是同一种数据类型,因此只取一个 streamDcl 来填充至 streamData var typeSet = [] for (let comp of COStreamJS.ast.filter(node => node instanceof compositeNode)) { for (let stmt of comp.body.stmt_list) { if (stmt instanceof declareNode && stmt.type instanceof strdclNode) { typeSet = typeSet.concat(stmt.type.id_list) } } } //数组去重 由[{type:'int', identifier:'x'}] 先转为字符串形式的 [ 'int x' ] 来完成去重, 再给后面调用 typeSet = typeSet.map(o => o.type + ' ' + o.identifier) typeSet = [...new Set(typeSet)] typeSet = typeSet.map(str => ({ type: str.match(/\S+/g)[0], identifier: str.match(/\S+/g)[1] })) //写入数据流数据类型结构体 buf += "struct streamData{\n" for (let it of typeSet) { buf += it.type + ' ' + it.identifier + ';' } buf += "};\n\n" //声明流边 for (let flat of this.ssg.flatNodes) { for (let nextFlat of flat.outFlatNodes) { var edgename = flat.name + '_' + nextFlat.name buf += `extern Buffer<streamData>${edgename};\n` } } buf += `\n#endif\n` // 返回前调用插件功能对文本进行处理 buf = Plugins.after('CGGlobalHeader', buf) COStreamJS.files['Global.h'] = buf.beautify() } /** * 生成 Global.cpp 用于存储边的信息 */ X86CodeGeneration.prototype.CGGlobal = function () { var buf = ` #include "Buffer.h" #include "Global.h" #include <vector> using namespace std;\n ` for (let flat of this.ssg.flatNodes) { for (let out of flat.outFlatNodes) { let stageminus = out.stageNum - flat.stageNum //发送方和接受方的软件流水阶段差 let edgePos = flat.outFlatNodes.indexOf(out) // out 在 flat 的输出边的序号 let perSteadyPushCount = flat.steadyCount * flat.outPushWeights[edgePos]//发送actor每次调用steadywork需要push的个数 let copySize = 0, copyStartPos = 0; //拷贝的数据大小,copy存放的开始位置 let inEdgeIndex = out.inFlatNodes.indexOf(flat) // out节点中 flat 对应的这条边的下标 let perWorkPeekCount = out.inPeekWeights[inEdgeIndex] //接收边actor每次peek的个数,b let perWorkPopCount = out.inPopWeights[inEdgeIndex]; //接收边actor每次调用work需要pop的个数 let init1 = flat.initCount * flat.outPushWeights[edgePos] //发送actor调用initwork产生的数据量 let init2 = out.initCount * perWorkPopCount //接受actor的数据量 let size = init1 + perSteadyPushCount * (stageminus + 2) //缓冲区的大小 if (perWorkPeekCount === perWorkPopCount) { if (perSteadyPushCount) { copySize = (init1 - init2) % perSteadyPushCount copyStartPos = init2 % perSteadyPushCount } } else { //peek != pop 情况的特殊处理, 目前测试用例无此种情况, 需对这方面单独测试, 目前不保证下面5代码的正确性 FIXME let leftnum = ((init1 - init2) % perSteadyPushCount + perSteadyPushCount - (perWorkPeekCount - perWorkPopCount) % perSteadyPushCount) % perSteadyPushCount; copySize = leftnum + perWorkPeekCount - perWorkPopCount; let addtime = copySize % perSteadyPushCount ? copySize / perSteadyPushCount + 1 : copySize / perSteadyPushCount; copyStartPos = init2 % perSteadyPushCount; size += addtime * perSteadyPushCount; } /* 优先构建那些很明显不能被复用的边: * 1. peak != pop 的 * 2. copySize 或 copyStartPos 不为0的 * 3. 上下游有阶段差的 */ if (1|| perWorkPeekCount != perWorkPopCount || copySize || copyStartPos || stageminus) { let edgename = flat.name + '_' + out.name //边的名称 this.bufferMatch.set(edgename, new bufferSpace(edgename, edgename, size, 1, copySize, copyStartPos)) } } } //为同一核上可以共享内存的缓冲区分配内存 this.shareBuffers() //检查是否有缓冲区没有分配, 若分配了, 则加入返回字符串中 for (let flat of this.ssg.flatNodes) { for (let out of flat.outFlatNodes) { let edgename = flat.name + '_' + out.name if (!this.bufferMatch.has(edgename)) { throw new Error('有缓冲区未分配, 程序异常, 请联系管理员') } else { let b = this.bufferMatch.get(edgename) let str = `Buffer<streamData>${edgename}(${b.buffersize},${b.copySize},${b.copyStartPos});` if (b.original !== b.instance) { str = '//' + str + ` 该缓冲区复用了${b.instance}的内存` } buf += str + '\n' } } } COStreamJS.files['Global.cpp'] = buf.beautify() debugger } /** * 为同一核上可以共享内存的缓冲区分配内存 * @description 根据拓扑排序模拟运行各个CPU上的计算节点找出可以被复用的缓冲区 */ X86CodeGeneration.prototype.shareBuffers = function () { //获取 核号 => 该核上所有节点的拓扑排序 的 Map let processor2topoactors = this.GetProcessor2topoactors(this.mp.FlatNode2PartitionNum) for (let nodes of processor2topoactors.values()) { let vb = [] //用来存储在一次稳态调度中已经使用完的缓冲区 //按拓扑排序访问各个节点 nodes.forEach(flat => { flat.outFlatNodes.forEach((out, edgePos) => { let edgename = flat.name + '_' + out.name if (this.bufferMatch.has(edgename)) { return }//如果该边为特殊缓冲区在之前已经分配完成则进入下一条边的分配 //计算所需要占用的缓冲区大小 let stageminus = out.stageNum - flat.stageNum let perSteadyPushCount = flat.steadyCount * flat.outPushWeights[edgePos]//稳态时产生的数据量 let size = perSteadyPushCount * (stageminus + 2); //分配时首先搜索队列中是否有已经使用完的缓冲区,没有再自己分配内存,使用队列中的缓冲区要将其从队列中删除 if (vb.length) { let sameClassification = vb.filter(b => b.classification === 'int_x') if (sameClassification) { let availableBuffer = sameClassification.find(b => b.buffersize >= size) if (availableBuffer) { //对当前可用缓冲区中最小的进行内存共享 let buffer = new bufferSpace() buffer.original = edgename buffer.instance = availableBuffer.instance buffer.buffersize = availableBuffer.buffersize buffer.buffertype = 2 this.bufferMatch.set(edgename, buffer) vb.splice(vb.indexOf(availableBuffer), 1) } else { //若当前可用缓冲区大小都不符合要求则对最大缓冲区进行扩容 let maxBuffer = sameClassification.pop() this.bufferMatch.get(maxBuffer.instance).buffersize = size this.bufferMatch.get(maxBuffer.original).buffersize = size let buffer = new bufferSpace() buffer.original = edgename buffer.instance = maxBuffer.instance buffer.buffersize = size buffer.buffertype = 2 this.bufferMatch.set(edgename, buffer) vb.splice(vb.indexOf(availableBuffer), 1) } } } else { //找不到可以复用的缓冲区,自己进行分配 let buffer = new bufferSpace(edgename, edgename, size, 2) this.bufferMatch.set(edgename, buffer) } }) //由于空闲缓冲区队列中的缓冲区与原始缓冲区存在映射关系,所以要更新其缓冲区大小 vb.forEach(v => v.buffersize = this.bufferMatch.get(v.instance).buffersize) //当该节点内存分配完之后说明该节点执行完毕,可以将节点上游能够复用的缓冲区加入到队列中 flat.inFlatNodes.forEach(src => { let buffer = this.bufferMatch.get(src.name + '_' + flat.name) if(!buffer){ console.log(this.bufferMatch) console.warn(src.name, flat.name); } if (buffer.buffertype == 2) { vb.push(buffer) } }) //对 vb 进行排序, 按照 buffersize 升序排列 vb.sort((a, b) => a.buffersize - b.buffersize) }) } } /** * 输入一个保存了 FlatNode => 划分核号 的 Map, 返回 核号 => 该核上所有节点的拓扑排序 的 Map * @param {Map<FlatNode,number>} map * @return {Map<number,FlatNode[]} processor2topoactors */ X86CodeGeneration.prototype.GetProcessor2topoactors = function (map) { var processor2topoactors = new Map() for (let flat of COStreamJS.topologic) { let coreNum = map.get(flat) let set = processor2topoactors.get(coreNum) || [] processor2topoactors.set(coreNum, set.concat(flat)) } return processor2topoactors } /** * 循环渲染一段字符串, 用 i 替换 $, 用 i+1 替换$$ * 例如输入 str='extern_$' , start =0, end=3, 则渲染为 'extern_0 \n extern_1 \n extern_2' */ function circleRender(str, start, end) { let result = '' for (let i = start; i < end; i++) { result += str.replace(/\$\$/g, i + 1).replace(/\$/g, i) + '\n' } return result } const IOHandler = false X86CodeGeneration.prototype.CGMain = function () { var buf = ` #include <iostream> #include <fstream> #include <stdlib.h> #include <pthread.h> #include "setCpu.h" #include "lock_free_barrier.h" //包含barrier函数 #include "Global.h" #include "GlobalVar.h" #include "RingBuffer.h" using namespace std; int MAX_ITER=1;//默认的执行次数是1 #SLOT1 ${circleRender('extern void thread_$_fun();', 0, this.nCpucore)} pthread_t tid[${this.nCpucore}]; ${circleRender(` void* thread_$_fun_start(void *) { set_cpu($, tid[$]); thread_$_fun(); return 0; } `, 1, this.nCpucore)} int main(int argc,char **argv){ void setRunIterCount(int,char**); setRunIterCount(argc,argv); #SLOT2 set_cpu(0,tid[0]); allocBarrier(${this.nCpucore}); ${circleRender('pthread_create (&tid[$], NULL, thread_$_fun_start, (void*)NULL);', 1, this.nCpucore)} #SLOT3 thread_0_fun(); #SLOT4 return 0; } //设置运行次数 void setRunIterCount(int argc,char **argv) { int oc; while((oc=getopt(argc,argv,"i:"))!=-1) { switch(oc) { case 'i': MAX_ITER=atoi(optarg); break; } } } ` if (!IOHandler) { buf = buf.replace(/#SLOT\d/g, '') } else { let workcount = this.ssg.flatNodes[0].steadyCount let IOHandler_strings = getIOHandlerStrings(this.workLen, 0, workcount) buf = buf.replace(/#SLOT1/, IOHandler_strings[0]) buf = buf.replace(/#SLOT2/, IOHandler_strings[1]) buf = buf.replace(/#SLOT3/, IOHandler_strings[2]) buf = buf.replace(/#SLOT4/, IOHandler_strings[3]) } buf = Plugins.after('CGMain', buf) COStreamJS.files['main.cpp'] = buf.beautify() } /** * 生成包含所有 actor 头文件的 AllActorHeader.h */ X86CodeGeneration.prototype.CGAllActorHeader = function () { var buf = '' this.ssg.flatNodes.forEach(flat => { buf += `#include "${flat.PreName}.h"\n` }) COStreamJS.files['AllActorHeader.h'] = buf } /** * 生成所有线程文件 */ X86CodeGeneration.prototype.CGThreads = function () { debugger; for (let i = 0; i < this.nCpucore; i++) { var buf = '' let MaxStageNum = COStreamJS.MaxStageNum buf = ` /*该文件定义各thread的入口函数,在函数内部完成软件流水迭代*/ #include "Buffer.h" #include "Producer.h" #include "Consumer.h" #include "Global.h" #include "AllActorHeader.h" //包含所有actor的头文件 #include "lock_free_barrier.h" //包含barrier函数 #include "rdtsc.h" #include <fstream> extern int MAX_ITER; ` buf += `void thread_${i}_fun()\n{\n` let syncString = (i > 0 ? `workerSync(` + i : `masterSync(` + this.nCpucore) + `);\n` buf += syncString let actorSet = this.mp.PartitonNum2FlatNode.get(i) //获取到当前线程上所有flatNode actorSet.forEach(flat => { //准备构造如下格式的声明语句: Name Name_obj(out1,out2,in1,in2,steadyC,initC,[params?]); buf += flat.PreName + ' ' + flat.name + '_obj(' let streamNames = [], comments = [] flat.outFlatNodes.forEach(out => { let edgename = flat.name + '_' + out.name let buffer = this.bufferMatch.get(edgename) if (buffer.instance !== buffer.original) { comments.push(buffer.original + '使用了' + buffer.instance + '的缓冲区') } streamNames.push(buffer.instance) //使用实际的缓冲区 }) flat.inFlatNodes.forEach(src => { let edgename = src.name + '_' + flat.name let buffer = this.bufferMatch.get(edgename) if (buffer.instance !== buffer.original) { comments.push(buffer.original + '使用了' + buffer.instance + '的缓冲区') } streamNames.push(buffer.instance) //使用实际的缓冲区 }) streamNames.push(flat.steadyCount) streamNames.push(flat.initCount) buf += streamNames.concat(flat.params).join(',') + ');' comments.length && (buf += ' //' + comments.join(',')) buf += '\n' }) const constant_array = [1].concat(Array(MaxStageNum-1).fill(0)) // 得到这样的数组: [1,0,0,...,0] 长度为阶段数 buf += `char stage[${MaxStageNum}] = {${constant_array.join()}};\n` //生成初态的 initWork 对应的 for 循环 let initFor = ` for(int _stageNum = 0; _stageNum < ${MaxStageNum}; _stageNum++){ #SLOT ${syncString} } ` var forBody = '' let stageSet = this.mapNum2Stage.get(i) //查找该thread对应的阶段号集合 for (let stage = MaxStageNum - 1; stage >= 0; stage--) { if (stageSet.has(stage)) { //如果该线程在阶段i有actor let ifStr = `if(${stage} == _stageNum){` //获取既在这个thread i 上 && 又在这个 stage 上的 actor 集合 let flatVec = this.mp.PartitonNum2FlatNode.get(i).filter(flat => flat.stageNum == stage) ifStr += flatVec.map(flat => flat.name + '_obj.runInitScheduleWork();\n').join('') + '}\n' forBody += ifStr } } buf += initFor.replace('#SLOT', forBody) //初态的 initWork 对应的 for 循环生成完毕 //生成稳态的 steadyWork 对应的 for 循环 let steadyFor = ` for(int _stageNum = ${MaxStageNum}; _stageNum < 2*${MaxStageNum}+MAX_ITER-1; _stageNum++){ #SLOT ${syncString} } ` var forBody = '' for (let stage = MaxStageNum - 1; stage >= 0; stage--) { if (stageSet.has(stage)) { //如果该线程在阶段i有actor let ifStr = `if(stage[${stage}]){` //获取既在这个thread i 上 && 又在这个 stage 上的 actor 集合 debugger; let flatVec = this.mp.PartitonNum2FlatNode.get(i).filter(flat => flat.stageNum == stage) ifStr += flatVec.map(flat => flat.name + '_obj.runSteadyScheduleWork();\n').join('') + '}\n' forBody += ifStr } } forBody += `for(int index=${MaxStageNum-1}; index>=1; --index){ stage[index] = stage[index-1]; } if(_stageNum == MAX_ITER - 1 + ${MaxStageNum}){ stage[0] = 0; } ` buf += steadyFor.replace('#SLOT', forBody) //稳态的 steadyWork 对应的 for 循环生成完毕 buf += '}' COStreamJS.files[`thread_${i}.cpp`] = buf.beautify() } } /** COStream 内建节点, 无需去重 */ const ProtectedActor = ['join', 'duplicate', 'roundrobin'] /** * 生成各个计算节点, 例如 source.h sink.h */ X86CodeGeneration.prototype.CGactors = function () { var hasGenerated = new Set() //存放已经生成过的 FlatNode 的 PreName , 用来做去重操作 this.ssg.flatNodes.forEach(flat => { // 先对 FileReader 进行特殊处理 if(flat.contents instanceof fileReaderNode){ COStreamJS.files[`${flat.PreName}.h`] = this.cgFileReaderActor(flat); return; } // 再处理普通 oper /** 暂时不对COStream 内建节点做去重操作 */ if(ProtectedActor.includes(flat.PreName)){ flat.PreName = flat.name } if (hasGenerated.has(flat.PreName)) return hasGenerated.add(flat.PreName) var buf = ` #ifndef _${flat.PreName}_ #define _${flat.PreName}_ #include <string> #include <iostream> #include "Buffer.h" #include "Consumer.h" #include "Producer.h" #include "Global.h" #include "GlobalVar.h" using namespace std; ` //如果当前节点为IO节点 if (flat.name.match(/FILEREADER/i)) { buf += "#include \"RingBuffer.h\"\n"; this.workLen = flat.outPushWeights[0]; //由于目前不支持多类型流变量,这里先强制设置为int buf += ` struct source{ int buffer[${this.workLen}]; }; extern RingBuffer<source> ringBuffer; ` } //开始构建 class buf += `class ${flat.PreName}{\n` buf += `public:\n` /*写入类成员函数*/ let inEdgeNames = flat.contents.inputs let outEdgeNames = flat.contents.outputs buf += this.CGactorsConstructor(flat, inEdgeNames, outEdgeNames); buf += this.CGactorsRunInitScheduleWork(inEdgeNames, outEdgeNames); buf += this.CGactorsRunSteadyScheduleWork(inEdgeNames, outEdgeNames); /*写入类成员变量*/ buf += "private:\n"; outEdgeNames.forEach(out => buf += `Producer<streamData>${out};\n` ) inEdgeNames.forEach(src => buf += `Consumer<streamData>${src};\n`) flat._symbol_table.paramNames.forEach(param => buf += `int ${param};\n`) buf += "int steadyScheduleCount;\t//稳态时一次迭代的执行次数\n"; buf += "int initScheduleCount;\n"; //写入init部分前的statement定义,调用tostring()函数,解析成规范的类变量定义格式 buf += this.CGactorsStmts(flat.contents.operBody.stmt_list); buf += this.CGactorsPopToken(flat, inEdgeNames); buf += this.CGactorsPushToken(flat, outEdgeNames); //init部分前的statement赋值 buf += this.CGactorsinitVarAndState(flat.contents.operBody.stmt_list); buf += this.CGactorsInit(flat.contents.operBody.init); buf += this.CGactorsWork(flat.contents.operBody.work, flat, inEdgeNames, outEdgeNames); /* 类体结束*/ buf += "};\n"; buf += "#endif"; COStreamJS.files[`${flat.PreName}.h`] = buf.beautify() }) } X86CodeGeneration.prototype.cgFileReaderActor = function(/** @type {FlatNode} */flat){ /** @type {fileReaderNode} */ const fnode = flat.contents const fileName = fnode.fileName.slice(1,-1) // 原始数据首尾都有双引号, 例如 "./mnist_train.csv" const length = fnode.dataLength return ` #ifndef _FileReader_ #define _FileReader_ #include <string> #include <iostream> #include <fstream> #include "Buffer.h" #include "Consumer.h" #include "Producer.h" #include "Global.h" #include "GlobalVar.h" using namespace std; class FileReader{ public: FileReader(Buffer<streamData>& Out,int steadyC,int initC):Out(Out){ steadyScheduleCount = steadyC; initScheduleCount = initC; } void runInitScheduleWork() { initVarAndState(); init(); for(int i=0;i<initScheduleCount;i++){ work(); } Out.resetTail(); } void runSteadyScheduleWork() { for(int i=0;i<steadyScheduleCount;i++){ work(); } Out.resetTail(); } private: Producer<streamData>Out; int steadyScheduleCount; //稳态时一次迭代的执行次数 int initScheduleCount; ifstream ifile; void popToken(){ } void pushToken(){ Out.updatetail(${length}); } void initVarAndState() { ifile = ifstream("${fileName}"); if(!ifile){ cout<<"打开文件 ${fileName} 失败"<<endl; exit(0); } } void init() { } void work(){ int a; char c; for(int i=0;i<${length};i++){ ifile>>a; Out[i].x = a; if(i != 784) ifile>>c; //这里读入一个 c 是为了在处理"1,2,3 \\n 4,5,6"的输入文件时, 跳过逗号',' } pushToken(); popToken(); } }; #endif ` } /** * 生成actors constructor * @example * rtest_3(Buffer<streamData>& Out,Buffer<streamData>& In, int steadyC, int initC, [params?]):Out(Out),In(In),param(param){ * steadyScheduleCount = steadyC; * initScheduleCount = initC; * } */ X86CodeGeneration.prototype.CGactorsConstructor = function(flat, inEdgeNames, outEdgeNames) { var OutAndInEdges = (outEdgeNames || []).concat(inEdgeNames) // 把 out 放前面, in 放后面 var paramNames = flat._symbol_table.paramNames var paramWithType = paramNames.map(name => 'int '+name) // FIXME 暂时先都用 int var buf = flat.PreName + '(' buf += OutAndInEdges.map(s => 'Buffer<streamData>& ' + s).concat(['int steadyC','int initC']).concat(paramWithType).join(',') + '):' buf += OutAndInEdges.concat(paramNames).map(s => s + '(' + s + ')').join(',') + '{' buf += ` steadyScheduleCount = steadyC; initScheduleCount = initC; } ` return buf } /** * @example * void runInitScheduleWork() { * initVarAndState(); * init(); * for(int i=0;i<initScheduleCount;i++) * work(); * round1_0.resetTail(); * round1_1.resetTail(); * dup0_0.resetHead(); * } */ X86CodeGeneration.prototype.CGactorsRunInitScheduleWork = function (inEdgeNames, outEdgeNames) { var buf = ` void runInitScheduleWork() { initVarAndState(); init(); for(int i=0;i<initScheduleCount;i++){ work(); }`; (outEdgeNames || []).forEach(out => buf += out + '.resetTail();\n'); (inEdgeNames || []).forEach(src => buf += src + '.resetHead();\n'); return buf + '}\n' } /** * @example * void runSteadyScheduleWork() { * for(int i=0;i<steadyScheduleCount;i++) * work(); * round1_0.resetTail2(); * round1_1.resetTail(); * dup0_0.resetHead2(); * } */ X86CodeGeneration.prototype.CGactorsRunSteadyScheduleWork = function(inEdgeNames, outEdgeNames) { var buf = ` void runSteadyScheduleWork() { for(int i=0;i<steadyScheduleCount;i++){ work(); }`; // var use1Or2 = str => this.bufferMatch.get(str).buffertype == 1 ? '' : '2'; (outEdgeNames || []).forEach(out => buf += out + '.resetTail();\n'); (inEdgeNames || []).forEach(src => buf += src + '.resetHead();\n'); return buf + '}\n' } /** * 将.cos 文件中的 operator 的 init 前的变量声明转为新的 class 的 private 成员,例如 * private: * int i; * int j; * 而赋值操作放到 initVarAndState 中去做 * @param {declareNode[]} stmt_list */ X86CodeGeneration.prototype.CGactorsStmts = function (stmt_list) { /*解析等号类似int i=0,j=1形式变成int i; int j;的形式,因为类的成员变量定义不能初始化*/ var result = '' stmt_list.forEach(declare => { declare.init_declarator_list.forEach(item => { result += item.type + ' ' + item.identifier + ';\n' }) }) return result; } /** * 生成 class 的 private 部分的 popToken 函数, 例如 * void popToken() { * Rstream0_0.updatehead(1); * Rstream0_1.updatehead(1); * } * @param {FlatNode} flat */ X86CodeGeneration.prototype.CGactorsPopToken = function (flat, inEdgeNames) { let streams = {} for(let winStmt of flat.contents.operBody.win){ streams[winStmt.winName] = winStmt.arg_list[0].toString() } const stmts = inEdgeNames.map(src => `${src}.updatehead(${streams[src]});\n`).join('') return `\n void popToken(){ ${stmts} }\n` } /** * 生成 class 的 private 部分的 pushToken 函数, 例如 * void pushToken() { * Dstream0_1.updatetail(2); * } * @param {FlatNode} flat */ X86CodeGeneration.prototype.CGactorsPushToken = function (flat, outEdgeNames) { let streams = {} for(let winStmt of flat.contents.operBody.win){ streams[winStmt.winName] = winStmt.arg_list[0].toString() } const stmts = outEdgeNames.map(out => `${out}.updatetail(${streams[out]});\n`).join('') return `\n void pushToken(){ ${stmts} }\n` } /** * 将 stmt_list 中的 int i=0部分转换为 i=0; * @param {declareNode[]} stmt_list */ X86CodeGeneration.prototype.CGactorsinitVarAndState = function (stmt_list){ var result = 'void initVarAndState() {' stmt_list.forEach( declare =>{ declare.init_declarator_list.forEach(item =>{ if(item.initializer){ result += item.identifier + '=' + item.initializer +';\n' } }) }) return result+'}'; } X86CodeGeneration.prototype.CGactorsInit = function(init){ return `void init() ${init|| '{ }'} \n` } /** * @param {blockNode} work * @param {FlatNode} flat */ X86CodeGeneration.prototype.CGactorsWork = function (work, flat, inEdgeNames, outEdgeNames){ // 将 work 的 toString 的头尾两个花括号去掉}, 例如 { cout << P[0].x << endl; } 变成 cout << P[0].x << endl; setTop(work._symbol_table) var innerWork = (work + '').replace(/^\s*{/, '').replace(/}\s*$/, '') // 替换流变量名 , 例如 P = B(S)(88,99);Sink(P){...} 则将 P 替换为 B_1_Sink_2 flat.contents.inputs.forEach((src, idx) => replace(src, inEdgeNames[idx])) flat.contents.outputs.forEach((out, idx) => replace(out, outEdgeNames[idx])) return `void work(){ ${innerWork} pushToken(); popToken(); }\n` function replace(A, B) { const reg = new RegExp(`\\b${A}\\b`, 'g') innerWork = innerWork.replace(reg, B) } }