aboutsummaryrefslogtreecommitdiff
path: root/lib/llvm-opt-transformer.ts
blob: 40ac66b63b89a0d09528469b93b42709bcf534d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Copyright (c) 2017 Jared Wyles, fixes by Aviv Polak and Ofek Shilon
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import {Transform, TransformCallback, TransformOptions} from 'stream';

import yaml from 'yaml';

import {logger} from './logger.js';

type Path = string;
type OptType = 'Missed' | 'Passed' | 'Analysis';

type OptInfo = {
    optType: OptType;
    displayString: string;
};

export type LLVMOptInfo = OptInfo & {
    Pass: string;
    Name: string;
    DebugLoc: DebugLoc;
    Function: string;
    Args: Array<object>;
};

type DebugLoc = {
    File: Path;
    Line: number;
    Column: number;
};

function DisplayOptInfo(optInfo: LLVMOptInfo) {
    return optInfo.Args.reduce((acc, x) => {
        let inc = '';
        for (const [key, value] of Object.entries(x)) {
            if (key === 'DebugLoc') {
                if (value['Line'] !== 0) {
                    inc += ' (' + value['Line'] + ':' + value['Column'] + ')';
                }
            } else {
                inc += value;
            }
        }
        return acc + inc;
    }, '');
}

const optTypeMatcher = /---\s(.*)\r?\n/;
const docStart = '---';
const docEnd = '\n...';
const IsDocumentStart = (x: string) => x.startsWith(docStart);
const FindDocumentEnd = (x: string) => {
    const index = x.indexOf(docEnd);
    return {found: index > -1, endpos: index + docEnd.length};
};
const splitAt = (index, xs) => [xs.slice(0, index), xs.slice(index)];

export class LLVMOptTransformer extends Transform {
    _buffer: string;
    _prevOpts: Set<string>; // Avoid duplicate display of remarks
    constructor(options?: TransformOptions) {
        super({...(options || {}), objectMode: true});
        this._buffer = '';
        this._prevOpts = new Set<string>();
    }

    override _flush(done: TransformCallback) {
        this.processBuffer();
        done();
    }

    override _transform(chunk: any, encoding: string, done: TransformCallback) {
        this._buffer += chunk.toString();
        //buffer until we have a start and end if at any time i care about improving performance stash the offset
        this.processBuffer();
        done();
    }

    processBuffer() {
        while (IsDocumentStart(this._buffer)) {
            const {found, endpos} = FindDocumentEnd(this._buffer);
            if (found) {
                const [head, tail] = splitAt(endpos, this._buffer);
                const optTypeMatch = head.match(optTypeMatcher);
                const opt = yaml.parse(head, {logLevel: 'error'});
                const strOpt = JSON.stringify(opt);
                if (!this._prevOpts.has(strOpt)) {
                    this._prevOpts.add(strOpt);

                    if (optTypeMatch) {
                        opt.optType = optTypeMatch[1].replace('!', '');
                    } else {
                        logger.warn('missing optimization type');
                    }
                    opt.displayString = DisplayOptInfo(opt);
                    this.push(opt as LLVMOptInfo);
                }
                this._buffer = tail.replace(/^\n/, '');
            } else {
                break;
            }
        }
    }
}