Dce.hx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. package hxsl;
  2. using hxsl.Ast;
  3. import hxsl.Debug.trace in debug;
  4. private class Exit {
  5. public function new() {
  6. }
  7. }
  8. private class VarRel {
  9. public var v : VarDeps;
  10. public var fields : Int = 0;
  11. public function new(v) {
  12. this.v = v;
  13. }
  14. }
  15. private class VarDeps {
  16. public var v : TVar;
  17. public var keep : Int = 0;
  18. public var used : Int = 0;
  19. public var deps : Map<Int,VarRel>;
  20. public var adeps : Array<VarRel>;
  21. public function new(v) {
  22. this.v = v;
  23. deps = new Map();
  24. adeps = [];
  25. }
  26. }
  27. private class WriteTo {
  28. public var vars : Array<VarDeps>;
  29. public var bits : Array<Int>;
  30. public function new() {
  31. vars = [];
  32. bits = [];
  33. }
  34. public function push(v,b) {
  35. vars.push(v);
  36. bits.push(b);
  37. }
  38. public function pop() {
  39. vars.pop();
  40. bits.pop();
  41. }
  42. public function append( v: VarDeps, b : Int ) {
  43. for( i => v2 in vars )
  44. if( v2 == v ) {
  45. bits[i] |= b;
  46. return;
  47. }
  48. push(v,b);
  49. }
  50. public function appendTo( w : WriteTo ) {
  51. for( i => v in vars )
  52. w.append(v, bits[i]);
  53. }
  54. }
  55. class Dce {
  56. var used : Map<Int,VarDeps>;
  57. var channelVars : Array<TVar>;
  58. var markAsKeep : Bool;
  59. public function new() {
  60. }
  61. public function dce( shaders : Array<ShaderData> ) {
  62. // collect vars dependencies
  63. used = new Map();
  64. channelVars = [];
  65. var inputs = [];
  66. for( s in shaders ) {
  67. for( v in s.vars ) {
  68. var i = get(v);
  69. if( v.kind == Input )
  70. inputs.push(i);
  71. if( v.kind == Output || v.type.match(TBuffer(_,_,RW) | TRWTexture(_)) )
  72. i.keep = 15;
  73. }
  74. }
  75. // collect dependencies
  76. for( s in shaders ) {
  77. for( f in s.funs )
  78. check(f.expr, new WriteTo(), new WriteTo());
  79. }
  80. var outExprs = [];
  81. while( true ) {
  82. debug("DCE LOOP");
  83. for( v in used )
  84. if( v.keep != 0 )
  85. markRec(v, v.keep);
  86. while( inputs.length > 1 && inputs[inputs.length - 1].used == 0 )
  87. inputs.pop();
  88. for( v in inputs )
  89. markRec(v, 15);
  90. outExprs = [];
  91. for( s in shaders ) {
  92. for( f in s.funs )
  93. outExprs.push(mapExpr(f.expr, false));
  94. }
  95. // post add conditional branches
  96. markAsKeep = false;
  97. for( e in outExprs )
  98. checkBranches(e);
  99. if( !markAsKeep ) break;
  100. }
  101. for( s in shaders ) {
  102. for( f in s.funs )
  103. f.expr = outExprs.shift();
  104. }
  105. for( v in used ) {
  106. if( v.used != 0 ) continue;
  107. if( v.v.kind == VarKind.Input) continue;
  108. for( s in shaders )
  109. s.vars.remove(v.v);
  110. }
  111. return shaders.copy();
  112. }
  113. function get( v : TVar ) {
  114. var vd = used.get(v.id);
  115. if( vd == null ) {
  116. vd = new VarDeps(v);
  117. used.set(v.id, vd);
  118. }
  119. return vd;
  120. }
  121. function varName( v : TVar, bits = 15 ) {
  122. return Debug.varName(v, bits);
  123. }
  124. function markRec( v : VarDeps, bits : Int ) {
  125. if( v.used & bits == bits ) return;
  126. bits &= ~v.used;
  127. debug(varName(v.v,bits)+" is used");
  128. v.used |= bits;
  129. for( d in v.adeps ) {
  130. var mask = makeFieldsBits(15, bits);
  131. if( d.fields & mask != 0 )
  132. markRec(d.v, extractFieldsBits(d.fields,bits));
  133. }
  134. }
  135. function extractFieldsBits( fields : Int, write : Int ) {
  136. return ((write & 1 == 0 ? 0 : fields) | (write & 2 == 0 ? 0 : fields>>4) | (write & 4 == 0 ? 0 : fields>>8) | (write & 8 == 0 ? 0 : fields>>12)) & 15;
  137. }
  138. function makeFieldsBits( read : Int, write : Int ) {
  139. return read * ((write & 1) + ((write & 2) << 3) + ((write & 4) << 6) + ((write & 8) << 9));
  140. }
  141. function link( v : TVar, writeTo : WriteTo, bits = 15 ) {
  142. var vd = get(v);
  143. for( i => w in writeTo.vars ) {
  144. if( w == null ) {
  145. // mark for conditional
  146. if( vd.keep == 0 ) {
  147. debug("Force keep "+varName(vd.v));
  148. vd.keep = 15;
  149. markAsKeep = true;
  150. }
  151. continue;
  152. }
  153. var d = w.deps.get(v.id);
  154. if( d == null ) {
  155. d = new VarRel(vd);
  156. w.deps.set(v.id, d);
  157. w.adeps.push(d);
  158. }
  159. var fields = makeFieldsBits(bits, writeTo.bits[i]);
  160. if( d.fields & fields != fields ) {
  161. d.fields |= fields;
  162. debug(varName(w.v,writeTo.bits[i])+" depends on "+varName(vd.v,bits));
  163. }
  164. }
  165. }
  166. function swizBits( sw : Array<Component> ) {
  167. var b = 0;
  168. for( c in sw )
  169. b |= 1 << c.getIndex();
  170. return b;
  171. }
  172. function check( e : TExpr, writeTo : WriteTo, isAffected : WriteTo ) : Void {
  173. switch( e.e ) {
  174. case TVar(v):
  175. link(v, writeTo);
  176. case TSwiz({ e : TVar(v) }, swiz):
  177. link(v, writeTo, swizBits(swiz));
  178. case TBinop(OpAssign | OpAssignOp(_), { e : TVar(v) }, e):
  179. var v = get(v);
  180. writeTo.push(v,15);
  181. check(e, writeTo, isAffected);
  182. writeTo.pop();
  183. isAffected.append(v,15);
  184. case TBinop(OpAssign | OpAssignOp(_), { e : TSwiz({ e : TVar(v) },swiz) }, e):
  185. var v = get(v);
  186. var bits = swizBits(swiz);
  187. writeTo.push(v,bits);
  188. check(e, writeTo, isAffected);
  189. writeTo.pop();
  190. isAffected.append(v,bits);
  191. case TBinop(OpAssign | OpAssignOp(_), { e : (TArray({ e: TVar(v) }, i) | TSwiz({ e : TArray({ e : TVar(v) }, i) },_) | TField({e : TArray({ e : TVar(v) }, i) }, _)) }, e):
  192. var v = get(v);
  193. writeTo.push(v,15);
  194. check(i, writeTo, isAffected);
  195. check(e, writeTo, isAffected);
  196. writeTo.pop();
  197. isAffected.append(v,15);
  198. case TBlock(el):
  199. var noWrite = new WriteTo();
  200. for( i in 0...el.length )
  201. check(el[i],i < el.length - 1 ? noWrite : writeTo, isAffected);
  202. case TVarDecl(v, init) if( init != null ):
  203. writeTo.push(get(v),15);
  204. check(init, writeTo, isAffected);
  205. writeTo.pop();
  206. case TIf(e, eif, eelse):
  207. var affect = new WriteTo();
  208. check(eif, writeTo, affect);
  209. if( eelse != null ) check(eelse, writeTo, affect);
  210. affect.appendTo(isAffected);
  211. writeTo.appendTo(affect);
  212. check(e, affect, isAffected);
  213. case TFor(v, it, loop):
  214. var affect = new WriteTo();
  215. check(loop, writeTo, affect);
  216. affect.appendTo(isAffected);
  217. check(it, affect, isAffected);
  218. case TWhile(e, loop, _):
  219. var affect = new WriteTo();
  220. check(loop, writeTo, affect);
  221. affect.appendTo(isAffected);
  222. writeTo.appendTo(affect);
  223. check(e, affect, isAffected);
  224. case TCall({ e : TGlobal(ChannelRead) }, [{ e : TVar(c) }, uv, { e : TConst(CInt(cid)) }]):
  225. check(uv, writeTo, isAffected);
  226. if( channelVars[cid] == null ) {
  227. channelVars[cid] = c;
  228. link(c, writeTo);
  229. } else {
  230. link(channelVars[cid], writeTo);
  231. }
  232. case TCall({ e : TGlobal(ChannelReadLod) }, [{ e : TVar(c) }, uv, lod, { e : TConst(CInt(cid)) }]):
  233. check(uv, writeTo, isAffected);
  234. check(lod, writeTo, isAffected);
  235. if( channelVars[cid] == null ) {
  236. channelVars[cid] = c;
  237. link(c, writeTo);
  238. } else {
  239. link(channelVars[cid], writeTo);
  240. }
  241. case TCall({ e : TGlobal(ImageStore)}, [{ e : TVar(v) }, uv, val]):
  242. var v = get(v);
  243. writeTo.push(v,15);
  244. check(uv, writeTo, isAffected);
  245. check(val, writeTo, isAffected);
  246. writeTo.pop();
  247. isAffected.append(v,15);
  248. case TSyntax(_, _, args):
  249. for ( arg in args ) {
  250. if ( arg.access != Read ) {
  251. var tvars : Array<VarDeps> = [];
  252. function findTVars( e : TExpr ) {
  253. switch ( e.e ) {
  254. case TVar(v): tvars.push(get(v));
  255. default: e.iter(findTVars);
  256. }
  257. }
  258. findTVars(arg.e);
  259. for ( v in tvars ) {
  260. writeTo.push(v, 15);
  261. for ( arg2 in args ) {
  262. if ( arg2.access != Write ) check(arg2.e, writeTo, isAffected);
  263. }
  264. writeTo.pop();
  265. isAffected.append(v, 15);
  266. }
  267. } else {
  268. check(arg.e, writeTo, isAffected);
  269. }
  270. }
  271. default:
  272. e.iter(check.bind(_, writeTo, isAffected));
  273. }
  274. }
  275. function checkBranches( e : TExpr ) {
  276. // found a branch with side effect left, this condition vars needs to be kept
  277. switch( e.e ) {
  278. case TIf(cond, _, _):
  279. var writeTo = new WriteTo();
  280. writeTo.append(null,0);
  281. check(cond, writeTo, new WriteTo());
  282. case TWhile(cond, loop, _):
  283. var writeTo = new WriteTo();
  284. writeTo.append(null,0);
  285. check(cond, writeTo, new WriteTo());
  286. default:
  287. }
  288. e.iter(checkBranches);
  289. }
  290. function mapExpr( e : TExpr, isVar ) : TExpr {
  291. switch( e.e ) {
  292. case TBlock(el):
  293. var out = [];
  294. var count = 0;
  295. for( e in el ) {
  296. var isVar = isVar && count == el.length - 1;
  297. var e = mapExpr(e, isVar);
  298. if( e.hasSideEffect() || isVar )
  299. out.push(e);
  300. count++;
  301. }
  302. return { e : TBlock(out), p : e.p, t : e.t };
  303. case TVarDecl(v,e2) | TBinop(OpAssign | OpAssignOp(_), { e : (TVar(v) | TSwiz( { e : TVar(v) }, _) | TArray( { e : TVar(v) }, _)) }, e2) if( get(v).used == 0 ):
  304. return (e2 != null && e2.hasSideEffect()) ? mapExpr(e2, false) : { e : TConst(CNull), t : e.t, p : e.p };
  305. case TBinop(OpAssign | OpAssignOp(_), { e : TSwiz( { e : TVar(v) }, swiz) }, e2) if( get(v).used & swizBits(swiz) == 0 ):
  306. return e2.hasSideEffect() ? mapExpr(e2, false) : { e : TConst(CNull), t : e.t, p : e.p };
  307. case TCall({ e : TGlobal(ChannelRead) }, [_, uv, { e : TConst(CInt(cid)) }]):
  308. var c = channelVars[cid];
  309. return { e : TCall({ e : TGlobal(Texture), p : e.p, t : TVoid }, [{ e : TVar(c), t : c.type, p : e.p }, mapExpr(uv,true)]), t : TVoid, p : e.p };
  310. case TCall({ e : TGlobal(ChannelReadLod) }, [_, uv, lod, { e : TConst(CInt(cid)) }]):
  311. var c = channelVars[cid];
  312. return { e : TCall({ e : TGlobal(TextureLod), p : e.p, t : TVoid }, [{ e : TVar(c), t : c.type, p : e.p }, mapExpr(uv,true), mapExpr(lod,true)]), t : TVoid, p : e.p };
  313. case TCall({ e : TGlobal(ChannelFetch) }, [_, pos, { e : TConst(CInt(cid)) }]):
  314. var c = channelVars[cid];
  315. return { e : TCall({ e : TGlobal(Texel), p : e.p, t : TVoid }, [{ e : TVar(c), t : c.type, p : e.p }, mapExpr(pos,true)]), t : TVoid, p : e.p };
  316. case TCall({ e : TGlobal(ChannelFetch) }, [_, pos, lod, { e : TConst(CInt(cid)) }]):
  317. var c = channelVars[cid];
  318. return { e : TCall({ e : TGlobal(Texel), p : e.p, t : TVoid }, [{ e : TVar(c), t : c.type, p : e.p }, mapExpr(pos,true), mapExpr(lod,true)]), t : TVoid, p : e.p };
  319. case TCall({ e : TGlobal(ChannelTextureSize) }, [_, { e : TConst(CInt(cid)) }]):
  320. var c = channelVars[cid];
  321. return { e : TCall({ e : TGlobal(TextureSize), p : e.p, t : TVoid }, [{ e : TVar(c), t : c.type, p : e.p }]), t : TVoid, p : e.p };
  322. case TCall({ e : TGlobal(ChannelTextureSize) }, [_, lod, { e : TConst(CInt(cid)) }]):
  323. var c = channelVars[cid];
  324. return { e : TCall({ e : TGlobal(TextureSize), p : e.p, t : TVoid }, [{ e : TVar(c), t : c.type, p : e.p }, mapExpr(lod,true)]), t : TVoid, p : e.p };
  325. case TIf(e, econd, eelse):
  326. var e = mapExpr(e, true);
  327. var econd = mapExpr(econd, isVar);
  328. var eelse = eelse == null ? null : mapExpr(eelse, isVar);
  329. if( !isVar && !econd.hasSideEffect() && (eelse == null || !eelse.hasSideEffect()) )
  330. return { e : TConst(CNull), t : e.t, p : e.p };
  331. return { e : TIf(e, econd, eelse), p : e.p, t : e.t };
  332. case TFor(v, it, loop):
  333. var it = mapExpr(it, true);
  334. var loop = mapExpr(loop, false);
  335. if( !loop.hasSideEffect() )
  336. return { e : TConst(CNull), t : e.t, p : e.p };
  337. return { e : TFor(v, it, loop), p : e.p, t : e.t };
  338. case TWhile(e, loop, normalWhile):
  339. var e = mapExpr(e, true);
  340. var loop = mapExpr(loop, isVar);
  341. if( !loop.hasSideEffect() )
  342. return { e : TConst(CNull), t : e.t, p : e.p };
  343. return { e : TWhile(e, loop, normalWhile), p : e.p, t : e.t };
  344. case TMeta(m, args, em):
  345. var em = mapExpr(em, isVar);
  346. if( !isVar && !em.hasSideEffect() )
  347. return { e : TConst(CNull), t : e.t, p : e.p };
  348. return { e : TMeta(m, args, em), t : e.t, p : e.p };
  349. default:
  350. return e.map(function(e) return mapExpr(e,true));
  351. }
  352. }
  353. }