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

Last change on this file since 5925 was 5925, checked in by cameron, 14 months ago

File processing for wc - recursive option

File size: 13.5 KB
Line 
1/*
2 *  Copyright (c) 2018 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 <iostream>
8#include <iomanip>
9#include <vector>
10#include <string>
11#include <toolchain/toolchain.h>
12#include <llvm/IR/Function.h>
13#include <llvm/IR/Module.h>
14#include <llvm/Support/CommandLine.h>
15#include <llvm/Support/raw_ostream.h>
16#include <cc/cc_compiler.h>
17#include <pablo/pablo_kernel.h>
18#include <kernels/kernel_builder.h>
19#include <IR_Gen/idisa_target.h>
20#include <kernels/streamset.h>
21#include <kernels/source_kernel.h>
22#include <kernels/s2p_kernel.h>
23#include <pablo/pablo_compiler.h>
24#include <pablo/pablo_toolchain.h>
25#include <toolchain/cpudriver.h>
26#include <fcntl.h>
27#include <boost/filesystem.hpp>
28
29using namespace llvm;
30
31static cl::OptionCategory wcFlags("Command Flags", "wc options");
32
33static cl::list<std::string> inputFiles(cl::Positional, cl::desc("<input file ...>"), cl::OneOrMore, cl::cat(wcFlags));
34
35std::vector<std::string> allFiles;
36
37enum CountOptions {
38    LineOption, WordOption, CharOption, ByteOption
39};
40
41static cl::list<CountOptions> wcOptions(
42  cl::values(clEnumValN(LineOption, "l", "Report the number of lines in each input file."),
43             clEnumValN(WordOption, "w", "Report the number of words in each input file."),
44             clEnumValN(CharOption, "m", "Report the number of characters in each input file (override -c)."),
45             clEnumValN(ByteOption, "c", "Report the number of bytes in each input file (override -m).")
46             CL_ENUM_VAL_SENTINEL), cl::cat(wcFlags), cl::Grouping);
47                                                 
48static std::string wc_modes = "";
49
50static int defaultDisplayColumnWidth = 7;  // default field width
51
52
53bool RecursiveFlag;
54static cl::opt<bool, true> RecursiveOption("r", cl::location(RecursiveFlag), cl::desc("Recursively process files within directories, (but follow only top-level symlinks unless -R)."), cl::cat(wcFlags), cl::Grouping);
55static cl::alias RecursiveAlias("recursive", cl::desc("Alias for -r"), cl::aliasopt(RecursiveOption));
56
57bool DereferenceRecursiveFlag;
58static cl::opt<bool, true> DereferenceRecursiveOption("R", cl::location(DereferenceRecursiveFlag), cl::desc("Recursively process files within directories, following symlinks at all levels."), cl::cat(wcFlags), cl::Grouping);
59static cl::alias DereferenceRecursiveAlias("dereference-recursive", cl::desc("Alias for -R"), cl::aliasopt(DereferenceRecursiveOption));
60
61
62// This is a stub, to be expanded later.
63bool excludeDirectory(boost::filesystem::path dirpath) { return dirpath.filename() == ".svn";}
64
65// Determine whether to skip a path based on -D skip or -d skip settings.
66bool skip_path(boost::filesystem::path p) {
67    using namespace boost::filesystem;
68    switch (status(p).type()) {
69        case directory_file: return !(RecursiveFlag|DereferenceRecursiveFlag);
70        case block_file:
71        case character_file:
72        case fifo_file:
73        case socket_file:
74            return true;
75        default:
76            return false;
77    }
78}
79
80
81std::vector<std::string> getFullFileList(cl::list<std::string> & inputFiles) {
82    using namespace boost::filesystem;
83    symlink_option follow_symlink = DereferenceRecursiveFlag ? symlink_option::recurse : symlink_option::none;
84    std::vector<std::string> expanded_paths;
85    boost::system::error_code errc;
86    for (const std::string & f : inputFiles) {
87        //        if (f == "-") {
88        //            continue;
89        //        }
90        path p(f);
91        if (skip_path(p)) {
92            continue;
93        }
94        if (LLVM_UNLIKELY((RecursiveFlag || DereferenceRecursiveFlag) && is_directory(p))) {
95            if (!excludeDirectory(p)) {
96                recursive_directory_iterator di(p, follow_symlink, errc), end;
97                if (errc) {
98                    // If we cannot enter the directory, keep it in the list of files.
99                    expanded_paths.push_back(f);
100                    continue;
101                }
102                while (di != end) {
103                    auto & e = di->path();
104                    if (is_directory(e)) {
105                        if (LLVM_UNLIKELY(excludeDirectory(e))) {
106                            di.no_push();
107                        }
108                    } else {
109                        if (!skip_path(e)) expanded_paths.push_back(e.string());
110                    }
111                    di.increment(errc);
112                    if (errc) {
113                        expanded_paths.push_back(e.string());
114                    }
115                }
116            }
117        } else {
118            expanded_paths.push_back(p.string());
119        }
120    }
121    return expanded_paths;
122}
123
124
125
126
127
128bool CountLines = false;
129bool CountWords = false;
130bool CountChars = false;
131bool CountBytes = false;
132
133std::vector<uint64_t> lineCount;
134std::vector<uint64_t> wordCount;
135std::vector<uint64_t> charCount;
136std::vector<uint64_t> byteCount;
137
138uint64_t TotalLines = 0;
139uint64_t TotalWords = 0;
140uint64_t TotalChars = 0;
141uint64_t TotalBytes = 0;
142
143using namespace pablo;
144using namespace kernel;
145using namespace parabix;
146
147//  The callback routine that records counts in progress.
148//
149extern "C" {
150    void record_counts(uint64_t lines, uint64_t words, uint64_t chars, uint64_t bytes, uint64_t fileIdx) {
151        lineCount[fileIdx] = lines;
152        wordCount[fileIdx] = words;
153        charCount[fileIdx] = chars;
154        byteCount[fileIdx] = bytes;
155        TotalLines += lines;
156        TotalWords += words;
157        TotalChars += chars;
158        TotalBytes += bytes;
159    }
160}
161
162class WordCountKernel final: public pablo::PabloKernel {
163public:
164    WordCountKernel(const std::unique_ptr<kernel::KernelBuilder> & b, Binding && inputStreamSet);
165    bool isCachable() const override { return true; }
166    bool hasSignature() const override { return false; }
167protected:
168    void generatePabloMethod() override;
169};
170
171WordCountKernel::WordCountKernel (const std::unique_ptr<kernel::KernelBuilder> & b, Binding && inputStreamSet)
172: PabloKernel(b, "wc_" + wc_modes,
173    {inputStreamSet},
174    {},
175    {},
176    {Binding{b->getSizeTy(), "lineCount"}, Binding{b->getSizeTy(), "wordCount"}, Binding{b->getSizeTy(), "charCount"}}) {
177
178}
179
180void WordCountKernel::generatePabloMethod() {
181    PabloBuilder pb(getEntryScope());
182    std::unique_ptr<cc::CC_Compiler> ccc;
183    if (CountWords || CountChars) {
184        ccc = make_unique<cc::Parabix_CC_Compiler>(getEntryScope(), getInputStreamSet("u8bit"));
185    } else {
186        ccc = make_unique<cc::Direct_CC_Compiler>(getEntryScope(), pb.createExtract(getInput(0), pb.getInteger(0)));
187    }
188
189    //  output: 3 counters
190    Var * lc = getOutputScalarVar("lineCount");
191    Var * wc = getOutputScalarVar("wordCount");
192    Var * cc = getOutputScalarVar("charCount");
193
194    if (CountLines) {
195        PabloAST * LF = ccc->compileCC(re::makeByte(0x0A));
196        pb.createAssign(lc, pb.createCount(LF));
197    }
198    if (CountWords) {
199        PabloAST * WS = ccc->compileCC(re::makeCC(re::makeByte(0x09, 0x0D), re::makeByte(0x20)));
200        PabloAST * wordChar = pb.createNot(WS);
201        // WS_follow_or_start = 1 past WS or at start of file
202        PabloAST * WS_follow_or_start = pb.createNot(pb.createAdvance(wordChar, 1));
203        PabloAST * wordStart = pb.createInFile(pb.createAnd(wordChar, WS_follow_or_start));
204        pb.createAssign(wc, pb.createCount(wordStart));
205    }
206    if (CountChars) {
207        //
208        // FIXME: This correctly counts characters assuming valid UTF-8 input.  But what if input is
209        // not UTF-8, or is not valid?
210        //
211        PabloAST * u8Begin = ccc->compileCC(re::makeCC(re::makeByte(0, 0x7F), re::makeByte(0xC2, 0xF4)));
212        pb.createAssign(cc, pb.createCount(u8Begin));
213    }
214}
215
216typedef void (*WordCountFunctionType)(uint32_t fd, size_t fileIdx);
217
218void wcPipelineGen(ParabixDriver & pxDriver) {
219
220    auto & iBuilder = pxDriver.getBuilder();
221    Module * m = iBuilder->getModule();
222    const unsigned segmentSize = codegen::SegmentSize;
223    const unsigned bufferSegments = codegen::ThreadNum+1;
224
225   
226    Type * const int32Ty = iBuilder->getInt32Ty();
227    Type * const sizeTy = iBuilder->getSizeTy();
228    Type * const voidTy = iBuilder->getVoidTy();
229
230    FunctionType * const recordCountsType = FunctionType::get(voidTy, {sizeTy, sizeTy, sizeTy, sizeTy, sizeTy}, false);
231    Constant * const recordCounts = m->getOrInsertFunction("record_counts", recordCountsType);
232
233    FunctionType * const mainType = FunctionType::get(voidTy, {int32Ty, sizeTy}, false);
234    Function * const main = cast<Function>(m->getOrInsertFunction("Main", mainType));
235    main->setCallingConv(CallingConv::C);
236    Function::arg_iterator args = main->arg_begin();   
237    Value * const fileDecriptor = &*(args++);
238    fileDecriptor->setName("fileDecriptor");
239    Value * const fileIdx = &*(args++);
240    fileIdx->setName("fileIdx");
241
242    iBuilder->SetInsertPoint(BasicBlock::Create(m->getContext(), "entry", main,0));
243
244    StreamSetBuffer * const ByteStream = pxDriver.addBuffer<SourceBuffer>(iBuilder, iBuilder->getStreamSetTy(1, 8));
245
246
247    Kernel * mmapK = pxDriver.addKernelInstance<MMapSourceKernel>(iBuilder);
248    mmapK->setInitialArguments({fileDecriptor});
249    pxDriver.makeKernelCall(mmapK, {}, {ByteStream});
250   
251    Kernel * wck  = nullptr;
252    if (CountWords || CountChars) {
253        StreamSetBuffer * const BasisBits = pxDriver.addBuffer<CircularBuffer>(iBuilder, iBuilder->getStreamSetTy(8, 1), segmentSize * bufferSegments);
254        Kernel * s2pk = pxDriver.addKernelInstance<S2PKernel>(iBuilder);
255        pxDriver.makeKernelCall(s2pk, {ByteStream}, {BasisBits});
256       
257        wck = pxDriver.addKernelInstance<WordCountKernel>(iBuilder, Binding{iBuilder->getStreamSetTy(8, 1), "u8bit"});
258        pxDriver.makeKernelCall(wck, {BasisBits}, {});
259
260
261    } else {
262        wck = pxDriver.addKernelInstance<WordCountKernel>(iBuilder, Binding{iBuilder->getStreamSetTy(1, 8), "u8byte"});
263        pxDriver.makeKernelCall(wck, {ByteStream}, {});
264    }
265
266    pxDriver.generatePipelineIR();
267   
268    iBuilder->setKernel(mmapK);
269    Value * const fileSize = iBuilder->getAccumulator("fileSize");
270    iBuilder->setKernel(wck);
271    Value * const lineCount = iBuilder->getAccumulator("lineCount");
272    Value * const wordCount = iBuilder->getAccumulator("wordCount");
273    Value * const charCount = iBuilder->getAccumulator("charCount");
274
275    iBuilder->CreateCall(recordCounts, {lineCount, wordCount, charCount, fileSize, fileIdx});
276    pxDriver.deallocateBuffers();
277    iBuilder->CreateRetVoid();
278
279    pxDriver.finalizeObject();
280}
281
282void wc(WordCountFunctionType fn_ptr, const int64_t fileIdx) {
283    std::string fileName = allFiles[fileIdx];
284    const int fd = open(fileName.c_str(), O_RDONLY);
285    if (LLVM_UNLIKELY(fd == -1)) {
286        std::cerr << "Error: cannot open " << fileName << " for processing. Skipped.\n";
287    } else {
288        fn_ptr(fd, fileIdx);
289        close(fd);
290    }
291}
292
293int main(int argc, char *argv[]) {
294    codegen::ParseCommandLineOptions(argc, argv, {&wcFlags, pablo_toolchain_flags(), codegen::codegen_flags()});
295    if (wcOptions.size() == 0) {
296        CountLines = true;
297        CountWords = true;
298        CountBytes = true;
299    } else {
300        CountLines = false;
301        CountWords = false;
302        CountBytes = false;
303        CountChars = false;
304        for (unsigned i = 0; i < wcOptions.size(); i++) {
305            switch (wcOptions[i]) {
306                case WordOption: CountWords = true; break;
307                case LineOption: CountLines = true; break;
308                case CharOption: CountChars = true; CountBytes = false; break;
309                case ByteOption: CountBytes = true; CountChars = false; break;
310            }
311        }
312    }
313    if (CountLines) wc_modes += "l";
314    if (CountWords) wc_modes += "w";
315    if (CountChars) wc_modes += "m";
316    if (CountBytes) wc_modes += "c";
317
318    ParabixDriver pxDriver("wc");
319    wcPipelineGen(pxDriver);
320    auto wordCountFunctionPtr = reinterpret_cast<WordCountFunctionType>(pxDriver.getMain());
321
322    allFiles = getFullFileList(inputFiles);
323    const auto fileCount = allFiles.size();
324    lineCount.resize(fileCount);
325    wordCount.resize(fileCount);
326    charCount.resize(fileCount);
327    byteCount.resize(fileCount);
328   
329    for (unsigned i = 0; i < fileCount; ++i) {
330        wc(wordCountFunctionPtr, i);
331    }
332   
333    size_t maxCount = 0;
334    if (CountLines) maxCount = TotalLines;
335    if (CountWords) maxCount = TotalWords;
336    if (CountChars) maxCount = TotalChars;
337    if (CountBytes) maxCount = TotalBytes;
338   
339   
340   
341    int displayColumnWidth = std::to_string(maxCount).size() + 1;
342    if (displayColumnWidth < defaultDisplayColumnWidth) displayColumnWidth = defaultDisplayColumnWidth;
343
344    for (unsigned i = 0; i < fileCount; ++i) {
345        std::cout << std::setw(displayColumnWidth-1);
346        if (CountLines) {
347            std::cout << lineCount[i] << std::setw(displayColumnWidth);
348        }
349        if (CountWords) {
350            std::cout << wordCount[i] << std::setw(displayColumnWidth);
351        }
352        if (CountChars) {
353            std::cout << charCount[i] << std::setw(displayColumnWidth);
354        }
355        if (CountBytes) {
356            std::cout << byteCount[i];
357        }
358        std::cout << " " << allFiles[i] << std::endl;
359    }
360    if (inputFiles.size() > 1) {
361        std::cout << std::setw(displayColumnWidth-1);
362        if (CountLines) {
363            std::cout << TotalLines << std::setw(displayColumnWidth);
364        }
365        if (CountWords) {
366            std::cout << TotalWords << std::setw(displayColumnWidth);
367        }
368        if (CountChars) {
369            std::cout << TotalChars << std::setw(displayColumnWidth);
370        }
371        if (CountBytes) {
372            std::cout << TotalBytes;
373        }
374        std::cout << " total" << std::endl;
375    }
376
377    return 0;
378}
Note: See TracBrowser for help on using the repository browser.