├── .gitignore ├── touchall ├── straces ├── find-binaries ├── pytags ├── filter-disasm ├── clang-format-all ├── cpptags ├── boilerplate ├── c ├── perl ├── sh └── python3 ├── gcc-predefs ├── lddr ├── pylintrc ├── mangle ├── gcc-compare-checks ├── section-size ├── deja-compare-checks ├── llvm-collect-logs ├── git-reset-dates ├── py-lint ├── gdb-qemu ├── git-bareize ├── LICENSE.txt ├── llvm-print-crit-path ├── gcc-bisect ├── gcc-debug ├── llvm-tags ├── gcc-tags ├── README.md ├── git-all ├── llvm-splitlog ├── gcov-tool-many ├── update-copyrights ├── configure-build-install ├── insert-license-header ├── plot-percentiles ├── llvm-build ├── gcc-build ├── llvm-classify-error ├── gnu-compare-projects ├── gcc-bootstrap-and-regtest └── llvm-print-fatpoints /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | __pycache__ 3 | -------------------------------------------------------------------------------- /touchall: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2021-2022 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Unify times of all files in folder. 9 | 10 | set -eu 11 | 12 | find "${1:-.}" | xargs touch -r "${1:-.}" 13 | -------------------------------------------------------------------------------- /straces: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2021 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Open straces of all child processes in editor. 9 | 10 | set -eu 11 | 12 | strace -ff -s1024 "$@" 2>&1 | ${EDITOR:-vim} - 13 | -------------------------------------------------------------------------------- /find-binaries: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2021-2022 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Find all binary files in folder. 9 | 10 | set -eu 11 | 12 | find -L "$@" -type f -a ! -size 0 -print0 | xargs -0 grep -IL . 13 | -------------------------------------------------------------------------------- /pytags: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2018-2020 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Collect tags in Python repo. 9 | 10 | if ctags --version 2>&1 | grep -q Emacs; then 11 | FLAGS='-l python' 12 | else 13 | # Exuberant Ctags 14 | FLAGS=--python-kinds=-i 15 | fi 16 | 17 | ctags $FLAGS "$@" 18 | -------------------------------------------------------------------------------- /filter-disasm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2018-2022 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Canonizes registers and immediates in objdump output for easier diffing 9 | 10 | set -eu 11 | set -x 12 | 13 | sed -e ' 14 | s/%[a-z0-9]\+/R/g 15 | s/0x[0-9a-fA-F]\+/IMM/g 16 | s/\<[0-9][0-9a-fA-F]*/IMM/g 17 | ' 18 | -------------------------------------------------------------------------------- /clang-format-all: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2022-2023 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Format files from top commit 9 | 10 | set -eu 11 | set -x 12 | 13 | cd $(git rev-parse --show-toplevel) 14 | git show | grep '^diff' | awk '{print $4}' | cut -d/ -f2- | grep '\(\.c\|\.cpp\|\.cc\|\.hpp\|.h\|\.hh\)$' | xargs clang-format -i 15 | -------------------------------------------------------------------------------- /cpptags: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2017-2020 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Collect tags in C++ codebase. 9 | 10 | if ctags --version 2>&1 | grep -q Emacs; then 11 | FLAGS='-l c++' 12 | else 13 | # Exuberant Ctags 14 | FLAGS='--c++-kinds=+p --fields=+iaS --extras=+q --language-force=C++' 15 | fi 16 | 17 | ctags $FLAGS "$@" 18 | -------------------------------------------------------------------------------- /boilerplate/c: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 Yury Gribov 2 | // 3 | // Use of this source code is governed by MIT license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef __clang__ 11 | __attribute__((optnone)) 12 | #else 13 | __attribute__((noipa)) 14 | #endif 15 | void foo() { 16 | } 17 | 18 | int main() { 19 | foo(); 20 | printf("Hello world\n"); 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /gcc-predefs: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2018-2022 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Prints predefined GCC macro 9 | 10 | set -eu 11 | 12 | test -n "${CC:-}" || CC=gcc 13 | if test $# -gt 0; then 14 | if which "$1" >/dev/null 2>&1; then 15 | CC="$1" 16 | shift 17 | fi 18 | fi 19 | 20 | TMP=$(mktemp --suffix=.h) 21 | trap "rm -f $TMP" EXIT INT TERM 22 | 23 | $CC "$@" -E -dM $TMP 24 | -------------------------------------------------------------------------------- /lddr: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2020-2022 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Identify all shlibs used by files in folder 9 | 10 | set -eu 11 | 12 | if test $# = 0; then 13 | eval set -- "$PWD" 14 | fi 15 | 16 | # libbz2.so.1.0 => /lib/x86_64-linux-gnu/libbz2.so.1.0 (0x00007fe9221d1000) 17 | for f in $(find -L "$@" -type f -a -executable); do 18 | if file -b $f | grep -q ELF; then 19 | ldd $f 20 | fi 21 | done | sed -ne '/=>/{ s/ *(0x.*//; p }' | sort -u 22 | -------------------------------------------------------------------------------- /pylintrc: -------------------------------------------------------------------------------- 1 | # Remove useless Pylint warnings. Run as `pylint --rcfile=path/to/pylintrc'. 2 | [MASTER] 3 | 4 | # Run in parallel by default 5 | jobs=0 6 | 7 | [MESSAGES CONTROL] 8 | 9 | # Disable style warnings 10 | disable=bad-continuation, 11 | line-too-long, 12 | bad-indentation, 13 | invalid-name, 14 | too-many-locals, 15 | too-many-branches, 16 | too-many-statements, 17 | too-many-arguments, 18 | too-few-public-methods, 19 | too-many-instance-attributes, 20 | too-many-lines, 21 | missing-docstring, 22 | misplaced-comparison-constant, 23 | global-statement, 24 | unused-wildcard-import 25 | -------------------------------------------------------------------------------- /mangle: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2020-2021 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Mangle function prototype (primitive types only). 9 | 10 | set -eu 11 | 12 | if test $# = 0; then 13 | proto=$(cat | tr -d '\n') 14 | else 15 | proto=$(echo "$@") 16 | fi 17 | 18 | proto=$(echo "$proto" | sed '/^[ ]*$/d; $s/[; ]*$/{}/') 19 | 20 | # Predefine std types 21 | proto=" 22 | #define uchar unsigned char 23 | #define ushort unsigned short 24 | #define uint unsigned 25 | $proto" 26 | 27 | tmp=$(mktemp --suffix .cpp) 28 | trap "rm -f $tmp" EXIT INT TERM 29 | echo "$proto" > $tmp 30 | 31 | g++ $tmp -S -o- | grep '^_Z.*:$' | tr -d ':' 32 | -------------------------------------------------------------------------------- /gcc-compare-checks: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2015-2019 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Compares results of 2 GCC regression tests. 9 | 10 | set -eu 11 | 12 | if [ $# != 1 -a $# != 2 ]; then 13 | echo "Syntax: $(basename $0) REF-DIR [NEW-DIR]" >&2 14 | exit 1 15 | fi 16 | 17 | REF=$(cd $1; echo $PWD) 18 | 19 | if [ $# = 1 ]; then 20 | NEW=$PWD 21 | else 22 | NEW=$(cd $2; echo $PWD) 23 | fi 24 | 25 | SRC=$(dirname $(grep '^ \+\$.*configure' $1/config.log | head -1 | awk '{print $2}')) 26 | 27 | for sum in $(cd $REF; find -name \*.sum); do 28 | echo "========== Comparing $sum" 29 | $SRC/contrib/dg-cmp-results.sh -v -v unix $REF/$sum $NEW/$sum 30 | done 31 | 32 | -------------------------------------------------------------------------------- /section-size: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2016-2021 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Compute section size. 9 | 10 | set -eu 11 | 12 | if test -z "$1" || test -z "$2"; then 13 | cat <&2 "$(basename $0): error: $@" 18 | exit 1 19 | } 20 | 21 | if test $# != 2; then 22 | error "Syntax: $(basename $0) dir1 dir2" 23 | fi 24 | 25 | if ! test -d "$1"; then 26 | error "Not a directory: $1" 27 | fi 28 | 29 | if ! test -d "$2"; then 30 | error "Not a directory: $2" 31 | fi 32 | 33 | d1=$(absolutize $1) 34 | d2=$(absolutize $2) 35 | 36 | cd $d1 37 | 38 | for sum in $(find -name *.sum); do 39 | grep -v 'Test Run By\|home.ygribov' $sum > $sum.f || true 40 | grep -v 'Test Run By\|home.ygribov' $d2/$sum > $d2/$sum.f || true 41 | diff $sum.f $d2/$sum.f || true 42 | done 43 | -------------------------------------------------------------------------------- /llvm-collect-logs: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2019-2024 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # A simple wrapper for llvm-splitlog 9 | # so that one can simply run: 10 | # $ llvm-collect-logs clang tmp.c -S 11 | # or 12 | # $ llvm-collect-logs -o logs-new clang ... 13 | 14 | set -eu 15 | 16 | if test $# = 0; then 17 | echo "Usage: $(basename $0) [-o dir] clang arg1..." 18 | exit 19 | fi 20 | 21 | if test "${1:-}" = -o; then 22 | O=$2 23 | shift 2 24 | else 25 | O=debug 26 | fi 27 | 28 | case "$1" in 29 | *clang | *clang.exe | *clang++ | *clang++.exe) 30 | DEBUG_FLAGS='-mllvm -print-after-all' 31 | ;; 32 | *llc | *llc.exe) 33 | DEBUG_FLAGS='-print-after-all' 34 | ;; 35 | *) 36 | echo >&2 "Unknown executable: $1" 37 | exit 1 38 | ;; 39 | esac 40 | 41 | "$@" $DEBUG_FLAGS 2>$O.log || true 42 | $(dirname $0)/llvm-splitlog -o $O $O.log 43 | -------------------------------------------------------------------------------- /git-reset-dates: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2020-2021 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Reset dates of N last commits. 9 | 10 | set -eu 11 | 12 | error() { 13 | echo >&2 "$(basename $0): error: $@" 14 | exit 1 15 | } 16 | 17 | if test $# != 1 -a $# != 2; then 18 | error "Syntax: $(basename $0) NUMCOMMITS [DATE]" 19 | fi 20 | 21 | N=$1 22 | if test $# -lt 2; then 23 | D="$(date)" 24 | fi 25 | 26 | BRANCH=$(git rev-parse --abbrev-ref HEAD) 27 | COMMITS=$(git show HEAD~$N..HEAD | awk '/^commit/{print $2}' | tac) 28 | 29 | TMP_BRANCH=git-reset-dates-branch 30 | git branch -D $TMP_BRANCH 2>/dev/null || true 31 | git checkout -b $TMP_BRANCH HEAD~$N 32 | 33 | for commit in $COMMITS; do 34 | git cherry-pick $commit 35 | git commit --amend --no-edit --date "$D" 36 | done 37 | 38 | git checkout $BRANCH 39 | echo "Updated commits were saved in $TMP_BRANCH" 40 | -------------------------------------------------------------------------------- /py-lint: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2018-2022 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Pylint wrapper which disables useless checks 9 | # and massages repo to satisfy pylint conventions. 10 | 11 | set -eu 12 | 13 | log=$(basename $0).log 14 | rm -f $log 15 | 16 | files= 17 | for d in $(find -name \*.py | xargs dirname | sort -u); do 18 | if ! test -f $d/__init__.py; then 19 | touch $d/__init__.py 20 | files="$files $PWD/$d/__init__.py" 21 | fi 22 | done 23 | trap "rm -f $files" EXIT INT TERM 24 | 25 | name=$(basename $PWD) 26 | 27 | cd .. 28 | # PYTHONPATH used to avoid import-error warnings 29 | # on 'from subfolder import submodule' statements 30 | # in files in subfolders. 31 | PYTHONPATH=$name${PYTHONPATH:+:$PYTHONPATH} pylint \ 32 | --rcfile=$(dirname $0)/pylintrc 33 | "$@" \ 34 | $name | tee -a $name/$log 35 | 36 | echo "Copy of output was saved to $log" 37 | -------------------------------------------------------------------------------- /gdb-qemu: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2023 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | set -eu 9 | set -x 10 | 11 | TARGET="$1" 12 | shift 13 | 14 | get_sysroot() { 15 | TARGETS=$(ls -1 /usr | grep "^$TARGET" | sed 's!^!/usr/!') 16 | 17 | if test -z "$TARGETS"; then 18 | echo >&2 "Sysroot for $TARGET not found" 19 | exit 1 20 | fi 21 | 22 | if test $(echo "$TARGETS" | wc -l) -gt 1; then 23 | echo >&2 "More than one match for $TARGET: $TARGETS" 24 | exit 1 25 | fi 26 | 27 | echo "$TARGETS" 28 | } 29 | 30 | PORT=1234 31 | SYSROOT=$(get_sysroot $TARGET) 32 | 33 | ARCH=$(echo $TARGET | sed 's/^\([^-]\+\).*/\1/') 34 | QEMU=qemu-$ARCH 35 | if ! which $QEMU > /dev/null 2>&2; then 36 | echo >&2 "$QEMU not installed" 37 | exit 1 38 | fi 39 | 40 | $QEMU -L $SYSROOT -g $PORT "$@" & 41 | sleep 1 42 | 43 | gdb-multiarch -ex "set sysroot $SYSROOT" -ex "target remote localhost:$PORT" "$1" 44 | -------------------------------------------------------------------------------- /git-bareize: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2019-2021 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Convert repo to bare format. 9 | 10 | set -eu 11 | 12 | me=$(basename $0) 13 | 14 | if test $# = 0; then 15 | echo "Usage: $me path/to/repo1 path/to/repo2 ..." 16 | exit 1 17 | fi 18 | 19 | for REPO; do 20 | REPO=$(echo "$REPO" | sed 's![\/\\]*$!!') 21 | 22 | if ! test -d "$REPO"; then 23 | echo >&2 "Folder $REPO not found" 24 | exit 1 25 | fi 26 | 27 | if ! test -d "$REPO/.git"; then 28 | echo >&2 "Folder $REPO is not a git repo" 29 | exit 1 30 | fi 31 | 32 | if ! (cd "$REPO" && git status) 2>&1 | grep -q 'nothing to commit, working tree clean'; then 33 | echo >&2 "Cowardly refusing to remove uncommitted changes in $REPO" 34 | exit 1 35 | fi 36 | 37 | rm -rf "$REPO.git" 38 | mv "$REPO"/.git "$REPO.git" 39 | git --git-dir="$REPO.git" config --bool core.bare true 40 | rm -rf "$REPO" "$REPO.git"/{index,ORIG_HEAD} 41 | echo 'ref: refs/heads/master' > "$REPO.git"/HEAD 42 | 43 | echo "Successfully converted $REPO" 44 | done 45 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2025 Yury Gribov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /llvm-print-crit-path: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Copyright 2019-2022 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # A simple script for extracting critical path from LLVM MachineScheduler's dump. 9 | 10 | use strict; 11 | use warnings; 12 | 13 | my $su; 14 | my $depth; 15 | my $height; 16 | my @su_max; 17 | my $cpm_max = 0; 18 | while(<>) { 19 | s/[\r\n]*//g; 20 | if(/^(SU\([0-9]+\).*)/) { 21 | if(defined $su) { 22 | (defined $depth or defined $height) or die "Depth or height for $su not found"; 23 | my $cpm = $depth + $height; 24 | print "$su:\n depth $depth\n CPM $cpm\n"; 25 | if($cpm > $cpm_max) { 26 | $cpm_max = $cpm; 27 | @su_max = ([$su, $depth]); 28 | } elsif($cpm == $cpm_max) { 29 | push @su_max, [$su, $depth]; 30 | } 31 | $depth = $height = undef; 32 | } 33 | $su = $1; 34 | 35 | } elsif(/^ *Depth *: *([0-9]+)$/) { 36 | $depth = $1; 37 | } elsif(/^ *Height *: *([0-9]+)$/) { 38 | $height = $1; 39 | } 40 | } 41 | 42 | print "Max $cpm_max:\n"; 43 | for (@su_max) { 44 | my $su = $_->[0]; 45 | my $depth = $_->[1]; 46 | print " $depth: $su\n"; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /gcc-bisect: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2017-2022 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Helper script for locating GCC regressions (or progressions). 9 | # It automates building of GCC and then calls user-provided 10 | # secondary script to do actual test. 11 | # 12 | # User's script is run in the folder where it's located. 13 | # It should take a single parameter - path to GCC builddir. 14 | # 15 | # For regressions, call as 16 | # $ git bisect start gcc 17 | # $ git bisect run gcc-bisect exit_1_if_bad.sh 18 | # 19 | # To locate progression (i.e. new optimizations or bugfixes), 20 | # calls as 21 | # $ git bisect start gcc 22 | # $ git bisect run gcc-bisect exit_1_if_good.sh 23 | # 24 | # TODO: 25 | # - test 26 | # - parametrize 27 | 28 | set -eu 29 | set -x 30 | 31 | me=$(basename $0) 32 | 33 | if test $# != 1; then 34 | echo >&2 "Usage: $me myscript.sh" 35 | exit 1 36 | fi 37 | 38 | SRCDIR=$PWD 39 | BUILDDIR=$(mktemp -d) 40 | TESTDIR=$(cd $(dirname $1); pwd) 41 | 42 | if ! test -d .git -a -f gcc/gcc.h; then 43 | echo >&2 "$me: must be run from GCC git repo" 44 | exit 1 45 | fi 46 | 47 | if ! test -d $SRCDIR/gmp; then 48 | contrib/download_prerequisites 49 | find -L gmp mpc mpfr isl | xargs touch -r . 50 | fi 51 | 52 | trap "rm -rf $BUILDDIR" EXIT INT TERM 53 | mkdir -p $BUILDDIR 54 | rm -rf $BUILDDIR/* 55 | gcc-build -C $BUILDDIR --no-libs $SRCDIR --enable-languages=c CFLAGS=-O0 CXXFLAGS=-O0 || exit 125 56 | 57 | cd $TESTDIR 58 | "$1" $BUILDDIR 59 | -------------------------------------------------------------------------------- /gcc-debug: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2015-2021 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Runs GCC under debugger (automates extraction of cc1 runline, 9 | # loads hooks and sets some useful flags). 10 | # 11 | # An example of typical usage: 12 | # $ gcc-debug -ex 'b generic-match.c:2564' xgcc -Bgcc repro.c 13 | 14 | set -eu 15 | 16 | GDBFLAGS= 17 | #GDBFLAGS='-tui' 18 | GCC= 19 | 20 | NFLAGS=0 21 | for arg; do 22 | if test -z "$GCC" && which "$arg" > /dev/null 2>&1; then 23 | shift $NFLAGS 24 | break 25 | fi 26 | GDBFLAGS="$GDBFLAGS '$arg'" 27 | NFLAGS=$((NFLAGS + 1)) 28 | done 29 | 30 | if [ $# -eq 0 ]; then 31 | echo >&2 "Syntax: $(basename $0) [gdb-args] path/to/gcc [gcc-args]" 32 | exit 1 33 | fi 34 | 35 | GCC="$1" 36 | shift 37 | 38 | CMD=$($GCC -### "$@" 2>&1 | grep '\(cc1\|cc1plus\|f951\|lto1\) ' | tail -1 | sed -e 's/\"//g') 39 | CC=$(echo "$CMD" | awk '{print $1}') 40 | if ! test -x "$CC"; then 41 | echo >&2 "Failed to find compiler proper" 42 | exit 1 43 | fi 44 | 45 | # TODO: source gcc/gdbinit.in? 46 | SRC=$($GCC -v 2>&1 | sed -ne '/^Configured with:/{s/Configured with: \([^ ]\+\)*\/configure .*/\1/; p}') 47 | GDBHOOKS=$SRC/gcc/gdbhooks.py 48 | if ! test -f "$GDBHOOKS"; then 49 | echo >&2 "warning: failed to find GDB hooks" 50 | else 51 | GDBHOOKS="-x $GDBHOOKS" 52 | fi 53 | 54 | # Need eval because of escapes in GDBFLAGS 55 | eval gdb $GDBHOOKS \ 56 | -ex "'b internal_error'" \ 57 | -ex "'set pagination off'" \ 58 | -ex "'set print pretty on'" \ 59 | -ex "'set print array on'" \ 60 | $GDBFLAGS \ 61 | -ex run \ 62 | --args $CMD 63 | 64 | -------------------------------------------------------------------------------- /llvm-tags: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2023 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Indexes LLVM source code with ctags, respecting the supermacro, build directory 9 | # and avoiding irrelevant files. 10 | 11 | set -eu 12 | 13 | rm -f cscope.* ctags tags 14 | 15 | if [ -d tools/clang ]; then 16 | # Legacy layout from ancient times 17 | LLVM_DIR=. 18 | CLANG_DIR=tools/clang 19 | ROOTS=. 20 | elif [ -f llvm/CMakeLists.txt ]; then 21 | LLVM_DIR=llvm 22 | CLANG_DIR=clang 23 | ROOTS='llvm clang' 24 | if test -d mlir; then 25 | ROOTS="$ROOTS mlir" 26 | fi 27 | else 28 | echo 'You must run me from LLVM root directory' >&2 29 | exit 1 30 | fi 31 | 32 | find $ROOTS \ 33 | -path $LLVM_DIR/test -prune -o -path $CLANG_DIR/test -prune \ 34 | -o -path $LLVM_DIR/unittests -prune -o -path $LLVM_DIR/bindings -prune \ 35 | -o -path $LLVM_DIR/examples -prune -o -path $CLANG_DIR/examples -prune \ 36 | -o -path mlir/examples -prune -o -path mlir/test -prune -o -path mlir/unittests -prune \ 37 | -o \( -name \*.h -o -name \*.c -o -name \*.cpp \) -print > cscope.files 38 | 39 | if test $# -ge 1; then 40 | BUILD=$1 41 | else 42 | BUILD=$PWD/build 43 | fi 44 | 45 | if [ -f "$BUILD/CMakeCache.txt" ]; then 46 | find $BUILD -name \*.inc -print >> cscope.files 47 | else 48 | echo >&2 "Unable to find build directory at $BUILD, index may be incomplete" 49 | fi 50 | 51 | if ctags --version | grep -q 'Universal Ctags'; then 52 | EXTRA=extras 53 | else 54 | # Exuberant Ctags 55 | EXTRA=extra 56 | fi 57 | 58 | echo 'Building ctags database...' 59 | ctags --c++-kinds=+p --fields=+iaS --$EXTRA=+q -a -I LLVM_EXTERNAL_VISIBILITY,LLVM_LIBRARY_VISIBILITY -L cscope.files 60 | 61 | # TODO: merge contents of .def files 62 | -------------------------------------------------------------------------------- /gcc-tags: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2015-2024 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Indexes GCC source code with ctags, respecting the supermacro, build directory 9 | # and avoiding irrelevant files. 10 | 11 | set -eu 12 | 13 | if [ ! -f gcc/gcc.h ]; then 14 | echo 'You must run me from GCC root directory' >&2 15 | exit 1 16 | fi 17 | 18 | rm -f cscope.* ctags tags 19 | 20 | find gcc libiberty libcpp \ 21 | -path gcc/testsuite -prune -o -path 'lib*' -prune -o -path gcc/testsuite/config -prune \ 22 | -o \( -name \*.h -o -name \*.c -o -name \*.cc \) -print > cscope.files 23 | 24 | if test $# -ge 1; then 25 | BUILD=$1 26 | else 27 | BUILD=$PWD/build 28 | fi 29 | 30 | if [ -f "$BUILD/gcc/options.h" ]; then 31 | find $BUILD/gcc -name \*.h -o -name \*.c -o -name \*.cc -print >> cscope.files 32 | else 33 | echo >&2 "Unable to find build directory at $BUILD, index may be incomplete" 34 | fi 35 | 36 | if ctags --version | grep -q 'Universal Ctags'; then 37 | EXTRA=extras 38 | else 39 | # Exuberant Ctags 40 | EXTRA=extra 41 | fi 42 | 43 | echo 'Building ctags database...' 44 | ctags --c++-kinds=+p --fields=+iaS --$EXTRA=+q -a -I GTY+,ATTRIBUTE_UNUSED -L cscope.files 45 | 46 | # Now manually merge contents of .def files 47 | TMP=mktemp 48 | rm -f $TMP 49 | trap "rm -f $TMP" EXIT INT TERM 50 | for def in \ 51 | 'gcc/tree.def DEFTREECODE' \ 52 | 'gcc/gimple.def DEFGSCODE' \ 53 | 'gcc/rtl.def DEF_RTL_EXPR' \ 54 | 'gcc/builtins.def DEF[A-Z0-9_]*_BUILTIN' \ 55 | 'gcc/cif-code.def DEFCIFCODE CIF_' \ 56 | 'gcc/internal-fn.def DEF_INTERNAL[A-Z0-9_]*_FN'; do 57 | file=$(echo $def | awk '{print $1}') 58 | macro=$(echo $def | awk '{print $2}') 59 | prefix=$(echo $def | awk '{print $3}') 60 | cat $file | sed -ne "s!^\($macro *(\([^,]*\)\).*\$!$prefix\2 $file /\1,/!p" >> $TMP 61 | done 62 | 63 | # And .opt files 64 | for file in gcc/params.opt gcc/common.opt; do 65 | cat $file | sed -ne "s!^\(.*\> $TMP 66 | done 67 | 68 | cat tags >> $TMP 69 | LC_COLLATE=C sort $TMP > tags 70 | 71 | #echo 'Building cscope database...' 72 | if which cscope >/dev/null 2>&1; then 73 | cscope -Igcc -Iinclude -q -R -b -i cscope.files 74 | fi 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repo contains a bunch of scripts which can be useful 2 | for typical toolchain/distro maintenance tasks: 3 | * `boilerplate/*` : boilerplate codes for various languages 4 | * `clang-format-all`: format files from top commit via clang-format 5 | * `configure-build-install`: configure, build and install a typical Autoconf project 6 | * `cpptags`, `pytags`: wrappers for `ctags` to generate tags for different languages 7 | * `deja-compare-checks`, `gcc-compare-checks`: compare two sets of Dejagnu reports 8 | * `filter-disasm`: canonize objdump disasm 9 | * `find-binaries`: find all binary files in folder 10 | * `gcc-build`, `llvm-build`: build compilers 11 | * `gcc-bootstrap-and-regtest`: test a single GCC change (builds compiler, runs testsuite and compares results) 12 | * `gcc-bisect`: bisect GCC regression 13 | * `gcc-debug`: debug GCC compiler proper instead of driver 14 | * `gcc-predefs`: print predefined GCC macros 15 | * `gcc-tags`, `llvm-tags`: set up ctags for GCC/LLVM source dir (avoiding irrelevant subdirs and respecting supermacro) 16 | * `gcov-tool-many`: apply `gcov-tool` to more than 2 files 17 | * `gdb-qemu`: debug program under QEMU 18 | * `git-all`: run Git command in all repos in current folder 19 | * `git-bareize` : convert normal git repo to bare format 20 | * `git-reset-dates`: change date of last N commits 21 | * `gnu-compare-projects`: compare ChangeLogs of two OSS projects 22 | * `insert-license-header`: insert license header into all files in a folder 23 | * `lddr`: recursively applies ldd to all files in folder (and its subfolders) 24 | * `llvm-collect-logs`, `llvm-splitlog`: generate Clang debug logs 25 | * `llvm-classify-error`: print short summary of LLVM error log 26 | * `llvm-print-fatpoints`: print fatpoints from LLVM MIR dump 27 | * `llvm-print-crit-path`: print critical path from based on MachineScheduler's dump 28 | * `mangle`: mangle function prototype (primitive types only) 29 | * `py-lint`: wrapper around `pylint` to make it more usable 30 | * `plot-percentiles`: a simple pyplot-based script which plots percentile-based benchmark summaries 31 | * `straces`: open straces of all child processes in editor 32 | * `touchall`: unify times of all files in folder 33 | * `update-copyrights`: update copyright comments in all files in a folder 34 | 35 | All the code is MIT-licensed. 36 | -------------------------------------------------------------------------------- /boilerplate/perl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Copyright 2019-2020 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | # 8 | # Shortly describe script here. 9 | 10 | use strict; 11 | use warnings; 12 | 13 | use File::Basename; 14 | use File::Find; 15 | use Getopt::Long qw(:config posix_default bundling no_ignore_case); 16 | use Data::Dumper; 17 | 18 | # File::Slurp not installed by default... 19 | sub read_file($) { 20 | my $f = $_[0]; 21 | open FILE, $f or die "Failed to open $f"; 22 | my @lines; 23 | while() { 24 | s/[\r\n]+//g; 25 | push @lines, $_; 26 | } 27 | close FILE; 28 | return @lines if(wantarray); 29 | return join("\n", @lines); 30 | } 31 | 32 | my $V = 0; 33 | my $help = 0; 34 | my $flag; 35 | my $param; 36 | my @multi; 37 | 38 | sub usage() { 39 | my $me = basename($0); 40 | print < \$help, 71 | 'verbose|v+' => \$V, 72 | 'flag|f' => \$flag, 73 | 'param|p=s' => \$param, 74 | 'multi|m=s@' => \@multi, 75 | ); 76 | 77 | usage() if $help; 78 | usage_short('No inputs specified.') if $#ARGV < 0; 79 | 80 | my @roots = @ARGV; 81 | 82 | sub find_files() { 83 | # TODO (use $File::Find::name, $File::Find::dir) 84 | } 85 | 86 | foreach my $root (@roots) { 87 | -d $root or die "$root is not a directory"; 88 | find({ wanted => \&find_files, no_chdir => 1 }, $root); 89 | } 90 | 91 | my $file = $ARGV[0]; 92 | open FILE, $file or die "Failed to open $file"; 93 | while() { 94 | s/[\r\n]//g; 95 | 96 | # TODO 97 | } 98 | close FILE; 99 | -------------------------------------------------------------------------------- /git-all: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2018-2020 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Execute git command for all repos in current dir. 9 | 10 | set -eu 11 | 12 | print_help_and_exit() { 13 | cat < $TMP/$(basename $d).log 2>&1) & 86 | JOBS="$JOBS $d,$!" 87 | sleep 0.1 88 | done 89 | 90 | BAD_REPOS= 91 | for d_pid in $JOBS; do 92 | d=${d_pid%,*} 93 | pid=${d_pid#*,} 94 | echo "=== $me: running in $d..." 95 | if ! wait $pid; then 96 | BAD_REPOS="${BAD_REPOS} $d" 97 | fi 98 | cat $TMP/$(basename $d).log 99 | done 100 | 101 | if test -n "$BAD_REPOS"; then 102 | echo >&2 "=== $me: failed in following repositories:" 103 | echo $BAD_REPOS | tr ' ' '\n' | sed 's/^/ /' 104 | exit 1 105 | fi 106 | -------------------------------------------------------------------------------- /llvm-splitlog: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Copyright 2019-2022 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Split LLVM -print-after-all log to separate files. 9 | 10 | use strict; 11 | use warnings; 12 | 13 | use File::Find; 14 | use File::Basename; 15 | use Getopt::Long qw(:config posix_default bundling no_ignore_case); 16 | 17 | my $v; 18 | my $help; 19 | my $out = '.'; 20 | 21 | GetOptions( 22 | 'verbose|v' => \$v, 23 | 'help|h' => \$help, 24 | 'o=s' => \$out 25 | ); 26 | 27 | if ($help) { 28 | my $me = basename($0); 29 | print < \&unlink_old_logs, no_chdir => 1}, $out); 70 | $clean = 0; 71 | } 72 | my $file = sprintf "%s/%0${ndigits}d-%s%s-%s.log", $out, $num, (defined $fun ? "$fun-" : ''), $pass, $when; 73 | push @passes, $file; 74 | open FILE, ">$file" or die "Failed to open $file"; 75 | print FILE "$text"; 76 | close FILE; 77 | } 78 | 79 | open INP, "<$file" or die "Failed to open '$file'"; 80 | while () { 81 | my $line = $_; 82 | $line =~ s/[\r\n]*//g; 83 | if ($line =~ /IR Dump (Before|After) (.*) \*\*\*/) { 84 | $when = $1; 85 | print_pass() if defined $text; 86 | ++$num; 87 | $pass = $2; 88 | $pass =~ s/ on Loop at depth.*//g; # Split loop info 89 | $pass =~ s/(\s+|\/)/-/g; 90 | $pass =~ s/[^a-zA-Z0-9_]*//g; 91 | $fun = undef; 92 | $multifun = 0; 93 | $text = "$line\n"; 94 | } else { 95 | if ($line =~ /Machine code for function ([^ :]+)/) { 96 | die "Duplicate function attribution in pass $pass: $fun and $1:\n$line\n" if defined $fun and $fun ne $1; 97 | $fun = $1 98 | } elsif (! $multifun and $line =~ /define .* @([_A-Za-z0-9]+)\(/) { 99 | if (defined $fun) { 100 | $fun = undef; 101 | $multifun = 1; 102 | } else { 103 | $fun = $1; 104 | } 105 | } 106 | $text .= "$line\n"; 107 | } 108 | } 109 | close INP; 110 | 111 | print_pass(); 112 | 113 | open FILE, ">$out/passes.txt" or die "Failed to write $out/passes.txt"; 114 | print FILE "$_\n" for @passes; 115 | close FILE; 116 | -------------------------------------------------------------------------------- /gcov-tool-many: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2022 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | set -eu 9 | 10 | error() { 11 | prefix="error:" 12 | if test -t 2; then 13 | prefix="${RED}${prefix}${END_COLOR}" 14 | fi 15 | printf "$(basename $0): $prefix $@\\n" >&2 16 | exit 1 17 | } 18 | 19 | warn() { 20 | prefix="warning:" 21 | if test -t 2; then 22 | prefix="${RED}${prefix}${END_COLOR}" 23 | fi 24 | printf "$(basename $0): $prefix $@\\n" >&2 25 | } 26 | 27 | mkcleandir() { 28 | mkdir -p "$1" 29 | rm -rf "$1"/* 30 | } 31 | 32 | usage() { 33 | cat <&2 <&2 "$me: action '$action' not implemented" 142 | exit 1 143 | ;; 144 | esac 145 | -------------------------------------------------------------------------------- /update-copyrights: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2017-2022 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Update dates in license headers for all files in codebase. 9 | 10 | set -eu 11 | 12 | ME=$(basename $0) 13 | 14 | absolutize() { 15 | realpath -s "$1" 16 | } 17 | 18 | usage() { 19 | cat <&2 "Usage: $ME [OPT]... DIR" 72 | exit 1 73 | fi 74 | 75 | if ! test -d "$DIR"; then 76 | echo >&2 "$ME: $DIR is not a directory" 77 | exit 1 78 | fi 79 | 80 | if ! test -d "$DIR"/.git; then 81 | echo >&2 "$ME: $DIR is not a git repo" 82 | exit 1 83 | fi 84 | 85 | if test -z "$LICENSE"; then 86 | LICENSE=$DIR/LICENSE.txt 87 | if ! test -f $LICENSE; then 88 | LICENSE=$DIR/LICENSE 89 | fi 90 | fi 91 | 92 | if ! test -f "$LICENSE"; then 93 | echo >&2 "$ME: can't open $LICENSE" 94 | exit 1 95 | fi 96 | 97 | update() { 98 | sed -i -e ' 99 | /Copyright\s/ { 100 | s/ \([0-9]\+-\)\?[0-9]\+[, ]/ '$2' /g; 101 | }' $1 102 | } 103 | 104 | Y1=0 105 | Y2=0 106 | for f in $(find $DIR -iname '.[a-z0-9]*' -prune -o -type f -a -print); do 107 | git log $f | grep -q . || continue 108 | 109 | d1=$(git log --follow --find-copies-harder --format=%ai $f | tail -n1) 110 | y1=$(date --date="$d1" +%Y) 111 | if test -n "$PRESERVE"; then 112 | start=$(sed -ne 's/^.*Copyright[^0-9]*\([0-9]\+\).*$/\1/p' < $f) 113 | if test -n "$start"; then 114 | if test $(echo "$start" | wc -l) -gt 1; then 115 | echo "warning: too many copyrights in $f" >&2 116 | else 117 | y1=$start 118 | fi 119 | fi 120 | fi 121 | 122 | if test $Y1 = 0 -o $Y1 -gt $y1; then 123 | Y1=$y1 124 | fi 125 | 126 | d2=$(git log --follow --find-copies-harder --format=%ai $f | head -n1) 127 | y2=$(date --date="$d2" +%Y) 128 | if test $Y2 = 0 -o $Y2 -lt $y2; then 129 | Y2=$y2 130 | fi 131 | 132 | if test $y1 = $y2; then 133 | yy=$y1 134 | else 135 | yy="$y1-$y2" 136 | fi 137 | 138 | update $f "$yy" 139 | done 140 | 141 | if test $Y1 = 0 -o $Y2 = 0; then 142 | echo >&2 "$ME: failed to extract last modification date for $LICENSE" 143 | exit 1 144 | fi 145 | 146 | if test $Y1 = $Y2; then 147 | YY=$Y1 148 | else 149 | YY="$Y1-$Y2" 150 | fi 151 | 152 | update $LICENSE "$YY" 153 | -------------------------------------------------------------------------------- /configure-build-install: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2015-2025 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # A simple script to automate deploy of typical OSS projects. 9 | 10 | set -eu 11 | 12 | # TODO: 13 | # * .tar.gz inputs 14 | 15 | absolutize() { 16 | realpath -s "$1" 17 | } 18 | 19 | error() { 20 | echo >&2 "$(basename $0): error: $@" 21 | exit 1 22 | } 23 | 24 | warn() { 25 | echo >&2 "$(basename $0): warning: $@" 26 | } 27 | 28 | isemptydir() { 29 | test -d "$1" && test $(find -L "$1" -maxdepth 0 -empty) 30 | } 31 | 32 | BUILD_ROOT=$HOME/build 33 | INSTALL_ROOT=$HOME/install 34 | 35 | build_project() { 36 | SOURCE=$(absolutize $1) 37 | shift 38 | 39 | PRJ=$(basename $SOURCE) 40 | test -n "$PRJ" || error "failed to extract project name" 41 | 42 | BUILD=$BUILD_ROOT/$PRJ 43 | INSTALL=$INSTALL_ROOT/$PRJ 44 | 45 | mkdir -p $BUILD 46 | if ! isemptydir $BUILD; then 47 | if test -f $BUILD/config.log || test "$CLEAN"; then 48 | rm -rf $BUILD/* 49 | else 50 | error "cowardly refusing to remove contents of $BUILD" 51 | fi 52 | fi 53 | 54 | if test "$INPLACE"; then 55 | cp -r $SOURCE/* $BUILD 56 | SOURCE=$BUILD 57 | fi 58 | 59 | if test -f $SOURCE/configure; then 60 | (cd $BUILD && $SOURCE/configure --prefix=$INSTALL "$@") 61 | elif test -f $SOURCE/configure.ac -a -f $SOURCE/autogen.sh; then 62 | ( 63 | cd $SOURCE && ./autogen.sh 64 | cd $BUILD && $SOURCE/configure --prefix=$INSTALL "$@" 65 | ) 66 | elif test -f $SOURCE/CMakeLists.txt; then 67 | (cd $BUILD && cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_INSTALL_PREFIX=$INSTALL $SOURCE) 68 | else 69 | error "not an Autoconf or CMake project: $SOURCE" 70 | fi 71 | 72 | nice make -C $BUILD -j$NJOBS 73 | 74 | # Parallel makeinstalls tend to fail... 75 | make -C $BUILD -j$NJOBS install || make -C $BUILD install 76 | } 77 | 78 | print_help_and_exit() { 79 | cat <' \ 51 | || echo $1 | grep -q '\.hs$' 52 | } 53 | 54 | ARGS=$(getopt -o 'hl:' --long 'short,license:,help' -n $ME -- "$@") 55 | eval set -- "$ARGS" 56 | 57 | SHORT= 58 | LICENSE= 59 | 60 | while true; do 61 | case "$1" in 62 | --short) 63 | SHORT=1 64 | shift 65 | ;; 66 | -h | --help) 67 | usage 68 | ;; 69 | -l | --license) 70 | LICENSE=$2 71 | shift 2 72 | ;; 73 | --) 74 | shift 75 | break 76 | ;; 77 | -*) 78 | error "unknown option: $1" 79 | ;; 80 | *) 81 | error 'internal error' 82 | ;; 83 | esac 84 | done 85 | 86 | if test $# -eq 1; then 87 | DIR=$1 88 | elif test $# -eq 0; then 89 | DIR=. 90 | else 91 | echo >&2 "Usage: $ME [OPT]... DIR" 92 | exit 1 93 | fi 94 | 95 | if ! test -d "$DIR"; then 96 | echo >&2 "$ME: $DIR is not a directory" 97 | exit 1 98 | fi 99 | 100 | if test -z "$LICENSE"; then 101 | LICENSE=$DIR/LICENSE.txt 102 | fi 103 | 104 | if ! test -f "$LICENSE"; then 105 | echo >&2 "$ME: can't open $LICENSE" 106 | exit 1 107 | fi 108 | 109 | LICENSE=$(absolutize $LICENSE) 110 | 111 | cd "$DIR" 112 | 113 | if test -z "$SHORT"; then 114 | cp $LICENSE $TMP.lic 115 | else 116 | LIC_NAME=$(grep -m1 -i license $LICENSE) 117 | if test -z "$LIC_NAME"; then 118 | echo >&2 "Failed to extract license name from $LICENSE" 119 | exit 1 120 | fi 121 | 122 | sed '/Copyright (c)/q' $LICENSE > $TMP.lic 123 | cat >> $TMP.lic < $EULA_C 132 | 133 | EULA_SH=$TMP.shell 134 | sed 's/^/# /' $TMP.lic > $EULA_SH 135 | 136 | EULA_HS=$TMP.haskell 137 | sed 's/^/-- /' $TMP.lic > $EULA_HS 138 | 139 | for f in $(find -iname '.[a-z0-9]*' -prune -o -type f -a -print); do 140 | if is_script $f; then 141 | echo "Patching script $f" 142 | if is_haskell_script $f; then 143 | EULA=$EULA_HS 144 | else 145 | EULA=$EULA_SH 146 | fi 147 | if has_shebang $f; then 148 | head -n 1 $f > $TMP.shebang 149 | tail -n +2 $f > $TMP.body 150 | ( cat $TMP.shebang; echo; cat $EULA; cat $TMP.body ) > $f 151 | else 152 | cp $f $TMP 153 | ( cat $EULA; echo; cat $TMP ) > $f 154 | fi 155 | elif is_source $f; then 156 | echo "Patching source file $f" 157 | cp $f $TMP 158 | ( cat $EULA_C; echo; cat $TMP ) > $f 159 | else 160 | echo "Ignoring $f" 161 | fi 162 | done 163 | 164 | -------------------------------------------------------------------------------- /plot-percentiles: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2022 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # A simple pyplot-based script which plots percentile-based benchmark summaries. 9 | # Inspired by https://developers.redhat.com/blog/2016/03/11/practical-micro-benchmarking-with-ltrace-and-sched 10 | 11 | import sys 12 | import os.path 13 | import matplotlib.pyplot as plt 14 | 15 | me = os.path.basename(__file__) 16 | 17 | # TODO: distinguish single run and series of runs 18 | class Bench: 19 | def __init__(self, name, files): 20 | self.name = name 21 | self.files = files 22 | self.data = [] 23 | 24 | def usage(): 25 | # TODO 26 | print('''\ 27 | %s [OPT]... --axis=NAME FILE [FILE...] --axis=NAME FILE [FILE...] --axis=NAME ... 28 | Print percentiles of groups of benchmark runs of measurements on single multi-axis plot. 29 | 30 | Options: 31 | --axis=NAME Adds new axis to the plot; followup FILEs 32 | will be plotted on this axis. 33 | --ylabel LAB Specifies Y-axis label on plots. 34 | --delim D, -d D Specifies delimeter for values in files (default is newline). 35 | 36 | Example: 37 | In the following run 38 | %s --ylabel 'Time, sec.' \ 39 | --axis=O2 trunk_O2_times.txt branch_O2_times.txt \ 40 | --axis=O3 trunk_O3_times.txt branch_O3_times.txt 41 | we display 2 plots (O2 and O3) comparing trunk against PR branch. 42 | 43 | A typical way to generate the times.txt files would be 44 | $ rm -f *_O[23].log 45 | $ for ver in trunk branch; do 46 | for opt in O2 O3; do 47 | for i in `seq 1 100`; do 48 | \time -apo ${ver}_${opt}.log ~/src/$ver-$opt/prog 49 | done; done; done 50 | $ for ver in trunk branch; do 51 | for opt in O2 O3; do 52 | cat ${ver}_${opt}.log | awk '/user/{print $2}' > ${ver}_${opt}_times.txt 53 | done; done 54 | ''' % (me, me)) 55 | sys.exit(0) 56 | 57 | def parse_args(args): 58 | benches = [] 59 | ylabel = None 60 | 61 | i = 0 62 | while i < len(args): 63 | arg = args[i] 64 | is_last_arg = i == len(args) - 1 65 | if arg == '--help': 66 | usage() 67 | elif arg == '--ylabel' and not is_last_arg: 68 | ylabel = args[i + 1] 69 | i += 2 70 | elif arg == '--axis' and not is_last_arg: 71 | name = args[i + 1] 72 | i += 2 73 | # Read all files following an --axis argument 74 | files = [] 75 | while i < len(args): 76 | if args[i].startswith('-'): 77 | break 78 | files.append(args[i]) 79 | i += 1 80 | benches.append(Bench(name, files)) 81 | else: 82 | sys.stderr.write(f"{me}: unexpected parameter '{arg}'\n") 83 | sys.exit(1) 84 | 85 | return ylabel, benches 86 | 87 | ylabel, benches = parse_args(sys.argv[1:]) 88 | 89 | # Read data 90 | 91 | for bench in benches: 92 | for file in bench.files: 93 | bench.data.append([]) 94 | with open(file, 'r') as f: 95 | for line in f: 96 | bench.data[-1].append(float(line)) 97 | # Build percentiles 98 | n = len(bench.data[-1]) 99 | bench.data[-1] = [0] + [100.0 * (i + 1) / n for i in range(n)], \ 100 | [0] + sorted(bench.data[-1]) 101 | 102 | # Plot data 103 | 104 | fig, axes = plt.subplots(len(benches)) 105 | 106 | # Silly pyplot returns scalar axes for len==1 107 | try: 108 | iter(axes) 109 | except TypeError: 110 | axes = [axes] 111 | 112 | for bench, axis in zip(benches, axes): 113 | axis.set_title(bench.name) 114 | if ylabel is not None: 115 | axis.set_ylabel(ylabel) 116 | axis.set_xlim((0, 100)) 117 | for file, (x, y) in zip(bench.files, bench.data): 118 | axis.plot(x, y, label=os.path.basename(file)) 119 | axis.legend() 120 | 121 | plt.show() 122 | -------------------------------------------------------------------------------- /boilerplate/sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2015-2022 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Log to $me.log 9 | if test "${1:-}" = __tee; then 10 | shift 11 | else 12 | export _rc=$(mktemp) 13 | trap "rm -f $_rc" EXIT INT TERM 14 | (set +e; exec "$0" __tee "$@" 2>&1; echo $? > $_rc) | tee $(basename $0).log 15 | exit $(cat $_rc) 16 | fi 17 | 18 | set -eu 19 | #if set -o | grep -q pipefail; then set -o pipefail; fi 20 | 21 | # Based on https://misc.flogisoft.com/bash/tip_colors_and_formatting 22 | END_COLOR='\033[0m' 23 | BLACK='\033[30m' 24 | RED='\033[31m' 25 | GREEN='\033[32m' 26 | YELLOW='\033[33m' 27 | BLUE='\033[34m' 28 | MAGENTA='\033[35m' 29 | CYAN='\033[36m' 30 | WHITE='\033[97m' 31 | UNDER='\033[2m' 32 | 33 | absolutize() { 34 | realpath -s "$1" 35 | } 36 | 37 | error() { 38 | prefix="error:" 39 | if test -t 2; then 40 | prefix="${RED}${prefix}${END_COLOR}" 41 | fi 42 | printf "$(basename $0): $prefix $@\\n" >&2 43 | exit 1 44 | } 45 | 46 | warn() { 47 | prefix="warning:" 48 | if test -t 2; then 49 | prefix="${RED}${prefix}${END_COLOR}" 50 | fi 51 | printf "$(basename $0): $prefix $@\\n" >&2 52 | } 53 | 54 | mkcleandir() { 55 | mkdir -p "$1" 56 | rm -rf "$1"/* 57 | } 58 | 59 | isemptydir() { 60 | #test $(ls -A "$1" | wc -l) = 0 61 | test -d "$1" && test "$(find -L "$1" -maxdepth 0 -empty)" 62 | } 63 | 64 | strip_ext() { 65 | # echo "$1" | sed -e 's!\.[^.]*$!!' 66 | echo "${1%.*}" 67 | } 68 | 69 | # Get the only file matching regex in directory or abort 70 | get_the_match() { 71 | local N=$(ls -1 "$1" | grep "$2" | wc -l) 72 | case $N in 73 | 0) 74 | echo >&2 "No matches for '$2' in $1" 75 | exit 1 76 | ;; 77 | 1) 78 | ls -1 "$1" | grep "$2" | xargs basename 79 | ;; 80 | *) 81 | echo >&2 "Too many matches for '$2' in $1" 82 | exit 1 83 | esac 84 | } 85 | 86 | # Splits string to pieces and assigns them to shell variables. 87 | # Run like `read_str ';' '1;2;3' x y z'. 88 | read_str() { 89 | local OIFS="$IFS" 90 | IFS="$1" 91 | local str="$2" 92 | shift 2 93 | local read_str_piece 94 | for read_str_piece in $str; do 95 | eval "$1='$read_str_piece'" 96 | shift 97 | done 98 | IFS="$OIFS" 99 | } 100 | 101 | # Wait until all processes have completed 102 | wait_pids() { 103 | local failed= 104 | for p; do 105 | if ! wait $p; then 106 | echo >&2 "wait_pids: process $p failed" 107 | failed=1 108 | fi 109 | done 110 | if test -n "$failed"; then 111 | return 1 112 | fi 113 | } 114 | 115 | usage() { 116 | cat <&2 <&2 "$(basename $0): error: $@" 18 | exit 1 19 | } 20 | 21 | print_short_help() { 22 | cat < /dev/null 2>&1; then 53 | CMAKE_FLAGS="$CMAKE_FLAGS -DLLVM_USE_LINKER=lld" 54 | fi 55 | 56 | if which ninja > /dev/null 2>&1; then 57 | CMAKE_FLAGS="$CMAKE_FLAGS -G Ninja" 58 | fi 59 | 60 | BUILD_DIR=$PWD 61 | PROJECTS=clang 62 | CLEAN= 63 | INSTALL= 64 | TEST= 65 | 66 | while true; do 67 | case "$1" in 68 | -C) 69 | BUILD_DIR=$2 70 | mkdir -p $BUILD_DIR 71 | shift 2 72 | ;; 73 | -g|--debug) 74 | CMAKE_FLAGS="$CMAKE_FLAGS -DCMAKE_BUILD_TYPE=Debug -DCOMPILER_RT_DEBUG=ON" 75 | shift 76 | ;; 77 | --clean) 78 | CLEAN=1 79 | shift 80 | ;; 81 | --install) 82 | INSTALL=1 83 | shift 84 | ;; 85 | --test) 86 | TEST=1 87 | shift 88 | ;; 89 | --projects | -p) 90 | PROJECTS="$2" 91 | shift 2 92 | ;; 93 | --targets | -t) 94 | CMAKE_FLAGS="$CMAKE_FLAGS -DLLVM_TARGETS_TO_BUILD='$2'" 95 | shift 2 96 | ;; 97 | --help | -h) 98 | print_help 99 | exit 1 100 | ;; 101 | -j) 102 | NJOBS=$2 103 | shift 2 104 | ;; 105 | -j*) 106 | NJOBS=$(echo "$1" | sed -e 's/^-j//') 107 | shift 108 | ;; 109 | -*) 110 | error "unknown option: $1" 111 | ;; 112 | *) 113 | break 114 | ;; 115 | esac 116 | done 117 | 118 | if test -n "$PROJECTS"; then 119 | CMAKE_FLAGS="$CMAKE_FLAGS -DLLVM_ENABLE_PROJECTS='$PROJECTS'" 120 | fi 121 | 122 | if [ $# -lt 1 ]; then 123 | print_short_help >&2 124 | exit 1 125 | fi 126 | 127 | SRC=$(absolutize $1) 128 | shift 129 | 130 | if [ ! -f $SRC/CMakeLists.txt ]; then 131 | error "file $SRC/CMakeLists.txt is missing" 132 | fi 133 | 134 | cd $BUILD_DIR 135 | 136 | if [ $(ls | wc -l) -gt 0 ]; then 137 | if [ -z "$CLEAN" ]; then 138 | error "Build must be performed in empty directory; do `rm -rf *` in $BUILD_DIR or run with --clean" 139 | else 140 | # Be cautious 141 | test -f ./CMakeCache.txt \ 142 | || error "cowardly refusing to clean $BUILD_DIR which does not look like a build dir" 143 | rm -rf * 144 | fi 145 | fi 146 | 147 | HOST=x86_64-unknown-linux-gnu 148 | 149 | TIME_START=$(date +%s) 150 | 151 | #module load gcc/pc/483 # LLVM wants GCC 4.8+ 152 | 153 | LOG=$(basename $0).log 154 | 155 | echo "cmake $CMAKE_FLAGS \"$@\" $SRC" >> cmake_command.log 156 | eval cmake $CMAKE_FLAGS \ 157 | "$@" $SRC | tee $LOG 158 | 159 | if test -f Makefile; then 160 | MAKE=make 161 | elif test -f build.ninja; then 162 | MAKE=ninja 163 | fi 164 | 165 | export VERBOSE=1 166 | 167 | nice $MAKE -j$NJOBS 2>&1 | tee -a $LOG 168 | 169 | if test -n "$INSTALL"; then 170 | nice $MAKE install 2>&1 | tee -a $LOG 171 | fi 172 | 173 | if test -n "$TEST"; then 174 | nice $MAKE check-all 2>&1 | tee -a $LOG 175 | fi 176 | 177 | DT=$(($(date +%s) - TIME_START)) 178 | echo "Total time is $((DT / 60)) minutes (using $NJOBS cores)." > times.log 179 | 180 | -------------------------------------------------------------------------------- /gcc-build: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2015-2022 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Configure and build GCC in current directory. 9 | 10 | set -eu 11 | 12 | absolutize() { 13 | realpath -s "$1" 14 | } 15 | 16 | error() { 17 | echo >&2 "$(basename $0): error: $@" 18 | exit 1 19 | } 20 | 21 | print_short_help() { 22 | cat <&2 134 | exit 1 135 | fi 136 | 137 | if [ ! -d $1 ]; then 138 | error "directory $1 does not exist" 139 | fi 140 | 141 | SRC=$(absolutize $1) 142 | shift 143 | 144 | if [ ! -x $SRC/configure ]; then 145 | error "file $SRC/configure is missing or is not executable" 146 | fi 147 | 148 | cd $BUILD_DIR 149 | 150 | if test -f configure; then 151 | error 'can not build GCC in source dir' 152 | elif [ $(ls | wc -l) -gt 0 ]; then 153 | if [ -z "$CLEAN" ]; then 154 | error "build must be performed in empty directory; do \`rm -rf $BUILD_DIR/*\` or run with --clean" 155 | else 156 | # Be cautious 157 | test -f ./config.log && grep -q GCC_FOR_TARGET ./config.log \ 158 | || error 'cowardly refusing to clean directory which does not look like a build dir' 159 | rm -rf * 160 | fi 161 | fi 162 | 163 | # Support only native build for now 164 | TARGET=$(gcc -v 2>&1 | grep Target | awk '{print $2}') 165 | HOST=$TARGET 166 | 167 | INSTALL_DIR=$HOME/install/$(basename $PWD) 168 | 169 | TIME_START=$(date +%s) 170 | 171 | eval $SRC/configure \ 172 | --disable-multilib --enable-checking \ 173 | --target=$TARGET --host=$HOST --build=$HOST \ 174 | --prefix=$INSTALL_DIR \ 175 | $BOOTSTRAP "$CFGFLAGS" "$@" 2>&1 | tee output.log 176 | 177 | nice make -j$NJOBS $GOAL 2>&1 | tee -a output.log 178 | 179 | if test -n "$INSTALL"; then 180 | nice make install 2>&1 | tee -a output.log 181 | fi 182 | 183 | DT=$(($(date +%s) - TIME_START)) 184 | echo "Total time is $((DT / 60)) minutes (using $NJOBS cores)." > times.log 185 | -------------------------------------------------------------------------------- /llvm-classify-error: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Copyright 2019 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Print short summary of LLVM errors for easier deduplication of mass runs. 9 | 10 | use strict; 11 | use warnings; 12 | 13 | use File::Basename; 14 | use Getopt::Long qw(:config posix_default bundling no_ignore_case); 15 | use Data::Dumper; 16 | 17 | # File::Slurp not installed by default... 18 | sub read_file($) { 19 | my $f = $_[0]; 20 | open FILE, $f or die "Failed to open $f"; 21 | my @lines; 22 | while() { 23 | s/[\r\n]+//g; 24 | push @lines, $_; 25 | } 26 | close FILE; 27 | return @lines if(wantarray); 28 | return join("\n", @lines); 29 | } 30 | 31 | my $verbose = 0; 32 | my $split_code_errors = 0; 33 | 34 | sub classify($$) { 35 | my ($file, $log) = @_; 36 | 37 | # Canonize register names 38 | $log =~ s/(%[RVW][hl]?)[0-9]+\b/$1/g; 39 | $log =~ s/(%[DP])[0-9]+\b/$1/g; 40 | $log =~ s/(%[VW]PR)[0-9]+[_A-Z0-9]*/$1/g; 41 | $log =~ s/\bvreg[0-9]+/vreg/g; 42 | 43 | # Canonize other noisy parts 44 | $log =~ s/ uid:[0-9].*//g; 45 | $log =~ s/ mem:(LD|ST)[0-9].*//g; # mem:LD32[%3](align=1) 46 | $log =~ s/tbaa=<0x[0-9a-f]+>/tbaa=<>/g; # ch = store) 47 | 48 | my $type; 49 | my $key = ''; 50 | if($log =~ /error: (.*file not found)/) { 51 | # some-file.c:4:10: fatal error: 'some-header.h' file not found 52 | $type = 'code error'; 53 | $key = $1 if $split_code_errors; 54 | } elsif($log =~ /\.(?:h|hpp|inc|c|C|cpp|cxx):[0-9]+(?::[0-9]+)?: error: (.*)/) { 55 | # some-header.h:45:27: error: other-header.h: No such file or directory 56 | $type = 'code error'; 57 | $key = $1 if $split_code_errors; 58 | } elsif($log =~ /fatal error: error in backend: Cannot select: *(.*)/) { 59 | # fatal error: error in backend: Cannot select: t130: v2i16 = vselect t172, t255, t258 60 | $type = 'select'; 61 | $key = $1; 62 | $key =~ s/\bt[0-9]+/t/g; 63 | $key =~ s/\bset[a-z][a-z]\b/setXX/g; # Replace setgt, setne, etc. with "setXX" 64 | $key =~ s/@[a-zA-Z_][a-zA-Z_.0-9]*/\@GLOB/g; # Use same name for all globals 65 | } elsif($log =~ /\*\*\* Bad machine code: (.*)/) { 66 | # *** Bad machine code: MBB exits via unconditional fall-through 67 | $type = 'bad ir'; 68 | $key = $1; 69 | } elsif($log =~ /fatal error: (.*)/) { 70 | # fatal error: error in backend: ran out of registers during register allocation 71 | $type = 'assert'; 72 | $key = $1; 73 | } elsif($log =~ /Assertion failed: *(.*)/) { 74 | # Assertion failed: I.Value && "No live-in value found", file C:\cygwin64\home\iuriig\src\llvm\lib\CodeGen\LiveRangeCalc.cpp, line 227 75 | $type = 'assert'; 76 | $key = $1; 77 | } elsif($log =~ /(UNREACHABLE executed.*)/) { 78 | # UNREACHABLE executed at XXXInstrInfo.cpp 79 | $type = 'assert'; 80 | $key = $1; 81 | } elsif($log =~ /clang frontend command failed due to signal/) { 82 | # ---> clang.exe: error: clang frontend command failed due to signal (use -v to see invocation) 83 | $type = 'clang crash'; 84 | } else { 85 | print STDERR "Unknown error in file $file\n" if $verbose; 86 | $type = 'other'; 87 | } 88 | 89 | # TODO: simulator, linker, asm errors 90 | 91 | return $type, $key; 92 | } 93 | 94 | # Parse options 95 | 96 | my $help = 0; 97 | my $file_list; 98 | my $print_files; 99 | 100 | GetOptions( 101 | 'help|h' => \$help, 102 | 'verbose|v+' => \$verbose, 103 | 'split-code-errors' => \$split_code_errors, 104 | 'file-list' => \$file_list, 105 | 'print-files|f' => \$print_files, 106 | ); 107 | 108 | if($help) { 109 | my $me = basename($0); 110 | print < $type, key => $key, count => 0, files => [] } if ! exists $errors{$hash_key}; 145 | ++$errors{$hash_key}->{count}; 146 | push @{$errors{$hash_key}->{files}}, $file; 147 | 148 | $errors{total} = { type => 'total', key => '', count => 0, files => [] } if ! exists $errors{total}; 149 | ++$errors{total}->{count}; 150 | } 151 | 152 | # Print report 153 | 154 | my @sorted_errors = sort {$b->{count} <=> $a->{count}} values %errors; 155 | for (@sorted_errors) { 156 | print "$_->{type}: $_->{key}: $_->{count}\n"; 157 | if ($print_files) { 158 | print " $_\n" for @{$_->{files}}; 159 | print "\n"; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /boilerplate/python3: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # Copyright 2017-2022 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | # 8 | # Shortly describe script here. 9 | 10 | import argparse 11 | import atexit 12 | import multiprocessing 13 | import os 14 | import os.path 15 | import re 16 | import subprocess 17 | import sys 18 | import tempfile 19 | import time 20 | from typing import NoReturn 21 | 22 | me = os.path.basename(__file__) 23 | 24 | 25 | def warn(msg): 26 | """ 27 | Print nicely-formatted warning message. 28 | """ 29 | sys.stderr.write(f"{me}: warning: {msg}\n") 30 | 31 | 32 | def error(msg) -> NoReturn: 33 | """ 34 | Print nicely-formatted error message and exit. 35 | """ 36 | sys.stderr.write(f"{me}: error: {msg}\n") 37 | sys.exit(1) 38 | 39 | 40 | def warn_if(cond, msg): 41 | if cond: 42 | warn(msg) 43 | 44 | 45 | def error_if(cond, msg): 46 | if cond: 47 | error(msg) 48 | 49 | 50 | def ensure_module(module, package=None, user=True, quiet=False): 51 | """ 52 | Installs module if it's missing. Call like 53 | ensure_module('configparser') 54 | ensure_module('wx', 'wxPython') 55 | """ 56 | import imp, site, subprocess 57 | 58 | try: 59 | imp.find_module(module) 60 | except ImportError: 61 | if not quiet: 62 | print(f"Installing Python module {module}...") 63 | exe = sys.executable 64 | package = package or module 65 | try: 66 | subprocess.check_call([exe, "-mensurepip"]) 67 | except subprocess.CalledProcessError: 68 | warn("failed to ensure pip") 69 | subprocess.check_call( 70 | [exe, "-mpip", "install"] + (["--user"] if user else []) + [package] 71 | ) 72 | # User site packages are often not in PATH by default 73 | for d in site.getusersitepackages() if user else site.getsitepackages(): 74 | if d not in sys.path: 75 | sys.path.append(d) 76 | try: 77 | imp.find_module(module) 78 | except ImportError: 79 | error(f"module '{module}' not found in package '{package}'") 80 | 81 | 82 | class Re: 83 | """ 84 | "Regex cacher" gets rid of temporary match objects e.g. 85 | if Re.match(...): 86 | x = Re.group(1) 87 | """ 88 | 89 | _last_match = None 90 | 91 | @classmethod 92 | def match(self, *args, **kwargs): 93 | self._last_match = re.match(*args, **kwargs) 94 | return self._last_match 95 | 96 | @classmethod 97 | def search(self, *args, **kwargs): 98 | self._last_match = re.search(*args, **kwargs) 99 | return self._last_match 100 | 101 | @classmethod 102 | def fullmatch(self, *args, **kwargs): 103 | self._last_match = re.fullmatch(*args, **kwargs) 104 | return self._last_match 105 | 106 | @classmethod 107 | def group(self, *args, **kwargs): 108 | return self._last_match.group(*args, *kwargs) 109 | 110 | @classmethod 111 | def groups(self, *args, **kwargs): 112 | return self._last_match.groups(*args, **kwargs) 113 | 114 | 115 | def run(cmd, fatal=True, tee=False, **kwargs): 116 | """ 117 | Simple wrapper for subprocess. 118 | """ 119 | if isinstance(cmd, str): 120 | cmd = cmd.split(" ") 121 | # print(cmd) 122 | t1 = time.perf_counter_ns() 123 | p = subprocess.run( 124 | cmd, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs 125 | ) 126 | t2 = time.perf_counter_ns() 127 | out = p.stdout.decode() 128 | err = p.stderr.decode() 129 | if fatal and p.returncode != 0: 130 | cmds = " ".join(cmd) 131 | error(f"'{cmds}' failed:\n{out}{err}") 132 | if tee: 133 | sys.stdout.write(out) 134 | sys.stderr.write(err) 135 | return p.returncode, out, err, (t2 - t1) / 1e9 136 | 137 | 138 | def run_no_deadlocks(cmd, fatal=True, **kwargs): 139 | """ 140 | A simple wrapper for subprocess to avoid deadlocks 141 | with subprocess.PIPE. 142 | """ 143 | if isinstance(cmd, str): 144 | cmd = cmd.split(" ") 145 | # print(cmd) 146 | out_file = tempfile.mktemp() 147 | err_file = tempfile.mktemp() 148 | with open(out_file, "w") as out_fileno, open(err_file, "w") as err_fileno: 149 | p = subprocess.Popen(cmd, stdout=out_fileno, stderr=err_fileno, **kwargs) 150 | p.communicate() 151 | with open(out_file, "r") as f: 152 | out = f.read() 153 | os.unlink(out_file) 154 | with open(err_file, "r") as f: 155 | err = f.read() 156 | os.unlink(err_file) 157 | if fatal and p.returncode != 0: 158 | cmds = " ".join(cmd) 159 | error(f"'{cmds}' failed:\n{out}{err}") 160 | return p.returncode, out, err 161 | 162 | 163 | def main(): 164 | class Formatter( 165 | argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter 166 | ): 167 | pass 168 | 169 | parser = argparse.ArgumentParser( 170 | description="Describe script here", 171 | formatter_class=Formatter, 172 | epilog=f"""\ 173 | Examples: 174 | $ python {me} arg 175 | """, 176 | ) 177 | parser.add_argument( 178 | "--flag", 179 | "-f", 180 | help="Describe flag here", 181 | dest="flag", 182 | action="store_true", 183 | default=False, 184 | ) 185 | parser.add_argument( 186 | "--no-flag", help="Inverse of --flag", dest="flag", action="store_false" 187 | ) 188 | parser.add_argument( 189 | "--param", 190 | "-p", 191 | metavar="PARAM_NAME", 192 | help="Describe scalar parameter here", 193 | default="0", 194 | ) 195 | parser.add_argument( 196 | "--multi", 197 | "-m", 198 | metavar="MULTI_PARAM_NAME", 199 | help="Describe array parameter here (can be specified more than once)", 200 | action="append", 201 | default=[], 202 | ) 203 | parser.add_argument( 204 | "--verbose", 205 | "-v", 206 | help="Print diagnostic info (can be specified more than once)", 207 | action="count", 208 | default=0, 209 | ) 210 | parser.add_argument("arg", help="First positional argument", metavar="ARG") 211 | parser.add_argument("rest", nargs=argparse.REMAINDER, default=[]) 212 | 213 | args = parser.parse_args() 214 | 215 | test_file = tempfile.mktemp() 216 | atexit.register(lambda: os.unlink(test_file)) 217 | 218 | files = [] 219 | for d, _, ff in os.walk("."): 220 | files.extend(os.path.join(d, f) for f in ff if f.endswith(".txt")) 221 | 222 | def func(): 223 | pass # TODO 224 | 225 | with multiprocessing.Pool(processes=10) as pool: 226 | results = pool.map(func, files) 227 | 228 | return 0 229 | 230 | 231 | if __name__ == "__main__": 232 | sys.exit(main()) 233 | -------------------------------------------------------------------------------- /gnu-compare-projects: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2015-2016 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # Compares 2 versions of same OSS project by intelligently 9 | # comparing ChangeLogs. This is useful because you often 10 | # don't have VCS at hand (e.g. just two .tar.gz coming from 11 | # noname vendors). 12 | 13 | import sys 14 | import re 15 | import os.path 16 | import os 17 | import getopt 18 | 19 | me = os.path.basename(sys.argv[0]) 20 | 21 | def error(msg): 22 | sys.stderr.write("%s: error: %s\n" % (me, msg)) 23 | sys.exit(1) 24 | 25 | def warn(msg): 26 | sys.stderr.write("%s: warning: %s\n" % (me, msg)) 27 | 28 | def is_tail(s1, s2): 29 | i = s2.find(s1) 30 | return i != -1 and len(s2) == i + len(s1) 31 | 32 | # Get rid of irrelevant whitespace changes in changelogs 33 | # to avoid spurious differences. 34 | def canonize_key(k): 35 | if not hasattr(canonize_key, 'inner_whites_re'): 36 | canonize_key.inner_whites_re = re.compile(r'\s+') 37 | canonize_key.remove_backport_msg_re = re.compile(r'^\s*Backport(ed)? from .*$', re.IGNORECASE | re.MULTILINE) 38 | canonize_key.author_re = re.compile(r'^\s*[12][0-9]{3}-[01][0-9]-[0123][0-9] .* <.*@.*>$', re.MULTILINE) 39 | 40 | # err_string = '2014-09-02 Cary Coutant ' 41 | # if k.find(err_string) != -1: 42 | # sys.stderr.write('Here: %s' % k) 43 | 44 | # Remove useless parts of CL entry 45 | k = canonize_key.remove_backport_msg_re.sub('', k) 46 | k = canonize_key.author_re.sub('', k) 47 | 48 | # Remove trailing whites 49 | k = k.rstrip() 50 | 51 | # Canonize whites 52 | k = canonize_key.inner_whites_re.sub(' ', k) 53 | 54 | # Remove line breaks (this also removes newlines) 55 | k = "".join(k.splitlines()) 56 | 57 | # if k.find(err_string) != -1: 58 | # sys.stderr.write('Result: %s' % k) 59 | 60 | return k 61 | 62 | def is_very_old_change(date): 63 | return int(date) < 2012 64 | 65 | def get_change_date(change_head): 66 | return change_head[0:4] 67 | 68 | # Parse changelog and extract changes. 69 | def parse_changelog(cl, changes_re, changes): 70 | if not hasattr(parse_changelog, 'change_head_re'): 71 | # We allow any number of whites to tolerate against broken ChangeLogs 72 | parse_changelog.change_head_re = re.compile(r'^[12][0-9]{3}-[01][0-9]-[0-3][0-9] +[A-Z]') 73 | parse_changelog.metainfo_re = re.compile(r'^[^\s]') 74 | 75 | head = None 76 | change = '' 77 | f = open(cl) 78 | lines = f.readlines() 79 | f.close() 80 | 81 | lines.append('2001-01-01 Mister X') 82 | 83 | for line in lines: 84 | if parse_changelog.change_head_re.match(line): 85 | if head is not None: 86 | date = get_change_date(head) 87 | if not is_very_old_change(date) and (changes_re is None or changes_re.search(change)): 88 | changes[canonize_key(change)] = head + change 89 | head = line 90 | change = '' 91 | elif parse_changelog.metainfo_re.match(line): 92 | # Skip metainfo (modelines, etc.) 93 | pass 94 | else: 95 | # FIXME: concat is inefficient 96 | change += line 97 | 98 | # Debug helper. 99 | def dump_changes(changes, lim = 10): 100 | n = 0 101 | for k, v in changes.iteritems(): 102 | print "Next: %s" % v 103 | n += 1 104 | if n > lim: 105 | break 106 | 107 | # Read all changelogs in directory 108 | # (i.e. both upstream and vendor changelogs). 109 | def collect_changes(cls, changes_re): 110 | changes = {} 111 | for cl in cls: 112 | parse_changelog(cl, changes_re, changes) 113 | 114 | return changes 115 | 116 | # Compare changelog contents and report missing changes. 117 | def compare_changes(dir1, dir2, relroot, changes1, changes2): 118 | # TODO: usually only heads of changelogs will be different; 119 | # stripping the longest common suffix to improve speed. 120 | 121 | hdr = "=== %s/ChangeLog ===" % relroot 122 | 123 | has_left_only = False 124 | for k, v in changes1.iteritems(): 125 | if k not in changes2: 126 | if not has_left_only: 127 | has_left_only = True 128 | print hdr 129 | print "ONLY IN %s:" % dir1 130 | print v 131 | 132 | has_right_only = False 133 | for k, v in changes2.iteritems(): 134 | if k not in changes1: 135 | if not has_right_only: 136 | has_right_only = True 137 | # See if we already printed header 138 | if not has_left_only: 139 | print hdr 140 | print "ONLY IN %s:" % dir2 141 | print v 142 | 143 | def is_interesting_cl(f): 144 | if not hasattr(is_interesting_cl, 'date_re'): 145 | is_interesting_cl.date_re = re.compile(r'\b[0-9]{4}\b') 146 | 147 | if f.find('ChangeLog') != 0: 148 | return False 149 | 150 | dates = is_interesting_cl.date_re.findall(f) 151 | if len(dates) == 1 and is_very_old_change(dates[0]): 152 | return False 153 | 154 | return True 155 | 156 | def print_help_and_exit(): 157 | sys.stderr.write("""\ 158 | Usage: %s [-m match-expr] [-p prune-dir] dir1 dir2 159 | Recursively compare corresponding changelogs in directories. 160 | 161 | Example: 162 | # Compare FSF Binutils 2.24 against trunk 163 | $ %s src/binutils-gdb-2.24 src/binutils-gdb 164 | 165 | # Only consider Aarch64 changes ('eabi' not included because it clashes with other targets) 166 | $ %s -m 'aarch|\\barm\\b|cortex|\\ba5[37]' -p gdb src/binutils-gdb-2.24 src/binutils-gdb 167 | """ % (me, me, me)) 168 | sys.exit(1) 169 | 170 | def main(): 171 | match = None 172 | prunes = ['testsuite', 'libiberty', '.git', '.svn', 'CVS'] 173 | try: 174 | opts, args = getopt.getopt(sys.argv[1:], "hm:p:", ["help", "match=", "prune="]) 175 | except getopt.GetoptError as err: 176 | error(str(err)) 177 | output = None 178 | verbose = False 179 | changes_re = None 180 | for o, a in opts: 181 | if o in ("-h", "--help"): 182 | print_help_and_exit() 183 | elif o in ("-p", "--prune"): 184 | prunes.append(a) 185 | elif o in ("-m", "--match"): 186 | changes_re = re.compile(a, re.IGNORECASE) 187 | else: 188 | assert False, "unhandled option" 189 | 190 | if len(args) != 2: 191 | error('invalid arguments; for more details run with -h') 192 | 193 | dir1 = args[0] 194 | if not os.path.exists(dir1): 195 | error("directory %s does not exist" % dir1) 196 | 197 | dir2 = args[1] 198 | if not os.path.exists(dir2): 199 | error("directory %s does not exist" % dir2) 200 | 201 | for root, dirs, files in os.walk(dir1): 202 | # Skip useless changelogs 203 | if os.path.basename(root) in prunes: 204 | del dirs[:] 205 | continue 206 | 207 | cls1 = map(lambda f: os.path.join(root, f), filter(is_interesting_cl, files)) 208 | changes1 = collect_changes(cls1, changes_re) 209 | 210 | # Strip dir1 211 | if not root.startswith(dir1): 212 | error("internal error in os.walk") 213 | relroot = root[len(dir1):] 214 | if relroot: 215 | prefix = '.' if relroot[0] == '/' else './' 216 | relroot = prefix + relroot 217 | 218 | root2 = os.path.join(dir2, relroot) 219 | if not os.path.isdir(root2): 220 | warn("directory %s does not exist in %s" % (relroot, dir2)) 221 | continue 222 | 223 | cls2 = map(lambda f: os.path.join(root2, f), filter(is_interesting_cl, os.listdir(root2))) 224 | changes2 = collect_changes(cls2, changes_re) 225 | 226 | compare_changes(dir1, dir2, relroot, changes1, changes2) 227 | 228 | if __name__ == "__main__": 229 | main() 230 | 231 | 232 | -------------------------------------------------------------------------------- /gcc-bootstrap-and-regtest: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2015-2022 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # This script bootstraps normal and patched versions of GCC, 9 | # runs regression tests and compares results. 10 | # 11 | # Prerequisites: autogen dejagnu 12 | # 13 | # TODO: rewrite to use Makefiles 14 | 15 | set -eu 16 | 17 | absolutize() { 18 | realpath -s "$1" 19 | } 20 | 21 | error() { 22 | echo >&2 "$(basename $0): error: $@" 23 | exit 1 24 | } 25 | 26 | mkcleandir() { 27 | mkdir -p "$1" 28 | rm -rf "$1"/* 29 | } 30 | 31 | isemptydir() { 32 | #test $(ls -A "$1" | wc -l) = 0 33 | test -d "$1" && test $(find -L "$1" -maxdepth 0 -empty) 34 | } 35 | 36 | P=x86_64-unknown-linux-gnu 37 | 38 | gcc_configure() { 39 | SRC=$1 40 | BUILD=$2 41 | 42 | sleep 1 43 | 44 | cd $BUILD 45 | if ! isemptydir .; then 46 | error 'build directory not empty' 47 | fi 48 | 49 | # TODO: --enable-checking=all ? 50 | nice $SRC/configure --enable-checking --enable-bootstrap \ 51 | --target=$P --host=$P --build=$P \ 52 | $CFGFLAGS 53 | } 54 | 55 | gcc_build() { 56 | BUILD=$1 57 | NJOBS=$2 58 | nice make -C $BUILD -j$NJOBS 59 | } 60 | 61 | gcc_check() { 62 | BUILD=$1 63 | NJOBS=$2 64 | nice make -C $BUILD -j$NJOBS -k check || true 65 | } 66 | 67 | gcc_compare() { 68 | SRC=$1 69 | BUILD=$2 70 | BUILD_P=$3 71 | for sum in $(cd $BUILD; find -name \*.sum); do 72 | echo "========== Comparing $sum" 73 | $SRC/contrib/dg-cmp-results.sh -v -v \* $BUILD/$sum $BUILD_P/$sum 74 | done 2>&1 75 | } 76 | 77 | is_gcc_dir() { 78 | test -d "$1" && test -f "$1"/gcc/gcc.h 79 | } 80 | 81 | tic() { 82 | TIME_START=$(date +%s) 83 | # Would be nice to re-use TIME_START/END but `date -d' fails 84 | # with "date: invalid date ‘Sun 22 Jan 12:00 GMT 2017’" on some machines 85 | echo "Started $1: $(date)" >> $OUT/times.log 86 | } 87 | 88 | toc() { 89 | TIME_END=$(date +%s) 90 | echo "Finished $1: $(date)" >> $OUT/times.log 91 | DT=$((TIME_END - TIME_START)) 92 | echo "Total time is $((DT / 60)) minutes." >> $OUT/times.log 93 | } 94 | 95 | killtree() { 96 | local status 97 | 98 | status=0 99 | 100 | # Stop parent so that it does not respawn children 101 | kill -s SIGSTOP $1 || status=1 102 | 103 | for kid in $(ps -o pid --no-headers --ppid $1); do 104 | killtree $kid $2 || status=1 105 | done 106 | 107 | # Need to continue stopped process so that system can kill it 108 | kill -$2 $1 && kill -s SIGCONT $1 || status=1 109 | 110 | return $status 111 | } 112 | 113 | killkids() { 114 | for kid in $(ps -o pid --no-headers --ppid $$); do 115 | killtree $kid TERM 116 | done 117 | } 118 | 119 | me=$(basename $0) 120 | 121 | help() { 122 | cat < /dev/null; then 210 | # That's branch name 211 | : 212 | else 213 | error "I can't make sense of $PATCH" 214 | fi 215 | 216 | if ! is_gcc_dir $SRC; then 217 | error "reference directory $SRC is not a GCC dir" 218 | fi 219 | 220 | if [ -d $SRC/.git ]; then 221 | if [ master != $(git --git-dir=$SRC/.git rev-parse --abbrev-ref HEAD) ]; then 222 | echo >&2 "Current branch in reference directory $SRC is not master!" 223 | fi 224 | fi 225 | 226 | mkcleandir $OUT 227 | echo "Temporary files will be stored in $OUT" 228 | 229 | BUILD=$OUT/build-orig 230 | mkdir $BUILD 231 | 232 | BUILD_P=$OUT/build-patched 233 | mkdir $BUILD_P 234 | 235 | #if [ ! -d $SRC/mpfr -o ! -d $SRC/gmp -o ! -d $SRC/mpc -o ! -d $SRC/isl -o ! -d $SRC/cloog ]; then 236 | # echo 'Downloading libs...' 237 | # ( cd $SRC; contrib/download_prerequisites ) 238 | #fi 239 | 240 | if [ -d $PATCH ]; then 241 | SRC_P=$PATCH 242 | else 243 | SRC_P=$OUT/src-patched 244 | echo 'Copying...' 245 | mkcleandir $SRC_P 246 | if [ -f $PATCH ]; then 247 | cp -r $SRC/* $SRC_P 248 | patch -d $SRC_P -p1 < $PATCH 249 | else 250 | git --git-dir=$SRC/.git archive $PATCH | tar -x -C $SRC_P 251 | find -L $SRC_P | xargs touch -r $SRC_P 252 | fi 253 | fi 254 | 255 | trap "echo 'Encountered error, killing children...'; killkids" EXIT INT TERM 256 | 257 | echo 'Configuring...' 258 | gcc_configure $SRC $BUILD > $OUT/configure-orig.log 2>&1 & 259 | gcc_configure $SRC_P $BUILD_P > $OUT/configure-patched.log 2>&1 & 260 | wait %2 261 | wait %1 262 | 263 | echo 'Building...' 264 | tic build 265 | gcc_build $BUILD $NJOBS > $OUT/build-orig.log 2>&1 & 266 | gcc_build $BUILD_P $NJOBS > $OUT/build-patched.log 2>&1 & 267 | wait %2 268 | wait %1 269 | toc build 270 | 271 | # GCC people ask us to test i386 as well... 272 | echo 'Checking (32-bit)...' 273 | tic 'checking (32-bit)' 274 | RUNTESTFLAGS='--target_board="unix/-m32"' gcc_check $BUILD $NJOBS > $OUT/check-orig-32.log 2>&1 & 275 | RUNTESTFLAGS='--target_board="unix/-m32"' gcc_check $BUILD_P $NJOBS > $OUT/check-patched-32.log 2>&1 & 276 | wait %2 277 | wait %1 278 | gcc_compare $SRC $BUILD $BUILD_P | tee $OUT/compare-32.log 279 | toc 'checking (32-bit)' 280 | 281 | echo 'Checking...' 282 | tic checking 283 | gcc_check $BUILD $NJOBS > $OUT/check-orig.log 2>&1 & 284 | gcc_check $BUILD_P $NJOBS > $OUT/check-patched.log 2>&1 & 285 | wait %2 286 | wait %1 287 | gcc_compare $SRC $BUILD $BUILD_P | tee $OUT/compare.log 288 | toc checking 289 | 290 | trap '' EXIT INT TERM 291 | -------------------------------------------------------------------------------- /llvm-print-fatpoints: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Copyright 2019 Yury Gribov 4 | # 5 | # Use of this source code is governed by MIT license that can be 6 | # found in the LICENSE.txt file. 7 | 8 | # A simple script for printing fatpoints from LLVM MIR dump. 9 | # Run `print_fatpoints.pl -h' for more details. 10 | 11 | use strict; 12 | use warnings; 13 | 14 | use Getopt::Long qw(GetOptions); 15 | use Data::Dumper; 16 | 17 | my $v = 0; 18 | my $help = 0; 19 | my $no_header = 0; 20 | 21 | GetOptions( 22 | 'help|h' => \$help, 23 | 'verbose|v+' => \$v, 24 | 'no-header' => \$no_header, 25 | ); 26 | 27 | if($help) { 28 | print <{$k} or $b->{$k} ne $a->{$k}); 51 | } 52 | return 1; 53 | } 54 | 55 | sub cmp_hashes($$) { 56 | my ($a, $b) = @_; 57 | return (cmp_hashes_1($a, $b) and cmp_hashes_1($b, $a)); 58 | } 59 | 60 | my $in_func = $no_header; # Do not look for header if user wants this 61 | my $bb; 62 | 63 | my %cfg; 64 | my @bb_list; 65 | my %symtab; 66 | 67 | sub dumpp($) { 68 | my $suffix = $_[0]; 69 | 70 | print "MIR ($suffix)\n"; 71 | for my $bb (@bb_list) { 72 | print <{name} 74 | <- @{[ join(', ', map {ref $_ ? $_->{name} : $_} @{$bb->{preds}}) ]} 75 | -> @{[ join(', ', map {ref $_ ? $_->{name} : $_} @{$bb->{succs}}) ]} 76 | in: @{[ join(', ', sort keys %{$bb->{in}}) ]} 77 | out: @{[ join(', ', sort keys %{$bb->{out}}) ]} 78 | EOF 79 | for my $inst (@{$bb->{insts}}) { 80 | print <{text} 82 | uses: @{[ join(', ', @{$inst->{uses}}) ]} 83 | defs: @{[ join(', ', @{$inst->{defs}}) ]} 84 | in: @{[ join(', ', sort keys %{$inst->{in}}) ]} 85 | out: @{[ join(', ', sort keys %{$inst->{out}}) ]} 86 | EOF 87 | } 88 | } 89 | 90 | print "\nSymtab ($suffix)\n"; 91 | my %rc_map; 92 | for my $reg (sort keys %symtab) { 93 | my $rc = $symtab{$reg}; 94 | $rc_map{$rc} = [] if ! exists $rc_map{$rc}; 95 | push @{$rc_map{$rc}}, $reg; 96 | } 97 | for my $rc (sort keys %rc_map) { 98 | print " $rc: " . join(', ', @{$rc_map{$rc}}) . "\n"; 99 | } 100 | } 101 | 102 | while(<>) { 103 | s/[\r\n]*//g; 104 | 105 | if(/^# Machine code for function/) { 106 | $in_func = 1; 107 | } elsif(/^# End machine code for function/) { 108 | $in_func = 0; 109 | last; 110 | } 111 | 112 | next if !$in_func; 113 | 114 | # 0B BB#0: derived from LLVM BB %entry 115 | s/^[0-9]+[A-Z]\t//; 116 | 117 | if(/^(BB#[0-9]+)/) { 118 | $cfg{$1} = $bb = { name => $1, insts => [] }; 119 | push @bb_list, $bb; 120 | } elsif(/^\s*(Predecessors|Successors) according to CFG:/) { 121 | my $type = $1; 122 | my @names = /(BB#[0-9]+)/g; 123 | $bb->{$type eq 'Predecessors' ? 'preds' : 'succs'} = \@names; 124 | print "Adding '$type' neighbors for $bb->{name}: @names \n" if $v > 1; 125 | } elsif (/Live Ins/) { 126 | # Skip 127 | } elsif(/^\t/) { 128 | # "%vreg0, %vreg23 = frob97_addrr %vreg1, %vreg46; GR32:%vreg0 DR32:%vreg46 129 | my $text = $_; 130 | 131 | $text =~ s/^\s+//; 132 | 133 | $text =~ /([^;]*);(.*)/; 134 | my $mnemonic = $1; 135 | my $attr = $2; 136 | 137 | # Strip non-reg attrs 138 | $attr =~ s/ mem:[^\s]+//g; 139 | $attr =~ s/ dbg:.*//; 140 | $attr =~ s/ (LD|ST)[0-9]+[^\s]+//g; 141 | $attr =~ s/^\s+|\s+$//; 142 | 143 | print "Split '$text' to\n $mnemonic\n $attr\n" if $v > 1; 144 | 145 | # Collect regclasses 146 | my %op_idx; 147 | for my $rc_regs (split(/\s+/, $attr)) { 148 | $rc_regs =~ /^([^:]+):([^:]+)/ or die "Unrecognized regclass $rc_regs in '$attr'"; 149 | print "RC info: $rc_regs\n" if $v > 1; 150 | my $rc = $1; 151 | my @regs = split(/,/, $2); 152 | for my $reg (@regs) { 153 | #! exists $op_idx{$reg} or die "Dup entry for register $reg in '$text'"; 154 | $op_idx{$reg} = 1; 155 | if(exists $symtab{$reg}) { 156 | my $old_rc = $symtab{$reg}; 157 | $old_rc eq $rc or die "Mismatched types for register $reg in '$text': $rc vs $old_rc"; 158 | } else { 159 | $symtab{$reg} = $rc; 160 | print "Setting type $rc for $reg\n" if $v > 1; 161 | } 162 | } 163 | } 164 | my $inst = { 165 | text => $mnemonic, 166 | uses => [], 167 | defs => [], 168 | }; 169 | for my $reg (sort keys %op_idx) { 170 | my @insts = ($mnemonic =~ /$reg(?::[A-Za-z0-9_]+)?(?:<[^>]+)?/g); 171 | for my $x (@insts) { 172 | $_ =~ /$reg(:[A-Za-z0-9_]+)?(<[^>]+)?/ or die; 173 | my $subreg = $1; 174 | my $attrs = defined $2 ? $2 : ''; 175 | # Subreg def is both use and def unless there is a read-undef flag 176 | if($subreg and $attrs =~ /{uses}}, $reg; 179 | print "Found implicit use of $reg ('$attrs')\n" if $v > 1; 180 | } 181 | } 182 | if($attrs =~ /{defs}}, $reg; 184 | print "Found def of $reg ('$attrs')\n" if $v > 1; 185 | } 186 | if(! $attrs or $attrs !~ /\bdef\b/) { 187 | push @{$inst->{uses}}, $reg; 188 | print "Found use of $reg ('$attrs')\n" if $v > 1; 189 | } 190 | } 191 | } 192 | push @{$bb->{insts}}, $inst; 193 | } else { 194 | print "Skipping unknown line: $_\n" if $v > 1; 195 | } 196 | } 197 | 198 | if($v) { 199 | dumpp('after parse'); 200 | print "\n"; 201 | } 202 | 203 | # Resolve CFG 204 | 205 | for my $bb (@bb_list) { 206 | for my $type ('succs', 'preds') { 207 | my @neighbors; 208 | for my $other_name (@{$bb->{$type}}) { 209 | exists $cfg{$other_name} or die "Unable to resolve $other_name in CFG (referenced in $type of $bb->{name})"; 210 | push @neighbors, $cfg{$other_name}; 211 | } 212 | $bb->{$type} = \@neighbors; 213 | } 214 | } 215 | 216 | # Compute liveness 217 | 218 | my %wl_idx; 219 | my @wl; 220 | 221 | for my $bb (@bb_list) { 222 | $bb->{in} = {}; 223 | $bb->{out} = {}; 224 | push @wl, $bb; 225 | $wl_idx{$bb->{name}} = 1; 226 | } 227 | 228 | my $iter = 0; 229 | while(@wl) { 230 | ++$iter; 231 | if($v > 2) { 232 | dumpp("dataflow iteration no. $iter"); 233 | print "\n"; 234 | } 235 | my $bb = pop @wl; # Start from exit block 236 | delete $wl_idx{$bb->{name}}; 237 | my %live = %{$bb->{out}}; 238 | for my $inst (reverse(@{$bb->{insts}})) { 239 | $inst->{out} = {%live}; 240 | # kill 241 | for my $reg_name (@{$inst->{defs}}) { 242 | delete $live{$reg_name}; 243 | } 244 | # gen 245 | for my $reg_name (@{$inst->{uses}}) { 246 | $live{$reg_name} = 1; 247 | } 248 | $inst->{in} = {%live}; 249 | } 250 | if(!cmp_hashes($bb->{in}, \%live)) { 251 | $bb->{in} = \%live; 252 | for my $pred (@{$bb->{preds}}) { 253 | for my $reg (sort keys %live) { 254 | $pred->{out}->{$reg} = 1; 255 | } 256 | next if exists $wl_idx{$pred->{name}}; 257 | push @wl, $pred; 258 | $wl_idx{$pred->{name}} = 1; 259 | print "Pushing $pred->{name} after computation of $bb->{name}\n" if $v > 1; 260 | } 261 | } 262 | } 263 | 264 | if($v) { 265 | dumpp('after liveness analysis'); 266 | print "\n"; 267 | } 268 | 269 | # Finally print fatpoints 270 | 271 | print "FATPOINTS:\n"; 272 | for my $bb (@bb_list) { 273 | print "$bb->{name}\n"; 274 | for my $inst (@{$bb->{insts}}) { 275 | print " $inst->{text}\n"; 276 | my %rc_map; 277 | for my $reg (sort keys %{$inst->{in}}) { 278 | my $rc = $symtab{$reg}; 279 | $rc_map{$rc} = [] if ! exists $rc_map{$rc}; 280 | push @{$rc_map{$rc}}, $reg; 281 | } 282 | for my $rc (sort keys %rc_map) { 283 | my $count = scalar @{$rc_map{$rc}}; 284 | my $reg_str = join(', ', @{$rc_map{$rc}}); 285 | print " $rc ($count): $reg_str\n"; 286 | } 287 | } 288 | } 289 | --------------------------------------------------------------------------------