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

Last change on this file since 6135 was 6047, checked in by nmedfort, 12 months ago

Major refactoring of buffer types. Static buffers replace Circular and CircularCopyback?. External buffers unify Source/External?.

File size: 11.3 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 <sys/stat.h>
27#include <fcntl.h>
28#include <util/file_select.h>
29#include <boost/filesystem.hpp>
30namespace fs = boost::filesystem;
31
32using namespace llvm;
33
34static cl::OptionCategory wcFlags("Command Flags", "wc options");
35
36static cl::list<std::string> inputFiles(cl::Positional, cl::desc("<input file ...>"), cl::OneOrMore, cl::cat(wcFlags));
37
38std::vector<fs::path> allFiles;
39
40enum CountOptions {
41    LineOption, WordOption, CharOption, ByteOption
42};
43
44static cl::list<CountOptions> wcOptions(
45  cl::values(clEnumValN(LineOption, "l", "Report the number of lines in each input file."),
46             clEnumValN(WordOption, "w", "Report the number of words in each input file."),
47             clEnumValN(CharOption, "m", "Report the number of characters in each input file (override -c)."),
48             clEnumValN(ByteOption, "c", "Report the number of bytes in each input file (override -m).")
49             CL_ENUM_VAL_SENTINEL), cl::cat(wcFlags), cl::Grouping);
50                                                 
51static std::string wc_modes = "";
52
53static int defaultDisplayColumnWidth = 7;  // default field width
54
55
56
57bool CountLines = false;
58bool CountWords = false;
59bool CountChars = false;
60bool CountBytes = false;
61
62std::vector<uint64_t> lineCount;
63std::vector<uint64_t> wordCount;
64std::vector<uint64_t> charCount;
65std::vector<uint64_t> byteCount;
66
67uint64_t TotalLines = 0;
68uint64_t TotalWords = 0;
69uint64_t TotalChars = 0;
70uint64_t TotalBytes = 0;
71
72using namespace pablo;
73using namespace kernel;
74using namespace parabix;
75
76//  The callback routine that records counts in progress.
77//
78extern "C" {
79    void record_counts(uint64_t lines, uint64_t words, uint64_t chars, uint64_t bytes, uint64_t fileIdx) {
80        lineCount[fileIdx] = lines;
81        wordCount[fileIdx] = words;
82        charCount[fileIdx] = chars;
83        byteCount[fileIdx] = bytes;
84        TotalLines += lines;
85        TotalWords += words;
86        TotalChars += chars;
87        TotalBytes += bytes;
88    }
89}
90
91class WordCountKernel final: public pablo::PabloKernel {
92public:
93    WordCountKernel(const std::unique_ptr<kernel::KernelBuilder> & b, Binding && inputStreamSet);
94    bool isCachable() const override { return true; }
95    bool hasSignature() const override { return false; }
96protected:
97    void generatePabloMethod() override;
98};
99
100WordCountKernel::WordCountKernel (const std::unique_ptr<kernel::KernelBuilder> & b, Binding && inputStreamSet)
101: PabloKernel(b, "wc_" + wc_modes,
102    {inputStreamSet},
103    {},
104    {},
105    {Binding{b->getSizeTy(), "lineCount"}, Binding{b->getSizeTy(), "wordCount"}, Binding{b->getSizeTy(), "charCount"}}) {
106
107}
108
109void WordCountKernel::generatePabloMethod() {
110    PabloBuilder pb(getEntryScope());
111    std::unique_ptr<cc::CC_Compiler> ccc;
112    if (CountWords || CountChars) {
113        ccc = make_unique<cc::Parabix_CC_Compiler>(getEntryScope(), getInputStreamSet("u8bit"));
114    } else {
115        ccc = make_unique<cc::Direct_CC_Compiler>(getEntryScope(), pb.createExtract(getInput(0), pb.getInteger(0)));
116    }
117
118    //  output: 3 counters
119    Var * lc = getOutputScalarVar("lineCount");
120    Var * wc = getOutputScalarVar("wordCount");
121    Var * cc = getOutputScalarVar("charCount");
122
123    if (CountLines) {
124        PabloAST * LF = ccc->compileCC(re::makeByte(0x0A));
125        pb.createAssign(lc, pb.createCount(LF));
126    }
127    if (CountWords) {
128        PabloAST * WS = ccc->compileCC(re::makeCC(re::makeByte(0x09, 0x0D), re::makeByte(0x20)));
129        PabloAST * wordChar = pb.createNot(WS);
130        // WS_follow_or_start = 1 past WS or at start of file
131        PabloAST * WS_follow_or_start = pb.createNot(pb.createAdvance(wordChar, 1));
132        PabloAST * wordStart = pb.createInFile(pb.createAnd(wordChar, WS_follow_or_start));
133        pb.createAssign(wc, pb.createCount(wordStart));
134    }
135    if (CountChars) {
136        //
137        // FIXME: This correctly counts characters assuming valid UTF-8 input.  But what if input is
138        // not UTF-8, or is not valid?
139        //
140        PabloAST * u8Begin = ccc->compileCC(re::makeCC(re::makeByte(0, 0x7F), re::makeByte(0xC2, 0xF4)));
141        pb.createAssign(cc, pb.createCount(u8Begin));
142    }
143}
144
145typedef void (*WordCountFunctionType)(uint32_t fd, size_t fileIdx);
146
147void wcPipelineGen(ParabixDriver & pxDriver) {
148
149    auto & iBuilder = pxDriver.getBuilder();
150    Module * m = iBuilder->getModule();
151    const unsigned segmentSize = codegen::SegmentSize;
152    const unsigned bufferSegments = codegen::ThreadNum+1;
153
154   
155    Type * const int32Ty = iBuilder->getInt32Ty();
156    Type * const sizeTy = iBuilder->getSizeTy();
157    Type * const voidTy = iBuilder->getVoidTy();
158
159    FunctionType * const recordCountsType = FunctionType::get(voidTy, {sizeTy, sizeTy, sizeTy, sizeTy, sizeTy}, false);
160    Constant * const recordCounts = m->getOrInsertFunction("record_counts", recordCountsType);
161
162    FunctionType * const mainType = FunctionType::get(voidTy, {int32Ty, sizeTy}, false);
163    Function * const main = cast<Function>(m->getOrInsertFunction("Main", mainType));
164    main->setCallingConv(CallingConv::C);
165    Function::arg_iterator args = main->arg_begin();   
166    Value * const fileDecriptor = &*(args++);
167    fileDecriptor->setName("fileDecriptor");
168    Value * const fileIdx = &*(args++);
169    fileIdx->setName("fileIdx");
170
171    iBuilder->SetInsertPoint(BasicBlock::Create(m->getContext(), "entry", main,0));
172
173    StreamSetBuffer * const ByteStream = pxDriver.addBuffer<ExternalBuffer>(iBuilder, iBuilder->getStreamSetTy(1, 8));
174
175
176    Kernel * mmapK = pxDriver.addKernelInstance<MMapSourceKernel>(iBuilder);
177    mmapK->setInitialArguments({fileDecriptor});
178    pxDriver.makeKernelCall(mmapK, {}, {ByteStream});
179   
180    Kernel * wck  = nullptr;
181    if (CountWords || CountChars) {
182        StreamSetBuffer * const BasisBits = pxDriver.addBuffer<StaticBuffer>(iBuilder, iBuilder->getStreamSetTy(8, 1), segmentSize * bufferSegments);
183        Kernel * s2pk = pxDriver.addKernelInstance<S2PKernel>(iBuilder);
184        pxDriver.makeKernelCall(s2pk, {ByteStream}, {BasisBits});
185       
186        wck = pxDriver.addKernelInstance<WordCountKernel>(iBuilder, Binding{iBuilder->getStreamSetTy(8, 1), "u8bit"});
187        pxDriver.makeKernelCall(wck, {BasisBits}, {});
188
189
190    } else {
191        wck = pxDriver.addKernelInstance<WordCountKernel>(iBuilder, Binding{iBuilder->getStreamSetTy(1, 8), "u8byte"});
192        pxDriver.makeKernelCall(wck, {ByteStream}, {});
193    }
194
195    pxDriver.generatePipelineIR();
196   
197    iBuilder->setKernel(mmapK);
198    Value * const fileSize = iBuilder->getAccumulator("fileSize");
199    iBuilder->setKernel(wck);
200    Value * const lineCount = iBuilder->getAccumulator("lineCount");
201    Value * const wordCount = iBuilder->getAccumulator("wordCount");
202    Value * const charCount = iBuilder->getAccumulator("charCount");
203
204    iBuilder->CreateCall(recordCounts, {lineCount, wordCount, charCount, fileSize, fileIdx});
205    pxDriver.deallocateBuffers();
206    iBuilder->CreateRetVoid();
207
208    pxDriver.finalizeObject();
209}
210
211
212
213void wc(WordCountFunctionType fn_ptr, const int64_t fileIdx) {
214    std::string fileName = allFiles[fileIdx].string();
215    struct stat sb;
216    const int fd = open(fileName.c_str(), O_RDONLY);
217    if (LLVM_UNLIKELY(fd == -1)) {
218        if (errno == EACCES) {
219            std::cerr << "wc: " << fileName << ": Permission denied.\n";
220        }
221        else if (errno == ENOENT) {
222            std::cerr << "wc: " << fileName << ": No such file.\n";
223        }
224        else {
225            std::cerr << "wc: " << fileName << ": Failed.\n";
226        }
227        return;
228    }
229    if (stat(fileName.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) {
230        std::cerr << "wc: " << fileName << ": Is a directory.\n";
231        close(fd);
232        return;
233    }
234    fn_ptr(fd, fileIdx);
235    close(fd);
236}
237
238int main(int argc, char *argv[]) {
239    codegen::ParseCommandLineOptions(argc, argv, {&wcFlags, pablo_toolchain_flags(), codegen::codegen_flags()});
240    if (argv::RecursiveFlag || argv::DereferenceRecursiveFlag) {
241        argv::DirectoriesFlag = argv::Recurse;
242    }
243    allFiles = argv::getFullFileList(inputFiles);
244   
245    const auto fileCount = allFiles.size();
246    if (wcOptions.size() == 0) {
247        CountLines = true;
248        CountWords = true;
249        CountBytes = true;
250    } else {
251        CountLines = false;
252        CountWords = false;
253        CountBytes = false;
254        CountChars = false;
255        for (unsigned i = 0; i < wcOptions.size(); i++) {
256            switch (wcOptions[i]) {
257                case WordOption: CountWords = true; break;
258                case LineOption: CountLines = true; break;
259                case CharOption: CountChars = true; CountBytes = false; break;
260                case ByteOption: CountBytes = true; CountChars = false; break;
261            }
262        }
263    }
264    if (CountLines) wc_modes += "l";
265    if (CountWords) wc_modes += "w";
266    if (CountChars) wc_modes += "m";
267    if (CountBytes) wc_modes += "c";
268
269    ParabixDriver pxDriver("wc");
270    wcPipelineGen(pxDriver);
271    auto wordCountFunctionPtr = reinterpret_cast<WordCountFunctionType>(pxDriver.getMain());
272    lineCount.resize(fileCount);
273    wordCount.resize(fileCount);
274    charCount.resize(fileCount);
275    byteCount.resize(fileCount);
276
277    for (unsigned i = 0; i < fileCount; ++i) {
278        wc(wordCountFunctionPtr, i);
279    }
280   
281    size_t maxCount = 0;
282    if (CountLines) maxCount = TotalLines;
283    if (CountWords) maxCount = TotalWords;
284    if (CountChars) maxCount = TotalChars;
285    if (CountBytes) maxCount = TotalBytes;
286   
287    int displayColumnWidth = std::to_string(maxCount).size() + 1;
288    if (displayColumnWidth < defaultDisplayColumnWidth) displayColumnWidth = defaultDisplayColumnWidth;
289
290    for (unsigned i = 0; i < fileCount; ++i) {
291        std::cout << std::setw(displayColumnWidth-1);
292        if (CountLines) {
293            std::cout << lineCount[i] << std::setw(displayColumnWidth);
294        }
295        if (CountWords) {
296            std::cout << wordCount[i] << std::setw(displayColumnWidth);
297        }
298        if (CountChars) {
299            std::cout << charCount[i] << std::setw(displayColumnWidth);
300        }
301        if (CountBytes) {
302            std::cout << byteCount[i];
303        }
304        std::cout << " " << allFiles[i].string() << std::endl;
305    }
306    if (inputFiles.size() > 1) {
307        std::cout << std::setw(displayColumnWidth-1);
308        if (CountLines) {
309            std::cout << TotalLines << std::setw(displayColumnWidth);
310        }
311        if (CountWords) {
312            std::cout << TotalWords << std::setw(displayColumnWidth);
313        }
314        if (CountChars) {
315            std::cout << TotalChars << std::setw(displayColumnWidth);
316        }
317        if (CountBytes) {
318            std::cout << TotalBytes;
319        }
320        std::cout << " total" << std::endl;
321    }
322
323    return 0;
324}
Note: See TracBrowser for help on using the repository browser.