source: icGREP/icgrep-devel/icgrep/generate_predefined_ucd_functions.cpp @ 4960

Last change on this file since 4960 was 4860, checked in by nmedfort, 4 years ago

Back up check in. Memory leaks should be fixed.

File size: 22.9 KB
Line 
1/*
2 *  Copyright (c) 2015 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 <cc/cc_compiler.h>
8#include <UCD/unicode_set.h>
9#include <UCD/PropertyObjectTable.h>
10#include <UCD/ucd_compiler.hpp>
11#include <pablo/pablo_compiler.h>
12#include <pablo/builder.hpp>
13#include <pablo/function.h>
14#include <llvm/Support/CommandLine.h>
15#include <utf_encoding.h>
16#include <pablo/analysis/pabloverifier.hpp>
17#include <pablo/optimizers/pablo_simplifier.hpp>
18#include <pablo/optimizers/codemotionpass.h>
19#ifdef ENABLE_MULTIPLEXING
20#include <pablo/optimizers/pablo_bddminimization.h>
21#include <pablo/optimizers/pablo_automultiplexing.hpp>
22#endif
23#include <pablo/optimizers/booleanreassociationpass.h>
24#include <llvm/IR/Type.h>
25#include <llvm/IR/Verifier.h>
26#include <llvm/Support/Debug.h>
27#include <llvm/Support/TargetRegistry.h>
28#include <llvm/Support/TargetSelect.h>
29#include <llvm/Target/TargetLibraryInfo.h>
30#include <llvm/Target/TargetMachine.h>
31#include <llvm/Support/Host.h>
32#include <llvm/ADT/Triple.h>
33#include <llvm/Support/ToolOutputFile.h>
34#include <llvm/Pass.h>
35#include <llvm/PassManager.h>
36#include <llvm/ADT/STLExtras.h>
37#include <llvm/Target/TargetSubtargetInfo.h>
38#include <llvm/Support/FormattedStream.h>
39#include "llvm/Support/FileSystem.h"
40#include <llvm/Transforms/Scalar.h>
41#include <llvm/Support/raw_ostream.h>
42#include <llvm/Analysis/DependenceAnalysis.h>
43#include <boost/container/flat_map.hpp>
44#include <queue>
45#include <unordered_map>
46#include <pablo/printer_pablos.h>
47#include <llvm/Analysis/PostDominators.h>
48
49using namespace pablo;
50using namespace UCD;
51using namespace cc;
52using namespace llvm;
53using namespace boost::container;
54
55enum IfHierarchy {DefaultIfHierarchy, NoIfHierarchy};
56
57static cl::opt<std::string>
58ObjectFilename("o", cl::desc("Output object filename"), cl::value_desc("filename"), cl::Required);
59
60static cl::opt<std::string>
61UCDSourcePath("dir", cl::desc("UCD source code directory"), cl::value_desc("directory"), cl::Required);
62
63static cl::opt<std::string>
64PrintLongestDependenceChain("ldc", cl::desc("print longest dependency chain metrics."), cl::value_desc("filename"));
65
66static cl::opt<IfHierarchy> IfHierarchyStrategy(cl::desc("If Hierarchy strategy:"),
67                                                cl::values(clEnumVal(DefaultIfHierarchy, "Default"),
68                                                           clEnumVal(NoIfHierarchy, "None"),
69                                                           clEnumValEnd));
70
71static cl::opt<bool> EnableReassociation("reassoc", cl::init(false),
72                                      cl::desc("Enable reassociation and distribution optimization of Boolean functions."), cl::Optional);
73
74
75static raw_fd_ostream * LongestDependenceChainFile = nullptr;
76
77#ifdef ENABLE_MULTIPLEXING
78static cl::opt<bool> EnableMultiplexing("multiplexing", cl::init(false),
79    cl::desc("combine Advances whose inputs are mutual exclusive into the fewest number of advances possible (expensive)."));
80
81static cl::opt<std::string>
82MultiplexingDistribution("multiplexing-dist",
83    cl::desc("Generate a CSV containing the # of Advances found in each UCD function before and after applying multiplexing."),
84    cl::value_desc("filename"));
85
86static raw_fd_ostream * MultiplexingDistributionFile = nullptr;
87#else
88const bool EnableMultiplexing = false;
89#endif
90
91using property_list = std::vector<std::string>;
92
93/** ------------------------------------------------------------------------------------------------------------- *
94 * @brief getNumOfAdvances
95 ** ------------------------------------------------------------------------------------------------------------- */
96unsigned getNumOfAdvances(const PabloBlock & entry) {
97    unsigned advances = 0;
98    for (const Statement * stmt : entry ) {
99        if (isa<Advance>(stmt)) {
100            ++advances;
101        }
102        else if (LLVM_UNLIKELY(isa<If>(stmt) || isa<While>(stmt))) {
103            advances += getNumOfAdvances(isa<If>(stmt) ? cast<If>(stmt)->getBody() : cast<While>(stmt)->getBody());
104        }
105    }
106    return advances;
107}
108
109/** ------------------------------------------------------------------------------------------------------------- *
110 * @brief computePabloDependencyMetrics
111 ** ------------------------------------------------------------------------------------------------------------- */
112unsigned computePabloDependencyChainMetrics(const PabloBlock & b, std::unordered_map<const PabloAST *, unsigned> & G) {
113    unsigned lpl = 0;
114    flat_map<const PabloAST *, unsigned> L;
115    for (const Statement * stmt : b) {
116        unsigned local_lpl = 0;
117        unsigned global_lpl = 0;
118        for (unsigned i = 0; i != stmt->getNumOperands(); ++i) {
119            const PabloAST * const op = stmt->getOperand(i);
120            if (isa<String>(op) || isa<Integer>(op)) {
121                continue;
122            }
123            const auto l = L.find(op);
124            if (l != L.end()) {
125                local_lpl = std::max<unsigned>(local_lpl, l->second);
126            }
127            const auto g = G.find(op);
128            if (LLVM_UNLIKELY(g == G.end())) {
129                throw std::runtime_error("Could not find dependency chain length for all operands!");
130            }
131            global_lpl = std::max<unsigned>(global_lpl, g->second);
132        }
133        L.emplace(stmt, local_lpl + 1);
134        G.insert(std::make_pair(stmt, global_lpl + 1));
135        if (LLVM_UNLIKELY(isa<If>(stmt) || isa<While>(stmt))) {
136            for (const auto & l : L) {
137                lpl = std::max(lpl, l.second);
138            }
139            L.clear();
140            lpl = std::max(lpl, computePabloDependencyChainMetrics(isa<If>(stmt) ? cast<If>(stmt)->getBody() : cast<While>(stmt)->getBody(), G));
141        }
142    }
143    return lpl;
144}
145
146/** ------------------------------------------------------------------------------------------------------------- *
147 * @brief computePabloDependencyMetrics
148 ** ------------------------------------------------------------------------------------------------------------- */
149std::pair<unsigned, unsigned> computePabloDependencyChainMetrics(const PabloFunction * f) {
150    std::unordered_map<const PabloAST *, unsigned> G;
151    G.insert(std::make_pair(PabloBlock::createZeroes(), 0));
152    G.insert(std::make_pair(PabloBlock::createOnes(), 0));
153    for (unsigned i = 0; i != f->getNumOfParameters(); ++i) {
154        G.insert(std::make_pair(f->getParameter(i), 0));
155    }
156    const unsigned local_lpl = computePabloDependencyChainMetrics(f->getEntryBlock(), G);
157    unsigned global_lpl = 0;
158    for (unsigned i = 0; i != f->getNumOfResults(); ++i) {
159        const auto e = G.find(f->getResult(i));
160        if (e == G.end()) {
161            throw std::runtime_error("No result computed!");
162        }
163        global_lpl = std::max<unsigned>(global_lpl, e->second);
164    }
165    return std::make_pair(global_lpl, local_lpl);
166}
167
168/** ------------------------------------------------------------------------------------------------------------- *
169 * @brief computeLLVMDependencyMetrics
170 ** ------------------------------------------------------------------------------------------------------------- */
171unsigned computeLLVMDependencyChainMetrics(const DomTreeNode * t, std::unordered_map<const Value *, unsigned> & G) {
172    unsigned lpl = 0;
173    if (true) {
174        flat_map<const Value *, unsigned> L;
175        const BasicBlock * b = t->getBlock();
176        for (auto itr = b->rbegin(); itr != b->rend(); ++itr) {
177            unsigned local_lpl = 0;
178            unsigned global_lpl = 0;
179            const Instruction & inst = *itr;
180            for (const Value * user : inst.users()) {
181                if (LLVM_LIKELY(isa<Instruction>(user))) {
182                    const auto l = L.find(user);
183                    if (l != L.end()) {
184                        local_lpl = std::max<unsigned>(local_lpl, l->second);
185                    }
186                    const auto g = G.find(user);
187                    if (LLVM_UNLIKELY(g == G.end())) {
188                        throw std::runtime_error("Could not find chain length for all users!");
189                    }
190                    global_lpl = std::max<unsigned>(global_lpl, g->second);
191                }
192            }
193            L.emplace(&inst, local_lpl + 1);
194            G.insert(std::make_pair(&inst, global_lpl + 1));
195            lpl = std::max(lpl, local_lpl + 1);
196        }
197    }
198    for (const DomTreeNode * pt : *t) {
199        lpl = std::max(lpl, computeLLVMDependencyChainMetrics(pt, G));
200    }
201    return lpl;
202}
203
204/** ------------------------------------------------------------------------------------------------------------- *
205 * @brief computeLLVMDependencyMetrics
206 ** ------------------------------------------------------------------------------------------------------------- */
207std::pair<unsigned, unsigned> computeLLVMDependencyChainMetrics(llvm::Function * f) {
208    std::unordered_map<const llvm::Value *, unsigned> G;
209
210    auto itr = f->getArgumentList().begin();
211    const Argument & input = *itr++;
212    const Argument & output = *itr;
213    for (const User * user : output.users()) {
214        G.insert(std::make_pair(user, 0));
215    }
216
217    PostDominatorTree dt;
218    dt.runOnFunction(*f);
219    const unsigned local_lpl = computeLLVMDependencyChainMetrics(dt.getRootNode(), G);
220    dt.releaseMemory();
221
222    unsigned global_lpl = 0;
223    for (const User * user : input.users()) {
224        const auto e = G.find(user);
225        if (e == G.end()) {
226            throw std::runtime_error("No result computed!");
227        }
228        global_lpl = std::max<unsigned>(global_lpl, e->second);
229    }
230    return std::make_pair(global_lpl, local_lpl);
231}
232
233/** ------------------------------------------------------------------------------------------------------------- *
234 * @brief compileUnicodeSet
235 ** ------------------------------------------------------------------------------------------------------------- */
236void compileUnicodeSet(std::string name, UnicodeSet && set, PabloCompiler & pc, Module * module) {
237    #ifdef ENABLE_MULTIPLEXING
238    if (MultiplexingDistributionFile) {
239        (*MultiplexingDistributionFile) << name;
240    }
241    #endif
242    if (LongestDependenceChainFile) {
243        (*LongestDependenceChainFile) << name;
244    }
245    #ifndef NDEBUG
246    std::cerr << name << std::endl;
247    #endif
248    PabloFunction * pbFunction = PabloFunction::Create(std::move(name), 8, 1);
249    Encoding encoding(Encoding::Type::UTF_8, 8);
250    CC_Compiler ccCompiler(*pbFunction, encoding);
251    UCDCompiler ucdCompiler(ccCompiler);
252    PabloBuilder builder(pbFunction->getEntryBlock());
253    // Build the unicode set function
254    PabloAST * result = nullptr;
255    if (IfHierarchyStrategy == IfHierarchy::DefaultIfHierarchy) {
256        result = ucdCompiler.generateWithDefaultIfHierarchy(&set, builder);
257    } else if (IfHierarchyStrategy == IfHierarchy::NoIfHierarchy) {
258        result = ucdCompiler.generateWithoutIfHierarchy(&set, builder);
259    } else {
260        throw std::runtime_error("Unknown if hierarchy strategy!");
261    }
262    pbFunction->setResult(0, builder.createAssign("matches", result));
263    // Optimize it at the pablo level
264    PabloVerifier::verify(*pbFunction, "creation");
265    Simplifier::optimize(*pbFunction);
266    CodeMotionPass::optimize(*pbFunction);
267    #ifdef ENABLE_MULTIPLEXING
268    BDDMinimizationPass::optimize(*function);
269    if (EnableMultiplexing) {
270        if (LongestDependenceChainFile) {
271            const auto pablo_metrix = computePabloDependencyChainMetrics(function);
272            (*LongestDependenceChainFile) << ',' << pablo_metrix.first << ',' << pablo_metrix.second;
273            Module module("tmp", getGlobalContext());
274            llvm::Function * func = pc.compile(function, &module);
275            const auto llvm_metrix = computeLLVMDependencyChainMetrics(func);
276            (*LongestDependenceChainFile) << ',' << llvm_metrix.first << ',' << llvm_metrix.second;
277        }
278
279        if (MultiplexingDistributionFile) {
280            (*MultiplexingDistributionFile) << ',' << getNumOfAdvances(function->getEntryBlock());
281        }
282        AutoMultiplexing::optimize(*function);
283        if (MultiplexingDistributionFile) {
284            (*MultiplexingDistributionFile) << ',' << getNumOfAdvances(function->getEntryBlock()) << '\n';
285        }
286    }
287    #endif
288    if (EnableReassociation) {
289        BooleanReassociationPass::optimize(*pbFunction);
290    }
291
292    // Now compile the function ...
293    llvm::Function * func = pc.compile(pbFunction, module);
294
295    if (LongestDependenceChainFile) {
296        const auto pablo_metrix = computePabloDependencyChainMetrics(pbFunction);
297        (*LongestDependenceChainFile) << ',' << pablo_metrix.first << ',' << pablo_metrix.second;
298        const auto llvm_metrix = computeLLVMDependencyChainMetrics(func);
299        (*LongestDependenceChainFile) << ',' << llvm_metrix.first << ',' << llvm_metrix.second << '\n';
300    }
301
302    delete pbFunction;
303}
304
305/** ------------------------------------------------------------------------------------------------------------- *
306 * @brief writePropertyInstaller
307 ** ------------------------------------------------------------------------------------------------------------- */
308
309void writePrecompiledProperties(property_list && properties) {
310
311    const std::string headerFilename = UCDSourcePath + "/precompiled_properties.h";
312    #ifdef USE_LLVM_3_5
313    std::string error;
314    raw_fd_ostream header(headerFilename.c_str(), error, sys::fs::F_None);
315    if (!error.empty()) {
316        throw std::runtime_error(error);
317    }
318    #else
319    std::error_code error;
320    raw_fd_ostream header(headerFilename, error, sys::fs::F_None);
321    if (error) {
322        throw std::runtime_error(error.message());
323    }
324    #endif
325
326    header << "#ifndef PRECOMPILED_PROPERTIES\n";
327    header << "#define PRECOMPILED_PROPERTIES\n\n";
328    header << "#include <string>\n\n";
329    header << "#include <tuple>\n";
330    header << "namespace UCD {\n\n";
331    header << "using ExternalProperty = std::tuple<void *, unsigned, unsigned>;\n\n";
332    header << "const ExternalProperty & resolveExternalProperty(const std::string & name);\n\n";
333    header << "}\n\n";
334    header << "#endif\n";
335    header.close();
336
337    const std::string cppFilename = UCDSourcePath + "/precompiled_properties.cpp";
338    #ifdef USE_LLVM_3_5
339    raw_fd_ostream cpp(cppFilename.c_str(), error, sys::fs::F_None);
340    if (!error.empty()) {
341        throw std::runtime_error(error);
342    }
343    #else
344    raw_fd_ostream cpp(cppFilename, error, sys::fs::F_None);
345    if (error) {
346        throw std::runtime_error(error.message());
347    }
348    #endif
349
350    cpp << "#include \"precompiled_properties.h\"\n";
351    cpp << "#include <include/simd-lib/bitblock.hpp>\n";
352    cpp << "#include <stdexcept>\n";
353    cpp << "#include <unordered_map>\n\n";
354    cpp << "namespace UCD {\nnamespace {\n\n";
355    cpp << "struct Input {\n    BitBlock bit[8];\n};\n\n";
356    cpp << "struct Output {\n    BitBlock bit[1];\n};\n\n";
357    for (auto prop : properties) {
358        cpp << "extern \"C\" void " + prop + "(const Input &, Output &);\n";
359    }
360
361    cpp << "\nconst static std::unordered_map<std::string, ExternalProperty> EXTERNAL_UCD_PROPERTY_MAP = {\n";
362    for (auto itr = properties.begin(); itr != properties.end(); ) {
363        cpp << "    {\"" + *itr + "\", std::make_tuple(reinterpret_cast<void *>(&" + *itr + "), 8, 1)}";
364        if (++itr != properties.end()) {
365            cpp << ",";
366        }
367        cpp << "\n";
368    }
369    cpp << "};\n\n} // end of anonymous namespace\n\n";
370
371    cpp << "const ExternalProperty & resolveExternalProperty(const std::string & name) {\n";
372    cpp << "    auto f = EXTERNAL_UCD_PROPERTY_MAP.find(name);\n";
373    cpp << "    if (f == EXTERNAL_UCD_PROPERTY_MAP.end())\n";
374    cpp << "        throw std::runtime_error(\"No external property named \\\"\" + name + \"\\\" found!\");\n";
375    cpp << "    return f->second;\n";
376    cpp << "}\n\n} // end of UCD namespace\n";
377
378    cpp.close();
379
380}
381
382/** ------------------------------------------------------------------------------------------------------------- *
383 * @brief generateUCDModule
384 ** ------------------------------------------------------------------------------------------------------------- */
385Module * generateUCDModule() {
386
387    property_list properties;
388
389    PabloCompiler pc(VectorType::get(IntegerType::get(getGlobalContext(), 64), BLOCK_SIZE/64));
390    Module * module = new Module("ucd", getGlobalContext());
391    for (PropertyObject * obj : property_object_table) {
392        if (EnumeratedPropertyObject * enumObj = dyn_cast<EnumeratedPropertyObject>(obj)) {
393            for (const std::string value : *enumObj) {
394                UnicodeSet set = enumObj->GetCodepointSet(canonicalize_value_name(value));
395                std::string name = "__get_" + property_enum_name[enumObj->getPropertyCode()] + "_" + value;
396                compileUnicodeSet(name, std::move(set), pc, module);
397                properties.emplace_back(name);
398            }
399        }
400        else if (ExtensionPropertyObject * extObj = dyn_cast<ExtensionPropertyObject>(obj)) {
401            for (const std::string value : *extObj) {
402                UnicodeSet set = extObj->GetCodepointSet(canonicalize_value_name(value));
403                std::string name = "__get_" + property_enum_name[extObj->getPropertyCode()] + "_" + value;
404                compileUnicodeSet(name, std::move(set), pc, module);
405                properties.emplace_back(name);
406            }
407        }
408        else if (BinaryPropertyObject * binObj = dyn_cast<BinaryPropertyObject>(obj)) {
409            UnicodeSet set = binObj->GetCodepointSet(Binary_ns::Y);
410            std::string name = "__get_" + property_enum_name[binObj->getPropertyCode()] + "_Y";
411            compileUnicodeSet(name, std::move(set), pc, module);
412            properties.emplace_back(name);
413        }
414    }
415
416    // Print an error message if our module is malformed in any way.
417    verifyModule(*module, &dbgs());
418
419    writePrecompiledProperties(std::move(properties));
420
421    return module;
422}
423
424/** ------------------------------------------------------------------------------------------------------------- *
425 * @brief compileUCDModule
426 ** ------------------------------------------------------------------------------------------------------------- */
427void compileUCDModule(Module * module) {
428    Triple TheTriple;
429
430    TheTriple.setTriple(sys::getDefaultTargetTriple());
431
432    // Get the target specific parser.
433    std::string msg;
434    const Target * TheTarget = TargetRegistry::lookupTarget(TheTriple.getTriple(), msg);
435    if (TheTarget == nullptr) {
436        throw std::runtime_error(msg);
437    }
438
439    TargetOptions Options;
440
441    std::unique_ptr<TargetMachine> Target(
442                TheTarget->createTargetMachine(TheTriple.getTriple(), sys::getHostCPUName(), "", Options,
443                                               Reloc::Default, CodeModel::Small, CodeGenOpt::Aggressive));
444
445    if (Target == nullptr) {
446        throw std::runtime_error("Could not allocate target machine!");
447    }
448
449    #ifdef USE_LLVM_3_5
450    std::string error;
451    std::unique_ptr<tool_output_file> out = make_unique<tool_output_file>(ObjectFilename.c_str(), error, sys::fs::F_None);
452    if (!error.empty()) {
453        throw std::runtime_error(error);
454    }
455    #else
456    std::error_code error;
457    std::unique_ptr<tool_output_file> out = make_unique<tool_output_file>(ObjectFilename, error, sys::fs::F_None);
458    if (error) {
459        throw std::runtime_error(error.message());
460    }
461    #endif
462
463    // Build up all of the passes that we want to do to the module.
464    PassManager PM;
465
466    // Add an appropriate TargetLibraryInfo pass for the module's triple.
467    PM.add(new TargetLibraryInfo(TheTriple));
468
469    // Add the target data from the target machine, if it exists, or the module.
470    #ifdef USE_LLVM_3_5
471    const DataLayout * DL = Target->getDataLayout();
472    #else
473    const DataLayout * DL = Target->getSubtargetImpl()->getDataLayout();
474    #endif
475    if (DL) {
476        module->setDataLayout(DL);
477    }
478    #ifdef USE_LLVM_3_5
479    PM.add(new DataLayoutPass(module));
480    #else
481    PM.add(new DataLayoutPass());
482    #endif   
483    PM.add(createReassociatePass());
484    PM.add(createInstructionCombiningPass());
485    PM.add(createSinkingPass());
486
487    formatted_raw_ostream outStream(out->os());
488    // Ask the target to add backend passes as necessary.
489    if (Target->addPassesToEmitFile(PM, outStream, TargetMachine::CGFT_ObjectFile)) {
490        throw std::runtime_error("Target does not support generation of object file type!\n");
491    }
492
493    PM.run(*module);
494
495
496    out->keep();
497}
498
499/** ------------------------------------------------------------------------------------------------------------- *
500 * @brief main
501 ** ------------------------------------------------------------------------------------------------------------- */
502int main(int argc, char *argv[]) {
503    // Initialize targets first, so that --version shows registered targets.
504    InitializeAllTargets();
505    InitializeAllTargetMCs();
506    InitializeAllAsmPrinters();
507    InitializeAllAsmParsers();
508    cl::ParseCommandLineOptions(argc, argv, "UCD Compiler\n");
509
510
511    #ifdef ENABLE_MULTIPLEXING
512    if (MultiplexingDistribution.length() > 0) {
513        #ifdef USE_LLVM_3_5
514        std::string error;
515        MultiplexingDistributionFile = new raw_fd_ostream(MultiplexingDistribution.c_str(), error, sys::fs::F_Text);
516        if (!error.empty()) {
517            throw std::runtime_error(error);
518        }
519        #else
520        std::error_code error;
521        MultiplexingDistributionFile = new raw_fd_ostream(MultiplexingDistribution, error, sys::fs::F_Text);
522        if (error) {
523            throw std::runtime_error(error.message());
524        }
525        #endif
526    }
527    #endif
528
529    if (PrintLongestDependenceChain.length() > 0) {
530        #ifdef USE_LLVM_3_5
531        std::string error;
532        LongestDependenceChainFile = new raw_fd_ostream(PrintLongestDependenceChain.c_str(), error, sys::fs::F_Text);
533        if (!error.empty()) {
534            throw std::runtime_error(error);
535        }
536        #else
537        std::error_code error;
538        LongestDependenceChainFile = new raw_fd_ostream(PrintLongestDependenceChain, error, sys::fs::F_Text);
539        if (error) {
540            throw std::runtime_error(error.message());
541        }
542        #endif
543
544        if (LongestDependenceChainFile) {
545            if (EnableMultiplexing) {
546                (*LongestDependenceChainFile) << ",Pre-Multiplexing,,,,Post-Multiplexing\n";
547            }
548            (*LongestDependenceChainFile) << ",Pablo,,LLVM,";
549            if (EnableMultiplexing) {
550                (*LongestDependenceChainFile) << ",Pablo,,LLVM,";
551            }
552            (*LongestDependenceChainFile) << "\nName,Global,Max Local,Global,Max Local";
553            if (EnableMultiplexing) {
554                (*LongestDependenceChainFile) << ",Global,Max Local,Global,Max Local";
555            }
556            (*LongestDependenceChainFile) << "\n";
557        }
558    }
559
560    Module * module = generateUCDModule();
561    #ifdef ENABLE_MULTIPLEXING
562    if (MultiplexingDistributionFile) {
563        MultiplexingDistributionFile->close();
564        delete MultiplexingDistributionFile;
565    }   
566    #endif
567    if (LongestDependenceChainFile) {
568        LongestDependenceChainFile->close();
569        delete LongestDependenceChainFile;
570    }
571    compileUCDModule(module);
572    return 0;
573}
Note: See TracBrowser for help on using the repository browser.