source: icGREP/icgrep-devel/icgrep/pablo/codegenstate.cpp @ 4980

Last change on this file since 4980 was 4980, checked in by cameron, 4 years ago

Remove mod64 junk

File size: 20.6 KB
Line 
1/*
2 *  Copyright (c) 2014 International Characters.
3 *  This software is licensed to the public under the Open Software License 3.0.
4 *  icgrep is a trademark of International Characters.
5 */
6
7#include <pablo/codegenstate.h>
8#include <iostream>
9#include <pablo/printer_pablos.h>
10
11namespace pablo {
12
13Zeroes PabloBlock::mZeroes;
14
15Ones PabloBlock::mOnes;
16
17inline PabloAST * PabloBlock::renameNonNamedNode(PabloAST * expr, const std::string && prefix) {
18    if (Statement * stmt = dyn_cast<Statement>(expr)) {
19        if (stmt->getName()->isGenerated()) {
20            stmt->setName(makeName(prefix, false));
21        }
22    }
23    return expr;
24}
25
26void PabloBlock::insert(Statement * const statement) {
27    assert (statement);
28    if (LLVM_UNLIKELY(mInsertionPoint == nullptr)) {
29        if (mFirst) {
30            statement->insertBefore(mFirst);
31        } else {
32            statement->removeFromParent();
33            statement->mParent = this;
34            mFirst = mLast = statement;
35        }
36    } else if (LLVM_LIKELY(statement != mInsertionPoint)) {
37        statement->insertAfter(mInsertionPoint);
38        mLast = (mLast == mInsertionPoint) ? statement : mLast;
39        assert (statement->mPrev == mInsertionPoint);
40    }
41    mInsertionPoint = statement;
42}
43
44/// UNARY CREATE FUNCTIONS
45
46Assign * PabloBlock::createAssign(const std::string && prefix, PabloAST * const expr)  {
47    assert ("Assign expression cannot be null!" && expr);
48    return insertAtInsertionPoint(new Assign(expr, makeName(prefix, false)));
49}
50
51PabloAST * PabloBlock::createAdvance(PabloAST * expr, PabloAST * shiftAmount) {
52    if (isa<Zeroes>(expr) || cast<Integer>(shiftAmount)->value() == 0) {
53        return expr;
54    }
55    return insertAtInsertionPoint(new Advance(expr, shiftAmount, makeName("advance")));
56}
57
58PabloAST * PabloBlock::createAdvance(PabloAST * expr, PabloAST * shiftAmount, const std::string prefix) {
59    if (isa<Zeroes>(expr) || cast<Integer>(shiftAmount)->value() == 0) {
60        return expr;
61    }
62    return insertAtInsertionPoint(new Advance(expr, shiftAmount, makeName(prefix, false)));
63}
64
65PabloAST * PabloBlock::createAdvance(PabloAST * expr, const Integer::Type shiftAmount) {
66    if (isa<Zeroes>(expr) || shiftAmount == 0) {
67        return expr;
68    }
69    return insertAtInsertionPoint(new Advance(expr, getInteger(shiftAmount), makeName("advance")));
70}
71
72PabloAST * PabloBlock::createAdvance(PabloAST * expr, const Integer::Type shiftAmount, const std::string prefix) {
73    if (isa<Zeroes>(expr) || shiftAmount == 0) {
74        return renameNonNamedNode(expr, std::move(prefix));
75    }
76    return insertAtInsertionPoint(new Advance(expr, getInteger(shiftAmount), makeName(prefix, false)));
77}
78
79PabloAST * PabloBlock::createLookahead(PabloAST * expr, PabloAST * shiftAmount) {
80    if (isa<Zeroes>(expr) || cast<Integer>(shiftAmount)->value() == 0) {
81        return expr;
82    }
83    return insertAtInsertionPoint(new Lookahead(expr, shiftAmount, makeName("lookahead")));
84}
85
86PabloAST * PabloBlock::createLookahead(PabloAST * expr, PabloAST * shiftAmount, const std::string prefix) {
87    if (isa<Zeroes>(expr) || cast<Integer>(shiftAmount)->value() == 0) {
88        return expr;
89    }
90    return insertAtInsertionPoint(new Lookahead(expr, shiftAmount, makeName(prefix, false)));
91}
92
93PabloAST * PabloBlock::createLookahead(PabloAST * expr, const Integer::Type shiftAmount) {
94    if (isa<Zeroes>(expr) || shiftAmount == 0) {
95        return expr;
96    }
97    return insertAtInsertionPoint(new Lookahead(expr, getInteger(shiftAmount), makeName("lookahead")));
98}
99
100PabloAST * PabloBlock::createLookahead(PabloAST * expr, const Integer::Type shiftAmount, const std::string prefix) {
101    if (isa<Zeroes>(expr) || shiftAmount == 0) {
102        return renameNonNamedNode(expr, std::move(prefix));
103    }
104    return insertAtInsertionPoint(new Lookahead(expr, getInteger(shiftAmount), makeName(prefix, false)));
105}
106
107Call * PabloBlock::createCall(PabloAST * prototype, const std::vector<PabloAST *> &) {
108    assert (prototype);
109    return insertAtInsertionPoint(new Call(prototype));
110}
111
112
113PabloAST * PabloBlock::createNot(PabloAST * expr) {
114    assert (expr);
115    if (isa<Ones>(expr)) {
116        return createZeroes();
117    }
118    else if (isa<Zeroes>(expr)){
119        return createOnes();
120    }
121    else if (Not * not1 = dyn_cast<Not>(expr)) {
122        return not1->getOperand(0);
123    }
124    return insertAtInsertionPoint(new Not(expr, makeName("not_")));
125}
126
127PabloAST * PabloBlock::createNot(PabloAST * expr, const std::string prefix) {
128    assert (expr);
129    if (isa<Ones>(expr)) {
130        return createZeroes();
131    }
132    else if (isa<Zeroes>(expr)){
133        return createOnes();
134    }
135    else if (Not * not1 = dyn_cast<Not>(expr)) {       
136        return renameNonNamedNode(not1->getOperand(0), std::move(prefix));
137    }
138    return insertAtInsertionPoint(new Not(expr, makeName(prefix, false)));
139}
140
141Var * PabloBlock::createVar(PabloAST * name) {
142    assert (name);
143    return new Var(name);
144}
145
146PabloAST * PabloBlock::createCount(PabloAST * expr) {
147    assert (expr);
148    return insertAtInsertionPoint(new Count(expr, makeName("count_")));
149}
150
151PabloAST * PabloBlock::createCount(PabloAST * expr, const std::string prefix) {
152    assert (expr);
153    return insertAtInsertionPoint(new Count(expr, makeName(prefix, false)));
154}
155
156   
157/// BINARY CREATE FUNCTIONS
158
159Next * PabloBlock::createNext(Assign * assign, PabloAST * expr) {
160    assert (assign && expr);
161    return insertAtInsertionPoint(new Next(assign, expr));
162}
163
164PabloAST * PabloBlock::createMatchStar(PabloAST * marker, PabloAST * charclass) {
165    assert (marker && charclass);
166    if (isa<Zeroes>(marker) || isa<Zeroes>(charclass)) {
167        return marker;
168    }
169    return insertAtInsertionPoint(new MatchStar(marker, charclass, makeName("matchstar")));
170}
171
172PabloAST * PabloBlock::createMatchStar(PabloAST * marker, PabloAST * charclass, const std::string prefix) {
173    assert (marker && charclass);
174    if (isa<Zeroes>(marker) || isa<Zeroes>(charclass)) {
175        return renameNonNamedNode(marker, std::move(prefix));
176    }
177    return insertAtInsertionPoint(new MatchStar(marker, charclass, makeName(prefix, false)));
178}
179
180PabloAST * PabloBlock::createScanThru(PabloAST * from, PabloAST * thru) {
181    assert (from && thru);
182    if (isa<Zeroes>(from) || isa<Zeroes>(thru)) {
183        return from;
184    }
185    return insertAtInsertionPoint(new ScanThru(from, thru, makeName("scanthru")));
186}
187
188PabloAST * PabloBlock::createScanThru(PabloAST * from, PabloAST * thru, const std::string prefix) {
189    assert (from && thru);
190    if (isa<Zeroes>(from) || isa<Zeroes>(thru)) {       
191        return renameNonNamedNode(from, std::move(prefix));
192    }
193    return insertAtInsertionPoint(new ScanThru(from, thru, makeName(prefix, false)));
194}
195
196template<typename Type>
197static inline Type * isBinary(PabloAST * expr) {
198    if (isa<Type>(expr) && cast<Type>(expr)->getNumOperands() == 2) {
199        return cast<Type>(expr);
200    }
201    return nullptr;
202}
203
204PabloAST * PabloBlock::createAnd(PabloAST * expr1, PabloAST * expr2) {
205    assert (expr1 && expr2);
206    if (isa<Zeroes>(expr2) || isa<Ones>(expr1)) {
207        return expr2;
208    } else if (isa<Zeroes>(expr1) || isa<Ones>(expr2) || equals(expr1, expr2)){
209        return expr1;
210    } else if (Not * not1 = dyn_cast<Not>(expr1)) {
211        if (Not * not2 = dyn_cast<Not>(expr2)) {
212            return createNot(createOr(not1->getOperand(0), not2->getOperand(0)));
213        } else if (equals(not1->getOperand(0), expr2)) {
214            return createZeroes();
215        }
216    } else if (Not * not2 = dyn_cast<Not>(expr2)) {
217        if (equals(expr1, not2->getOperand(0))) {
218            return createZeroes();
219        }
220    } else if (Or * or1 = isBinary<Or>(expr1)) {
221        if (equals(or1->getOperand(0), expr2) || equals(or1->getOperand(1), expr2)) {
222            return expr2;
223        }
224    } else if (Or * or2 = isBinary<Or>(expr2)) {
225        if (equals(or2->getOperand(0), expr1) || equals(or2->getOperand(1), expr1)) {
226            return expr1;
227        }
228    }
229    return insertAtInsertionPoint(new And(expr1, expr2, makeName("and_")));
230}
231
232PabloAST * PabloBlock::createAnd(PabloAST * expr1, PabloAST * expr2, const std::string prefix) {
233    assert (expr1 && expr2);
234    if (isa<Zeroes>(expr2) || isa<Ones>(expr1)) {
235        return renameNonNamedNode(expr2, std::move(prefix));
236    }
237    else if (isa<Zeroes>(expr1) || isa<Ones>(expr2) || equals(expr1, expr2)){
238        return renameNonNamedNode(expr1, std::move(prefix));
239    } else if (Not * not1 = dyn_cast<Not>(expr1)) {
240        if (Not * not2 = dyn_cast<Not>(expr2)) {
241            return createNot(createOr(not1->getOperand(0), not2->getOperand(0)), prefix);
242        }
243        else if (equals(not1->getOperand(0), expr2)) {
244            return createZeroes();
245        }
246    } else if (Not * not2 = dyn_cast<Not>(expr2)) {
247        if (equals(expr1, not2->getOperand(0))) {
248            return createZeroes();
249        }
250    } else if (Or * or1 = isBinary<Or>(expr1)) {
251        if (equals(or1->getOperand(0), expr2) || equals(or1->getOperand(1), expr2)) {
252            return expr2;
253        }
254    } else if (Or * or2 = isBinary<Or>(expr2)) {
255        if (equals(or2->getOperand(0), expr1) || equals(or2->getOperand(1), expr1)) {
256            return expr1;
257        }
258    }
259    return insertAtInsertionPoint(new And(expr1, expr2, makeName(prefix, false)));
260}
261
262And * PabloBlock::createAnd(const unsigned reserved) {
263    return insertAtInsertionPoint(new And(reserved, makeName("and_")));
264}
265
266And * PabloBlock::createAnd(const unsigned reserved, const std::string prefix) {
267    return insertAtInsertionPoint(new And(reserved, makeName(prefix, false)));
268}
269
270PabloAST * PabloBlock::createOr(PabloAST * expr1, PabloAST * expr2) {
271    assert (expr1 && expr2);
272    if (isa<Zeroes>(expr1) || isa<Ones>(expr2)){
273        return expr2;
274    }
275    if (isa<Zeroes>(expr2) || isa<Ones>(expr1) || equals(expr1, expr2)) {
276        return expr1;
277    } else if (Not * not1 = dyn_cast<Not>(expr1)) {
278        // ¬a√b = ¬¬(¬a √ b) = ¬(a ∧ ¬b)
279        return createNot(createAnd(not1->getOperand(0), createNot(expr2)));
280    } else if (Not * not2 = dyn_cast<Not>(expr2)) {
281        // a√¬b = ¬¬(¬b √ a) = ¬(b ∧ ¬a)
282        return createNot(createAnd(not2->getOperand(0), createNot(expr1)));
283    } else if (equals(expr1, expr2)) {
284        return expr1;
285    } else if (And * and1 = isBinary<And>(expr1)) {
286        if (And * and2 = isBinary<And>(expr2)) {
287            PabloAST * const expr1a = and1->getOperand(0);
288            PabloAST * const expr1b = and1->getOperand(1);
289            PabloAST * const expr2a = and2->getOperand(0);
290            PabloAST * const expr2b = and2->getOperand(1);
291            //These optimizations factor out common components that can occur when sets are formed by union
292            //(e.g., union of [a-z] and [A-Z].
293            if (equals(expr1a, expr2a)) {
294                return createAnd(expr1a, createOr(expr1b, expr2b));
295            } else if (equals(expr1b, expr2b)) {
296                return createAnd(expr1b, createOr(expr1a, expr2a));
297            } else if (equals(expr1a, expr2b)) {
298                return createAnd(expr1a, createOr(expr1b, expr2a));
299            } else if (equals(expr1b, expr2a)) {
300                return createAnd(expr1b, createOr(expr1a, expr2b));
301            }
302        } else if (equals(and1->getOperand(0), expr2) || equals(and1->getOperand(1), expr2)) {
303            // (a ∧ b) √ a = a
304            return expr2;
305        }
306    } else if (And * and2 = isBinary<And>(expr2)) {
307        if (equals(and2->getOperand(0), expr1) || equals(and2->getOperand(1), expr1)) {
308            return expr1;
309        }
310    }
311    return insertAtInsertionPoint(new Or(expr1, expr2, makeName("or_")));
312}
313
314PabloAST * PabloBlock::createOr(PabloAST * expr1, PabloAST * expr2, const std::string prefix) {
315    assert (expr1 && expr2);
316    if (isa<Zeroes>(expr1) || isa<Ones>(expr2)){
317        return renameNonNamedNode(expr2, std::move(prefix));
318    }
319    else if (isa<Zeroes>(expr2) || isa<Ones>(expr1) || equals(expr1, expr2)) {
320        return renameNonNamedNode(expr1, std::move(prefix));
321    } else if (Not * not1 = dyn_cast<Not>(expr1)) {
322        // ¬a√b = ¬¬(¬a√b) = ¬(a ∧ ¬b)
323        return createNot(createAnd(not1->getOperand(0), createNot(expr2)), prefix);
324    } else if (Not * not2 = dyn_cast<Not>(expr2)) {
325        // a√¬b = ¬¬(¬b√a) = ¬(b ∧ ¬a)
326        return createNot(createAnd(not2->getOperand(0), createNot(expr1)), prefix);
327    } else if (And * and1 = isBinary<And>(expr1)) {
328        if (And * and2 = isBinary<And>(expr2)) {
329            PabloAST * const expr1a = and1->getOperand(0);
330            PabloAST * const expr1b = and1->getOperand(1);
331            PabloAST * const expr2a = and2->getOperand(0);
332            PabloAST * const expr2b = and2->getOperand(1);
333            //These optimizations factor out common components that can occur when sets are formed by union
334            //(e.g., union of [a-z] and [A-Z].
335            if (equals(expr1a, expr2a)) {
336                return createAnd(expr1a, createOr(expr1b, expr2b), prefix);
337            } else if (equals(expr1b, expr2b)) {
338                return createAnd(expr1b, createOr(expr1a, expr2a), prefix);
339            } else if (equals(expr1a, expr2b)) {
340                return createAnd(expr1a, createOr(expr1b, expr2a), prefix);
341            } else if (equals(expr1b, expr2a)) {
342                return createAnd(expr1b, createOr(expr1a, expr2b), prefix);
343            }
344        } else if (equals(and1->getOperand(0), expr2) || equals(and1->getOperand(1), expr2)) {
345            // (a∧b) √ a = a
346            return expr2;
347        }
348    } else if (And * and2 = isBinary<And>(expr2)) {
349        if (equals(and2->getOperand(0), expr1) || equals(and2->getOperand(1), expr1)) {
350            return expr1;
351        }
352    }
353    return insertAtInsertionPoint(new Or(expr1, expr2, makeName(prefix, false)));
354}
355
356Or * PabloBlock::createOr(const unsigned reserved) {
357    return insertAtInsertionPoint(new Or(reserved, makeName("or_")));
358}
359
360Or * PabloBlock::createOr(const unsigned reserved, const std::string prefix) {
361    return insertAtInsertionPoint(new Or(reserved, makeName(prefix, false)));
362}
363
364PabloAST * PabloBlock::createXor(PabloAST * expr1, PabloAST * expr2) {
365    assert (expr1 && expr2);
366    if (expr1 == expr2) {
367        return PabloBlock::createZeroes();
368    }
369    if (isa<Ones>(expr1)) {
370        return createNot(expr2);
371    } else if (isa<Zeroes>(expr1)){
372        return expr2;
373    } else if (isa<Ones>(expr2)) {
374        return createNot(expr1);
375    } else if (isa<Zeroes>(expr2)){
376        return expr1;
377    } else if (Not * not1 = dyn_cast<Not>(expr1)) {
378        if (Not * not2 = dyn_cast<Not>(expr2)) {
379            return createXor(not1->getOperand(0), not2->getOperand(0));
380        }
381    }
382    return insertAtInsertionPoint(new Xor(expr1, expr2, makeName("xor_")));
383}
384
385PabloAST * PabloBlock::createXor(PabloAST * expr1, PabloAST * expr2, const std::string prefix) {
386    assert (expr1 && expr2);
387    if (expr1 == expr2) {
388        return PabloBlock::createZeroes();
389    }
390    if (isa<Ones>(expr1)) {
391        return createNot(expr2, prefix);
392    } else if (isa<Zeroes>(expr1)){
393        return expr2;
394    } else if (isa<Ones>(expr2)) {
395        return createNot(expr1, prefix);
396    } else if (isa<Zeroes>(expr2)){
397        return expr1;
398    } else if (Not * not1 = dyn_cast<Not>(expr1)) {
399        if (Not * not2 = dyn_cast<Not>(expr2)) {
400            return createXor(not1->getOperand(0), not2->getOperand(0), prefix);
401        }
402    }
403    return insertAtInsertionPoint(new Xor(expr1, expr2, makeName(prefix, false)));
404}
405
406Xor * PabloBlock::createXor(const unsigned reserved) {
407    return insertAtInsertionPoint(new Xor(reserved, makeName("xor_")));
408}
409
410Xor * PabloBlock::createXor(const unsigned reserved, const std::string prefix) {
411    return insertAtInsertionPoint(new Xor(reserved, makeName(prefix, false)));
412}
413
414/// TERNARY CREATE FUNCTION
415
416PabloAST * PabloBlock::createSel(PabloAST * condition, PabloAST * trueExpr, PabloAST * falseExpr) {
417    assert (condition && trueExpr && falseExpr);
418    if (isa<Ones>(condition)) {
419        return trueExpr;
420    } else if (isa<Zeroes>(condition)){
421        return falseExpr;
422    } else if (isa<Ones>(trueExpr)) {
423        return createOr(condition, falseExpr);
424    } else if (isa<Zeroes>(trueExpr)){
425        return createAnd(createNot(condition), falseExpr);
426    } else if (isa<Ones>(falseExpr)) {
427        return createOr(createNot(condition), trueExpr);
428    } else if (isa<Zeroes>(falseExpr)){
429        return createAnd(condition, trueExpr);
430    } else if (equals(trueExpr, falseExpr)) {
431        return trueExpr;
432    } else if (isa<Not>(trueExpr) && equals(cast<Not>(trueExpr)->getOperand(0), falseExpr)) {
433        return createXor(condition, falseExpr);
434    } else if (isa<Not>(falseExpr) && equals(trueExpr, cast<Not>(falseExpr)->getOperand(0))){
435        return createXor(condition, trueExpr);
436    }
437    return insertAtInsertionPoint(new Sel(condition, trueExpr, falseExpr, makeName("sel")));
438}
439
440PabloAST * PabloBlock::createSel(PabloAST * condition, PabloAST * trueExpr, PabloAST * falseExpr, const std::string prefix) {
441    assert (condition && trueExpr && falseExpr);
442    if (isa<Zeroes>(condition)){
443        return renameNonNamedNode(falseExpr, std::move(prefix));
444    }
445    else if (isa<Ones>(condition) || equals(trueExpr, falseExpr)) {
446        return renameNonNamedNode(trueExpr, std::move(prefix));
447    }
448    else if (isa<Ones>(trueExpr)) {
449        return createOr(condition, falseExpr, prefix);
450    }
451    else if (isa<Zeroes>(trueExpr)){
452        return createAnd(createNot(condition), falseExpr, prefix);
453    }
454    else if (isa<Ones>(falseExpr)) {
455        return createOr(createNot(condition), trueExpr, prefix);
456    }
457    else if (isa<Zeroes>(falseExpr)){
458        return createAnd(condition, trueExpr, prefix);
459    }
460    else if (isa<Not>(trueExpr) && equals(cast<Not>(trueExpr)->getOperand(0), falseExpr)) {
461        return createXor(condition, falseExpr, prefix);
462    }
463    else if (isa<Not>(falseExpr) && equals(trueExpr, cast<Not>(falseExpr)->getOperand(0))){
464        return createXor(condition, trueExpr, prefix);
465    }
466    return insertAtInsertionPoint(new Sel(condition, trueExpr, falseExpr, makeName(prefix, false)));
467}
468
469If * PabloBlock::createIf(PabloAST * condition, const std::initializer_list<Assign *> definedVars, PabloBlock * body) {
470    assert (condition);
471    return insertAtInsertionPoint(new If(condition, definedVars, body));
472}
473
474If * PabloBlock::createIf(PabloAST * condition, const std::vector<Assign *> & definedVars, PabloBlock * body) {
475    assert (condition);
476    return insertAtInsertionPoint(new If(condition, definedVars, body));
477}
478
479If * PabloBlock::createIf(PabloAST * condition, std::vector<Assign *> && definedVars, PabloBlock * body) {
480    assert (condition);
481    return insertAtInsertionPoint(new If(condition, definedVars, body));
482}
483
484While * PabloBlock::createWhile(PabloAST * condition, const std::initializer_list<Next *> nextVars, PabloBlock * body) {
485    assert (condition);
486    return insertAtInsertionPoint(new While(condition, nextVars, body));
487}
488
489While * PabloBlock::createWhile(PabloAST * condition, const std::vector<Next *> & nextVars, PabloBlock * body) {
490    assert (condition);
491    return insertAtInsertionPoint(new While(condition, nextVars, body));
492}
493
494While * PabloBlock::createWhile(PabloAST * condition, std::vector<Next *> && nextVars, PabloBlock * body) {
495    assert (condition);
496    return insertAtInsertionPoint(new While(condition, nextVars, body));
497}
498
499/** ------------------------------------------------------------------------------------------------------------- *
500 * @brief eraseFromParent
501 ** ------------------------------------------------------------------------------------------------------------- */
502void PabloBlock::eraseFromParent(const bool recursively) {
503    Statement * stmt = front();
504    // Note: by erasing the scope block, any Assign/Next nodes will be replaced with Zero and removed from
505    // the If/While node
506    while (stmt) {
507        stmt = stmt->eraseFromParent(recursively);
508    }
509    mAllocator.deallocate(reinterpret_cast<Allocator::pointer>(this));
510}
511
512
513// Assign sequential scope indexes, returning the next unassigned index   
514
515unsigned PabloBlock::enumerateScopes(unsigned baseScopeIndex) {
516    mScopeIndex = baseScopeIndex;
517    unsigned nextScopeIndex = baseScopeIndex + 1;
518    for (Statement * stmt : *this) {
519        if (If * ifStatement = dyn_cast<If>(stmt)) {
520            nextScopeIndex = ifStatement->getBody()->enumerateScopes(nextScopeIndex);
521        }
522        else if (While * whileStatement = dyn_cast<While>(stmt)) {
523            nextScopeIndex = whileStatement->getBody()->enumerateScopes(nextScopeIndex);
524        }
525    }
526    return nextScopeIndex;
527}   
528   
529/// CONSTRUCTOR
530
531PabloBlock::PabloBlock(SymbolGenerator * symbolGenerator) noexcept
532: PabloAST(PabloAST::ClassTypeId::Block)
533, mSymbolGenerator(symbolGenerator)
534, mParent(nullptr)
535, mBranch(nullptr)
536, mScopeIndex(0)
537{
538
539}
540
541PabloBlock::~PabloBlock() {
542
543}
544
545}
Note: See TracBrowser for help on using the repository browser.