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

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

Temporary check in.

File size: 13.5 KB
RevLine 
[4657]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
[4661]7#include <cc/cc_compiler.h>
[4657]8#include <UCD/unicode_set.h>
[4661]9#include <UCD/PropertyObjectTable.h>
[4657]10#include <UCD/ucd_compiler.hpp>
11#include <pablo/pablo_compiler.h>
[4661]12#include <pablo/builder.hpp>
[4657]13#include <pablo/function.h>
14#include <llvm/Support/CommandLine.h>
[4661]15#include <utf_encoding.h>
16#include <pablo/optimizers/pablo_simplifier.hpp>
17#include <pablo/optimizers/pablo_codesinking.hpp>
[4665]18#ifdef ENABLE_MULTIPLEXING
[4661]19#include <pablo/optimizers/pablo_automultiplexing.hpp>
[4665]20#endif
21#include <llvm/IR/Verifier.h>
22#include <llvm/Support/Debug.h>
23#include <llvm/Support/TargetRegistry.h>
24#include <llvm/Support/TargetSelect.h>
25#include <llvm/Target/TargetLibraryInfo.h>
26#include <llvm/Target/TargetMachine.h>
27#include <llvm/Support/Host.h>
28#include <llvm/ADT/Triple.h>
29#include <llvm/Support/ToolOutputFile.h>
30#include <llvm/Pass.h>
31#include <llvm/PassManager.h>
32#include <llvm/ADT/STLExtras.h>
33#include <llvm/Target/TargetSubtargetInfo.h>
34#include <llvm/Support/FormattedStream.h>
35#include "llvm/Support/FileSystem.h"
[4666]36#include <llvm/Transforms/Scalar.h>
[4686]37#include <llvm/Support/raw_ostream.h>
38#include <llvm/Analysis/DependenceAnalysis.h>
[4661]39
[4657]40using namespace pablo;
[4661]41using namespace UCD;
42using namespace cc;
[4665]43using namespace llvm;
[4657]44
[4684]45static cl::opt<std::string>
46ObjectFilename("o", cl::desc("Output object filename"), cl::value_desc("filename"), cl::Required);
[4657]47
[4665]48static cl::opt<std::string>
[4684]49UCDSourcePath("dir", cl::desc("UCD source code directory"), cl::value_desc("directory"), cl::Required);
[4657]50
[4686]51static cl::opt<bool> PrintDependenceAnalysis("print-da", cl::init(false), cl::desc("print Dependence Analysis."));
52
53
[4667]54#ifdef ENABLE_MULTIPLEXING
55static cl::opt<bool> EnableMultiplexing("multiplexing", cl::init(false),
56                                        cl::desc("combine Advances whose inputs are mutual exclusive into the fewest number of advances possible (expensive)."));
[4686]57
58static cl::opt<std::string>
59MultiplexingDistribution("multiplexing-dist", cl::desc("Generate a CSV containing the # of Advances found in each UCD function before and after applying multiplexing."), cl::value_desc("filename"));
60
61static raw_fd_ostream * MultiplexingDistributionFile = nullptr;
[4667]62#endif
63
[4671]64using property_list = std::vector<std::pair<std::string, size_t>>;
65
[4665]66/** ------------------------------------------------------------------------------------------------------------- *
[4686]67 * @brief getNumOfAdvances
68 ** ------------------------------------------------------------------------------------------------------------- */
69unsigned getNumOfAdvances(const PabloBlock & entry) {
70    unsigned advances = 0;
71    for (const Statement * stmt : entry ) {
72        if (isa<Advance>(stmt)) {
73            ++advances;
74        }
75        else if (LLVM_UNLIKELY(isa<If>(stmt) || isa<While>(stmt))) {
76            advances += getNumOfAdvances(isa<If>(stmt) ? cast<If>(stmt)->getBody() : cast<While>(stmt)->getBody());
77        }
78    }
79    return advances;
80}
81
82/** ------------------------------------------------------------------------------------------------------------- *
[4665]83 * @brief compileUnicodeSet
84 ** ------------------------------------------------------------------------------------------------------------- */
[4671]85size_t compileUnicodeSet(std::string name, const UnicodeSet & set, PabloCompiler & pc, Module * module) {
[4686]86    #ifdef ENABLE_MULTIPLEXING
87    if (MultiplexingDistributionFile) {
88        (*MultiplexingDistributionFile) << name;
89    }
90    #endif
[4680]91    PabloFunction function = PabloFunction::Create(std::move(name), 8, 1);
[4661]92    Encoding encoding(Encoding::Type::UTF_8, 8);
93    CC_Compiler ccCompiler(function, encoding);
94    UCDCompiler ucdCompiler(ccCompiler);
95    PabloBuilder builder(function.getEntryBlock());
96    // Build the unicode set function
[4686]97    function.setResult(0, builder.createAssign("matches", ucdCompiler.generateWithDefaultIfHierarchy(set, builder)));
[4661]98    // Optimize it at the pablo level
99    Simplifier::optimize(function);
100    CodeSinking::optimize(function);
[4665]101    #ifdef ENABLE_MULTIPLEXING
[4667]102    if (EnableMultiplexing) {
[4686]103        if (MultiplexingDistributionFile) {
104            (*MultiplexingDistributionFile) << ',' << getNumOfAdvances(function.getEntryBlock());
105        }
[4667]106        AutoMultiplexing::optimize(function);
[4685]107        Simplifier::optimize(function);
[4686]108        if (MultiplexingDistributionFile) {
109            (*MultiplexingDistributionFile) << ',' << getNumOfAdvances(function.getEntryBlock()) << '\n';
110        }
[4667]111    }
[4665]112    #endif
[4661]113    // Now compile the function ...
[4668]114    auto func = pc.compile(function, module);
[4665]115    releaseSlabAllocatorMemory();
[4671]116
117    return func.second;
[4661]118}
[4657]119
[4665]120/** ------------------------------------------------------------------------------------------------------------- *
[4671]121 * @brief writePropertyInstaller
[4665]122 ** ------------------------------------------------------------------------------------------------------------- */
[4668]123
[4684]124void writePrecompiledProperties(property_list && properties) {
[4671]125
[4684]126    const std::string headerFilename = UCDSourcePath + "/precompiled_properties.h";
[4668]127    #ifdef USE_LLVM_3_5
128    std::string error;
[4684]129    raw_fd_ostream header(headerFilename.c_str(), error, sys::fs::F_None);
[4668]130    if (!error.empty()) {
131        throw std::runtime_error(error);
132    }
133    #else
134    std::error_code error;
[4684]135    raw_fd_ostream header(headerFilename, error, sys::fs::F_None);
[4668]136    if (error) {
137        throw std::runtime_error(error.message());
138    }
139    #endif
140
[4684]141    header << "#ifndef PRECOMPILED_PROPERTIES\n";
142    header << "#define PRECOMPILED_PROPERTIES\n\n";
143    header << "#include <string>\n\n";
144    header << "#include <tuple>\n";
145    header << "namespace UCD {\n\n";
146    header << "using ExternalProperty = std::tuple<void *, unsigned, unsigned, size_t>;\n\n";
147    header << "const ExternalProperty & resolveExternalProperty(const std::string & name);\n\n";
148    header << "}\n\n";
149    header << "#endif\n";
150    header.close();
151
152    const std::string cppFilename = UCDSourcePath + "/precompiled_properties.cpp";
153    #ifdef USE_LLVM_3_5
154    raw_fd_ostream cpp(cppFilename.c_str(), error, sys::fs::F_None);
155    if (!error.empty()) {
156        throw std::runtime_error(error);
[4671]157    }
[4684]158    #else
159    raw_fd_ostream cpp(cppFilename, error, sys::fs::F_None);
160    if (error) {
161        throw std::runtime_error(error.message());
162    }
163    #endif
164
165    cpp << "#include \"precompiled_properties.h\"\n";
166    cpp << "#include <include/simd-lib/bitblock.hpp>\n";
167    cpp << "#include <stdexcept>\n";
168    cpp << "#include <unordered_map>\n\n";
[4686]169    cpp << "namespace UCD {\nnamespace {\n\n";
[4684]170    cpp << "struct Input {\n    BitBlock bit[8];\n};\n\n";
171    cpp << "struct Output {\n    BitBlock bit[1];\n};\n\n";
[4671]172    for (auto prop : properties) {
[4684]173        cpp << "extern \"C\" void " + prop.first + "(const Input &, BitBlock *, Output &);\n";
[4671]174    }
[4684]175
[4686]176    cpp << "\nconst static std::unordered_map<std::string, ExternalProperty> EXTERNAL_UCD_PROPERTY_MAP = {\n";
[4684]177    for (auto itr = properties.begin(); itr != properties.end(); ) {
178        cpp << "    {\"" + itr->first + "\", std::make_tuple(reinterpret_cast<void *>(&" + itr->first + "), 8, 1, " + std::to_string(itr->second) + ")}";
179        if (++itr != properties.end()) {
180            cpp << ",";
181        }
182        cpp << "\n";
183    }
[4686]184    cpp << "};\n\n} // end of anonymous namespace\n\n";
[4684]185
186    cpp << "const ExternalProperty & resolveExternalProperty(const std::string & name) {\n";
[4686]187    cpp << "    auto f = EXTERNAL_UCD_PROPERTY_MAP.find(name);\n";
188    cpp << "    if (f == EXTERNAL_UCD_PROPERTY_MAP.end())\n";
[4684]189    cpp << "        throw std::runtime_error(\"No external property named \\\"\" + name + \"\\\" found!\");\n";
190    cpp << "    return f->second;\n";
[4686]191    cpp << "}\n\n} // end of UCD namespace\n";
[4684]192
193    cpp.close();
194
[4671]195}
[4668]196
[4671]197
198/** ------------------------------------------------------------------------------------------------------------- *
199 * @brief generateUCDModule
200 ** ------------------------------------------------------------------------------------------------------------- */
201Module * generateUCDModule() {
202
203    property_list properties;
204
[4661]205    PabloCompiler pc;
[4665]206    Module * module = new Module("ucd", getGlobalContext());
[4661]207    for (PropertyObject * obj : property_object_table) {
[4667]208        if (EnumeratedPropertyObject * enumObj = dyn_cast<EnumeratedPropertyObject>(obj)) {
[4661]209            for (const std::string value : *enumObj) {
210                const UnicodeSet & set = enumObj->GetCodepointSet(canonicalize_value_name(value));
[4667]211                std::string name = "__get_" + property_enum_name[enumObj->getPropertyCode()] + "_" + value;
[4671]212                properties.emplace_back(name, compileUnicodeSet(name, set, pc, module));
[4661]213            }
214        }
[4667]215        else if (ExtensionPropertyObject * extObj = dyn_cast<ExtensionPropertyObject>(obj)) {
[4665]216            for (const std::string value : *extObj) {
217                const UnicodeSet & set = extObj->GetCodepointSet(canonicalize_value_name(value));
[4667]218                std::string name = "__get_" + property_enum_name[extObj->getPropertyCode()] + "_" + value;
[4671]219                properties.emplace_back(name, compileUnicodeSet(name, set, pc, module));
[4665]220            }
221        }
[4667]222        else if (BinaryPropertyObject * binObj = dyn_cast<BinaryPropertyObject>(obj)) {
[4665]223            const UnicodeSet & set = binObj->GetCodepointSet(Binary_ns::Y);
[4667]224            std::string name = "__get_" + property_enum_name[binObj->getPropertyCode()] + "_Y";
[4671]225            properties.emplace_back(name, compileUnicodeSet(name, set, pc, module));
[4665]226        }
227    }
[4657]228
[4665]229    // Print an error message if our module is malformed in any way.
230    verifyModule(*module, &dbgs());
[4657]231
[4684]232    writePrecompiledProperties(std::move(properties));
[4671]233
[4665]234    return module;
235}
236
237/** ------------------------------------------------------------------------------------------------------------- *
238 * @brief compileUCDModule
239 ** ------------------------------------------------------------------------------------------------------------- */
240void compileUCDModule(Module * module) {
241    Triple TheTriple;
242
[4666]243    TheTriple.setTriple(sys::getDefaultTargetTriple());
244
[4665]245    // Get the target specific parser.
246    std::string msg;
247    const Target * TheTarget = TargetRegistry::lookupTarget(TheTriple.getTriple(), msg);
248    if (TheTarget == nullptr) {
249        throw std::runtime_error(msg);
250    }
251
252    TargetOptions Options;
253
254    std::unique_ptr<TargetMachine> Target(
[4685]255                TheTarget->createTargetMachine(TheTriple.getTriple(), sys::getHostCPUName(), "", Options,
[4665]256                                               Reloc::Default, CodeModel::Small, CodeGenOpt::Aggressive));
257
258    if (Target == nullptr) {
259        throw std::runtime_error("Could not allocate target machine!");
260    }
261
262    #ifdef USE_LLVM_3_5
263    std::string error;
[4685]264    std::unique_ptr<tool_output_file> out = make_unique<tool_output_file>(ObjectFilename.c_str(), error, sys::fs::F_None);
[4665]265    if (!error.empty()) {
266        throw std::runtime_error(error);
267    }
268    #else
269    std::error_code error;
[4685]270    std::unique_ptr<tool_output_file> out = make_unique<tool_output_file>(ObjectFilename, error, sys::fs::F_None);
[4665]271    if (error) {
272        throw std::runtime_error(error.message());
273    }
274    #endif
275
276    // Build up all of the passes that we want to do to the module.
277    PassManager PM;
278
279    // Add an appropriate TargetLibraryInfo pass for the module's triple.
[4669]280    PM.add(new TargetLibraryInfo(TheTriple));
[4665]281
282    // Add the target data from the target machine, if it exists, or the module.
283    #ifdef USE_LLVM_3_5
284    const DataLayout * DL = Target->getDataLayout();
285    #else
286    const DataLayout * DL = Target->getSubtargetImpl()->getDataLayout();
287    #endif
288    if (DL) {
289        module->setDataLayout(DL);
290    }
[4669]291    #ifdef USE_LLVM_3_5
292    PM.add(new DataLayoutPass(module));
293    #else
[4665]294    PM.add(new DataLayoutPass());
[4692]295    #endif   
[4666]296    PM.add(createReassociatePass());
297    PM.add(createInstructionCombiningPass());
298    PM.add(createSinkingPass());
[4665]299
[4686]300    formatted_raw_ostream outStream(out->os());
[4665]301    // Ask the target to add backend passes as necessary.
[4686]302    if (Target->addPassesToEmitFile(PM, outStream, TargetMachine::CGFT_ObjectFile)) {
[4671]303        throw std::runtime_error("Target does not support generation of object file type!\n");
[4665]304    }
305
306    PM.run(*module);
307
[4692]308
[4685]309    out->keep();
[4665]310}
311
312/** ------------------------------------------------------------------------------------------------------------- *
313 * @brief main
314 ** ------------------------------------------------------------------------------------------------------------- */
315int main(int argc, char *argv[]) {
[4686]316    // Initialize targets first, so that --version shows registered targets.
317    InitializeAllTargets();
318    InitializeAllTargetMCs();
319    InitializeAllAsmPrinters();
320    InitializeAllAsmParsers();
[4665]321    cl::ParseCommandLineOptions(argc, argv, "UCD Compiler\n");
[4692]322
323
324
325
[4686]326    #ifdef ENABLE_MULTIPLEXING
327    if (MultiplexingDistribution.length() > 0) {
328        #ifdef USE_LLVM_3_5
329        std::string error;
330        MultiplexingDistributionFile = new raw_fd_ostream(MultiplexingDistribution.c_str(), error, sys::fs::F_Text);
331        if (!error.empty()) {
332            throw std::runtime_error(error);
333        }
334        #else
335        std::error_code error;
336        MultiplexingDistributionFile = new raw_fd_ostream(MultiplexingDistribution, error, sys::fs::F_Text);
337        if (error) {
338            throw std::runtime_error(error.message());
339        }
340        #endif
341    }
342    #endif
[4665]343    Module * module = generateUCDModule();
[4686]344    #ifdef ENABLE_MULTIPLEXING
345    if (MultiplexingDistributionFile) {
346        MultiplexingDistributionFile->close();
347        delete MultiplexingDistributionFile;
348    }
349    #endif
[4665]350    compileUCDModule(module);
[4661]351    return 0;
[4657]352}
Note: See TracBrowser for help on using the repository browser.