source: icGREP/icgrep-devel/icgrep/wc.cpp @ 5021

Last change on this file since 5021 was 5021, checked in by cameron, 18 months ago

Large file fix: use boost mapped_file_source

File size: 18.6 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 <string>
8#include <iostream>
9#include <iomanip>
10#include <fstream>
11#include <sstream>
12
13#include <llvm/IR/Function.h>
14#include <llvm/IR/Module.h>
15#include <llvm/ExecutionEngine/ExecutionEngine.h>
16#include <llvm/ExecutionEngine/MCJIT.h>
17#include <llvm/IRReader/IRReader.h>
18#include <llvm/IR/Verifier.h>
19#include <llvm/Support/Debug.h>
20
21#include <llvm/Support/CommandLine.h>
22#include <llvm/CodeGen/CommandFlags.h>
23#include <llvm/Support/SourceMgr.h>
24#include <llvm/Support/TargetSelect.h>
25#include <llvm/Support/Host.h>
26#include <llvm/Support/raw_ostream.h>
27
28#include <utf_encoding.h>
29#include <re/re_cc.h>
30#include <cc/cc_compiler.h>
31#include <pablo/function.h>
32#include <IDISA/idisa_builder.h>
33#include <IDISA/idisa_target.h>
34#include <kernels/instance.h>
35#include <kernels/kernel.h>
36#include <kernels/s2p_kernel.h>
37
38#include <pablo/pablo_compiler.h>
39#include <pablo/pablo_toolchain.h>
40
41// Dynamic processor detection
42#define ISPC_LLVM_VERSION ISPC_LLVM_3_6
43#include <util/ispc.cpp>
44
45#include <utf_encoding.h>
46
47// mmap system
48#include <boost/filesystem.hpp>
49#include <boost/iostreams/device/mapped_file.hpp>
50using namespace boost::iostreams;
51using namespace boost::filesystem;
52
53#include <fcntl.h>
54
55static cl::list<std::string> inputFiles(cl::Positional, cl::desc("<input file ...>"), cl::OneOrMore);
56
57static cl::opt<bool> CountLines("l", cl::desc("Report the number of lines in each input file."), cl::init(false));
58static cl::opt<bool> CountWords("w", cl::desc("Report the number of lines in each input file."), cl::init(false));
59static cl::opt<bool> CountBytes("c", cl::desc("Report the number of bytes in each input file."), cl::init(false));
60static cl::opt<bool> CountChars("m", cl::desc("Report the number of characters in each input file."), cl::init(false));
61
62
63static cl::OptionCategory eIRDumpOptions("LLVM IR Dump Options", "These options control dumping of LLVM IR.");
64static cl::opt<bool> DumpGeneratedIR("dump-generated-IR", cl::init(false), cl::desc("Print LLVM IR generated by Pablo Compiler."), cl::cat(eIRDumpOptions));
65
66static cl::OptionCategory cMachineCodeOptimization("Machine Code Optimizations", "These options control back-end compilier optimization levels.");
67
68static cl::opt<char> OptLevel("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] (default = '-O0')"),
69                              cl::cat(cMachineCodeOptimization), cl::Prefix, cl::ZeroOrMore, cl::init('0'));
70
71static cl::opt<unsigned> SegmentSize("segment-size", cl::desc("Segment Size"), cl::value_desc("positive integer"), cl::init(1));
72
73
74static int defaultFieldWidth = 7;  // default field width
75
76std::vector<uint64_t> lineCount;
77std::vector<uint64_t> wordCount;
78std::vector<uint64_t> charCount;
79std::vector<uint64_t> byteCount;
80
81uint64_t TotalLines = 0;
82uint64_t TotalWords = 0;
83uint64_t TotalChars = 0;
84uint64_t TotalBytes = 0;
85
86//
87//
88extern "C" {
89    void report_counts(uint64_t lines, uint64_t words, uint64_t chars, uint64_t bytes, uint64_t fileIdx) {
90        lineCount[fileIdx] = lines;
91        wordCount[fileIdx] = words;
92        charCount[fileIdx] = chars;
93        byteCount[fileIdx] = bytes;
94        TotalLines += lines;
95        TotalWords += words;
96        TotalChars += chars;
97        TotalBytes += bytes;
98    }
99}
100
101
102
103//
104//
105
106ExecutionEngine * wcJIT_to_ExecutionEngine (Module * m) {
107
108    InitializeNativeTarget();
109    InitializeNativeTargetAsmPrinter();
110    InitializeNativeTargetAsmParser();
111
112    PassRegistry * Registry = PassRegistry::getPassRegistry();
113    initializeCore(*Registry);
114    initializeCodeGen(*Registry);
115    initializeLowerIntrinsicsPass(*Registry);
116
117    std::string errMessage;
118    EngineBuilder builder(std::move(std::unique_ptr<Module>(m)));
119    builder.setErrorStr(&errMessage);
120    builder.setMCPU(sys::getHostCPUName());
121    CodeGenOpt::Level optLevel = CodeGenOpt::Level::None;
122    switch (OptLevel) {
123        case '0': optLevel = CodeGenOpt::None; break;
124        case '1': optLevel = CodeGenOpt::Less; break;
125        case '2': optLevel = CodeGenOpt::Default; break;
126        case '3': optLevel = CodeGenOpt::Aggressive; break;
127        default: errs() << OptLevel << " is an invalid optimization level.\n";
128    }
129    builder.setOptLevel(optLevel);
130
131    if ((strncmp(lGetSystemISA(), "avx2", 4) == 0)) {
132            std::vector<std::string> attrs;
133            attrs.push_back("avx2");
134            builder.setMAttrs(attrs);
135    }
136
137    // builder.selectTarget();
138
139    //builder.setOptLevel(mMaxWhileDepth ? CodeGenOpt::Level::Less : CodeGenOpt::Level::None);
140    ExecutionEngine * engine = builder.create();
141    if (engine == nullptr) {
142        throw std::runtime_error("Could not create ExecutionEngine: " + errMessage);
143    }
144    return engine;
145}
146
147
148pablo::PabloFunction * wc_gen(Encoding encoding) {
149    //  input: 8 basis bit streams
150    //  output: 3 count streams
151   
152    pablo::PabloFunction * function = pablo::PabloFunction::Create("wc", 8, 3);
153    cc::CC_Compiler ccc(*function, encoding);
154   
155    pablo::PabloBuilder pBuilder(ccc.getBuilder().getPabloBlock(), ccc.getBuilder());
156    const std::vector<pablo::Var *> u8_bits = ccc.getBasisBits();
157
158    if (CountLines) {
159        pablo::PabloAST * LF = ccc.compileCC(re::makeCC(0x0A));
160        function->setResult(0, pBuilder.createAssign("lineCount", pBuilder.createCount(LF)));
161    }
162    else function->setResult(0, pBuilder.createAssign("lineCount", pBuilder.createZeroes()));
163    // FIXME - we need to limit this to pablo.inFile() because null bytes past EOF are matched by wordChar
164    if (CountWords) {
165        pablo::PabloAST * WS = ccc.compileCC(re::makeCC(re::makeCC(0x09, 0x0D), re::makeCC(0x20)));
166       
167        pablo::PabloAST * wordChar = ccc.compileCC(re::makeCC(re::makeCC(re::makeCC(0x00, 0x08), re::makeCC(0xE, 0x1F)), re::makeCC(0x21, 0xFF)));
168        // WS_follow_or_start = 1 past WS or at start of file
169        pablo::PabloAST * WS_follow_or_start = pBuilder.createNot(pBuilder.createAdvance(pBuilder.createNot(WS), 1));
170        //
171        pablo::PabloAST * wordStart = pBuilder.createAnd(wordChar, WS_follow_or_start);
172        function->setResult(1, pBuilder.createAssign("wordCount", pBuilder.createCount(wordStart)));
173    }
174    else function->setResult(1, pBuilder.createAssign("wordCount", pBuilder.createZeroes()));
175    if (CountChars) {
176        //
177        // FIXME: This correctly counts characters assuming valid UTF-8 input.  But what if input is
178        // not UTF-8, or is not valid?
179        //
180        pablo::PabloAST * u8Begin = ccc.compileCC(re::makeCC(re::makeCC(0, 0x7F), re::makeCC(0xC2, 0xF4)));
181        function->setResult(2, pBuilder.createAssign("charCount", pBuilder.createCount(u8Begin)));
182    }
183    else function->setResult(2, pBuilder.createAssign("charCount", pBuilder.createZeroes()));
184    return function;
185}
186
187using namespace kernel;
188
189
190class wcPipelineBuilder {
191public:
192    wcPipelineBuilder(llvm::Module * m, IDISA::IDISA_Builder * b);
193   
194    ~wcPipelineBuilder();
195   
196    void CreateKernels(pablo::PabloFunction * function);
197    llvm::Function * ExecuteKernels();
198   
199private:
200    llvm::Module *                      mMod;
201    IDISA::IDISA_Builder *              iBuilder;
202    KernelBuilder *                     mS2PKernel;
203    KernelBuilder *                     mWC_Kernel;
204    llvm::Type *                        mBitBlockType;
205    int                                 mBlockSize;
206};
207
208
209using namespace pablo;
210using namespace kernel;
211
212wcPipelineBuilder::wcPipelineBuilder(Module * m, IDISA::IDISA_Builder * b)
213: mMod(m)
214, iBuilder(b)
215, mBitBlockType(b->getBitBlockType())
216, mBlockSize(b->getBitBlockWidth()){
217   
218}
219
220wcPipelineBuilder::~wcPipelineBuilder(){
221    delete mS2PKernel;
222    delete mWC_Kernel;
223}
224
225void wcPipelineBuilder::CreateKernels(PabloFunction * function){
226    mS2PKernel = new KernelBuilder(iBuilder, "s2p", SegmentSize);
227    mWC_Kernel = new KernelBuilder(iBuilder, "wc", SegmentSize);
228   
229    generateS2PKernel(mMod, iBuilder, mS2PKernel);
230   
231    pablo_function_passes(function);
232   
233    PabloCompiler pablo_compiler(mMod, iBuilder);
234    try {
235        pablo_compiler.setKernel(mWC_Kernel);
236        pablo_compiler.compile(function);
237        delete function;
238        releaseSlabAllocatorMemory();
239    } catch (std::runtime_error e) {
240        delete function;
241        releaseSlabAllocatorMemory();
242        std::cerr << "Runtime error: " << e.what() << std::endl;
243        exit(1);
244    }
245   
246}
247
248
249
250
251Function * wcPipelineBuilder::ExecuteKernels() {
252    Constant * report_counts_routine;
253    Type * const int64ty = iBuilder->getInt64Ty();
254    Type * const voidTy = Type::getVoidTy(mMod->getContext());
255    report_counts_routine = mMod->getOrInsertFunction("report_counts", voidTy, int64ty, int64ty, int64ty, int64ty, int64ty, nullptr);
256    Type * const inputType = PointerType::get(ArrayType::get(StructType::get(mMod->getContext(), std::vector<Type *>({ArrayType::get(mBitBlockType, 8)})), 1), 0);
257   
258    Function * const main = cast<Function>(mMod->getOrInsertFunction("Main", voidTy, inputType, int64ty, int64ty, nullptr));
259    main->setCallingConv(CallingConv::C);
260    Function::arg_iterator args = main->arg_begin();
261   
262    Value * const inputStream = &*(args++);
263    inputStream->setName("input");
264    Value * const bufferSize = &*(args++);
265    bufferSize->setName("bufferSize");
266    Value * const fileIdx = &*(args++);
267    fileIdx->setName("fileIdx");
268   
269    iBuilder->SetInsertPoint(BasicBlock::Create(mMod->getContext(), "entry", main,0));
270   
271    BasicBlock * entryBlock = iBuilder->GetInsertBlock();
272
273    BasicBlock * segmentCondBlock = nullptr;
274    BasicBlock * segmentBodyBlock = nullptr;
275    const unsigned segmentSize = SegmentSize;
276    if (segmentSize > 1) {
277        segmentCondBlock = BasicBlock::Create(mMod->getContext(), "segmentCond", main, 0);
278        segmentBodyBlock = BasicBlock::Create(mMod->getContext(), "segmentBody", main, 0);
279    }
280    BasicBlock * fullCondBlock = BasicBlock::Create(mMod->getContext(), "fullCond", main, 0);
281    BasicBlock * fullBodyBlock = BasicBlock::Create(mMod->getContext(), "fullBody", main, 0);
282    BasicBlock * finalBlock = BasicBlock::Create(mMod->getContext(), "final", main, 0);
283    BasicBlock * finalPartialBlock = BasicBlock::Create(mMod->getContext(), "partial", main, 0);
284    BasicBlock * finalEmptyBlock = BasicBlock::Create(mMod->getContext(), "empty", main, 0);
285    BasicBlock * endBlock = BasicBlock::Create(mMod->getContext(), "end", main, 0);
286
287    Instance * s2pInstance = mS2PKernel->instantiate(inputStream);
288    Instance * wcInstance = mWC_Kernel->instantiate(s2pInstance->getOutputStreamBuffer());
289
290    Value * initialBufferSize = nullptr;
291    BasicBlock * initialBlock = nullptr;
292   
293    if (segmentSize > 1) {
294        iBuilder->CreateBr(segmentCondBlock);
295        iBuilder->SetInsertPoint(segmentCondBlock);
296        PHINode * remainingBytes = iBuilder->CreatePHI(int64ty, 2, "remainingBytes");
297        remainingBytes->addIncoming(bufferSize, entryBlock);
298        Constant * const step = ConstantInt::get(int64ty, mBlockSize * segmentSize);
299        Value * segmentCondTest = iBuilder->CreateICmpULT(remainingBytes, step);
300        iBuilder->CreateCondBr(segmentCondTest, fullCondBlock, segmentBodyBlock);
301        iBuilder->SetInsertPoint(segmentBodyBlock);
302        for (unsigned i = 0; i < segmentSize; ++i) {
303            s2pInstance->CreateDoBlockCall();
304        }
305        for (unsigned i = 0; i < segmentSize; ++i) {
306            wcInstance->CreateDoBlockCall();
307        }
308        remainingBytes->addIncoming(iBuilder->CreateSub(remainingBytes, step), segmentBodyBlock);
309        iBuilder->CreateBr(segmentCondBlock);
310        initialBufferSize = remainingBytes;
311        initialBlock = segmentCondBlock;
312    } else {
313        initialBufferSize = bufferSize;
314        initialBlock = entryBlock;
315        iBuilder->CreateBr(fullCondBlock);
316    }
317
318    iBuilder->SetInsertPoint(fullCondBlock);
319    PHINode * remainingBytes = iBuilder->CreatePHI(int64ty, 2, "remainingBytes");
320    remainingBytes->addIncoming(initialBufferSize, initialBlock);
321
322    Constant * const step = ConstantInt::get(int64ty, mBlockSize);
323    Value * fullCondTest = iBuilder->CreateICmpULT(remainingBytes, step);
324    iBuilder->CreateCondBr(fullCondTest, finalBlock, fullBodyBlock);
325   
326    iBuilder->SetInsertPoint(fullBodyBlock);
327
328    s2pInstance->CreateDoBlockCall();
329    wcInstance->CreateDoBlockCall();
330
331    Value * diff = iBuilder->CreateSub(remainingBytes, step);
332
333    remainingBytes->addIncoming(diff, fullBodyBlock);
334    iBuilder->CreateBr(fullCondBlock);
335   
336    iBuilder->SetInsertPoint(finalBlock);
337    Value * emptyBlockCond = iBuilder->CreateICmpEQ(remainingBytes, ConstantInt::get(int64ty, 0));
338    iBuilder->CreateCondBr(emptyBlockCond, finalEmptyBlock, finalPartialBlock);
339   
340   
341    iBuilder->SetInsertPoint(finalPartialBlock);
342    s2pInstance->CreateDoBlockCall();
343    iBuilder->CreateBr(endBlock);
344   
345    iBuilder->SetInsertPoint(finalEmptyBlock);
346    s2pInstance->clearOutputStreamSet();
347    iBuilder->CreateBr(endBlock);
348   
349    iBuilder->SetInsertPoint(endBlock);
350
351    wcInstance->CreateDoBlockCall();
352   
353    Value * lineCount = iBuilder->CreateExtractElement(iBuilder->CreateBlockAlignedLoad(wcInstance->getOutputStream((int) 0)), iBuilder->getInt32(0));
354    Value * wordCount = iBuilder->CreateExtractElement(iBuilder->CreateBlockAlignedLoad(wcInstance->getOutputStream(1)), iBuilder->getInt32(0));
355    Value * charCount = iBuilder->CreateExtractElement(iBuilder->CreateBlockAlignedLoad(wcInstance->getOutputStream(2)), iBuilder->getInt32(0));
356   
357    iBuilder->CreateCall(report_counts_routine, std::vector<Value *>({lineCount, wordCount, charCount, bufferSize, fileIdx}));
358   
359    iBuilder->CreateRetVoid();
360   
361    return main;
362}
363
364
365typedef void (*wcFunctionType)(char * byte_data, size_t filesize, size_t fileIdx);
366
367static ExecutionEngine * wcEngine = nullptr;
368
369wcFunctionType wcCodeGen(void) {
370                           
371    Module * M = new Module("wc", getGlobalContext());
372   
373    IDISA::IDISA_Builder * idb = GetIDISA_Builder(M);
374
375    wcPipelineBuilder pipelineBuilder(M, idb);
376
377    Encoding encoding(Encoding::Type::UTF_8, 8);
378   
379    pablo::PabloFunction * function = wc_gen(encoding);
380   
381
382    pipelineBuilder.CreateKernels(function);
383
384    llvm::Function * main_IR = pipelineBuilder.ExecuteKernels();
385   
386    if (DumpGeneratedIR) {
387        M->dump();
388    }
389   
390    //verifyModule(*M, &dbgs());
391    //std::cerr << "ExecuteKernels(); done\n";
392    wcEngine = wcJIT_to_ExecutionEngine(M);
393   
394    wcEngine->finalizeObject();
395    //std::cerr << "finalizeObject(); done\n";
396
397    delete idb;
398    return reinterpret_cast<wcFunctionType>(wcEngine->getPointerToFunction(main_IR));
399}
400
401void wc(wcFunctionType fn_ptr, const int64_t fileIdx) {
402    std::string fileName = inputFiles[fileIdx];
403    size_t fileSize;
404    char * fileBuffer;
405   
406    const path file(fileName);
407    if (exists(file)) {
408        if (is_directory(file)) {
409            return;
410        }
411    } else {
412        std::cerr << "Error: cannot open " << fileName << " for processing. Skipped.\n";
413        return;
414    }
415   
416    fileSize = file_size(file);
417    mapped_file_source mappedFile;
418    if (fileSize == 0) {
419        fileBuffer = nullptr;
420    }
421    else {
422        try {
423            mappedFile.open(fileName);
424        } catch (std::exception &e) {
425            std::cerr << "Error: Boost mmap of " << fileName << ": " << e.what() << std::endl;
426            return;
427        }
428        fileBuffer = const_cast<char *>(mappedFile.data());
429    }
430    fn_ptr(fileBuffer, fileSize, fileIdx);
431
432    mappedFile.close();
433   
434}
435
436
437
438
439int main(int argc, char *argv[]) {
440    StringMap<cl::Option*> Map;
441    cl::getRegisteredOptions(Map);
442    Map["time-passes"]->setHiddenFlag(cl::Hidden);
443    Map["disable-spill-fusing"]->setHiddenFlag(cl::Hidden);
444    Map["enable-misched"]->setHiddenFlag(cl::Hidden);
445    Map["enable-tbaa"]->setHiddenFlag(cl::Hidden);
446    Map["exhaustive-register-search"]->setHiddenFlag(cl::Hidden);
447    Map["join-liveintervals"]->setHiddenFlag(cl::Hidden);
448    Map["limit-float-precision"]->setHiddenFlag(cl::Hidden);
449    Map["mc-x86-disable-arith-relaxation"]->setHiddenFlag(cl::Hidden);
450    Map["limit-float-precision"]->setHiddenFlag(cl::Hidden);
451    Map["print-after-all"]->setHiddenFlag(cl::Hidden);
452    Map["print-before-all"]->setHiddenFlag(cl::Hidden);
453    Map["print-machineinstrs"]->setHiddenFlag(cl::Hidden);
454    Map["regalloc"]->setHiddenFlag(cl::Hidden);
455    Map["rng-seed"]->setHiddenFlag(cl::Hidden);
456    Map["stackmap-version"]->setHiddenFlag(cl::Hidden);
457    Map["x86-asm-syntax"]->setHiddenFlag(cl::Hidden);
458    Map["verify-debug-info"]->setHiddenFlag(cl::Hidden);
459    Map["verify-dom-info"]->setHiddenFlag(cl::Hidden);
460    Map["verify-loop-info"]->setHiddenFlag(cl::Hidden);
461    Map["verify-regalloc"]->setHiddenFlag(cl::Hidden);
462    Map["verify-scev"]->setHiddenFlag(cl::Hidden);
463    Map["x86-recip-refinement-steps"]->setHiddenFlag(cl::Hidden);
464    Map["rewrite-map-file"]->setHiddenFlag(cl::Hidden);
465
466    cl::ParseCommandLineOptions(argc, argv);
467    if (!(CountLines || CountWords || CountChars || CountBytes)) {
468        CountLines = true;
469        CountWords = true;
470        CountBytes = true;
471    }
472   
473    wcFunctionType fn_ptr = wcCodeGen();
474
475    int fileCount = inputFiles.size();
476    lineCount.resize(fileCount);
477    wordCount.resize(fileCount);
478    charCount.resize(fileCount);
479    byteCount.resize(fileCount);
480   
481    for (unsigned i = 0; i < inputFiles.size(); ++i) {
482        wc(fn_ptr, i);
483    }
484   
485    delete wcEngine;
486   
487    size_t maxCount = 0;
488    if (CountLines) maxCount = TotalLines;
489    if (CountWords) maxCount = TotalWords;
490    if (CountChars) maxCount = TotalChars;
491    if (CountBytes) maxCount = TotalBytes;
492   
493    int fieldWidth = std::to_string(maxCount).size() + 1;
494    if (fieldWidth < defaultFieldWidth) fieldWidth = defaultFieldWidth;
495
496    for (unsigned i = 0; i < inputFiles.size(); ++i) {
497        if (CountLines) {
498            std::cout << std::setw(fieldWidth) << lineCount[i];
499        }
500        if (CountWords) {
501            std::cout << std::setw(fieldWidth) << wordCount[i];
502        }
503        if (CountChars) {
504            std::cout << std::setw(fieldWidth) << charCount[i];
505        }
506        if (CountBytes) {
507            std::cout << std::setw(fieldWidth) << byteCount[i];
508        }
509        std::cout << " " << inputFiles[i] << std::endl;
510    }
511    if (inputFiles.size() > 1) {
512        if (CountLines) {
513            std::cout << std::setw(fieldWidth) << TotalLines;
514        }
515        if (CountWords) {
516            std::cout << std::setw(fieldWidth) << TotalWords;
517        }
518        if (CountChars) {
519            std::cout << std::setw(fieldWidth) << TotalChars;
520        }
521        if (CountBytes) {
522            std::cout << std::setw(fieldWidth) << TotalBytes;
523        }
524        std::cout << " total" << std::endl;
525    }
526
527    return 0;
528}
529
530                       
Note: See TracBrowser for help on using the repository browser.