• R/O
  • SSH
  • HTTPS

naniya: Commit


Commit MetaInfo

Revision86 (tree)
Zeit2007-04-04 15:00:09
Autorchapuni

Log Message

LR生成の際に、式の相関により最適化を行うようにした。かなり効果があるハズだが、ときどき重いフェイズに突入してしまい、「ドツボ」ることがあるのでこれが今後の課題。

・木の最適化に、AND 1, OR 0 などの無意味項を丸める処理を追加。
・最適化の結果参照されなくなったLRノードを削除するようにした。ついでに削除ノードを最適化効果指標として報告するようにした。
・synth_extract_minterm() に記述されていた処理の大部分は、前段の synth_expand_terms() にて終わっているので、処理を大幅に削った。
・synth_simplify_qm() は、生成限界に達したときに全主項を返すようにした(未デバッグ)。

ついでに、N_INFLIGHT=2 にしてみた。いいのか?

Ändern Zusammenfassung

Diff

--- mty/synth.c (revision 85)
+++ mty/synth.c (revision 86)
@@ -23,6 +23,9 @@
2323
2424 static int g_tn = 32;
2525
26+static int n_lrq[11];
27+static struct ITREE **lrq[11];
28+
2629 /* アンチョコから引用したビット数え */
2730 static unsigned popcnt32(uint32_t x)
2831 {
@@ -297,7 +300,7 @@
297300 /***************************************************************
298301 *
299302 * ノードを切り離す
300- * ref == 0 になったらメモリからも削除して 1 を返す
303+ * ref == 0 になったらメモリからも削除して削除数を返す
301304 *
302305 */
303306
@@ -305,22 +308,38 @@
305308 synth_unlink(struct ITREE *node)
306309 {
307310 int i;
311+ int n = 0;
308312
309313 assert(node->ref > 0);
310314 if (--node->ref > 0)
311315 return 0;
312316
313- assert(node->opcode != NI_LR);
314317 assert(node->opcode != NI_DICT);
315318
316319 /* 枝を切り落とす */
317320 for (i = 0; i < node->n_ops; i++)
318- synth_unlink(node->ops[i]);
321+ n += synth_unlink(node->ops[i]);
319322
323+ /* LR の場合、リストから切り離す */
324+ if (node->opcode == NI_LR)
325+ {
326+ for (i = 0; i < n_lrq[node->lrn]; i++)
327+ if (lrq[node->lrn][i] == node)
328+ {
329+ memmove(&lrq[node->lrn][i],
330+ &lrq[node->lrn][i + 1],
331+ (--n_lrq[node->lrn] - i) * sizeof(*lrq[0][0]));
332+ goto deleted;
333+ }
334+ assert(!"XXX orphan node");
335+ deleted:
336+ ;
337+ }
338+
320339 /* 自分自身を削除 */
321340 synth_free(node);
322341
323- return 1;
342+ return n + 1;
324343 }
325344
326345 /***************************************************************
@@ -329,9 +348,6 @@
329348 *
330349 */
331350
332-static int n_lrq[11];
333-static struct ITREE **lrq[11];
334-
335351 struct ITREE *
336352 synth_make_lr(uint64_t bmp, int lrn)
337353 {
@@ -715,15 +731,14 @@
715731 static
716732 int
717733 synth_expand_terms(uint64_t *px, /* 展開先 */
718- int pxi, /* 展開先インデクス */
719- uint64_t x, /* 積 */
734+ int maxpx, /* 限界個数 */
720735 uint64_t const *cov, /* 被覆 */
721736 int covn) /* 被覆個数 */
722737 {
723738 int i, j, k;
739+ int pxi = 0;
724740
725741 assert(covn > 0);
726- assert(pxi == 0);
727742
728743 for (i = 0; i < covn; i++)
729744 {
@@ -756,6 +771,9 @@
756771 continue;
757772 }
758773 }
774+ assert(pxi <= maxpx);
775+ if (pxi == maxpx)
776+ return INT_MAX; /* 諦めてすべての項を選ばせる */
759777 px[pxi++] = u;
760778 exp_dup:
761779 ;
@@ -789,7 +807,7 @@
789807 int covn,
790808 int etn)
791809 {
792- int i, j;
810+ int i;
793811 int n;
794812 uint64_t m;
795813 int minc;
@@ -802,13 +820,14 @@
802820 assert(term != NULL);
803821
804822 n = synth_expand_terms(term, /* 展開先 */
805- 0,
806- 0ULL, /* 初期項 */
823+ etn,
807824 cov,
808825 covn);
809826 #if DEBUG>=1
810827 printf("generated etn=%d(pon=%d)\n", n, covn);
811828 #endif
829+ if (n == INT_MAX)
830+ return (uint64_t)-1;
812831 assert(n <= etn);
813832 assert(n > 0);
814833 #if DEBUG>=2
@@ -816,39 +835,18 @@
816835 printf("%3d: %08X%08X(%2d)\n", i, (unsigned)(term[i] >> 32), (unsigned)term[i], popcnt64(term[i]));
817836 #endif
818837
819- do
838+ m = 0;
839+ minc = INT_MAX;
840+ for (i = 0; i < n; i++)
820841 {
821- m = 0;
822- minc = INT_MAX;
823- for (i = 0; i < n; i++)
842+ unsigned c = popcnt64(term[i]);
843+ if (minc > c)
824844 {
825- for (j = i + 1; j < n; j++)
826- {
827- /* 包含関係を丸める
828- すなわち (a & b) | a => a */
829- if (term[i] == term[j])
830- continue;
831- else if ((term[i] & term[j]) == term[j])
832- term[i] = term[j];
833- else if ((term[i] & term[j]) == term[i])
834- term[j] = term[i];
835- else
836- continue;
837- minc = 0;
838- }
839- if (0 < minc)
840- {
841- /* カウントを取る */
842- unsigned c = popcnt64(term[i]);
843- if (minc > c)
844- {
845- minc = c;
846- m = term[i];
847- }
848- }
845+ minc = c;
846+ m = term[i];
849847 }
850848 }
851- while (!(0 < minc && minc <= 64));
849+ assert(minc > 0);
852850
853851 #if DEBUG>=2
854852 for (i = 0; i < n; i++)
@@ -869,7 +867,8 @@
869867 * Quine-McCluskey 法により bmp を簡単化する
870868 * 後段の被覆展開は Petrick 法による
871869 *
872- * 概算項数を返す(あきらめたときは INT_MAX)
870+ * 概算項数を返す
871+ * あきらめたときは INT_MAX を返し、全主項を生成する
873872 *
874873 * aox に積和形による結果を返す(m==0 terminated)
875874 *
@@ -889,6 +888,7 @@
889888 static
890889 int
891890 synth_simplify_qm(struct QMX *aox,
891+ int maxn, /* 積和展開上限(最適化レベル) */
892892 uint64_t bmp, /* 真理値表 */
893893 uint64_t dc) /* 1 のビットは D/C */
894894 {
@@ -895,7 +895,7 @@
895895 int i, j;
896896 uint64_t m;
897897 int tn;
898- int pon, dcn, prn, pgn, etn;
898+ int pon, dcn, prn, pgn, etn, mtn;
899899 uint64_t cov[QMX_LEN]; /* 被覆 */
900900 struct QMX pox[QMX_LEN]; /* 最小項 */
901901 struct QMX atmx[256 * QMX_LEN]; /* 64k を超えないくらいが目安? */
@@ -1066,15 +1066,6 @@
10661066 printf("%2d: %08X%08x\n", i, (unsigned)(cov[i] >> 32), (unsigned)cov[i]);
10671067 #endif
10681068
1069-#if 0
1070- /* 項見積もりが超デカイ場合はあきらめてみる */
1071- if (etn >= INT_MAX / sizeof(*cov)
1072- || etn == 0)
1073- return INT_MAX;
1074-#else
1075- etn = 0x20000; /* XXX */
1076-#endif
1077-
10781069 /* 最小被覆の抽出
10791070 (最小なのか?) */
10801071 #if DEBUG>=2
@@ -1081,15 +1072,17 @@
10811072 for (i = 0; i < pon; i++)
10821073 printf("%2d: %08X%08x\n", i, (unsigned)(cov[i] >> 32), (unsigned)cov[i]);
10831074 #endif
1084- m = synth_extract_minterm(cov, pon, etn);
1075+ m = synth_extract_minterm(cov, pon, maxn); /* 耐えきれなかったら全ビット1 */
10851076
10861077 /* 結果を展開 */
1087- tn = popcnt64(m) - 1;
1078+ mtn = popcnt64(m);
1079+ m &= ((1ULL << prn) - 1);
1080+ tn = mtn;
10881081 j = 0;
10891082 for (i = 0; i < prn; i++)
10901083 if (m & (1ULL << i))
10911084 {
1092- tn += popcnt32(tmx[i].m) - 1;
1085+ tn += popcnt32(tmx[i].m);
10931086 if (aox)
10941087 aox[j++] = tmx[i];
10951088 #if DEBUG>=1
@@ -1110,7 +1103,8 @@
11101103 if (tmx != atmx)
11111104 free(tmx);
11121105
1113- return tn;
1106+ /* 全部展開してしまった場合 */
1107+ return (mtn > prn ? INT_MAX : tn);
11141108 }
11151109
11161110 /***************************************************************
@@ -1120,7 +1114,7 @@
11201114 */
11211115
11221116 #define REG_RELOAD 0 /* 付け焼き刃の最適はむしろ有害 */
1123-#define N_INFLIGHT 1 /* 同時演算を試みる個数 */
1117+#define N_INFLIGHT 2 /* 同時演算を試みる個数 */
11241118
11251119 #define NREGS 8
11261120 static struct ITREE *a_regs[NREGS];
@@ -1190,12 +1184,77 @@
11901184 *
11911185 */
11921186
1187+struct LRX
1188+{
1189+ struct ITREE *node;
1190+ int mtn;
1191+ int otn;
1192+ /* 種類
1193+ 0:単独
1194+ 1:AND
1195+ 2:OR
1196+ 3:NAND */
1197+ int src;
1198+ struct QMX *xp;
1199+ unsigned mtm;
1200+};
1201+
11931202 static
11941203 int
1204+cmp_lrx(void const *pa, void const *pb)
1205+{
1206+ struct LRX const *a = pa;
1207+ struct LRX const *b = pb;
1208+ int r;
1209+ assert(a->node != NULL);
1210+ assert(b->node != NULL);
1211+ if ((r = a->mtn - b->mtn)
1212+ || (r = a->mtn - b->mtn)
1213+ || (r = a->mtm - b->mtm)
1214+ || (r = (int)popcnt64(a->node->bmp) - (int)popcnt64(b->node->bmp))
1215+ || (r = (int)(a->node->bmp >> 32) - (int)(b->node->bmp >> 32)))
1216+ return r;
1217+ return (int)a->node->bmp - (int)b->node->bmp;
1218+}
1219+
1220+static
1221+void
1222+synth_load_lr(FILE *sfp, int pos, int i)
1223+{
1224+ int o = tr_fp[6 * pos + (5 - i)];
1225+ if (o < 32)
1226+ reg_mem(sfp,
1227+ OP_MOV,
1228+ i,
1229+ PTR_LR,
1230+ sizeof(WS_T) * (o - 16));
1231+ else if (o < 64)
1232+ reg_mem(sfp,
1233+ OP_MOV,
1234+ i,
1235+ PTR_RL,
1236+ sizeof(WS_T) * (o - 32 - 16));
1237+ assert(o < 64);
1238+}
1239+
1240+static
1241+int
11951242 synth_assemble_lr(FILE *sfp, int pos, struct ITREE **nodea, int noden)
11961243 {
11971244 int i;
11981245 uint64_t m;
1246+ unsigned regs_loaded = 0;
1247+ int maxtn = 0x400;
1248+ int recon;
1249+ struct LRX *lrx;
1250+ struct QMX (*qmx)[QMX_LEN + 1];
1251+ int nts_total = 0;
1252+ int nts_reduced = 0;
1253+ assert(noden <= 10000);
1254+#if DEBUG>=1
1255+ fprintf(stderr, "WELCOME! (noden=%d)\n", noden);
1256+#endif
1257+ recon = 0;
11991258
12001259 if (noden == 0)
12011260 return 0;
@@ -1206,75 +1265,178 @@
12061265 ? 0x0000000000000000ULL
12071266 : 0xEEEEEEEEEEEEEEEEULL);
12081267
1209- /* オペランドのセットアップ */
1210- for (i = 0; i < 6; i++)
1211- {
1212- int o = tr_fp[6 * pos + (5 - i)];
1213- if (o < 32)
1214- reg_mem(sfp,
1215- OP_MOV,
1216- i,
1217- PTR_LR,
1218- sizeof(WS_T) * (o - 16));
1219- else if (o < 64)
1220- reg_mem(sfp,
1221- OP_MOV,
1222- i,
1223- PTR_RL,
1224- sizeof(WS_T) * (o - 32 - 16));
1225- }
1268+ /* ワークエリア */
1269+ lrx = calloc(noden, sizeof(*lrx));
1270+ assert(lrx != NULL);
1271+ qmx = calloc(noden, sizeof(*qmx));
1272+ assert(qmx != NULL);
12261273
12271274 for (i = 0; i < noden; i++)
12281275 {
1229- int tn;
1276+ int j, k;
12301277 unsigned opo, opa;
1231- struct ITREE *node = nodea[i];
1232- struct QMX xp[QMX_LEN + 1];
1278+ struct ITREE *node;
1279+ struct QMX *xp;
12331280 struct QMX const *px;
12341281
1235- assert(node != NULL);
1236- assert(node->opcode == NI_LR);
1237- xp[QMX_LEN].m = 0;
1282+ for (j = i; j < noden; j++)
1283+ {
1284+ /* 最初のループで通るはず */
1285+ if (lrx[j].node == NULL)
1286+ {
1287+ lrx[j].node = nodea[j];
1288+ lrx[j].xp = qmx[j];
1289+ lrx[j].mtn = synth_simplify_qm(lrx[j].xp, maxtn,
1290+ lrx[j].node->bmp,
1291+ m);
1292+ lrx[j].otn = lrx[j].mtn;
1293+ lrx[j].mtm = OP_MOV;
1294+ lrx[j].src = -256; /* 決してマッチしない */
1295+ }
1296+ if (recon && lrx[j].mtn == INT_MAX)
1297+ {
1298+ lrx[j].mtn = synth_simplify_qm(lrx[j].xp, maxtn,
1299+ lrx[j].node->bmp,
1300+ m);
1301+ lrx[j].otn = lrx[j].mtn;
1302+ assert(lrx[j].mtm == OP_MOV);
1303+ }
1304+ /* 被覆を求める */
1305+ for (k = 0; k < i; k++)
1306+ {
1307+ int ttn;
1308+ struct QMX txp[QMX_LEN + 1];
12381309
1310+ if (!recon && k < i - 2) /* 計算する必要ナシ */
1311+ continue;
1312+
1313+ if ((lrx[k].node->bmp & lrx[j].node->bmp) == lrx[j].node->bmp
1314+ && (ttn = synth_simplify_qm(txp, maxtn,
1315+ lrx[j].node->bmp,
1316+ m | ~lrx[k].node->bmp)) < INT_MAX)
1317+ {
1318+ ttn++; /* ロードの分浪費 */
1319+ if (lrx[j].mtn > ttn)
1320+ {
1321+ lrx[j].mtn = ttn;
1322+ lrx[j].mtm = OP_AND;
1323+ lrx[j].src = k;
1324+ memcpy(lrx[j].xp, txp, sizeof(txp));
1325+ }
1326+ }
1327+
1328+ if ((lrx[k].node->bmp & lrx[j].node->bmp) == lrx[k].node->bmp
1329+ && (ttn = synth_simplify_qm(txp, maxtn,
1330+ lrx[j].node->bmp,
1331+ m | lrx[k].node->bmp)) < INT_MAX)
1332+ {
1333+ /* OR */
1334+ if (k != i - 1) /* 直前のものでなければ1段コスト増 */
1335+ ttn++;
1336+ if (lrx[j].mtn > ttn)
1337+ {
1338+ lrx[j].mtn = ttn;
1339+ lrx[j].mtm = OP_OR;
1340+ lrx[j].src = k;
1341+ memcpy(lrx[j].xp, txp, sizeof(txp));
1342+ }
1343+ }
1344+
1345+#if defined (OP_ANDN)
1346+ if ((~lrx[k].node->bmp & lrx[j].node->bmp) == lrx[j].node->bmp
1347+ && (ttn = synth_simplify_qm(txp, maxtn,
1348+ lrx[j].node->bmp,
1349+ m | lrx[k].node->bmp)) < INT_MAX)
1350+ {
1351+ /* ANDN */
1352+ ttn++; /* ロードの分浪費 */
1353+ if (lrx[j].mtn > ttn)
1354+ {
1355+ lrx[j].mtn = ttn;
1356+ lrx[j].mtm = OP_ANDN;
1357+ lrx[j].src = k;
1358+ memcpy(lrx[j].xp, txp, sizeof(txp));
1359+ }
1360+ }
1361+#endif
1362+ }
1363+ }
1364+
1365+ /* ソート */
1366+ recon = 0;
1367+ qsort(&lrx[i], noden - i, sizeof(*lrx), cmp_lrx);
1368+
1369+ if (lrx[i].mtn == INT_MAX)
1370+ {
1371+ if (maxtn < 0x4000)
1372+ {
1373+ maxtn <<= 2;
1374+ assert(maxtn > 0);
1375+ fprintf(stderr,
1376+ "%d文字目にてドツボにハマり中…\t(%d)\n",
1377+ pos,
1378+ maxtn);
1379+#if DEBUG>=1
1380+ fprintf(stderr, "%4d: grow up to 0x%X\n", i, maxtn);
1381+#endif
1382+ recon = 1;
1383+ i--; continue; /* redo */
1384+ }
1385+ else
1386+ {
1387+ /* gave up! 全項でつくってしまう */
1388+ fprintf(stderr, "%d文字目には参った!\n", pos);
1389+ }
1390+ }
1391+
1392+#if DEBUG>=1
1393+ fprintf(stderr,
1394+ "%4d/%4d:n=%3d/%3d(mt=%02X:src=%4d) %08X%08X\n",
1395+ i, (noden - i),
1396+ lrx[i].mtn,
1397+ lrx[i].otn,
1398+ lrx[i].mtm,
1399+ lrx[i].src,
1400+ (unsigned)(lrx[i].node->bmp >> 32),
1401+ (unsigned)lrx[i].node->bmp);
1402+#endif
1403+ assert(lrx[i].mtn >= 0);
1404+ nts_total += lrx[i].mtn;
1405+ if (lrx[i].mtm && lrx[i].otn < INT_MAX)
1406+ nts_reduced += lrx[i].otn - lrx[i].mtn;
1407+ node = lrx[i].node;
1408+ xp = lrx[i].xp;
1409+
12391410 /* ワークエリア割り当て */
12401411 node->tn = g_tn++;
12411412 assert(g_tn <= sizeof(((struct PARAM *)NULL)->hit) / sizeof(SLICE));
12421413
1243- /* ノードが完全定数である場合 */
1244- if (!(node->bmp & ~m))
1414+ switch (lrx[i].mtm)
12451415 {
1246- /* nil */
1247- reg_op(sfp, OP_XOR, 7, 7);
1248- reg_mem(sfp,
1249- OP_STOR,
1250- 7,
1251- PTR_T,
1252- sizeof(WS_T) * (OFS_T + node->tn));
1253- continue;
1416+ case OP_OR: opo = OP_OR; break;
1417+ default: opo = OP_MOV; break;
12541418 }
1255- else if (!~(node->bmp | m))
1419+
1420+ /* 既出の項を利用して演算するばあい */
1421+ if (lrx[i].mtm != OP_MOV && lrx[i].src != i - 1)
1422+ reg_mem(sfp,
1423+ OP_MOV,
1424+ 7,
1425+ PTR_T,
1426+ sizeof(WS_T) * (OFS_T + lrx[lrx[i].src].node->tn));
1427+
1428+ if (!(node->bmp & ~m)
1429+ || !~(node->bmp | m))
12561430 {
1257- /* t てぬき */
1258- reg_mem(sfp,
1259- OP_MOV,
1260- 7,
1261- PTR_T,
1262- sizeof(WS_T) * (OFS_T - 32 + 16));
1263- reg_mem(sfp,
1264- OP_STOR,
1265- 7,
1266- PTR_T,
1267- sizeof(WS_T) * (OFS_T + node->tn));
1268- continue;
1431+ fprintf(stderr,
1432+ "やっちゃったー %08X%08X\n",
1433+ (unsigned)(node->bmp >> 32),
1434+ (unsigned)node->bmp);
1435+ assert(!!(node->bmp & ~m));
1436+ assert(!!~(node->bmp | m));
12691437 }
12701438
12711439 /* 圧縮された式を展開 */
1272- tn = synth_simplify_qm(xp,
1273- node->bmp,
1274- m);
1275- assert(0 <= tn && tn < INT_MAX);
1276- assert(xp[0].m);
1277- opo = OP_MOV;
12781440 for (px = xp; px->m; px++)
12791441 {
12801442 int j;
@@ -1287,6 +1449,11 @@
12871449 for (j = 0; j < 6; j++)
12881450 if (mi & (1 << j))
12891451 {
1452+ if (!(regs_loaded & (1 << j)))
1453+ {
1454+ synth_load_lr(sfp, pos, j);
1455+ regs_loaded |= (1 << j);
1456+ }
12901457 reg_op(sfp, opa, 6, j);
12911458 opa = OP_OR;
12921459 }
@@ -1306,6 +1473,11 @@
13061473 for (j = 0; j < 6; j++)
13071474 if (pi & (1 << j))
13081475 {
1476+ if (!(regs_loaded & (1 << j)))
1477+ {
1478+ synth_load_lr(sfp, pos, j);
1479+ regs_loaded |= (1 << j);
1480+ }
13091481 reg_op(sfp, opa, 6, j);
13101482 opa = OP_AND;
13111483 }
@@ -1325,7 +1497,45 @@
13251497 opo = OP_OR;
13261498 }
13271499
1328- assert(opo != OP_MOV);
1500+ if (opo == OP_MOV)
1501+ {
1502+ /* 積和項が生成されなかった場合 */
1503+ assert(lrx[i].mtm != OP_MOV);
1504+ assert(lrx[i].src >= 0);
1505+ if (lrx[i].src != i - 1)
1506+ reg_mem(sfp,
1507+ OP_MOV,
1508+ 7,
1509+ PTR_T,
1510+ sizeof(WS_T) * (OFS_T + lrx[lrx[i].src].node->tn));
1511+ switch (lrx[i].mtm)
1512+ {
1513+#ifdef OP_ANDN
1514+ case OP_ANDN:
1515+ reg_mem(sfp,
1516+ OP_XOR,
1517+ 7,
1518+ PTR_T,
1519+ sizeof(WS_T) * (OFS_T - 32 + 16));
1520+ break;
1521+#endif
1522+ }
1523+ }
1524+ else switch (lrx[i].mtm) /* 既出項との演算 */
1525+ {
1526+ case OP_AND:
1527+#ifdef OP_ANDN
1528+ case OP_ANDN:
1529+#endif
1530+ reg_mem(sfp,
1531+ lrx[i].mtm,
1532+ 7,
1533+ PTR_T,
1534+ sizeof(WS_T) * (OFS_T + lrx[lrx[i].src].node->tn));
1535+ break;
1536+ }
1537+
1538+ /* 結果の格納 */
13291539 reg_mem(sfp,
13301540 OP_STOR,
13311541 7,
@@ -1334,7 +1544,13 @@
13341544
13351545 a_regs[7] = node; /* XXX 気休め */
13361546 }
1547+#if DEBUG>=1
1548+ fprintf(stderr, "term=%d (reduced %d)\n", nts_total, nts_reduced);
1549+#endif
13371550
1551+ free(lrx);
1552+ free(qmx);
1553+
13381554 return 0;
13391555 }
13401556
@@ -1527,6 +1743,18 @@
15271743 /* 先に、枝の最適化に挑戦 */
15281744 neff += synth_optimize(child);
15291745
1746+ /* 意味のないノードを落とす */
1747+ if (child->opcode == NI_LR
1748+ && ((node->opcode == NI_AND && !~child->bmp)
1749+ || (node->opcode == NI_OR && ~child->bmp)))
1750+ {
1751+ neff += 1 + synth_unlink(child);
1752+ memmove(&node->ops[i],
1753+ &node->ops[i + 1],
1754+ (--node->n_ops - i) * sizeof(*node->ops));
1755+ i--; goto redo_i;
1756+ }
1757+
15301758 /* 同じポインタは無条件に融合できる */
15311759 for (j = 0; j < i; j++)
15321760 if (node->ops[j] == child)
@@ -1576,8 +1804,7 @@
15761804 assert(child->tn < 0);
15771805 node->ops[i] = child->ops[0];
15781806 child->n_ops--;
1579- synth_unlink(child);
1580- neff++;
1807+ neff += 1 + synth_unlink(child);
15811808 i--; goto redo_i;
15821809 }
15831810
Show on old repository browser