├── .gitignore ├── Makefile ├── LICENSE ├── README.md ├── O.h ├── O.1 ├── O.c ├── arm64.c └── x64.c /.gitignore: -------------------------------------------------------------------------------- 1 | O 2 | *.o 3 | *.core 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # O Makefile 2 | 3 | PREFIX ?= /usr/local 4 | 5 | CC ?= cc 6 | CFLAGS = -g -O2 -DTARGET=${TARGET} 7 | 8 | PROG = O 9 | OBJS = O.o arm64.o x64.o 10 | 11 | TARGET = "\"`${CC} -dumpmachine | cut -d '-' -f 1`\"" 12 | 13 | all: ${OBJS} 14 | ${CC} ${LDFLAGS} -o ${PROG} ${OBJS} 15 | 16 | install: 17 | install -c -S -s -m 755 ${PROG} ${PREFIX}/bin 18 | 19 | clean: 20 | rm -f ${PROG} ${OBJS} ${PROG}.core 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022, 2025 Brian Callahan 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | O 2 | = 3 | `O` is a peephole optimizer for 4 | [qbe](https://c9x.me/compile/). 5 | 6 | Why? 7 | ---- 8 | `O` is the subject of two 9 | [blog](https://briancallahan.net/blog/20220330.html) 10 | [posts](https://briancallahan.net/blog/20220402.html). 11 | 12 | Building 13 | -------- 14 | Just run `make`. 15 | 16 | The only non-C89 functions are 17 | [`getline(3)`](https://man.openbsd.org/getline.3) 18 | and 19 | [`snprintf(3)`](https://man.openbsd.org/snprintf.3). 20 | 21 | Running 22 | ------- 23 | ``` 24 | usage: O [-t target] [-o out.s] [in.s] 25 | ``` 26 | 27 | Output is listed on `stdout` unless `-o` is passed on the command line. 28 | 29 | You can pass this to an assembler using something like: 30 | ``` 31 | $ O file.s | as -o file.o - 32 | ``` 33 | 34 | `O` can read from `stdin` if invoked as `O -`. 35 | 36 | License 37 | ------- 38 | ISC License. See `LICENSE` for more information. 39 | -------------------------------------------------------------------------------- /O.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, 2025 Brian Callahan 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #define T_ARM64 0 18 | #define T_X64 1 19 | 20 | struct peephole { 21 | char *line1; 22 | char *line2; 23 | char *line3; 24 | }; 25 | 26 | extern void arm64(FILE *); 27 | extern int fillwindow(struct peephole *, FILE *); 28 | extern void shiftwindow(struct peephole *); 29 | extern void x64(FILE *); 30 | extern char *xstrdup(const char *); 31 | -------------------------------------------------------------------------------- /O.1: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" O - peephole optimizer 3 | .\" 4 | .\" Copyright (c) 2025 Brian Callahan 5 | .\" 6 | .\" Permission to use, copy, modify, and distribute this software for any 7 | .\" purpose with or without fee is hereby granted, provided that the above 8 | .\" copyright notice and this permission notice appear in all copies. 9 | .\" 10 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | .\" 18 | .Dd September 1, 2025 19 | .Dt O 1 20 | .Os 21 | .Sh NAME 22 | .Nm O 23 | .Nd peephole optimizer 24 | .Sh SYNOPSIS 25 | .Nm 26 | .Op Fl t Ar target 27 | .Op Fl o Ar out.s 28 | .Op Ar in.s 29 | .Sh DESCRIPTION 30 | .Nm 31 | is a utility that optimizes assembly language files. 32 | By default, 33 | .Nm 34 | reads from 35 | .Ar stdin 36 | and outputs to 37 | .Ar stdout . 38 | .Pp 39 | The options are as follows: 40 | .Bl -tag -width Ds 41 | .It Fl t Ar target 42 | Select CPU architecture 43 | .Ar target . 44 | Acceptable values for 45 | .Ar target 46 | are 47 | .Sy arm64 48 | and 49 | .Sy x64 . 50 | The default value of 51 | .Ar target 52 | is selected to match the build machine at build-time. 53 | .It Fl o Ar out.s 54 | Write output to 55 | .Ar out.s 56 | instead of 57 | .Ar stdout . 58 | .El 59 | .Sh EXIT STATUS 60 | The 61 | .Nm 62 | utility exits 0 on success, and >0 if an error occurs. 63 | .Sh EXAMPLES 64 | Optimize an assembly language file and write to 65 | .Sy stdout . 66 | .Pp 67 | .Dl O in.s 68 | .Pp 69 | Use 70 | .Nm 71 | as part of a larger compilation pipeline. 72 | .Pp 73 | .Dl qbe file.ssa | O | as -o file.o - 74 | .Sh AUTHORS 75 | .Nm 76 | was written by 77 | .An Brian Callahan Aq Mt bcallah@openbsd.org . 78 | -------------------------------------------------------------------------------- /O.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, 2025 Brian Callahan 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "O.h" 22 | 23 | char * 24 | xstrdup(const char *s) 25 | { 26 | char *p; 27 | 28 | if (s == NULL) 29 | return NULL; 30 | 31 | if ((p = strdup(s)) == NULL) { 32 | (void) fputs("O: error: xstrdup failed\n", stderr); 33 | 34 | exit(1); 35 | } 36 | 37 | return p; 38 | } 39 | 40 | int 41 | fillwindow(struct peephole *window, FILE *fp) 42 | { 43 | size_t size = 0; 44 | 45 | if (window->line1 == NULL) { 46 | if (getline(&window->line1, &size, fp) == -1) 47 | return 0; 48 | } 49 | 50 | if (window->line2 == NULL) { 51 | if (getline(&window->line2, &size, fp) == -1) 52 | return 0; 53 | } 54 | 55 | if (getline(&window->line3, &size, fp) == -1) 56 | return 0; 57 | 58 | return 1; 59 | } 60 | 61 | void 62 | shiftwindow(struct peephole *window) 63 | { 64 | 65 | free(window->line1); 66 | window->line1 = xstrdup(window->line2); 67 | 68 | free(window->line2); 69 | window->line2 = xstrdup(window->line3); 70 | } 71 | 72 | int 73 | main(int argc, char *argv[]) 74 | { 75 | FILE *fp; 76 | const char *input = NULL, *target = TARGET; 77 | int arch, i, in = 0, out = 0; 78 | 79 | for (i = 1; i < argc; ++i) { 80 | if (strcmp(argv[i], "-o") == 0) { 81 | if (argv[++i] == NULL) { 82 | usage: 83 | (void) fputs("usage: O [-o out.s] [in.s]\n", 84 | stderr); 85 | return 1; 86 | } 87 | if (out++) 88 | goto usage; 89 | if (freopen(argv[i], "w+", stdout) == NULL) { 90 | (void) fprintf(stderr, 91 | "O: error: couldn't open %s\n", argv[i]); 92 | } 93 | } else if (strcmp(argv[i], "-t") == 0) { 94 | if (argv[++i] == NULL) 95 | goto usage; 96 | target = argv[i]; 97 | } else { 98 | if (in++) 99 | goto usage; 100 | if (strcmp(argv[i], "-") != 0) 101 | input = argv[i]; 102 | } 103 | } 104 | 105 | if (strcmp(target, "arm64") == 0 || strcmp(target, "aarch64") == 0) { 106 | arch = T_ARM64; 107 | } else if (strcmp(target, "x86_64") == 0 || 108 | strcmp(target, "amd64") == 0 || strcmp(target, "x64") == 0) { 109 | arch = T_X64; 110 | } else { 111 | (void) fputs("O: error: values for -t are `arm64' and `x64'\n", 112 | stderr); 113 | return 1; 114 | } 115 | 116 | if (input == NULL) { 117 | if (arch == T_X64) 118 | x64(stdin); 119 | else if (arch == T_ARM64) 120 | arm64(stdin); 121 | 122 | return 0; 123 | } 124 | 125 | if ((fp = fopen(input, "r")) == NULL) { 126 | (void) fprintf(stderr, "O: error: couldn't open %s\n", input); 127 | return 1; 128 | } 129 | 130 | if (arch == T_ARM64) 131 | arm64(fp); 132 | else if (arch == T_X64) 133 | x64(fp); 134 | 135 | (void) fclose(fp); 136 | 137 | return 0; 138 | } 139 | -------------------------------------------------------------------------------- /arm64.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, 2025 Brian Callahan 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "O.h" 22 | 23 | static void 24 | add(struct peephole *window) 25 | { 26 | int c, i = 0, j = 0, r; 27 | char imm[3], r1[4]; 28 | 29 | if (window->line1 == NULL) 30 | return; 31 | 32 | if (strncmp("\tadd\tx", window->line1, 6) == 0) { 33 | c = window->line1[i++]; 34 | while (c != ',') { 35 | if (c == '\n' || c == '\0') 36 | return; 37 | c = window->line1[i++]; 38 | } 39 | ++i; 40 | c = window->line1[i++]; 41 | while (c != ',') { 42 | if (j == 3) 43 | return; 44 | r1[j++] = c; 45 | c = window->line1[i++]; 46 | } 47 | r = i; 48 | ++i; 49 | 50 | (void) memset(imm, 0, 3); 51 | 52 | j = 0; 53 | c = window->line1[i++]; 54 | while (c != '\n') { 55 | if (j == 2) 56 | return; 57 | imm[j++] = c; 58 | c = window->line1[i++]; 59 | } 60 | if (strcmp(imm, "#0") == 0) { 61 | window->line1[1] = 'm'; 62 | window->line1[2] = 'o'; 63 | window->line1[3] = 'v'; 64 | window->line1[r - 1] = '\n'; 65 | window->line1[r] = '\0'; 66 | } 67 | } 68 | } 69 | 70 | static void 71 | one(struct peephole *window) 72 | { 73 | 74 | add(window); 75 | } 76 | 77 | static int 78 | merge_immediate(struct peephole *window) 79 | { 80 | int c, i = 0, j = 0, r; 81 | char buf[64], imm[6], r1[4], r2[4], r3[4], tmp[32]; 82 | 83 | if (window->line1 == NULL || window->line2 == NULL) 84 | return 0; 85 | 86 | if (strncmp("\tmov\tw", window->line1, 6) != 0 && 87 | strncmp("\tmov\tx", window->line1, 6) != 0) { 88 | return 0; 89 | } 90 | 91 | if (strncmp("\tlsl\t", window->line2, 5) != 0 && 92 | strncmp("\tadd\t", window->line2, 5) != 0 && 93 | strncmp("\tsub\t", window->line2, 5) != 0) { 94 | return 0; 95 | } 96 | 97 | (void) memset(r1, 0, 4); 98 | 99 | c = window->line1[i + 5]; 100 | 101 | while (c != ',') { 102 | if (i == 3) 103 | return 0; 104 | r1[i++] = c; 105 | c = window->line1[i + 5]; 106 | } 107 | 108 | while (c == ',' || c == ' ') { 109 | c = window->line1[i + 5]; 110 | if (c == '\n') 111 | return 0; 112 | ++i; 113 | } 114 | 115 | (void) memset(imm, 0, 6); 116 | 117 | while (c != '\n') { 118 | if (j == 5) 119 | return 0; 120 | imm[j++] = c; 121 | c = window->line1[i + 5]; 122 | ++i; 123 | } 124 | 125 | if (imm[0] != '#') 126 | return 0; 127 | 128 | i = atoi(imm + 1); 129 | if (i < 0 || i > 4095) 130 | return 0; 131 | if (strncmp("\tlsl\t", window->line2, 5) == 0) { 132 | if (i > 63) 133 | return 0; 134 | } 135 | 136 | i = 0; 137 | c = window->line2[i + 5]; 138 | j = 0; 139 | 140 | (void) memset(r2, 0, 4); 141 | 142 | while (c != ',') { 143 | if (j == 3) 144 | return 0; 145 | r2[j++] = c; 146 | ++i; 147 | c = window->line2[i + 5]; 148 | } 149 | 150 | ++i; 151 | c = window->line2[i + 5]; 152 | 153 | while (c != ',') { 154 | ++i; 155 | c = window->line2[i + 5]; 156 | } 157 | 158 | r = i + 7; 159 | 160 | i += 2; 161 | c = window->line2[i + 5]; 162 | 163 | j = 0; 164 | (void) memset(r3, 0, 4); 165 | 166 | while (c != '\n') { 167 | if (j == 3) 168 | return 0; 169 | r3[j++] = c; 170 | ++i; 171 | c = window->line2[i + 5]; 172 | } 173 | 174 | r1[0] = 'x'; 175 | 176 | if (strcmp(r1, r2) == 0 && strcmp(r1, r3) == 0) { 177 | (void) memset(tmp, 0, 32); 178 | for (i = 0; i < r; ++i) { 179 | if (i == 31) 180 | return 0; 181 | tmp[i] = window->line2[i]; 182 | } 183 | (void) snprintf(buf, 64, "%s%s\n", tmp, imm); 184 | 185 | free(window->line1); 186 | window->line1 = xstrdup(buf); 187 | 188 | free(window->line2); 189 | window->line2 = xstrdup(window->line3); 190 | 191 | free(window->line3); 192 | window->line3 = NULL; 193 | 194 | return 1; 195 | } 196 | 197 | return 0; 198 | } 199 | 200 | static int 201 | mov(struct peephole *window) 202 | { 203 | int c, i = 0, j = 0; 204 | char r1[4], r2[4], r3[4], r4[4]; 205 | 206 | if (window->line1 == NULL || window->line2 == NULL) 207 | return 0; 208 | 209 | if (strncmp("\tmov\tx", window->line1, 6) != 0 || 210 | strncmp("\tmov\tx", window->line2, 6) != 0) { 211 | return 0; 212 | } 213 | 214 | (void) memset(r1, 0, 4); 215 | 216 | c = window->line1[i + 5]; 217 | 218 | while (c != ',') { 219 | if (i == 3) 220 | return 0; 221 | r1[i++] = c; 222 | c = window->line1[i + 5]; 223 | } 224 | 225 | while (c == ',' || c == ' ') { 226 | c = window->line1[i + 5]; 227 | if (c == '\n') 228 | return 0; 229 | ++i; 230 | } 231 | 232 | (void) memset(r2, 0, 4); 233 | 234 | while (c != '\n') { 235 | if (j == 3) 236 | return 0; 237 | r2[j++] = c; 238 | c = window->line1[i + 5]; 239 | ++i; 240 | } 241 | 242 | i = 0; 243 | (void) memset(r3, 0, 4); 244 | 245 | c = window->line2[i + 5]; 246 | 247 | while (c != ',') { 248 | if (i == 3) 249 | return 0; 250 | r3[i++] = c; 251 | c = window->line2[i + 5]; 252 | } 253 | 254 | while (c == ',' || c == ' ') { 255 | c = window->line2[i + 5]; 256 | if (c == '\n') 257 | return 0; 258 | ++i; 259 | } 260 | 261 | j = 0; 262 | (void) memset(r4, 0, 4); 263 | 264 | while (c != '\n') { 265 | if (j == 3) 266 | return 0; 267 | r4[j++] = c; 268 | c = window->line2[i + 5]; 269 | ++i; 270 | } 271 | 272 | if (r1[0] != 'x' || r2[0] != 'x' || r3[0] != 'x' || r4[0] != 'x') 273 | return 0; 274 | 275 | if (strcmp(r1, r4) != 0 || strcmp(r2, r3) != 0) 276 | return 0; 277 | 278 | free(window->line2); 279 | window->line2 = xstrdup(window->line3); 280 | 281 | free(window->line3); 282 | window->line3 = NULL; 283 | 284 | return 1; 285 | } 286 | 287 | static int 288 | two(struct peephole *window) 289 | { 290 | 291 | if (merge_immediate(window)) 292 | return 1; 293 | 294 | return mov(window); 295 | } 296 | 297 | static int 298 | three(struct peephole *window) 299 | { 300 | 301 | return 0; 302 | } 303 | 304 | void 305 | arm64(FILE *fp) 306 | { 307 | struct peephole window; 308 | int ret; 309 | 310 | window.line1 = NULL; 311 | window.line2 = NULL; 312 | window.line3 = NULL; 313 | 314 | while (fillwindow(&window, fp)) { 315 | again: 316 | ret = three(&window); 317 | if (ret == 0) 318 | ret = two(&window); 319 | one(&window); 320 | 321 | if (ret == 1) { 322 | if (fillwindow(&window, fp)) 323 | goto again; 324 | } 325 | 326 | if (window.line1 != NULL) 327 | (void) fputs(window.line1, stdout); 328 | 329 | shiftwindow(&window); 330 | } 331 | 332 | free(window.line3); 333 | window.line3 = NULL; 334 | 335 | while (window.line1 != NULL) { 336 | one(&window); 337 | 338 | if (window.line1 != NULL) 339 | (void) fputs(window.line1, stdout); 340 | 341 | shiftwindow(&window); 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /x64.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, 2025 Brian Callahan 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "O.h" 22 | 23 | static int 24 | xorq(struct peephole *window) 25 | { 26 | char buf[32], r1a, r1b; 27 | 28 | if (window->line1 == NULL) 29 | return 0; 30 | 31 | if (!strncmp("\tmovq $0, %r", window->line1, 12)) { 32 | if (strlen(window->line1) < 14) 33 | return 0; 34 | 35 | r1a = window->line1[12]; 36 | r1b = window->line1[13]; 37 | 38 | if (r1b == '\n') { 39 | (void) snprintf(buf, sizeof(buf), 40 | "\txorl %%r%cd, %%r%cd\n", r1a, r1a); 41 | } else if (r1a == '1') { 42 | (void) snprintf(buf, sizeof(buf), 43 | "\txorl %%r%c%cd, %%r%c%cd\n", r1a, r1b, r1a, r1b); 44 | } else { 45 | (void) snprintf(buf, sizeof(buf), 46 | "\txorl %%e%c%c, %%e%c%c\n", r1a, r1b, r1a, r1b); 47 | } 48 | 49 | free(window->line1); 50 | window->line1 = xstrdup(buf); 51 | 52 | return 1; 53 | } 54 | 55 | return 0; 56 | } 57 | 58 | static int 59 | xorl(struct peephole *window) 60 | { 61 | char buf[32], e1a, e1b; 62 | 63 | if (window->line1 == NULL) 64 | return 0; 65 | 66 | if (!strncmp("\tmovl $0, %e", window->line1, 12)) { 67 | if (strlen(window->line1) != 15) 68 | return 0; 69 | 70 | e1a = window->line1[12]; 71 | e1b = window->line1[13]; 72 | 73 | (void) snprintf(buf, sizeof(buf), "\txorl %%e%c%c, %%e%c%c\n", 74 | e1a, e1b, e1a, e1b); 75 | 76 | free(window->line1); 77 | window->line1 = xstrdup(buf); 78 | 79 | return 1; 80 | } else if (!strncmp("\tmovl $0, %r", window->line1, 12)) { 81 | if (strlen(window->line1) < 14) 82 | return 0; 83 | 84 | e1a = window->line1[12]; 85 | e1b = window->line1[13]; 86 | 87 | if (e1b == 'd') { 88 | (void) snprintf(buf, sizeof(buf), 89 | "\txorl %%r%cd, %%r%cd\n", e1a, e1a); 90 | } else { 91 | (void) snprintf(buf, sizeof(buf), 92 | "\txorl %%r%c%cd, %%r%c%cd\n", e1a, e1b, e1a, e1b); 93 | } 94 | 95 | free(window->line1); 96 | window->line1 = xstrdup(buf); 97 | 98 | return 1; 99 | } 100 | 101 | return 0; 102 | } 103 | 104 | static int 105 | incq(struct peephole *window) 106 | { 107 | char buf[32], r1a, r1b; 108 | 109 | if (window->line1 == NULL) 110 | return 0; 111 | 112 | if (!strncmp("\taddq $1, %r", window->line1, 12)) { 113 | if (strlen(window->line1) < 14) 114 | return 0; 115 | 116 | r1a = window->line1[12]; 117 | r1b = window->line1[13]; 118 | 119 | if (r1b == '\n') { 120 | (void) snprintf(buf, sizeof(buf), "\tincq %%r%c\n", 121 | r1a); 122 | } else { 123 | (void) snprintf(buf, sizeof(buf), "\tincq %%r%c%c\n", 124 | r1a, r1b); 125 | } 126 | 127 | free(window->line1); 128 | window->line1 = xstrdup(buf); 129 | 130 | return 1; 131 | } 132 | 133 | return 0; 134 | } 135 | 136 | static int 137 | incl(struct peephole *window) 138 | { 139 | char buf[32], e1a, e1b; 140 | 141 | if (window->line1 == NULL) 142 | return 0; 143 | 144 | if (!strncmp("\taddl $1, %e", window->line1, 12)) { 145 | if (strlen(window->line1) != 15) 146 | return 0; 147 | 148 | e1a = window->line1[12]; 149 | e1b = window->line1[13]; 150 | 151 | (void) snprintf(buf, sizeof(buf), "\tincl %%e%c%c\n", e1a, 152 | e1b); 153 | 154 | free(window->line1); 155 | window->line1 = xstrdup(buf); 156 | 157 | return 1; 158 | } else if (!strncmp("\taddl $1, %r", window->line1, 12)) { 159 | if (strlen(window->line1) < 14) 160 | return 0; 161 | 162 | e1a = window->line1[12]; 163 | e1b = window->line1[13]; 164 | 165 | if (e1b == 'd') { 166 | (void) snprintf(buf, sizeof(buf), "\tincl %%r%cd\n", 167 | e1a); 168 | } else { 169 | (void) snprintf(buf, sizeof(buf), "\tincl %%r%c%cd\n", 170 | e1a, e1b); 171 | } 172 | 173 | free(window->line1); 174 | window->line1 = xstrdup(buf); 175 | 176 | return 1; 177 | } 178 | 179 | return 0; 180 | } 181 | 182 | static int 183 | decq(struct peephole *window) 184 | { 185 | char buf[32], r1a, r1b; 186 | 187 | if (window->line1 == NULL) 188 | return 0; 189 | 190 | if (!strncmp("\tsubq $1, %r", window->line1, 12)) { 191 | if (strlen(window->line1) < 14) 192 | return 0; 193 | 194 | r1a = window->line1[12]; 195 | r1b = window->line1[13]; 196 | 197 | if (r1b == '\n') { 198 | (void) snprintf(buf, sizeof(buf), "\tdecq %%r%c\n", 199 | r1a); 200 | } else { 201 | (void) snprintf(buf, sizeof(buf), "\tdecq %%r%c%c\n", 202 | r1a, r1b); 203 | } 204 | 205 | free(window->line1); 206 | window->line1 = xstrdup(buf); 207 | 208 | return 1; 209 | } 210 | 211 | return 0; 212 | } 213 | 214 | static int 215 | decl(struct peephole *window) 216 | { 217 | char buf[32], e1a, e1b; 218 | 219 | if (window->line1 == NULL) 220 | return 0; 221 | 222 | if (!strncmp("\tsubl $1, %e", window->line1, 12)) { 223 | if (strlen(window->line1) != 15) 224 | return 0; 225 | 226 | e1a = window->line1[12]; 227 | e1b = window->line1[13]; 228 | 229 | (void) snprintf(buf, sizeof(buf), "\tdecl %%e%c%c\n", e1a, 230 | e1b); 231 | 232 | free(window->line1); 233 | window->line1 = xstrdup(buf); 234 | 235 | return 1; 236 | } else if (!strncmp("\tsubl $1, %r", window->line1, 12)) { 237 | if (strlen(window->line1) < 14) 238 | return 0; 239 | 240 | e1a = window->line1[12]; 241 | e1b = window->line1[13]; 242 | 243 | if (e1b == 'd') { 244 | (void) snprintf(buf, sizeof(buf), "\tdecl %%r%cd\n", 245 | e1a); 246 | } else { 247 | (void) snprintf(buf, sizeof(buf), "\tdecl %%r%c%cd\n", 248 | e1a, e1b); 249 | } 250 | 251 | free(window->line1); 252 | window->line1 = xstrdup(buf); 253 | 254 | return 1; 255 | } 256 | 257 | return 0; 258 | } 259 | 260 | static int 261 | imulq(struct peephole *window) 262 | { 263 | char buf[32], r1a, r1b, r1c; 264 | 265 | if (window->line1 == NULL) 266 | return 0; 267 | 268 | if (!strncmp("\timulq $", window->line1, 8)) { 269 | if (strlen(window->line1) < 19) 270 | return 0; 271 | 272 | r1a = window->line1[strlen(window->line1) - 10]; 273 | r1b = window->line1[strlen(window->line1) - 9]; 274 | r1c = window->line1[strlen(window->line1) - 8]; 275 | 276 | if (r1a == ' ' && r1b == '%' && r1c == 'r') { 277 | r1a = r1c; 278 | r1b = window->line1[strlen(window->line1) - 7]; 279 | 280 | if (window->line1[strlen(window->line1) - 3] != r1a || 281 | window->line1[strlen(window->line1) - 2] != r1b) { 282 | return 0; 283 | } 284 | 285 | r1c = ' '; 286 | } else if (window->line1[strlen(window->line1) - 4] != r1a || 287 | window->line1[strlen(window->line1) - 3] != r1b || 288 | window->line1[strlen(window->line1) - 2] != r1c) { 289 | return 0; 290 | } 291 | 292 | switch (window->line1[8]) { 293 | case '-': 294 | if (window->line1[9] == '1' && 295 | window->line1[10] == ',') { 296 | (void) snprintf(buf, sizeof(buf), 297 | "\tnegq %%%c%c%c\n", r1a, r1b, r1c); 298 | } else { 299 | return 0; 300 | } 301 | 302 | goto success; 303 | case '0': 304 | if (window->line1[9] == ',') { 305 | (void) snprintf(buf, sizeof(buf), 306 | "\txorq %%%c%c%c, %%%c%c%c\n", r1a, r1b, 307 | r1c, r1a, r1b, r1c); 308 | } else { 309 | return 0; 310 | } 311 | 312 | goto success; 313 | case '1': 314 | if (window->line1[9] == ',') { 315 | free(window->line1); 316 | window->line1 = NULL; 317 | 318 | return 1; 319 | } else if (window->line1[9] == '6' && 320 | window->line1[10] == ',') { 321 | (void) snprintf(buf, sizeof(buf), 322 | "\tsalq $4, %%%c%c%c\n", r1a, r1b, r1c); 323 | } else if (window->line1[9] == '2' && 324 | window->line1[10] == '8' && 325 | window->line1[11] == ',') { 326 | (void) snprintf(buf, sizeof(buf), 327 | "\tsalq $7, %%%c%c%c\n", r1a, r1b, r1c); 328 | } else if (window->line1[9] == '0' && 329 | window->line1[10] == '2' && 330 | window->line1[11] == '4' && 331 | window->line1[12] == ',') { 332 | (void) snprintf(buf, sizeof(buf), 333 | "\tsalq $10, %%%c%c%c\n", r1a, r1b, r1c); 334 | } else if (window->line1[9] == '6' && 335 | window->line1[10] == '3' && 336 | window->line1[11] == '8' && 337 | window->line1[12] == '4' && 338 | window->line1[13] == ',') { 339 | (void) snprintf(buf, sizeof(buf), 340 | "\tsalq $14, %%%c%c%c\n", r1a, r1b, r1c); 341 | } else { 342 | return 0; 343 | } 344 | 345 | goto success; 346 | case '2': 347 | if (window->line1[9] == ',') { 348 | (void) snprintf(buf, sizeof(buf), 349 | "\tsalq %%%c%c%c\n", r1a, r1b, r1c); 350 | } else if (window->line1[9] == '5' && 351 | window->line1[10] == '6' && 352 | window->line1[11] == ',') { 353 | (void) snprintf(buf, sizeof(buf), 354 | "\tsalq $8, %%%c%c%c\n", r1a, r1b, r1c); 355 | } else if (window->line1[9] == '0' && 356 | window->line1[10] == '4' && 357 | window->line1[11] == '8' && 358 | window->line1[12] == ',') { 359 | (void) snprintf(buf, sizeof(buf), 360 | "\tsalq $11, %%%c%c%c\n", r1a, r1b, r1c); 361 | } else { 362 | return 0; 363 | } 364 | 365 | goto success; 366 | case '3': 367 | if (window->line1[9] == '2' && 368 | window->line1[10] == ',') { 369 | (void) snprintf(buf, sizeof(buf), 370 | "\tsalq $5, %%%c%c%c\n", r1a, r1b, r1c); 371 | } else if (window->line1[9] == '2' && 372 | window->line1[10] == '7' && 373 | window->line1[11] == '6' && 374 | window->line1[12] == '8' && 375 | window->line1[13] == ',') { 376 | (void) snprintf(buf, sizeof(buf), 377 | "\tsalq $15, %%%c%c%c\n", r1a, r1b, r1c); 378 | } else { 379 | return 0; 380 | } 381 | 382 | goto success; 383 | case '4': 384 | if (window->line1[9] == ',') { 385 | (void) snprintf(buf, sizeof(buf), 386 | "\tsalq $2, %%%c%c%c\n", r1a, r1b, r1c); 387 | } else if (window->line1[9] == '0' && 388 | window->line1[10] == '9' && 389 | window->line1[11] == '6' && 390 | window->line1[12] == ',') { 391 | (void) snprintf(buf, sizeof(buf), 392 | "\tsalq $12, %%%c%c%c\n", r1a, r1b, r1c); 393 | } else { 394 | return 0; 395 | } 396 | 397 | goto success; 398 | case '5': 399 | if (window->line1[9] == '1' && 400 | window->line1[10] == '2' && 401 | window->line1[11] == ',') { 402 | (void) snprintf(buf, sizeof(buf), 403 | "\tsalq $9, %%%c%c%c\n", r1a, r1b, r1c); 404 | } else { 405 | return 0; 406 | } 407 | 408 | goto success; 409 | case '6': 410 | if (window->line1[9] == '4' && 411 | window->line1[10] == ',') { 412 | (void) snprintf(buf, sizeof(buf), 413 | "\tsalq $6, %%%c%c%c\n", r1a, r1b, r1c); 414 | } else if (window->line1[9] == '5' && 415 | window->line1[10] == '5' && 416 | window->line1[11] == '3' && 417 | window->line1[12] == '6' && 418 | window->line1[13] == ',') { 419 | (void) snprintf(buf, sizeof(buf), 420 | "\tsalq $16, %%%c%c%c\n", r1a, r1b, r1c); 421 | } else { 422 | return 0; 423 | } 424 | 425 | goto success; 426 | case '8': 427 | if (window->line1[9] == ',') { 428 | (void) snprintf(buf, sizeof(buf), 429 | "\tsalq $3, %%%c%c%c\n", r1a, r1b, r1c); 430 | } else if (window->line1[9] == '1' && 431 | window->line1[10] == '9' && 432 | window->line1[11] == '2' && 433 | window->line1[12] == ',') { 434 | (void) snprintf(buf, sizeof(buf), 435 | "\tsalq $13, %%%c%c%c\n", r1a, r1b, r1c); 436 | } else { 437 | return 0; 438 | } 439 | 440 | goto success; 441 | default: 442 | return 0; 443 | } 444 | } 445 | 446 | return 0; 447 | 448 | success: 449 | free(window->line1); 450 | window->line1 = xstrdup(buf); 451 | 452 | return 1; 453 | } 454 | 455 | static int 456 | imull(struct peephole *window) 457 | { 458 | char buf[32], e1a, e1b, e1c; 459 | 460 | if (window->line1 == NULL) 461 | return 0; 462 | 463 | if (!strncmp("\timull $", window->line1, 8)) { 464 | if (strlen(window->line1) < 21) 465 | return 0; 466 | 467 | e1a = window->line1[strlen(window->line1) - 10]; 468 | e1b = window->line1[strlen(window->line1) - 9]; 469 | e1c = window->line1[strlen(window->line1) - 8]; 470 | 471 | if (window->line1[strlen(window->line1) - 4] != e1a || 472 | window->line1[strlen(window->line1) - 3] != e1b || 473 | window->line1[strlen(window->line1) - 2] != e1c) { 474 | return 0; 475 | } 476 | 477 | switch (window->line1[8]) { 478 | case '-': 479 | if (window->line1[9] == '1' && 480 | window->line1[10] == ',') { 481 | (void) snprintf(buf, sizeof(buf), 482 | "\tnegl %%%c%c%c\n", e1a, e1b, e1c); 483 | } else { 484 | return 0; 485 | } 486 | 487 | goto success; 488 | case '0': 489 | if (window->line1[9] == ',') { 490 | (void) snprintf(buf, sizeof(buf), 491 | "\txorl %%%c%c%c, %%%c%c%c\n", e1a, e1b, 492 | e1c, e1a, e1b, e1c); 493 | } else { 494 | return 0; 495 | } 496 | 497 | goto success; 498 | case '1': 499 | if (window->line1[9] == ',') { 500 | free(window->line1); 501 | window->line1 = NULL; 502 | 503 | return 1; 504 | } else if (window->line1[9] == '6' && 505 | window->line1[10] == ',') { 506 | (void) snprintf(buf, sizeof(buf), 507 | "\tsall $4, %%%c%c%c\n", e1a, e1b, e1c); 508 | } else if (window->line1[9] == '2' && 509 | window->line1[10] == '8' && 510 | window->line1[11] == ',') { 511 | (void) snprintf(buf, sizeof(buf), 512 | "\tsall $7, %%%c%c%c\n", e1a, e1b, e1c); 513 | } else if (window->line1[9] == '0' && 514 | window->line1[10] == '2' && 515 | window->line1[11] == '4' && 516 | window->line1[12] == ',') { 517 | (void) snprintf(buf, sizeof(buf), 518 | "\tsall $10, %%%c%c%c\n", e1a, e1b, e1c); 519 | } else if (window->line1[9] == '6' && 520 | window->line1[10] == '3' && 521 | window->line1[11] == '8' && 522 | window->line1[12] == '4' && 523 | window->line1[13] == ',') { 524 | (void) snprintf(buf, sizeof(buf), 525 | "\tsall $14, %%%c%c%c\n", e1a, e1b, e1c); 526 | } else { 527 | return 0; 528 | } 529 | 530 | goto success; 531 | case '2': 532 | if (window->line1[9] == ',') { 533 | (void) snprintf(buf, sizeof(buf), 534 | "\tsall %%%c%c%c\n", e1a, e1b, e1c); 535 | } else if (window->line1[9] == '5' && 536 | window->line1[10] == '6' && 537 | window->line1[11] == ',') { 538 | (void) snprintf(buf, sizeof(buf), 539 | "\tsall $8, %%%c%c%c\n", e1a, e1b, e1c); 540 | } else if (window->line1[9] == '0' && 541 | window->line1[10] == '4' && 542 | window->line1[11] == '8' && 543 | window->line1[12] == ',') { 544 | (void) snprintf(buf, sizeof(buf), 545 | "\tsall $11, %%%c%c%c\n", e1a, e1b, e1c); 546 | } else { 547 | return 0; 548 | } 549 | 550 | goto success; 551 | case '3': 552 | if (window->line1[9] == '2' && 553 | window->line1[10] == ',') { 554 | (void) snprintf(buf, sizeof(buf), 555 | "\tsall $5, %%%c%c%c\n", e1a, e1b, e1c); 556 | } else if (window->line1[9] == '2' && 557 | window->line1[10] == '7' && 558 | window->line1[11] == '6' && 559 | window->line1[12] == '8' && 560 | window->line1[13] == ',') { 561 | (void) snprintf(buf, sizeof(buf), 562 | "\tsall $15, %%%c%c%c\n", e1a, e1b, e1c); 563 | } else { 564 | return 0; 565 | } 566 | 567 | goto success; 568 | case '4': 569 | if (window->line1[9] == ',') { 570 | (void) snprintf(buf, sizeof(buf), 571 | "\tsall $2, %%%c%c%c\n", e1a, e1b, e1c); 572 | } else if (window->line1[9] == '0' && 573 | window->line1[10] == '9' && 574 | window->line1[11] == '6' && 575 | window->line1[12] == ',') { 576 | (void) snprintf(buf, sizeof(buf), 577 | "\tsall $12, %%%c%c%c\n", e1a, e1b, e1c); 578 | } else { 579 | return 0; 580 | } 581 | 582 | goto success; 583 | case '5': 584 | if (window->line1[9] == '1' && 585 | window->line1[10] == '2' && 586 | window->line1[11] == ',') { 587 | (void) snprintf(buf, sizeof(buf), 588 | "\tsall $9, %%%c%c%c\n", e1a, e1b, e1c); 589 | } else { 590 | return 0; 591 | } 592 | 593 | goto success; 594 | case '6': 595 | if (window->line1[9] == '4' && 596 | window->line1[10] == ',') { 597 | (void) snprintf(buf, sizeof(buf), 598 | "\tsall $6, %%%c%c%c\n", e1a, e1b, e1c); 599 | } else if (window->line1[9] == '5' && 600 | window->line1[10] == '5' && 601 | window->line1[11] == '3' && 602 | window->line1[12] == '6' && 603 | window->line1[13] == ',') { 604 | (void) snprintf(buf, sizeof(buf), 605 | "\tsall $16, %%%c%c%c\n", e1a, e1b, e1c); 606 | } else { 607 | return 0; 608 | } 609 | 610 | goto success; 611 | case '8': 612 | if (window->line1[9] == ',') { 613 | (void) snprintf(buf, sizeof(buf), 614 | "\tsall $3, %%%c%c%c\n", e1a, e1b, e1c); 615 | } else if (window->line1[9] == '1' && 616 | window->line1[10] == '9' && 617 | window->line1[11] == '2' && 618 | window->line1[12] == ',') { 619 | (void) snprintf(buf, sizeof(buf), 620 | "\tsall $13, %%%c%c%c\n", e1a, e1b, e1c); 621 | } else { 622 | return 0; 623 | } 624 | 625 | goto success; 626 | default: 627 | return 0; 628 | } 629 | } 630 | 631 | return 0; 632 | 633 | success: 634 | free(window->line1); 635 | window->line1 = xstrdup(buf); 636 | 637 | return 1; 638 | } 639 | 640 | static void 641 | one(struct peephole *window) 642 | { 643 | 644 | if (xorq(window)) 645 | return; 646 | 647 | if (xorl(window)) 648 | return; 649 | 650 | if (incq(window)) 651 | return; 652 | 653 | if (incl(window)) 654 | return; 655 | 656 | if (decq(window)) 657 | return; 658 | 659 | if (decl(window)) 660 | return; 661 | 662 | if (imulq(window)) 663 | return; 664 | 665 | (void) imull(window); 666 | } 667 | 668 | static int 669 | two(struct peephole *window) 670 | { 671 | 672 | return 0; 673 | } 674 | 675 | static int 676 | mov(struct peephole *window) 677 | { 678 | 679 | if (window->line1 == NULL || window->line2 == NULL || 680 | window->line3 == NULL) { 681 | return 0; 682 | } 683 | 684 | if (strncmp("\tmovl %e", window->line1, 8) != 0 && 685 | strncmp("\tmovq %r", window->line1, 8) != 0) { 686 | return 0; 687 | } 688 | 689 | if (strncmp("\tmovl %e", window->line2, 8) != 0 && 690 | strncmp("\tmovq %r", window->line2, 8) != 0) { 691 | return 0; 692 | } 693 | 694 | if (strncmp("\tmovl %e", window->line3, 8) != 0 && 695 | strncmp("\tmovq %r", window->line3, 8) != 0 && 696 | strncmp("\tmovl $0, %e", window->line3, 12) != 0 && 697 | strncmp("\tmovq $0, %r", window->line3, 12) != 0) { 698 | return 0; 699 | } 700 | 701 | if (strlen(window->line1) < 16 || strlen(window->line2) < 16) 702 | return 0; 703 | 704 | if (window->line1[4] == window->line2[4] && 705 | window->line1[7] == window->line2[13] && 706 | window->line1[8] == window->line2[14] && 707 | window->line1[9] == window->line2[15] && 708 | window->line1[13] == window->line2[7] && 709 | window->line1[14] == window->line2[8] && 710 | window->line1[15] == window->line2[9] && 711 | window->line1[14] == window->line3[strlen(window->line3) - 3] && 712 | window->line1[15] == window->line3[strlen(window->line3) - 2]) { 713 | free(window->line2); 714 | window->line2 = NULL; 715 | 716 | free(window->line1); 717 | window->line1 = xstrdup(window->line3); 718 | 719 | free(window->line3); 720 | window->line3 = NULL; 721 | 722 | return 1; 723 | } 724 | 725 | return 0; 726 | } 727 | 728 | static int 729 | xchgq(struct peephole *window) 730 | { 731 | 732 | if (window->line1 == NULL || window->line2 == NULL || 733 | window->line3 == NULL) { 734 | return 0; 735 | } 736 | 737 | if (strncmp("\txchgq %r", window->line1, 9) != 0 && 738 | strncmp("\txchgq %r", window->line3, 9) != 0) { 739 | return 0; 740 | } 741 | 742 | if (window->line1[9] == window->line3[15] && 743 | window->line1[10] == window->line3[16] && 744 | window->line1[15] == window->line3[9] && 745 | window->line1[16] == window->line3[10] && 746 | window->line1[17] == '\n' && 747 | window->line1[17] == window->line3[17] && 748 | window->line2[7] == window->line1[8] && 749 | window->line2[8] == window->line1[9] && 750 | window->line2[9] == window->line1[10]) { 751 | free(window->line1); 752 | window->line1 = xstrdup(window->line2); 753 | 754 | free(window->line2); 755 | window->line2 = NULL; 756 | 757 | window->line1[7] = window->line3[8]; 758 | window->line1[8] = window->line3[9]; 759 | window->line1[9] = window->line3[10]; 760 | 761 | free(window->line3); 762 | window->line3 = NULL; 763 | 764 | return 1; 765 | } 766 | 767 | return 0; 768 | } 769 | 770 | static int 771 | three(struct peephole *window) 772 | { 773 | 774 | if (xchgq(window)) 775 | return 1; 776 | 777 | return mov(window); 778 | } 779 | 780 | void 781 | x64(FILE *fp) 782 | { 783 | struct peephole window; 784 | int ret; 785 | 786 | window.line1 = NULL; 787 | window.line2 = NULL; 788 | window.line3 = NULL; 789 | 790 | while (fillwindow(&window, fp)) { 791 | again: 792 | ret = three(&window); 793 | if (ret == 0) 794 | ret = two(&window); 795 | one(&window); 796 | 797 | if (ret == 1) { 798 | if (fillwindow(&window, fp)) 799 | goto again; 800 | } 801 | 802 | if (window.line1 != NULL) 803 | (void) fputs(window.line1, stdout); 804 | 805 | shiftwindow(&window); 806 | } 807 | 808 | free(window.line3); 809 | window.line3 = NULL; 810 | 811 | while (window.line1 != NULL) { 812 | one(&window); 813 | 814 | if (window.line1 != NULL) 815 | (void) fputs(window.line1, stdout); 816 | 817 | shiftwindow(&window); 818 | } 819 | } 820 | --------------------------------------------------------------------------------