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

Last change on this file since 6184 was 6184, checked in by nmedfort, 7 months ago

Initial version of PipelineKernel? + revised StreamSet? model.

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