-
Notifications
You must be signed in to change notification settings - Fork 0
/
peephole.sh
executable file
·143 lines (131 loc) · 3.14 KB
/
peephole.sh
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/bin/bash
if [[ "$#" -ne 1 ]]
then
echo 'Usage: peephole <file>'
exit 1
fi
export LC_ALL=C
# see: https://stackoverflow.com/a/8574392
containsElement () {
local e match="$1"
shift
for e; do [[ "$e" == "$match" ]] && return 0; done
return 1
}
# all commonly used regexes
regexlabel='L[0-9A-Z_]+'
regexcomment='^;.*$'
regexlabelcolon='^'$regexlabel':$'
regexgoto='^[[:space:]]+goto[[:space:]]+'$regexlabel'$'
regexjump='^[[:space:]]+(goto|(if[_a-z]+))[[:space:]]+'$regexlabel'$'
# read file into lines
lines=()
while IFS='' read -r line || [[ -n "$line" ]]; do
lines+=("$line")
done < <(grep -v $regexcomment "$1")
# L1:
# (and no instructions use L1)
# -->
# (empty)
removeDeadLabels() {
newLines=()
usedLabels=()
for line in "${lines[@]}"; do
if [[ "$line" =~ $regexjump ]]; then
label=$([[ "$line" =~ $regexlabel ]] && echo ${BASH_REMATCH[0]})
usedLabels+=("$label")
fi
done
for line in "${lines[@]}"; do
if [[ "$line" =~ $regexlabelcolon ]]; then
label=$([[ "$line" =~ $regexlabel ]] && echo ${BASH_REMATCH[0]})
if containsElement "$label" "${usedLabels[@]}"; then
newLines+=("$line")
fi
else
newLines+=("$line")
fi
done
lines=("${newLines[@]}")
}
# goto L1
# (code)
# L2:
# -->
# goto L1
# L2:
removeDeadCode() {
newLines=()
deadCode=false
for line in "${lines[@]}"; do
if [[ ( "$line" =~ $regexlabelcolon ) || ( "$deadCode" == false ) ]]; then
newLines+=("$line")
fi
if [[ "$line" =~ $regexlabelcolon ]]; then
deadCode=false
elif [[ "$line" =~ $regexgoto ]]; then
deadCode=true
fi
done
lines=("${newLines[@]}")
}
# goto/if* L1
# L1:
# -->
# L1:
removeRedundantJumps() {
newLines=()
lastLine=""
for line in "${lines[@]}"; do
if [[ "$line" =~ $regexlabelcolon ]]; then
if [[ "$lastLine" != "" ]]; then
labelLastLine=$([[ "$lastLine" =~ $regexlabel ]] && echo ${BASH_REMATCH[0]})
labelLine=$([[ "$line" =~ $regexlabel ]] && echo ${BASH_REMATCH[0]})
if [[ "$labelLastLine" != "$labelLine" ]]; then
newLines+=("$lastLine")
fi
fi
newLines+=("$line")
lastLine=""
else
if [[ "$lastLine" != "" ]]; then
newLines+=("$lastLine")
fi
if [[ "$line" =~ $regexjump ]]; then
lastLine="$line"
else
newLines+=("$line")
lastLine=""
fi
fi
done
lines=("${newLines[@]}")
}
# L1:
# L2:
# -->
# L1:
# (and all instructions using L2 use L1 now)
removeRedundantLabels() {
: # TODO implement
}
# goto L1
# L2:
# goto L3
# -->
# goto L1
# (and all instructions using L2 use L3 now)
removeRedundantIndirections() {
: # TODO implement
}
# apply all optimizations
# TODO in what order?
removeDeadLabels
removeDeadCode
removeDeadLabels
removeRedundantJumps
removeDeadLabels
# print lines
for line in "${lines[@]}"; do
echo "$line"
done