├── .gitignore ├── test ├── macro1.h ├── macro2.h ├── staticassert.c ├── scope.c ├── extern.c ├── cast.c ├── enum.c ├── test.h ├── align.c ├── conversion.c ├── union.c ├── assign.c ├── typeof.c ├── int.c ├── number.c ├── bitop.c ├── decl.c ├── global.c ├── comp.c ├── lex.c ├── generic.c ├── array.c ├── pointer.c ├── float.c ├── sizeof.c ├── varargs.c ├── arith.c ├── main │ └── testmain.c ├── literal.c ├── type.c ├── funcargs.c ├── function.c ├── initializer.c ├── control.c ├── struct.c └── macro.c ├── include ├── stdalign.h ├── stdbool.h ├── stddef.h └── stdarg.h ├── dict.h ├── error.h ├── error.c ├── COPYING ├── list.h ├── Makefile ├── dict.c ├── keyword.h ├── README.md ├── string.c ├── list.c ├── test.sh ├── utiltest.c ├── main.c ├── 8cc.h ├── debug.c ├── lex.c ├── cpp.c └── gen.c /.gitignore: -------------------------------------------------------------------------------- 1 | 8cc 2 | gen? 3 | *.o 4 | *.s 5 | tmp.* 6 | utiltest 7 | nqueen.s 8 | nqueen 9 | *.bin 10 | -------------------------------------------------------------------------------- /test/macro1.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #define MACRO_1 "macro1" 5 | -------------------------------------------------------------------------------- /test/macro2.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #define MACRO_2 "macro2" 5 | -------------------------------------------------------------------------------- /include/stdalign.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #ifndef __STDALIGN_H 5 | #define __STDALIGN_H 6 | 7 | #define alignof _Alignof 8 | #define __alignof_is_defined 1 9 | 10 | #endif /* __STDALIGN_H */ 11 | -------------------------------------------------------------------------------- /include/stdbool.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #ifndef __STDBOOL_H 5 | #define __STDBOOL_H 6 | 7 | #define bool _Bool 8 | #define true 1 9 | #define false 0 10 | #define __bool_true_false_are_defined 1 11 | 12 | #endif /* __STDBOOL_H */ 13 | -------------------------------------------------------------------------------- /test/staticassert.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void testmain(void) { 7 | print("static assert"); 8 | _Static_assert(1, "fail"); 9 | 10 | struct { 11 | _Static_assert(1, "fail"); 12 | } x; 13 | } 14 | -------------------------------------------------------------------------------- /test/scope.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void testmain(void) { 7 | print("scope"); 8 | 9 | int a = 31; 10 | { int a = 64; } 11 | expect(31, a); 12 | { 13 | int a = 64; 14 | expect(64, a); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/extern.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | extern int expect(int, int); 7 | extern int externvar1; 8 | int extern externvar2; 9 | 10 | void testmain(void) { 11 | print("extern"); 12 | expect(98, externvar1); 13 | expect(99, externvar2); 14 | } 15 | -------------------------------------------------------------------------------- /test/cast.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void testmain(void) { 7 | print("cast"); 8 | expectf(1, (int)1); 9 | expectf(1.0, (float)1); 10 | expectd(2.0, (double)2); 11 | 12 | int a[3]; 13 | *(int *)(a + 2) = 5; 14 | expect(5, a[2]); 15 | } 16 | -------------------------------------------------------------------------------- /include/stddef.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #ifndef __STDDEF_H 5 | #define __STDDEF_H 6 | 7 | #define NULL ((void *)0) 8 | 9 | typedef unsigned long size_t; 10 | typedef long ptrdiff_t; 11 | typedef char wchar_t; 12 | typedef long double max_align_t; 13 | 14 | #define offsetof(type, member) \ 15 | ((size_t)&(((type *)NULL)->member)) 16 | 17 | #endif /* __STDDEF_H */ 18 | -------------------------------------------------------------------------------- /test/enum.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | enum { g1, g2, g3 } global1; 7 | 8 | void testmain(void) { 9 | print("enum"); 10 | 11 | expect(0, g1); 12 | expect(2, g3); 13 | 14 | enum { x } v; 15 | expect(0, x); 16 | 17 | enum { y }; 18 | expect(0, y); 19 | 20 | enum tag { z }; 21 | enum tag a = z; 22 | expect(0, z); 23 | expect(0, a); 24 | } 25 | -------------------------------------------------------------------------------- /test/test.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "stdio.h" 5 | 6 | void exit(int); 7 | size_t strlen(const char *); 8 | 9 | extern int externvar1; 10 | extern int externvar2; 11 | 12 | void print(char *s); 13 | void fail(char *msg); 14 | void expect(int a, int b); 15 | void expect_string(char *a, char *b); 16 | void expectf(float a, float b); 17 | void expectd(double a, double b); 18 | void expectl(long a, long b); 19 | -------------------------------------------------------------------------------- /test/align.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | #include 6 | 7 | void test_alignof(void) { 8 | expect(1, __alignof_is_defined); 9 | expect(1, _Alignof(char)); 10 | expect(1, __alignof__(char)); 11 | expect(1, alignof(char)); 12 | expect(4, alignof(int)); 13 | expect(8, alignof(struct {char a; int b; })); 14 | } 15 | 16 | void testmain(void) { 17 | print("alignment"); 18 | test_alignof(); 19 | } 20 | -------------------------------------------------------------------------------- /test/conversion.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void test_bool(void) { 7 | _Bool v = 3; 8 | expect(1, v); 9 | v = 5; 10 | expect(1, v); 11 | v = 0.5; 12 | expect(1, v); 13 | v = 0.0; 14 | expect(0, v); 15 | } 16 | 17 | void test_float(void) { 18 | double a = 4.0; 19 | float b = a; 20 | expectf(4, b); 21 | } 22 | 23 | void testmain(void) { 24 | print("type conversion"); 25 | test_bool(); 26 | test_float(); 27 | } 28 | -------------------------------------------------------------------------------- /test/union.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void t1(void) { 7 | union { int a; int b; } x; 8 | x.a = 90; 9 | expect(90, x.b); 10 | } 11 | 12 | void t2(void) { 13 | union { char a[4]; int b; } x; 14 | x.b = 0; 15 | x.a[1] = 1; 16 | expect(256, x.b); 17 | } 18 | 19 | void t3(void) { 20 | union { char a[4]; int b; } x; 21 | x.a[0] = x.a[1] = x.a[2] = x.a[3] = 0; 22 | x.a[1]=1; 23 | expect(256, x.b); 24 | } 25 | 26 | void testmain(void) { 27 | print("union"); 28 | t1(); 29 | t2(); 30 | t3(); 31 | } 32 | -------------------------------------------------------------------------------- /test/assign.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void testmain(void) { 7 | print("compound assignment"); 8 | 9 | int a = 0; 10 | a += 5; 11 | expect(5, a); 12 | a -= 2; 13 | expect(3, a); 14 | a *= 10; 15 | expect(30, a); 16 | a /= 2; 17 | expect(15, a); 18 | a %= 6; 19 | expect(3, a); 20 | 21 | a = 14; 22 | a &= 7; 23 | expect(6, a); 24 | a |= 8; 25 | expect(14, a); 26 | a ^= 3; 27 | expect(13, a); 28 | a <<= 2; 29 | expect(52, a); 30 | a >>= 2; 31 | expect(13, a); 32 | } 33 | -------------------------------------------------------------------------------- /test/typeof.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void test_basic(void) { 7 | typeof(int) a = 5; 8 | expect(5, a); 9 | typeof(a) b = 6; 10 | expect(6, b); 11 | } 12 | 13 | void test_array(void) { 14 | char a[] = "abc"; 15 | typeof(a) b = "de"; 16 | expect_string("de", b); 17 | expect(4, sizeof(b)); 18 | 19 | typeof(typeof (char *)[4]) y; 20 | expect(4, sizeof(y) / sizeof(*y)); 21 | } 22 | 23 | void test_alt(void) { 24 | __typeof__(int) a = 10; 25 | expect(10, a); 26 | } 27 | 28 | void testmain(void) { 29 | print("typeof"); 30 | test_basic(); 31 | test_array(); 32 | test_alt(); 33 | } 34 | -------------------------------------------------------------------------------- /dict.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #ifndef EIGHTCC_DICT_H 5 | #define EIGHTCC_DICT_H 6 | 7 | #include "list.h" 8 | 9 | typedef struct Dict { 10 | List *list; 11 | struct Dict *parent; 12 | int size; 13 | } Dict; 14 | 15 | #define EMPTY_DICT \ 16 | ((Dict){ &EMPTY_LIST, NULL }) 17 | 18 | void *make_dict(void *parent); 19 | void *dict_get(Dict *dict, char *key); 20 | void dict_put(Dict *dict, char *key, void *val); 21 | void dict_remove(Dict *dict, char *key); 22 | bool dict_empty(Dict *dict); 23 | List *dict_keys(Dict *dict); 24 | List *dict_values(Dict *dict); 25 | void *dict_parent(Dict *dict); 26 | 27 | #endif /* EIGHTCC_DICT_H */ 28 | -------------------------------------------------------------------------------- /test/int.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void expects(short a, short b) { 7 | if (!(a == b)) { 8 | printf("Failed\n"); 9 | printf(" %d expected, but got %d\n", a, b); 10 | exit(1); 11 | } 12 | } 13 | 14 | void testmain(void) { 15 | print("long"); 16 | 17 | short a = 10; 18 | short int b = 15; 19 | expects(25, a + b); 20 | expects(20, a + 10); 21 | 22 | long x = 67; 23 | long int y = 69; 24 | expectl(67, x); 25 | expectl(136, x + y); 26 | expectl(10L, 10L); 27 | expectl(1152921504606846976, 1152921504606846976); 28 | expectl(1152921504606846977, 1152921504606846976 + 1); 29 | } 30 | -------------------------------------------------------------------------------- /test/number.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void testmain(void) { 7 | print("numeric constants"); 8 | 9 | expect(1, 0x1); 10 | expect(17, 0x11); 11 | expect(511, 0777); 12 | expect(11, 0b1011); // GNU extension 13 | 14 | expect(3, 3L); 15 | expect(3, 3LL); 16 | expect(3, 3UL); 17 | expect(3, 3LU); 18 | expect(3, 3ULL); 19 | expect(3, 3LU); 20 | expect(3, 3LLU); 21 | 22 | expectd(55.3, 55.3); 23 | expectd(200, 2e2); 24 | expectd(0x0.DE488631p8, 0xDE.488631); 25 | 26 | expect(4, sizeof(5)); 27 | expect(8, sizeof(5L)); 28 | expect(4, sizeof(3.0f)); 29 | expect(8, sizeof(3.0)); 30 | } 31 | -------------------------------------------------------------------------------- /include/stdarg.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #ifndef __STDARG_H 5 | #define __STDARG_H 6 | 7 | /** 8 | * Refer this document for the x86-64 ABI. 9 | * http://www.x86-64.org/documentation/abi.pdf 10 | */ 11 | 12 | typedef struct { 13 | unsigned int gp_offset; 14 | unsigned int fp_offset; 15 | void *overflow_arg_area; 16 | void *reg_save_area; 17 | } va_list[1]; 18 | 19 | #define va_start(ap, last) __builtin_va_start(ap) 20 | #define va_arg(ap, type) __builtin_va_arg(ap, type) 21 | #define va_end(ap) 1 22 | #define va_copy(dest, src) ((dest)[0] = (src)[0]) 23 | 24 | // Workaround to load stdio.h properly 25 | #define __GNUC_VA_LIST 1 26 | typedef va_list __gnuc_va_list; 27 | 28 | #endif /* __STDARG_H */ 29 | -------------------------------------------------------------------------------- /error.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #ifndef EIGHTCC_UTIL_H 5 | #define EIGHTCC_UTIL_H 6 | 7 | #define error(...) \ 8 | errorf(__FILE__, __LINE__, __VA_ARGS__) 9 | 10 | #define assert(expr) \ 11 | do { \ 12 | if (!(expr)) error("Assertion failed: " #expr); \ 13 | } while (0) 14 | 15 | #ifndef __8cc__ 16 | #define NORETURN __attribute__((noreturn)) 17 | #else 18 | #define NORETURN 19 | #endif 20 | 21 | extern void errorf(char *file, int line, char *fmt, ...) NORETURN; 22 | extern void warn(char *fmt, ...); 23 | extern char *quote_cstring(char *p); 24 | extern char *quote_char(char c); 25 | 26 | #endif /* EIGHTCC_UTIL_H */ 27 | -------------------------------------------------------------------------------- /test/bitop.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void test_or(void) { 7 | expect(3, 1 | 2); 8 | expect(7, 2 | 5); 9 | expect(7, 2 | 7); 10 | } 11 | 12 | void test_and(void) { 13 | expect(0, 1 & 2); 14 | expect(2, 2 & 7); 15 | } 16 | 17 | void test_not(void) { 18 | expect(-1, ~0); 19 | expect(-3, ~2); 20 | expect(0, ~-1); 21 | } 22 | 23 | void test_xor(void) { 24 | expect(10, 15 ^ 5); 25 | } 26 | 27 | void test_shift(void) { 28 | expect(16, 1 << 4); 29 | expect(48, 3 << 4); 30 | 31 | expect(1, 15 >> 3); 32 | expect(2, 8 >> 2); 33 | 34 | expect(1, ((unsigned)-1) >> 31); 35 | } 36 | 37 | void testmain(void) { 38 | print("bitwise operators"); 39 | test_or(); 40 | test_and(); 41 | test_not(); 42 | test_xor(); 43 | test_shift(); 44 | } 45 | -------------------------------------------------------------------------------- /test/decl.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void t1(void) { 7 | int a = 1; 8 | expect(3, a + 2); 9 | } 10 | 11 | void t2(void) { 12 | int a = 1; 13 | int b = 48 + 2; 14 | int c = a + b; 15 | expect(102, c * 2); 16 | } 17 | 18 | void t3(void) { 19 | int a[] = { 55 }; 20 | int *b = a; 21 | expect(55, *b); 22 | } 23 | 24 | void t4(void) { 25 | int a[] = { 55, 67 }; 26 | int *b = a + 1; 27 | expect(67, *b); 28 | } 29 | 30 | void t5(void) { 31 | int a[] = { 20, 30, 40 }; 32 | int *b = a + 1; 33 | expect(30, *b); 34 | } 35 | 36 | void t6(void) { 37 | int a[] = { 20, 30, 40 }; 38 | expect(20, *a); 39 | } 40 | 41 | void testmain(void) { 42 | print("declaration"); 43 | t1(); 44 | t2(); 45 | t3(); 46 | t4(); 47 | t5(); 48 | t6(); 49 | } 50 | -------------------------------------------------------------------------------- /test/global.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | int val = 21; 7 | int *p1 = &val; 8 | 9 | int a1[3]; 10 | int a2[3] = { 24, 25, 26 }; 11 | int x1, x2; 12 | int x3, x4 = 4; 13 | int x5 = 5, x6; 14 | 15 | char s1[] = "abcd"; 16 | char *s2 = "ABCD"; 17 | long l1 = 8; 18 | int *intp = &(int){ 9 }; 19 | 20 | void testmain(void) { 21 | print("global variable"); 22 | 23 | expect(21, val); 24 | val = 22; 25 | expect(22, val); 26 | expect(22, *p1); 27 | 28 | a1[1] = 23; 29 | expect(23, a1[1]); 30 | expect(25, a2[1]); 31 | 32 | x1 = 1; 33 | x2 = 2; 34 | expect(1, x1); 35 | expect(2, x2); 36 | x3 = 3; 37 | expect(3, x3); 38 | expect(4, x4); 39 | expect(5, x5); 40 | x6 = 6; 41 | expect(6, x6); 42 | 43 | expect_string("abcd", s1); 44 | expect_string("ABCD", s2); 45 | 46 | expectl(8, l1); 47 | expectl(9, *intp); 48 | } 49 | -------------------------------------------------------------------------------- /error.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include 5 | #include 6 | #include 7 | #include "8cc.h" 8 | 9 | bool suppress_warning = false; 10 | 11 | void errorf(char *file, int line, char *fmt, ...) { 12 | fprintf(stderr, isatty(fileno(stderr)) ? "\e[1;31m[ERROR]\e[0m " : "[ERROR] "); 13 | fprintf(stderr, "%s:%d: %s: ", file, line, input_position()); 14 | va_list args; 15 | va_start(args, fmt); 16 | vfprintf(stderr, fmt, args); 17 | fprintf(stderr, "\n"); 18 | va_end(args); 19 | exit(1); 20 | } 21 | 22 | void warn(char *fmt, ...) { 23 | if (suppress_warning) 24 | return; 25 | fprintf(stderr, isatty(fileno(stderr)) ? "\e[1;31m[WARNING]\e[0m " : "[WARNING] "); 26 | fprintf(stderr, "%s: ", input_position()); 27 | va_list args; 28 | va_start(args, fmt); 29 | vfprintf(stderr, fmt, args); 30 | fprintf(stderr, "\n"); 31 | va_end(args); 32 | } 33 | -------------------------------------------------------------------------------- /test/comp.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void testmain(void) { 7 | print("comparison operators"); 8 | expect(1, 1 < 2); 9 | expect(0, 2 < 1); 10 | expect(1, 1 == 1); 11 | expect(0, 1 == 2); 12 | expect(0, 1 != 1); 13 | expect(1, 1 != 2); 14 | 15 | expect(1, 1 <= 2); 16 | expect(1, 2 <= 2); 17 | expect(0, 2 <= 1); 18 | 19 | expect(0, 1 >= 2); 20 | expect(1, 2 >= 2); 21 | expect(1, 2 >= 1); 22 | 23 | int i = -1; 24 | expect(0, i >= 0); 25 | 26 | expect(1, 10.0 == 10.0); 27 | expect(0, 10.0 == 20.0); 28 | expect(0, 10.0 != 10.0); 29 | expect(1, 10.0 != 20.0); 30 | 31 | expect(1, 10.0f == 10.0f); 32 | expect(0, 10.0f == 20.0f); 33 | expect(0, 10.0f != 10.0f); 34 | expect(1, 10.0f != 20.0f); 35 | 36 | expect(1, 10.0f == 10.0); 37 | expect(0, 10.0f == 20.0); 38 | expect(0, 10.0f != 10.0); 39 | expect(1, 10.0f != 20.0); 40 | } 41 | -------------------------------------------------------------------------------- /test/lex.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | #define stringify(x) #x 7 | 8 | void digraph(void) { 9 | expect_string("[", stringify(<:)); 10 | expect_string("]", stringify(:>)); 11 | expect_string("{", stringify(<%)); 12 | expect_string("}", stringify(%>)); 13 | expect_string("#", stringify(%:)); 14 | expect_string("% :", stringify(% :)); 15 | expect_string("##", stringify(%:%:)); 16 | expect_string("#%", stringify(%:%)); 17 | } 18 | 19 | void escape(void) { 20 | int value = 10; 21 | expect(10, val\ 22 | ue); 23 | } 24 | 25 | void whitespace(void) { 26 | expect_string("x y", stringify( x y )); 27 | } 28 | 29 | void newline(void) { 30 | 31 | # 32 | } 33 | 34 | void dollar(void) { 35 | int $ = 1; 36 | expect(1, $); 37 | int $2 = 2; 38 | expect(2, $2); 39 | int a$ = 3; 40 | expect(3, a$); 41 | } 42 | 43 | void testmain(void) { 44 | print("lexer"); 45 | digraph(); 46 | escape(); 47 | whitespace(); 48 | newline(); 49 | dollar(); 50 | } 51 | -------------------------------------------------------------------------------- /test/generic.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void test_basic(void) { 7 | expect(1, _Generic(5, int: 1, float: 2)); 8 | expectd(3.0, _Generic(5.0, int: 1, float: 2.0, double: 3.0)); 9 | } 10 | 11 | void test_default(void) { 12 | expect(1, _Generic(5, default: 1, float: 2)); 13 | expectd(3.0, _Generic(5.0, int: 1, float: 2.0, default: 3.0)); 14 | } 15 | 16 | void test_struct() { 17 | struct t1 { int x, y; } v1; 18 | struct t2 { int x, y, z; } v2; 19 | expect(10, _Generic(v1, struct t1: 10, struct t2: 11, default: 12)); 20 | expect(11, _Generic(v2, struct t1: 10, struct t2: 11, default: 12)); 21 | expect(12, _Generic(99, struct t1: 10, struct t2: 11, default: 12)); 22 | } 23 | 24 | void test_array() { 25 | expect(20, _Generic("abc", char *: 20, default: 21)); 26 | expect(22, _Generic((int[]){ 0 }, int *: 22, default: 23)); 27 | expect(23, _Generic((int[]){ 0 }, int[]: 22, default: 23)); 28 | } 29 | 30 | void testmain(void) { 31 | print("_Generic"); 32 | test_basic(); 33 | test_default(); 34 | test_struct(); 35 | test_array(); 36 | } 37 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | This software is licensed under the MIT license. The full text of the license is 2 | this: 3 | 4 | 5 | Copyright (c) 2012 Rui Ueyama 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software is furnished to do so, 12 | subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #ifndef EIGHTCC_LIST_H 5 | #define EIGHTCC_LIST_H 6 | 7 | #include 8 | 9 | typedef struct ListNode { 10 | void *elem; 11 | struct ListNode *next; 12 | struct ListNode *prev; 13 | } ListNode; 14 | 15 | typedef struct List { 16 | int len; 17 | ListNode *head; 18 | ListNode *tail; 19 | } List; 20 | 21 | typedef struct Iter { 22 | ListNode *ptr; 23 | } Iter; 24 | 25 | #define EMPTY_LIST \ 26 | ((List){ .len = 0, .head = NULL, .tail = NULL }) 27 | 28 | extern List *make_list(void); 29 | extern List *make_list1(void *e); 30 | extern List *list_copy(List *list); 31 | extern void list_push(List *list, void *elem); 32 | extern void *list_pop(List *list); 33 | extern void list_append(List *a, List *b); 34 | extern void *list_shift(List *list); 35 | extern void list_unshift(List *list, void *elem); 36 | extern void *list_get(List *list, int index); 37 | extern void *list_head(List *list); 38 | extern void *list_tail(List *list); 39 | extern List *list_reverse(List *list); 40 | extern int list_len(List *list); 41 | extern Iter *list_iter(List *list); 42 | extern void *iter_next(Iter *iter); 43 | extern bool iter_end(Iter *iter); 44 | 45 | #endif /* EIGHTCC_LIST_H */ 46 | -------------------------------------------------------------------------------- /test/array.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void t1(void) { 7 | int a[2][3]; 8 | int *p = a; 9 | *p = 1; 10 | expect(1, *p); 11 | } 12 | 13 | void t2(void) { 14 | int a[2][3]; 15 | int *p = a + 1; 16 | *p = 1; 17 | int *q = a; 18 | *p = 32; 19 | expect(32, *(q + 3)); 20 | } 21 | 22 | void t3(void) { 23 | int a[4][5]; 24 | int *p = a; 25 | *(*(a + 1) + 2) = 62; 26 | expect(62, *(p + 7)); 27 | } 28 | 29 | void t4(void) { 30 | int a[3] = { 1, 2, 3 }; 31 | expect(1, a[0]); 32 | expect(2, a[1]); 33 | expect(3, a[2]); 34 | } 35 | 36 | void t5(void) { 37 | int a[2][3]; 38 | a[0][1] = 1; 39 | a[1][1] = 2; 40 | int *p = a; 41 | expect(1, p[1]); 42 | expect(2, p[4]); 43 | } 44 | 45 | void t6a(int e, int x[][3]) { 46 | expect(e, *(*(x + 1) + 1)); 47 | } 48 | 49 | void t6(void) { 50 | int a[2][3]; 51 | int *p = a; 52 | *(p + 4) = 65; 53 | t6a(65, a); 54 | } 55 | 56 | void t7(void) { 57 | int a[3*3]; // integer constant expression 58 | a[8] = 68; 59 | expect(68, a[8]); 60 | } 61 | 62 | void testmain(void) { 63 | print("array"); 64 | t1(); 65 | t2(); 66 | t3(); 67 | t4(); 68 | t5(); 69 | t6(); 70 | } 71 | -------------------------------------------------------------------------------- /test/pointer.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void t1(void) { 7 | int a = 61; 8 | int *b = &a; 9 | expect(61, *b); 10 | } 11 | 12 | void t2(void) { 13 | char *c = "ab"; 14 | expect(97, *c); 15 | } 16 | 17 | void t3(void) { 18 | char *c = "ab" + 1; 19 | expect(98, *c); 20 | } 21 | 22 | void t4(void) { 23 | char s[] = "xyz"; 24 | char *c = s + 2; 25 | expect(122, *c); 26 | } 27 | 28 | void t5(void) { 29 | char s[] = "xyz"; 30 | *s = 65; 31 | expect(65, *s); 32 | } 33 | 34 | void t6(void) { 35 | struct tag { 36 | int val; 37 | struct tag *next; 38 | }; 39 | struct tag node1 = { 1, NULL }; 40 | struct tag node2 = { 2, &node1 }; 41 | struct tag node3 = { 3, &node2 }; 42 | struct tag *p = &node3; 43 | expect(3, p->val); 44 | expect(2, p->next->val); 45 | expect(1, p->next->next->val); 46 | p->next = p->next->next; 47 | expect(1, p->next->val); 48 | } 49 | 50 | void subtract(void) { 51 | char *p = "abcdefg"; 52 | char *q = p + 5; 53 | expect(5, q - p); 54 | } 55 | 56 | void testmain(void) { 57 | print("pointer"); 58 | t1(); 59 | t2(); 60 | t3(); 61 | t4(); 62 | t5(); 63 | t6(); 64 | subtract(); 65 | } 66 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-Wall -std=gnu99 -g -I. -O0 2 | OBJS=cpp.o debug.o dict.o gen.o lex.o list.o parse.o string.o error.o 3 | SELF=cpp.s debug.s dict.s gen.s lex.s list.s parse.s string.s error.s main.s 4 | TESTS := $(patsubst %.c,%.bin,$(wildcard test/*.c)) 5 | 6 | 8cc: 8cc.h main.o $(OBJS) 7 | $(CC) -o $@ main.o $(OBJS) $(LDFLAGS) 8 | 9 | $(OBJS) utiltest.o main.o: 8cc.h 10 | 11 | utiltest: 8cc.h utiltest.o $(OBJS) 12 | $(CC) -o $@ utiltest.o $(OBJS) $(LDFLAGS) 13 | 14 | test: utiltest $(TESTS) 15 | @echo 16 | ./utiltest 17 | @for test in $(TESTS); do \ 18 | ./$$test || exit; \ 19 | done 20 | ./test.sh 21 | 22 | test/%.o: test/%.c 8cc 23 | ./8cc -c $< 24 | 25 | test/%.bin: test/%.o test/main/testmain.s 8cc 26 | $(CC) -o $@ $< test/main/testmain.o $(LDFLAGS) 27 | 28 | $(SELF) test/main/testmain.s: 8cc test/main/testmain.c 29 | ./8cc -c $(@:s=c) 30 | 31 | self: $(SELF) 32 | rm -f 8cc utiltest 33 | $(MAKE) 8cc 34 | 35 | fulltest: 36 | $(MAKE) clean 37 | $(MAKE) test 38 | cp 8cc gen1 39 | rm $(OBJS) main.o 40 | $(MAKE) self 41 | $(MAKE) test 42 | cp 8cc gen2 43 | rm $(OBJS) main.o 44 | $(MAKE) self 45 | $(MAKE) test 46 | cp 8cc gen3 47 | diff gen2 gen3 48 | 49 | clean: 50 | rm -f 8cc *.o *.s tmp.* test/*.s test/*.o sample/*.o 51 | rm -f utiltest gen[1-9] test/util/testmain.[os] 52 | rm -f $(TESTS) 53 | 54 | all: 8cc 55 | 56 | .PHONY: clean test all 57 | -------------------------------------------------------------------------------- /test/float.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | float tf1(float a) { return a; } 7 | float tf2(double a) { return a; } 8 | float tf3(int a) { return a; } 9 | 10 | double td1(float a) { return a; } 11 | double td2(double a) { return a; } 12 | double td3(int a) { return a; } 13 | 14 | double recursive(double a) { 15 | if (a < 10) return a; 16 | return recursive(3.33); 17 | } 18 | 19 | void testmain(void) { 20 | print("float"); 21 | 22 | expect(0.7, .7); 23 | float v1 = 10.0; 24 | float v2 = v1; 25 | expectf(10.0, v1); 26 | expectf(10.0, v2); 27 | return; 28 | double v3 = 20.0; 29 | double v4 = v3; 30 | expectd(20.0, v3); 31 | expectd(20.0, v4); 32 | 33 | expectf(1.0, 1.0); 34 | expectf(1.5, 1.0 + 0.5); 35 | expectf(0.5, 1.0 - 0.5); 36 | expectf(2.0, 1.0 * 2.0); 37 | expectf(0.25, 1.0 / 4.0); 38 | 39 | expectf(3.0, 1.0 + 2); 40 | expectf(2.5, 5 - 2.5); 41 | expectf(2.0, 1.0 * 2); 42 | expectf(0.25, 1.0 / 4); 43 | 44 | expectf(10.5, tf1(10.5)); 45 | expectf(10.0, tf1(10)); 46 | expectf(10.6, tf2(10.6)); 47 | expectf(10.0, tf2(10)); 48 | expectf(10.0, tf3(10.7)); 49 | expectf(10.0, tf3(10)); 50 | 51 | expectd(1.0, tf1(1.0)); 52 | expectd(10.0, tf1(10)); 53 | expectd(2.0, tf2(2.0)); 54 | expectd(10.0, tf2(10)); 55 | expectd(11.0, tf3(11.5)); 56 | expectd(10.0, tf3(10)); 57 | 58 | expectd(3.33, recursive(100)); 59 | } 60 | -------------------------------------------------------------------------------- /test/sizeof.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | #include 6 | 7 | void testmain(void) { 8 | print("sizeof"); 9 | 10 | expect(1, sizeof(void)); 11 | expect(1, sizeof(testmain)); 12 | expect(1, sizeof(char)); 13 | expect(1, sizeof(_Bool)); 14 | expect(1, sizeof(bool)); 15 | expect(2, sizeof(short)); 16 | expect(4, sizeof(int)); 17 | expect(8, sizeof(long)); 18 | 19 | expect(8, sizeof(char *)); 20 | expect(8, sizeof(short *)); 21 | expect(8, sizeof(int *)); 22 | expect(8, sizeof(long *)); 23 | 24 | expect(1, sizeof(unsigned char)); 25 | expect(2, sizeof(unsigned short)); 26 | expect(4, sizeof(unsigned int)); 27 | expect(8, sizeof(unsigned long)); 28 | 29 | expect(4, sizeof 1); 30 | expect(8, sizeof 1L); 31 | expect(8, sizeof 1.0); 32 | 33 | expect(1, sizeof(char[1])); 34 | expect(7, sizeof(char[7])); 35 | expect(30, sizeof(char[3][10])); 36 | expect(32, sizeof(int[4][2])); 37 | 38 | expect(4, sizeof('a')); 39 | expect(4, sizeof(1)); 40 | expect(8, sizeof(1L)); 41 | expect(4, sizeof(1.0f)); 42 | expect(8, sizeof(1.0)); 43 | 44 | char a[] = { 1, 2, 3 }; 45 | expect(3, sizeof(a)); 46 | char b[] = "abc"; 47 | expect(4, sizeof(b)); 48 | char *c[5]; 49 | expect(40, sizeof(c)); 50 | char *(*d)[3]; 51 | expect(8, sizeof(d)); 52 | expect(24, sizeof(*d)); 53 | expect(8, sizeof(**d)); 54 | expect(1, sizeof(***d)); 55 | 56 | expect(4, sizeof((int)a)); 57 | } 58 | -------------------------------------------------------------------------------- /test/varargs.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include 5 | #include "test.h" 6 | 7 | char buf[100]; 8 | 9 | void test_int(int a, ...) { 10 | va_list ap; 11 | va_start(ap, a); 12 | expect(1, a); 13 | expect(2, va_arg(ap, int)); 14 | expect(3, va_arg(ap, int)); 15 | expect(5, va_arg(ap, int)); 16 | expect(8, va_arg(ap, int)); 17 | va_end(ap); 18 | } 19 | 20 | void test_float(float a, ...) { 21 | va_list ap; 22 | va_start(ap, a); 23 | expectf(1.0, a); 24 | expectf(2.0, va_arg(ap, float)); 25 | expectf(4.0, va_arg(ap, float)); 26 | expectf(8.0, va_arg(ap, float)); 27 | va_end(ap); 28 | } 29 | 30 | void test_mix(char *p, ...) { 31 | va_list ap; 32 | va_start(ap, p); 33 | expect_string("abc", p); 34 | expectf(2.0, va_arg(ap, float)); 35 | expect(4, va_arg(ap, int)); 36 | expect_string("d", va_arg(ap, char *)); 37 | expect(5, va_arg(ap, int)); 38 | va_end(ap); 39 | } 40 | 41 | char *format(char *fmt, ...) { 42 | va_list ap; 43 | va_start(ap, p); 44 | vsprintf(buf, fmt, ap); 45 | va_end(ap); 46 | buf; 47 | } 48 | 49 | void test_va_list(void) { 50 | expect_string("", format("")); 51 | expect_string("3", format("%d", 3)); 52 | expect_string("3,1.0,6,2.0,abc", format("%d,%.1f,%d,%.1f,%s", 3, 1.0, 6, 2.0, "abc")); 53 | } 54 | 55 | void testmain(void) { 56 | print("varargs"); 57 | test_int(1, 2, 3, 5, 8); 58 | test_float(1.0, 2.0, 4.0, 8.0); 59 | test_mix("abc", 2.0, 4, "d", 5); 60 | test_va_list(); 61 | } 62 | -------------------------------------------------------------------------------- /test/arith.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void test_basic(void) { 7 | expect(0, 0); 8 | expect(3, 1 + 2); 9 | expect(3, 1 + 2); 10 | expect(10, 1 + 2 + 3 + 4); 11 | expect(11, 1 + 2 * 3 + 4); 12 | expect(14, 1 * 2 + 3 * 4); 13 | expect(4, 4 / 2 + 6 / 3); 14 | expect(4, 24 / 2 / 3); 15 | expect(3, 24 % 7); 16 | expect(0, 24 % 3); 17 | expect(98, 'a' + 1); 18 | int a = 0 - 1; 19 | expect(0 - 1, a); 20 | expect(-1, a); 21 | expect(0, a + 1); 22 | expect(1, +1); 23 | } 24 | 25 | void test_relative(void) { 26 | expect(1, 1 > 0); 27 | expect(1, 0 < 1); 28 | } 29 | 30 | void test_inc_dec(void) { 31 | int a = 15; 32 | expect(15, a++); 33 | expect(16, a); 34 | expect(16, a--); 35 | expect(15, a); 36 | expect(14, --a); 37 | expect(14, a); 38 | expect(15, ++a); 39 | expect(15, a); 40 | } 41 | 42 | void test_bool(void) { 43 | expect(0, !1); 44 | expect(1 ,!0); 45 | } 46 | 47 | void test_ternary(void) { 48 | expect(51, (1 + 2) ? 51 : 52); 49 | expect(52, (1 - 1) ? 51 : 52); 50 | expect(26, (1 - 1) ? 51 : 52 / 2); 51 | expect(17, (1 - 0) ? 51 / 3 : 52); 52 | expect(3, (1 + 2) ?: 52); 53 | } 54 | 55 | void test_comma(void) { 56 | expect(3, (1, 3)); 57 | expectf(7.0, (1, 3, 5, 7.0)); 58 | } 59 | 60 | void testmain(void) { 61 | print("basic arithmetic"); 62 | test_basic(); 63 | test_relative(); 64 | test_inc_dec(); 65 | test_bool(); 66 | test_ternary(); 67 | test_comma(); 68 | } 69 | -------------------------------------------------------------------------------- /test/main/testmain.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void testmain(void); 11 | 12 | // For test/extern.c 13 | int externvar1 = 98; 14 | int externvar2 = 99; 15 | 16 | // For test/function.c 17 | int booltest1(bool x) { 18 | return x; 19 | } 20 | 21 | void print(char *s) { 22 | printf("Testing %s ... ", s); 23 | fflush(stdout); 24 | } 25 | 26 | void printfail(void) { 27 | printf(isatty(fileno(stdout)) ? "\e[1;31mFailed\e[0m" : "Failed"); 28 | } 29 | 30 | void fail(char *msg) { 31 | printfail(); 32 | printf(": %s\n", msg); 33 | exit(1); 34 | } 35 | 36 | void expect(int a, int b) { 37 | if (!(a == b)) { 38 | printfail(); 39 | printf("\n %d expected, but got %d\n", a, b); 40 | exit(1); 41 | } 42 | } 43 | 44 | void expect_string(char *a, char *b) { 45 | if (strcmp(a, b)) { 46 | printfail(); 47 | printf("\n \"%s\" expected, but got \"%s\"\n", a, b); 48 | exit(1); 49 | } 50 | } 51 | 52 | void expectf(float a, float b) { 53 | if (!(a == b)) { 54 | printfail(); 55 | printf("\n %f expected, but got %f\n", a, b); 56 | exit(1); 57 | } 58 | } 59 | 60 | void expectd(double a, double b) { 61 | if (!(a == b)) { 62 | printfail(); 63 | printf("\n %lf expected, but got %lf\n", a, b); 64 | exit(1); 65 | } 66 | } 67 | 68 | void expectl(long a, long b) { 69 | if (!(a == b)) { 70 | printfail(); 71 | printf("\n %ld expected, but got %ld\n", a, b); 72 | exit(1); 73 | } 74 | } 75 | 76 | int main() { 77 | testmain(); 78 | printf(isatty(fileno(stdout)) ? "\e[32mOK\e[0m\n" : "OK\n"); 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /dict.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include 5 | #include 6 | #include "dict.h" 7 | 8 | typedef struct DictEntry { 9 | char *key; 10 | void *val; 11 | } DictEntry; 12 | 13 | void *make_dict(void *parent) { 14 | Dict *r = malloc(sizeof(Dict)); 15 | r->list = make_list(); 16 | r->parent = parent; 17 | return r; 18 | } 19 | 20 | void *dict_get(Dict *dict, char *key) { 21 | for (; dict; dict = dict->parent) { 22 | for (Iter *i = list_iter(dict->list); !iter_end(i);) { 23 | DictEntry *e = iter_next(i); 24 | if (!strcmp(key, e->key)) 25 | return e->val; 26 | } 27 | } 28 | return NULL; 29 | } 30 | 31 | void dict_put(Dict *dict, char *key, void *val) { 32 | DictEntry *e = malloc(sizeof(DictEntry)); 33 | e->key = key; 34 | e->val = val; 35 | list_unshift(dict->list, e); 36 | } 37 | 38 | void dict_remove(Dict *dict, char *key) { 39 | List *list = make_list(); 40 | for (Iter *i = list_iter(dict->list); !iter_end(i);) { 41 | DictEntry *e = iter_next(i); 42 | if (strcmp(key, e->key)) 43 | list_push(list, e); 44 | } 45 | dict->list = list; 46 | } 47 | 48 | bool dict_empty(Dict *dict) { 49 | return list_len(dict->list) == 0; 50 | } 51 | 52 | List *dict_keys(Dict *dict) { 53 | List *r = make_list(); 54 | for (; dict; dict = dict->parent) 55 | for (Iter *i = list_iter(dict->list); !iter_end(i);) 56 | list_unshift(r, ((DictEntry *)iter_next(i))->key); 57 | return r; 58 | } 59 | 60 | List *dict_values(Dict *dict) { 61 | List *r = make_list(); 62 | for (; dict; dict = dict->parent) 63 | for (Iter *i = list_iter(dict->list); !iter_end(i);) 64 | list_unshift(r, ((DictEntry *)iter_next(i))->val); 65 | return r; 66 | } 67 | 68 | void *dict_parent(Dict *dict) { 69 | return dict->parent; 70 | } 71 | -------------------------------------------------------------------------------- /test/literal.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | static void test_char(void) { 7 | expect(65, 'A'); 8 | expect(97, 'a'); 9 | expect(7, '\a'); 10 | expect(8, '\b'); 11 | expect(12, '\f'); 12 | expect(10, '\n'); 13 | expect(13, '\r'); 14 | expect(9, '\t'); 15 | expect(11, '\v'); 16 | expect(27, '\e'); 17 | 18 | expect(0, '\0'); 19 | expect(7, '\7'); 20 | expect(15, '\17'); 21 | expect(-99, '\235'); 22 | 23 | expect(0, '\x0'); 24 | expect(-1, '\xff'); 25 | expect(15, '\xF'); 26 | expect(18, '\x012'); 27 | } 28 | 29 | static void test_string(void) { 30 | expect_string("abc", "abc"); 31 | expect('a', "abc"[0]); 32 | expect(0, "abc"[3]); 33 | expect_string("abcd", "ab" "cd"); 34 | expect_string("abcdef", "ab" "cd" "ef"); 35 | 36 | char expected[] = { 65, 97, 7, 8, 12, 10, 13, 9, 11, 27, 7, 15, -99, -1, 18, 0 }; 37 | expect_string(expected, "Aa\a\b\f\n\r\t\v\e\7\17\235\xff\x012"); 38 | 39 | expect('c', L'c'); 40 | expect_string("asdf", L"asdf"); 41 | 42 | // make sure we can handle an identifier starting with "L" 43 | int L = 7; 44 | expect(7, L); 45 | int L123 = 123; 46 | expect(123, L123); 47 | } 48 | 49 | int g1 = (int){ 80 }; 50 | int *g2 = &(int){ 81 }; 51 | struct g3 { int x; } *g3 = &(struct g3){ 82 }; 52 | struct g4 { char x; struct g4a { int y[2]; } *z; } *g4 = &(struct g4){ 83, &(struct g4a){ 84, 85 } }; 53 | 54 | static void test_compound(void) { 55 | expect(1, (int){ 1 }); 56 | expect(3, (int[]){ 1, 2, 3 }[2]); 57 | expect(12, sizeof((int[]){ 1, 2, 3 })); 58 | expect(6, (struct { int x[3]; }){ 5, 6, 7 }.x[1]); 59 | 60 | expect(80, g1); 61 | expect(81, *g2); 62 | expect(82, g3->x); 63 | expect(83, g4->x); 64 | expect(84, g4->z->y[0]); 65 | expect(85, g4->z->y[1]); 66 | } 67 | 68 | void testmain(void) { 69 | print("literal"); 70 | test_char(); 71 | test_string(); 72 | test_compound(); 73 | } 74 | -------------------------------------------------------------------------------- /keyword.h: -------------------------------------------------------------------------------- 1 | punct(OP_ARROW, "->") 2 | punct(OP_A_ADD, "+=") 3 | punct(OP_A_AND, "&=") 4 | punct(OP_A_DIV, "/=") 5 | punct(OP_A_MOD, "%=") 6 | punct(OP_A_MUL, "*=") 7 | punct(OP_A_OR, "|=") 8 | punct(OP_A_SAL, "<<=") 9 | punct(OP_A_SAR, ">>=") 10 | punct(OP_A_SUB, "-=") 11 | punct(OP_A_XOR, "^=") 12 | punct(OP_DEC, "--") 13 | punct(OP_EQ, "==") 14 | punct(OP_GE, ">=") 15 | punct(OP_INC, "++") 16 | punct(OP_LE, "<=") 17 | punct(OP_LOGAND, "&&") 18 | punct(OP_LOGOR, "||") 19 | punct(OP_NE, "!=") 20 | punct(OP_SAL, "<<") 21 | punct(OP_SAR, ">>") 22 | 23 | keyword(KAUTO, "auto", true) 24 | keyword(KBREAK, "break", false) 25 | keyword(KCASE, "case", false) 26 | keyword(KCHAR, "char", true) 27 | keyword(KCONST, "const", true) 28 | keyword(KCONTINUE, "continue", false) 29 | keyword(KDEFAULT, "default", false) 30 | keyword(KDO, "do", false) 31 | keyword(KDOUBLE, "double", true) 32 | keyword(KELSE, "else", false) 33 | keyword(KENUM, "enum", true) 34 | keyword(KEXTERN, "extern", true) 35 | keyword(KFLOAT, "float", true) 36 | keyword(KFOR, "for", false) 37 | keyword(KGOTO, "goto", false) 38 | keyword(KIF, "if", false) 39 | keyword(KINLINE, "inline", true) 40 | keyword(KINT, "int", true) 41 | keyword(KLONG, "long", true) 42 | keyword(KREGISTER, "register", true) 43 | keyword(KRESTRICT, "restrict", true) 44 | keyword(KRETURN, "return", false) 45 | keyword(KSHORT, "short", true) 46 | keyword(KSIGNED, "signed", true) 47 | keyword(K__SIGNED__, "__signed__", true) 48 | keyword(KSIZEOF, "sizeof", false) 49 | keyword(KALIGNOF, "_Alignof", false) 50 | keyword(K__ALIGNOF__, "__alignof__", false) 51 | keyword(KSTATIC, "static", true) 52 | keyword(KSTRUCT, "struct", true) 53 | keyword(KSWITCH, "switch", false) 54 | keyword(KTYPEDEF, "typedef", true) 55 | keyword(KUNION, "union", true) 56 | keyword(KUNSIGNED, "unsigned", true) 57 | keyword(KVOID, "void", true) 58 | keyword(KVOLATILE, "volatile", true) 59 | keyword(KWHILE, "while", false) 60 | keyword(KBOOL, "_Bool", true) 61 | keyword(KCOMPLEX, "_Complex", true) 62 | keyword(KIMAGINARY, "_Imaginary", true) 63 | keyword(KTHREEDOTS, "...", false) 64 | keyword(KGENERIC, "_Generic", false) 65 | keyword(KTYPEOF, "typeof", true) 66 | keyword(K__TYPEOF__, "__typeof__", true) 67 | keyword(KSTATIC_ASSERT, "_Static_assert", false) 68 | -------------------------------------------------------------------------------- /test/type.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | #include 6 | #include 7 | 8 | void test_type(void) { 9 | char a; 10 | short b; 11 | int c; 12 | long d; 13 | long long e; 14 | short int f; 15 | long int g; 16 | long long int f; 17 | long int long g; 18 | float h; 19 | double i; 20 | long double j; 21 | _Bool k; 22 | bool l; 23 | } 24 | 25 | void test_signed(void) { 26 | signed char a; 27 | signed short b; 28 | signed int c; 29 | signed long d; 30 | signed long long e; 31 | signed short int f; 32 | signed long int g; 33 | signed long long int f; 34 | } 35 | 36 | void test_unsigned(void) { 37 | unsigned char a; 38 | unsigned short b; 39 | unsigned int c; 40 | unsigned long d; 41 | unsigned long long e; 42 | unsigned short int f; 43 | unsigned long int g; 44 | unsigned long long int f; 45 | } 46 | 47 | void test_storage_class(void) { 48 | static a; 49 | auto b; 50 | register c; 51 | static int d; 52 | auto int e; 53 | register int f; 54 | } 55 | 56 | void test_pointer(void) { 57 | int *a; 58 | expect(8, sizeof(a)); 59 | int *b[5]; 60 | expect(40, sizeof(b)); 61 | int (*c)[5]; 62 | expect(8, sizeof(c)); 63 | } 64 | 65 | void test_unusual_order(void) { 66 | int unsigned auto * const * const a; 67 | } 68 | 69 | void test_typedef(void) { 70 | typedef int integer; 71 | integer a = 5; 72 | expect(5, a); 73 | 74 | typedef int array[3]; 75 | array b = { 1, 2, 3 }; 76 | expect(2, b[1]); 77 | 78 | typedef struct tag { int x; } strtype; 79 | strtype c; 80 | c.x = 5; 81 | expect(5, c.x); 82 | 83 | typedef int mytype1; 84 | typedef int mytype2; 85 | mytype1 mytype2 = 3; 86 | expect(3, mytype2); 87 | } 88 | 89 | void test_align(void) { 90 | expect(16, sizeof(max_align_t)); 91 | } 92 | 93 | void testmain(void) { 94 | print("type system"); 95 | test_type(); 96 | test_signed(); 97 | test_unsigned(); 98 | test_storage_class(); 99 | test_pointer(); 100 | test_unusual_order(); 101 | test_typedef(); 102 | test_align(); 103 | } 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 8cc C Compiler 2 | ============== 3 | 4 | 8cc is a small C compiler. It supports a broad range of C99 features, such as 5 | compound literals or designated initializers. Some GNU extensions, such as 6 | computed gotos, are also supported. 8cc is self-hosting, which means 8cc can 7 | compile itself. 8 | 9 | 8cc's source code is carefully written to be as concise and easy-to-read as 10 | possible, so that the source code will eventually be a good study material to 11 | learn various techniques used in compilers. You might find the lexer, the 12 | preprocessor and the parser are already useful to learn how C source code is 13 | processed at each stage. 14 | 15 | It does not produce optimized assembly code; 8cc treats the CPU as a stack 16 | machine. Local variables are always assigned on the stack. Operations in the 17 | form of `A = B op C` are executed in the following way. 18 | 19 | 1. Load B and C to registers 20 | 2. Apply op to yield a result 21 | 3. Write the results back to A's location 22 | 23 | Producing optimized assembly is being planned. 24 | 25 | 26 | Build 27 | ----- 28 | 29 | Run make to build: 30 | 31 | make 32 | 33 | 8cc comes with unit tests. To run the tests, give "test" as an argument: 34 | 35 | make test 36 | 37 | The following command compiles 8cc three times. The second generation 38 | binary and the third are self-compiled ones, and it's tested that they 39 | are identical. The unit tests are run for each generation of binaries. 40 | 41 | make fulltest 42 | 43 | 8cc supports x86-64 Linux only. I'm using Ubuntu 11 as a development platform. 44 | It should work on other x86-64 Linux distributions. 45 | 46 | 47 | Author 48 | ------ 49 | 50 | Rui Ueyama 51 | 52 | 53 | Links for C compiler development 54 | -------------------------------- 55 | 56 | - lcc: A Retargetable C Compiler: Design and Implementation 57 | http://www.amazon.com/dp/0805316701, 58 | https://github.com/drh/lcc 59 | 60 | - TCC: Tiny C Compiler 61 | http://bellard.org/tcc/, 62 | http://repo.or.cz/w/tinycc.git/tree 63 | 64 | - C99 standard final draft 65 | http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf 66 | 67 | - C11 standard final draft 68 | http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf 69 | 70 | - Dave Prosser's C Preprocessing Algorithm 71 | http://www.spinellis.gr/blog/20060626/ 72 | 73 | - x86-64 ABI 74 | http://www.x86-64.org/documentation/abi.pdf 75 | -------------------------------------------------------------------------------- /test/funcargs.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | void many_ints(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, int v9) { 4 | expect(1, v1); expect(2, v2); expect(3, v3); expect(4, v4); 5 | expect(5, v5); expect(6, v6); expect(7, v7); expect(8, v8); 6 | expect(9, v9); 7 | } 8 | 9 | void many_floats(float v01, float v02, float v03, float v04, float v05, 10 | float v06, float v07, float v08, float v09, float v10, 11 | float v11, float v12, float v13, float v14, float v15, 12 | float v16, float v17) { 13 | expectf(1, v01); expectf(2, v02); expectf(3, v03); expectf(4, v04); 14 | expectf(5, v05); expectf(6, v06); expectf(7, v07); expectf(8, v08); 15 | expectf(9, v09); expectf(10, v10); expectf(11, v11); expectf(12, v12); 16 | expectf(13, v13); expectf(14, v14); expectf(15, v15); expectf(16, v16); 17 | expectf(17, v17); 18 | } 19 | 20 | void mixed(float v01, int v02, float v03, int v04, float v05, int v06, float v07, int v08, 21 | float v09, int v10, float v11, int v12, float v13, int v14, float v15, int v16, 22 | float v17, int v18, float v19, int v20, float v21, int v22, float v23, int v24, 23 | float v25, int v26, float v27, int v28, float v29, int v30, float v31, int v32, 24 | float v33, int v34, float v35, int v36, float v37, int v38, float v39, int v40) { 25 | expectf(1.0, v01); expect(2, v02); expectf(3.0, v03); expect(4, v04); 26 | expectf(5.0, v05); expect(6, v06); expectf(7.0, v07); expect(8, v08); 27 | expectf(9.0, v09); expect(10, v10); expectf(11.0, v11); expect(12, v12); 28 | expectf(13.0, v13); expect(14, v14); expectf(15.0, v15); expect(16, v16); 29 | expectf(17.0, v17); expect(18, v18); expectf(19.0, v19); expect(20, v20); 30 | expectf(21.0, v21); expect(22, v22); expectf(23.0, v23); expect(24, v24); 31 | expectf(25.0, v25); expect(26, v26); expectf(27.0, v27); expect(28, v28); 32 | expectf(29.0, v29); expect(30, v30); expectf(31.0, v31); expect(32, v32); 33 | expectf(33.0, v33); expect(34, v34); expectf(35.0, v35); expect(36, v36); 34 | expectf(37.0, v37); expect(38, v38); expectf(39.0, v39); expect(40, v40); 35 | } 36 | 37 | void testmain(void) { 38 | print("function argument"); 39 | 40 | many_ints(1, 2, 3, 4, 5, 6, 7, 8, 9); 41 | 42 | many_floats(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 43 | 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 44 | 17.0); 45 | 46 | mixed(1.0, 2, 3.0, 4, 5.0, 6, 7.0, 8, 9.0, 10, 47 | 11.0, 12, 13.0, 14, 15.0, 16, 17.0, 18, 19.0, 20, 48 | 21.0, 22, 23.0, 24, 25.0, 26, 27.0, 28, 29.0, 30, 49 | 31.0, 32, 33.0, 34, 35.0, 36, 37.0, 38, 39.0, 40); 50 | } 51 | -------------------------------------------------------------------------------- /string.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include 5 | #include 6 | #include 7 | #include "8cc.h" 8 | 9 | #define INIT_SIZE 8 10 | 11 | String *make_string(void) { 12 | String *r = malloc(sizeof(String)); 13 | r->body = malloc(INIT_SIZE); 14 | r->nalloc = INIT_SIZE; 15 | r->len = 0; 16 | r->body[0] = '\0'; 17 | return r; 18 | } 19 | 20 | static void realloc_body(String *s) { 21 | int newsize = s->nalloc * 2; 22 | char *body = malloc(newsize); 23 | strcpy(body, s->body); 24 | s->body = body; 25 | s->nalloc = newsize; 26 | } 27 | 28 | char *get_cstring(String *s) { 29 | return s->body; 30 | } 31 | 32 | int string_len(String *s) { 33 | return s->len; 34 | } 35 | 36 | void string_append(String *s, char c) { 37 | if (s->nalloc == (s->len + 1)) 38 | realloc_body(s); 39 | s->body[s->len++] = c; 40 | s->body[s->len] = '\0'; 41 | } 42 | 43 | void string_appendf(String *s, char *fmt, ...) { 44 | va_list args; 45 | for (;;) { 46 | int avail = s->nalloc - s->len; 47 | va_start(args, fmt); 48 | int written = vsnprintf(s->body + s->len, avail, fmt, args); 49 | va_end(args); 50 | if (avail <= written) { 51 | realloc_body(s); 52 | continue; 53 | } 54 | s->len += written; 55 | return; 56 | } 57 | } 58 | 59 | char *vformat(char *fmt, va_list ap) { 60 | String *s = make_string(); 61 | va_list aq; 62 | for (;;) { 63 | int avail = s->nalloc - s->len; 64 | va_copy(aq, ap); 65 | int written = vsnprintf(s->body + s->len, avail, fmt, aq); 66 | va_end(aq); 67 | if (avail <= written) { 68 | realloc_body(s); 69 | continue; 70 | } 71 | s->len += written; 72 | return get_cstring(s); 73 | } 74 | } 75 | 76 | char *format(char *fmt, ...) { 77 | va_list ap; 78 | va_start(ap, fmt); 79 | char *r = vformat(fmt, ap); 80 | va_end(ap); 81 | return r; 82 | } 83 | 84 | char *quote_cstring(char *p) { 85 | String *s = make_string(); 86 | while (*p) { 87 | if (*p == '\"' || *p == '\\') 88 | string_appendf(s, "\\%c", *p); 89 | else if (*p == '\n') 90 | string_appendf(s, "\\n"); 91 | else 92 | string_append(s, *p); 93 | p++; 94 | } 95 | return get_cstring(s); 96 | } 97 | 98 | char *quote_char(char c) { 99 | if (c == '\\') return format("'\\%c'", c); 100 | if (c == '\'') return format("'\\''"); 101 | return format("'%c'", c); 102 | } 103 | -------------------------------------------------------------------------------- /test/function.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | #include 6 | 7 | int t1(void) { 8 | return 77; 9 | } 10 | 11 | void t2(int a) { 12 | expect(79, a); 13 | } 14 | 15 | void t3(int a, int b, int c, int d, int e, int f) { 16 | expect(1, a); 17 | expect(2, b); 18 | expect(3, c); 19 | expect(4, d); 20 | expect(5, e); 21 | expect(6, f); 22 | } 23 | 24 | int t4a(int *p) { 25 | return *p; 26 | } 27 | 28 | void t4(void) { 29 | int a[] = { 98 }; 30 | expect(98, t4a(a)); 31 | } 32 | 33 | void t5a(int *p) { 34 | expect(99, *p); p=p+1; 35 | expect(98, *p); p=p+1; 36 | expect(97, *p); 37 | } 38 | 39 | void t5b(int p[]) { 40 | expect(99, *p); p=p+1; 41 | expect(98, *p); p=p+1; 42 | expect(97, *p); 43 | } 44 | 45 | void t5(void) { 46 | int a[] = {1, 2, 3}; 47 | int *p = a; 48 | *p = 99; p = p + 1; 49 | *p = 98; p = p + 1; 50 | *p = 97; 51 | t5a(a); 52 | t5b(a); 53 | } 54 | 55 | int t6(); 56 | int t6(void) { 57 | return 3; 58 | } 59 | 60 | int t7(int a, int b); 61 | int t7(int a, int b) { 62 | return a * b; 63 | } 64 | 65 | int t8(int a, ...) { 66 | expect(23, a); 67 | } 68 | 69 | void t9(void) { 70 | return; 71 | } 72 | 73 | int ptrtest1(void) { 74 | return 55; 75 | } 76 | 77 | int ptrtest2(int a) { 78 | return a * 2; 79 | } 80 | 81 | float ptrtest3(float a) { 82 | return a * 2; 83 | } 84 | 85 | void func_ptr_call(void) { 86 | expectf(4, ptrtest3(2)); 87 | int (*p1)(void) = ptrtest1; 88 | expect(55, p1()); 89 | int (*p2)(int) = ptrtest2; 90 | expect(110, p2(55)); 91 | float (*p3)(float) = ptrtest3; 92 | expectf(4, p3(2)); 93 | 94 | int (*p1)(void) = & & & &ptrtest1; 95 | expect(55, (**p1)()); 96 | } 97 | 98 | void func_name(void) { 99 | expect_string("func_name", __func__); 100 | expect_string("func_name", __FUNCTION__); 101 | } 102 | 103 | void empty(void) { 104 | } 105 | 106 | void empty2(void) { 107 | ;;; 108 | } 109 | 110 | int booltest1(int x); 111 | 112 | bool booltest2(int x) { 113 | return x; 114 | } 115 | 116 | void test_bool(void) { 117 | expect(0, booltest1(256)); 118 | expect(1, booltest1(257)); 119 | expect(1, booltest2(512)); 120 | expect(1, booltest2(513)); 121 | } 122 | 123 | void testmain(void) { 124 | print("function"); 125 | 126 | expect(77, t1()); 127 | t2(79); 128 | t3(1, 2, 3, 4, 5, 6); 129 | t4(); 130 | t5(); 131 | expect(3, t6()); 132 | expect(12, t7(3, 4)); 133 | t8(23); 134 | t9(); 135 | func_ptr_call(); 136 | func_name(); 137 | empty(); 138 | empty2(); 139 | test_bool(); 140 | } 141 | -------------------------------------------------------------------------------- /test/initializer.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void verify(int *expected, int *got, int len) { 7 | for (int i = 0; i < len; i++) 8 | expect(expected[i], got[i]); 9 | } 10 | 11 | void verify_short(short *expected, short *got, int len) { 12 | for (int i = 0; i < len; i++) 13 | expect(expected[i], got[i]); 14 | } 15 | 16 | void test_array(void) { 17 | int x[] = { 1, 3, 5 }; 18 | expect(1, x[0]); 19 | expect(3, x[1]); 20 | expect(5, x[2]); 21 | 22 | int ye[] = { 1, 3, 5, 2, 4, 6, 3, 5, 7, 0, 0, 0 }; 23 | int y1[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 }, }; 24 | verify(ye, y1, 12); 25 | int y2[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 }; 26 | verify(ye, y2, 12); 27 | 28 | int ze[] = { 1, 0, 0, 2, 0, 0, 3, 0, 0, 4, 0, 0 }; 29 | int z[4][3] = { { 1 }, { 2 }, { 3 }, { 4 } }; 30 | verify(ze, z, 12); 31 | 32 | short qe[24] = { 1, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 4, 5, 6 }; 33 | short q[4][3][2] = { { 1 }, { 2, 3 }, { 4, 5, 6 } }; 34 | verify_short(qe, q, 24); 35 | } 36 | 37 | void test_string(void) { 38 | char s[] = "abc"; 39 | expect_string("abc", s); 40 | char t[] = { "def" }; 41 | expect_string("def", t); 42 | } 43 | 44 | void test_struct(void) { 45 | int we[] = { 1, 0, 0, 0, 2, 0, 0, 0 }; 46 | struct { int a[3]; int b; } w[] = { { 1 }, 2 }; 47 | verify(we, &w, 8); 48 | } 49 | 50 | void test_primitive(void) { 51 | int a = { 59 }; 52 | expect(59, a); 53 | } 54 | 55 | void test_nested(void) { 56 | struct { 57 | struct { 58 | struct { int a; int b; } x; 59 | struct { char c[8]; } y; 60 | } w; 61 | } v = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, }; 62 | expect(1, v.w.x.a); 63 | expect(2, v.w.x.b); 64 | expect(3, v.w.y.c[0]); 65 | expect(10, v.w.y.c[7]); 66 | } 67 | 68 | void test_designated(void) { 69 | struct { int x; int y; } v1 = { .y = 1, .x = 5 }; 70 | expect(5, v1.x); 71 | expect(1, v1.y); 72 | 73 | struct { int x; int y; } v2 = { .y = 7 }; 74 | expect(7, v2.y); 75 | 76 | struct { int x; int y; int z; } v3 = { .y = 12, 17 }; 77 | expect(12, v3.y); 78 | expect(17, v3.z); 79 | } 80 | 81 | void test_zero(void) { 82 | struct tag { int x, y; }; 83 | struct tag v0 = (struct tag){ 6 }; 84 | expect(6, v0.x); 85 | expect(0, v0.y); 86 | 87 | struct { int x; int y; } v1 = { 6 }; 88 | expect(6, v1.x); 89 | expect(0, v1.y); 90 | 91 | struct { int x; int y; } v2 = { .y = 3 }; 92 | expect(0, v2.x); 93 | expect(3, v2.y); 94 | 95 | struct { union { int x, y; }; } v3 = { .x = 61 }; 96 | expect(61, v3.x); 97 | } 98 | 99 | 100 | void test_typedef(void) { 101 | typedef int A[]; 102 | A a = { 1, 2 }; 103 | A b = { 3, 4, 5 }; 104 | expect(2, sizeof(a) / sizeof(*a)); 105 | expect(3, sizeof(b) / sizeof(*b)); 106 | } 107 | 108 | void testmain(void) { 109 | print("initializer"); 110 | 111 | test_array(); 112 | test_string(); 113 | test_struct(); 114 | test_primitive(); 115 | test_nested(); 116 | test_designated(); 117 | test_zero(); 118 | test_typedef(); 119 | } 120 | -------------------------------------------------------------------------------- /list.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include 5 | #include "list.h" 6 | #include "error.h" 7 | 8 | List *make_list(void) { 9 | List *r = malloc(sizeof(List)); 10 | r->len = 0; 11 | r->head = r->tail = NULL; 12 | return r; 13 | } 14 | 15 | List *make_list1(void *e) { 16 | List *r = make_list(); 17 | list_push(r, e); 18 | return r; 19 | } 20 | 21 | void *make_node(void *elem) { 22 | ListNode *r = malloc(sizeof(ListNode)); 23 | r->elem = elem; 24 | r->next = NULL; 25 | r->prev = NULL; 26 | return r; 27 | } 28 | 29 | List *list_copy(List *list) { 30 | List *r = make_list(); 31 | for (Iter *i = list_iter(list); !iter_end(i);) 32 | list_push(r, iter_next(i)); 33 | return r; 34 | } 35 | 36 | void list_push(List *list, void *elem) { 37 | ListNode *node = make_node(elem); 38 | if (!list->head) { 39 | list->head = node; 40 | } else { 41 | list->tail->next = node; 42 | node->prev = list->tail; 43 | } 44 | list->tail = node; 45 | list->len++; 46 | } 47 | 48 | void list_append(List *a, List *b) { 49 | for (Iter *i = list_iter(b); !iter_end(i);) 50 | list_push(a, iter_next(i)); 51 | } 52 | 53 | void *list_pop(List *list) { 54 | if (!list->head) return NULL; 55 | void *r = list->tail->elem; 56 | list->tail = list->tail->prev; 57 | if (list->tail) 58 | list->tail->next = NULL; 59 | else 60 | list->head = NULL; 61 | list->len--; 62 | return r; 63 | } 64 | 65 | void *list_shift(List *list) { 66 | if (!list->head) return NULL; 67 | void *r = list->head->elem; 68 | list->head = list->head->next; 69 | if (list->head) 70 | list->head->prev = NULL; 71 | else 72 | list->tail = NULL; 73 | list->len--; 74 | return r; 75 | } 76 | 77 | void list_unshift(List *list, void *elem) { 78 | ListNode *node = make_node(elem); 79 | node->next = list->head; 80 | if (list->head) 81 | list->head->prev = node; 82 | list->head = node; 83 | if (!list->tail) 84 | list->tail = node; 85 | list->len++; 86 | } 87 | 88 | void *list_get(List *list, int index) { 89 | if (index < 0 || list->len <= index) 90 | return NULL; 91 | ListNode *p = list->head; 92 | for (int i = 0; i < index; i++) 93 | p = p->next; 94 | return p->elem; 95 | } 96 | 97 | void *list_head(List *list) { 98 | return list->head ? list->head->elem : NULL; 99 | } 100 | 101 | void *list_tail(List *list) { 102 | return list->tail ? list->tail->elem : NULL; 103 | } 104 | 105 | List *list_reverse(List *list) { 106 | List *r = make_list(); 107 | for (Iter *i = list_iter(list); !iter_end(i);) 108 | list_unshift(r, iter_next(i)); 109 | return r; 110 | } 111 | 112 | int list_len(List *list) { 113 | return list->len; 114 | } 115 | 116 | Iter *list_iter(List *list) { 117 | Iter *r = malloc(sizeof(Iter)); 118 | r->ptr = list->head; 119 | return r; 120 | } 121 | 122 | void *iter_next(Iter *iter) { 123 | if (!iter->ptr) 124 | return NULL; 125 | void *r = iter->ptr->elem; 126 | iter->ptr = iter->ptr->next; 127 | return r; 128 | } 129 | 130 | bool iter_end(Iter *iter) { 131 | return !iter->ptr; 132 | } 133 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function fail { 4 | echo -n -e '\e[1;31m[ERROR]\e[0m ' 5 | echo "$1" 6 | exit 1 7 | } 8 | 9 | function compile { 10 | echo "$1" | ./8cc -o tmp.s - || fail "Failed to compile $1" 11 | gcc -o tmp.out tmp.s 12 | [ $? -ne 0 ] && fail "GCC failed: $1" 13 | } 14 | 15 | function assertequal { 16 | [ "$1" != "$2" ] && fail "Test failed: $2 expected but got $1" 17 | } 18 | 19 | function testastf { 20 | result="$(echo "$2" | ./8cc -o - -a -)" 21 | [ $? -ne 0 ] && fail "Failed to compile $2" 22 | assertequal "$result" "$1" 23 | } 24 | 25 | function testast { 26 | testastf "$1" "int f(){$2}" 27 | } 28 | 29 | function testm { 30 | compile "$2" 31 | assertequal "$(./tmp.out)" "$1" 32 | } 33 | 34 | function testcpp { 35 | echo "$2" | ./8cc -o - -E $3 - > tmp.s || fail "Failed to compile $1" 36 | assertequal "$(cat tmp.s)" "$1" 37 | } 38 | 39 | 40 | function testfail { 41 | echo "$expr" | ./8cc -o /dev/null - 2> /dev/null 42 | expr="int f(){$1}" 43 | echo "$expr" | ./8cc -o /dev/null $OPTION - 2> /dev/null 44 | [ $? -eq 0 ] && fail "Should fail to compile, but succeded: $expr" 45 | } 46 | 47 | # Parser 48 | testast '(()=>int)f(){1;}' '1;' 49 | testast '(()=>int)f(){1L;}' '1L;' 50 | # testast '(()=>int)f(){1152921504606846976L;}' '1152921504606846976;' 51 | testast '(()=>int)f(){(+ (- (+ 1 2) 3) 4);}' '1+2-3+4;' 52 | testast '(()=>int)f(){(+ (+ 1 (* 2 3)) 4);}' '1+2*3+4;' 53 | testast '(()=>int)f(){(+ (* 1 2) (* 3 4));}' '1*2+3*4;' 54 | testast '(()=>int)f(){(+ (/ 4 2) (/ 6 3));}' '4/2+6/3;' 55 | testast '(()=>int)f(){(/ (/ 24 2) 4);}' '24/2/4;' 56 | testast '(()=>int)f(){(decl int a 3@0);}' 'int a=3;' 57 | testast "(()=>int)f(){(decl char c (conv 97=>char)@0);}" "char c='a';" 58 | testast '(()=>int)f(){(decl *char s "abcd"@0);}' 'char *s="abcd";' 59 | #testast "(()=>int)f(){(decl [5]char s 'a'@0 's'@1 'd'@2 'f'@3 '\0'@4);}" 'char s[5]="asdf";' 60 | testast "(()=>int)f(){(decl [5]char s 'a'@0 's'@1 'd'@2 'f'@3 '\0'@4);}" 'char s[]="asdf";' 61 | testast '(()=>int)f(){(decl [3]int a 1@0 2@4 3@8);}' 'int a[3]={1,2,3};' 62 | testast '(()=>int)f(){(decl [3]int a 1@0 2@4 3@8);}' 'int a[]={1,2,3};' 63 | testast '(()=>int)f(){(decl [3][5]int a);}' 'int a[3][5];' 64 | testast '(()=>int)f(){(decl [5]*int a);}' 'int *a[5];' 65 | testast '(()=>int)f(){(decl int a 1@0);(decl int b 2@0);(= lv=a (= lv=b 3));}' 'int a=1;int b=2;a=b=3;' 66 | testast '(()=>int)f(){(decl int a 3@0);(addr lv=a);}' 'int a=3;&a;' 67 | testast '(()=>int)f(){(decl int a 3@0);(deref (addr lv=a));}' 'int a=3;*&a;' 68 | testast '(()=>int)f(){(decl int a 3@0);(decl *int b (addr lv=a)@0);(deref lv=b);}' 'int a=3;int *b=&a;*b;' 69 | testast '(()=>int)f(){(if 1 {2;});}' 'if(1){2;}' 70 | testast '(()=>int)f(){(if 1 {2;} {3;});}' 'if(1){2;}else{3;}' 71 | testast '(()=>int)f(){(for (decl int a 1@0) 3 7 {5;});}' 'for(int a=1;3;7){5;}' 72 | testast '(()=>int)f(){"abcd";}' '"abcd";' 73 | testast "(()=>int)f(){99;}" "'c';" 74 | testast '(()=>int)f(){(int)a();}' 'a();' 75 | testast '(()=>int)f(){(int)a(1,2,3,4,5,6);}' 'a(1,2,3,4,5,6);' 76 | testast '(()=>int)f(){(return (conv 1=>int));}' 'return 1;' 77 | testast '(()=>int)f(){(< 1 2);}' '1<2;' 78 | testast '(()=>int)f(){(> 1 2);}' '1>2;' 79 | testast '(()=>int)f(){(== 1 2);}' '1==2;' 80 | # testast '(()=>int)f(){(deref (+ 1 2));}' '1[2];' 81 | testast '(()=>int)f(){(decl int a 1@0);(post++ lv=a);}' 'int a=1;a++;' 82 | testast '(()=>int)f(){(decl int a 1@0);(post-- lv=a);}' 'int a=1;a--;' 83 | testast '(()=>int)f(){(! 1);}' '!1;' 84 | testast '(()=>int)f(){(? 1 2 3);}' '1?2:3;' 85 | testast '(()=>int)f(){(and 1 2);}' '1&&2;' 86 | testast '(()=>int)f(){(or 1 2);}' '1||2;' 87 | testast '(()=>int)f(){(& 1 2);}' '1&2;' 88 | testast '(()=>int)f(){(| 1 2);}' '1|2;' 89 | testast '(()=>int)f(){1.200000;}' '1.2;' 90 | testast '(()=>int)f(){(+ 1.200000 (conv 1=>double));}' '1.2+1;' 91 | 92 | testastf '((int)=>int)f(int lv=c){lv=c;}' 'int f(int c){c;}' 93 | testastf '((int)=>int)f(int lv=c){lv=c;}((int)=>int)g(int lv=d){lv=d;}' 'int f(int c){c;} int g(int d){d;}' 94 | testastf '(decl int a 3@0)' 'int a=3;' 95 | 96 | testastf '(decl (struct) a)' 'struct {} a;' 97 | testastf '(decl (struct (int) (char)) a)' 'struct {int x; char y;} a;' 98 | testastf '(decl (struct ([3]int)) a)' 'struct {int x[3];} a;' 99 | testast '(()=>int)f(){(decl (struct (int)) a);(decl *(struct (int)) p);(deref lv=p).x;}' 'struct tag {int x;} a; struct tag *p; p->x;' 100 | testast '(()=>int)f(){(decl (struct (int)) a);lv=a.x;}' 'struct {int x;} a; a.x;' 101 | testast '(()=>int)f(){(decl (struct (int:0:5) (int:5:13)) x);}' 'struct { int a:5; int b:8; } x;' 102 | 103 | testfail '0abc;' 104 | # testfail '1+;' 105 | testfail '1=2;' 106 | 107 | # & is only applicable to an lvalue 108 | testfail '&"a";' 109 | testfail '&1;' 110 | testfail '&a();' 111 | 112 | # -D command line options 113 | testcpp '77' 'foo' '-Dfoo=77' 114 | 115 | echo "All tests passed" 116 | -------------------------------------------------------------------------------- /utiltest.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include 5 | #include "8cc.h" 6 | 7 | #define assert_true(expr) assert_true2(__LINE__, #expr, (expr)) 8 | #define assert_null(...) assert_null2(__LINE__, __VA_ARGS__) 9 | #define assert_string(...) assert_string2(__LINE__, __VA_ARGS__) 10 | #define assert_int(...) assert_int2(__LINE__, __VA_ARGS__) 11 | 12 | static void assert_true2(int line, char *expr, int result) { 13 | if (!result) 14 | error("%d: assert_true: %s", line, expr); 15 | } 16 | 17 | static void assert_null2(int line, void *p) { 18 | if (p) 19 | error("%d: Null expected", line); 20 | } 21 | 22 | static void assert_string2(int line, char *s, char *t) { 23 | if (strcmp(s, t)) 24 | error("%d: Expected \"%s\" but got \"%s\"", line, s, t); 25 | } 26 | 27 | static void assert_int2(int line, long a, long b) { 28 | if (a != b) 29 | error("%d: Expected %ld but got %ld", line, a, b); 30 | } 31 | 32 | static void test_string(void) { 33 | String *s = make_string(); 34 | string_append(s, 'a'); 35 | assert_string("a", get_cstring(s)); 36 | string_append(s, 'b'); 37 | assert_string("ab", get_cstring(s)); 38 | 39 | string_appendf(s, "."); 40 | assert_string("ab.", get_cstring(s)); 41 | string_appendf(s, "%s", "0123456789"); 42 | assert_string("ab.0123456789", get_cstring(s)); 43 | } 44 | 45 | static void test_list(void) { 46 | List *list = make_list(); 47 | assert_int(0, list_len(list)); 48 | list_push(list, (void *)1); 49 | assert_int(1, list_len(list)); 50 | list_push(list, (void *)2); 51 | assert_int(2, list_len(list)); 52 | 53 | Iter *iter = list_iter(list); 54 | assert_int(1, (long)iter_next(iter)); 55 | assert_int(false, iter_end(iter)); 56 | assert_int(2, (long)iter_next(iter)); 57 | assert_int(true, iter_end(iter)); 58 | assert_int(0, (long)iter_next(iter)); 59 | assert_int(true, iter_end(iter)); 60 | 61 | List *copy = list_copy(list); 62 | assert_int(2, list_len(copy)); 63 | assert_int(1, (long)list_get(copy, 0)); 64 | assert_int(2, (long)list_get(copy, 1)); 65 | 66 | List *rev = list_reverse(list); 67 | iter = list_iter(rev); 68 | assert_int(2, (long)iter_next(iter)); 69 | assert_int(1, (long)iter_next(iter)); 70 | assert_int(0, (long)iter_next(iter)); 71 | 72 | assert_int(2, list_len(rev)); 73 | assert_int(1, (long)list_pop(rev)); 74 | assert_int(1, list_len(rev)); 75 | assert_int(2, (long)list_pop(rev)); 76 | assert_int(0, list_len(rev)); 77 | assert_int(0, (long)list_pop(rev)); 78 | 79 | List *list2 = make_list(); 80 | list_push(list2, (void *)5); 81 | list_push(list2, (void *)6); 82 | assert_int(5, (long)list_shift(list2)); 83 | assert_int(6, (long)list_shift(list2)); 84 | assert_int(0, (long)list_shift(list2)); 85 | 86 | List *list3 = make_list(); 87 | assert_int(0, (long)list_head(list3)); 88 | assert_int(0, (long)list_tail(list3)); 89 | list_push(list3, (void *)1); 90 | assert_int(1, (long)list_head(list3)); 91 | assert_int(1, (long)list_tail(list3)); 92 | list_push(list3, (void *)2); 93 | assert_int(1, (long)list_head(list3)); 94 | assert_int(2, (long)list_tail(list3)); 95 | 96 | List *list4 = make_list(); 97 | list_push(list4, (void *)1); 98 | list_push(list4, (void *)2); 99 | assert_int(1, (long)list_get(list4, 0)); 100 | assert_int(2, (long)list_get(list4, 1)); 101 | assert_int(0, (long)list_get(list4, 2)); 102 | } 103 | 104 | static void test_dict(void) { 105 | Dict *dict = make_dict(NULL); 106 | assert_null(dict_parent(dict)); 107 | assert_null(dict_get(dict, "abc")); 108 | dict_put(dict, "abc", (void *)50); 109 | dict_put(dict, "xyz", (void *)70); 110 | assert_int(50, (long)dict_get(dict, "abc")); 111 | assert_int(70, (long)dict_get(dict, "xyz")); 112 | 113 | Dict *dict2 = make_dict(dict); 114 | assert_true(dict_parent(dict2) == dict); 115 | assert_int(50, (long)dict_get(dict, "abc")); 116 | assert_int(70, (long)dict_get(dict, "xyz")); 117 | dict_put(dict2, "ABC", (void *)110); 118 | assert_int(110, (long)dict_get(dict2, "ABC")); 119 | assert_null(dict_get(dict, "ABC")); 120 | 121 | assert_int(3, list_len(dict_values(dict2))); 122 | assert_int(2, list_len(dict_values(dict))); 123 | assert_int(3, list_len(dict_keys(dict2))); 124 | assert_int(2, list_len(dict_keys(dict))); 125 | 126 | Dict *dict3 = make_dict(NULL); 127 | dict_put(dict3, "abc", (void *)10); 128 | assert_int(10, (long)dict_get(dict3, "abc")); 129 | dict_remove(dict3, "abc"); 130 | assert_int(0, (long)dict_get(dict3, "abc")); 131 | 132 | Dict *dict4 = make_dict(NULL); 133 | dict_put(dict4, "abc", (void *)50); 134 | dict_put(dict4, "abc", (void *)60); 135 | assert_int(60, (long)dict_get(dict4, "abc")); 136 | } 137 | 138 | int main(int argc, char **argv) { 139 | test_string(); 140 | test_list(); 141 | test_dict(); 142 | printf("Passed\n"); 143 | return 0; 144 | } 145 | -------------------------------------------------------------------------------- /test/control.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | int test_if1(void) { if (1) { return 'a';} return 0; } 7 | int test_if2(void) { if (0) { return 0;} return 'b'; } 8 | int test_if3(void) { if (1) { return 'c';} else { return 0; } return 0; } 9 | int test_if4(void) { if (0) { return 0;} else { return 'd'; } return 0; } 10 | int test_if5(void) { if (1) return 'e'; return 0; } 11 | int test_if6(void) { if (0) return 0; return 'f'; } 12 | int test_if7(void) { if (1) return 'g'; else return 0; return 0; } 13 | int test_if8(void) { if (0) return 0; else return 'h'; return 0; } 14 | int test_if9(void) { if (0+1) return 'i'; return 0; } 15 | int test_if10(void) { if (1-1) return 0; return 'j'; } 16 | int test_if11(void) { if (0.5) return 'k'; return 0; } 17 | 18 | void test_if(void) { 19 | expect('a', test_if1()); 20 | expect('b', test_if2()); 21 | expect('c', test_if3()); 22 | expect('d', test_if4()); 23 | expect('e', test_if5()); 24 | expect('f', test_if6()); 25 | expect('g', test_if7()); 26 | expect('h', test_if8()); 27 | expect('i', test_if9()); 28 | expect('j', test_if10()); 29 | expect('k', test_if11()); 30 | } 31 | 32 | void test_for(void) { 33 | int i; 34 | int acc = 0; 35 | for (i = 0; i < 5; i++) { 36 | acc = acc + i; 37 | } 38 | expect(10, acc); 39 | 40 | acc = 0; 41 | for (i = 0; i < 5; i++) { 42 | acc = acc + i; 43 | } 44 | expect(10, acc); 45 | 46 | acc = 0; 47 | for (i = 0; i < 100; i++) { 48 | if (i < 5) continue; 49 | if (i == 9) break; 50 | acc += i; 51 | } 52 | expect(5 + 6 + 7 + 8, acc); 53 | 54 | for (;;) 55 | break; 56 | for (i = 0; i < 100; i++) 57 | ; 58 | 59 | i = 0; 60 | for (; 0.5;) { 61 | i = 68; 62 | break; 63 | } 64 | expect(68, i); 65 | } 66 | 67 | void test_while(void) { 68 | int acc = 0; 69 | int i = 0; 70 | while (i <= 100) 71 | acc = acc + i++; 72 | expect(5050, acc); 73 | 74 | acc = 1; 75 | i = 0; 76 | while (i <= 100) { 77 | acc = acc + i++; 78 | } 79 | expect(5051, acc); 80 | 81 | acc = 0; 82 | i = 0; 83 | while (i < 10) { 84 | if (i++ < 5) continue; 85 | acc += i; 86 | if (i == 9) break; 87 | } 88 | expect(6 + 7 + 8 + 9, acc); 89 | 90 | i = 0; 91 | while (i++ < 100) 92 | ; 93 | 94 | i = 0; 95 | while (0.5) { 96 | i = 67; 97 | break; 98 | } 99 | expect(67, i); 100 | } 101 | 102 | void test_do(void) { 103 | int acc = 0; 104 | int i = 0; 105 | do { 106 | acc = acc + i++; 107 | } while (i <= 100); 108 | expect(5050, acc); 109 | 110 | i = 0; 111 | do { i = 37; } while (0); 112 | expect(37, i); 113 | 114 | acc = 0; 115 | i = 0; 116 | do { 117 | if (i++ < 5) continue; 118 | acc += i; 119 | if (i == 9) break; 120 | } while (i < 10); 121 | expect(6 + 7 + 8 + 9, acc); 122 | 123 | i = 0; 124 | do 1; while (i++ < 100); 125 | 126 | i = 0; 127 | do; while (i++ < 100); 128 | 129 | float v = 1; 130 | i = 70; 131 | do i++; while (v -= 0.5); 132 | expect(72, i); 133 | } 134 | 135 | void test_switch(void) { 136 | int a = 0; 137 | switch (1+2) { 138 | case 0: fail("0"); 139 | case 3: a = 3; break; 140 | case 1: fail("1"); 141 | } 142 | expect(a, 3); 143 | 144 | a = 0; 145 | switch (1) { 146 | case 0: a++; 147 | case 1: a++; 148 | case 2: a++; 149 | case 3: a++; 150 | } 151 | a = 3; 152 | 153 | a = 0; 154 | switch (100) { 155 | case 0: a++; 156 | default: a = 55; 157 | } 158 | expect(a, 55); 159 | 160 | a = 0; 161 | switch (100) { 162 | case 0: a++; 163 | } 164 | expect(a, 0); 165 | 166 | a = 5; 167 | switch (3) { 168 | a++; 169 | } 170 | expect(a, 5); 171 | 172 | switch (7) { 173 | case 1 ... 2: fail("switch"); 174 | case 3: fail("switch"); 175 | case 5 ... 10: break; 176 | default: fail("switch"); 177 | } 178 | 179 | a = 0; 180 | int count = 27; 181 | switch (count % 8) { 182 | case 0: do { a++; 183 | case 7: a++; 184 | case 6: a++; 185 | case 5: a++; 186 | case 4: a++; 187 | case 3: a++; 188 | case 2: a++; 189 | case 1: a++; 190 | } while ((count -= 8) > 0); 191 | } 192 | expect(27, a); 193 | 194 | switch (1) 195 | ; 196 | } 197 | 198 | void test_goto(void) { 199 | int acc = 0; 200 | goto x; 201 | acc = 5; 202 | x: expect(0, acc); 203 | 204 | int i = 0; 205 | acc = 0; 206 | y: if (i > 10) goto z; 207 | acc += i++; 208 | goto y; 209 | z: if (i > 11) goto a; 210 | expect(55, acc); 211 | i++; 212 | goto y; 213 | a: 214 | } 215 | 216 | void test_computed_goto(void) { 217 | struct { void *x, *y, *z, *a; } t = { &&x, &&y, &&z, &&a }; 218 | int acc = 0; 219 | goto *t.x; 220 | acc = 5; 221 | x: expect(0, acc); 222 | 223 | int i = 0; 224 | acc = 0; 225 | y: if (i > 10) goto *t.z; 226 | acc += i++; 227 | goto *t.y; 228 | z: if (i > 11) goto *t.a; 229 | expect(55, acc); 230 | i++; 231 | goto *t.y; 232 | a: 233 | } 234 | 235 | void test_logor(void) { 236 | expect(1, 0 || 3); 237 | expect(1, 5 || 0); 238 | expect(0, 0 || 0); 239 | } 240 | 241 | void testmain(void) { 242 | print("control flow"); 243 | test_if(); 244 | test_for(); 245 | test_while(); 246 | test_do(); 247 | test_switch(); 248 | test_goto(); 249 | test_computed_goto(); 250 | test_logor(); 251 | } 252 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "8cc.h" 11 | 12 | static char *inputfile; 13 | static char *outputfile; 14 | static bool wantast; 15 | static bool cpponly; 16 | static bool dontasm; 17 | static bool dontlink; 18 | static String *cppdefs; 19 | static List *tmpfiles = &EMPTY_LIST; 20 | 21 | static void usage(void) { 22 | fprintf(stderr, 23 | "Usage: 8cc [ -E ][ -a ] [ -h ] \n\n" 24 | "\n" 25 | " -I add to include path\n" 26 | " -E print preprocessed source code\n" 27 | " -D name Predefine name as a macro\n" 28 | " -D name=def\n" 29 | " -S Stop before assembly (default)\n" 30 | " -c Do not run linker (default)\n" 31 | " -U name Undefine name\n" 32 | " -a print AST\n" 33 | " -d cpp print tokens for debugging\n" 34 | " -o filename Output to the specified file\n" 35 | " -h print this help\n" 36 | "\n" 37 | "One of -a, -c, -E or -S must be specified.\n\n"); 38 | exit(1); 39 | } 40 | 41 | static void delete_temp_files(void) { 42 | Iter *iter = list_iter(tmpfiles); 43 | while (!iter_end(iter)) 44 | unlink(iter_next(iter)); 45 | } 46 | 47 | static char *replace_suffix(char *filename, char suffix) { 48 | char *r = format("%s", filename); 49 | char *p = r + strlen(r) - 1; 50 | if (*p != 'c') 51 | error("filename suffix is not .c"); 52 | *p = suffix; 53 | return r; 54 | } 55 | 56 | static FILE *open_output_file(void) { 57 | if (!outputfile) { 58 | if (dontasm) { 59 | outputfile = replace_suffix(inputfile, 's'); 60 | } else { 61 | outputfile = format("/tmp/8ccXXXXXX.s"); 62 | if (!mkstemps(outputfile, 2)) 63 | perror("mkstemps"); 64 | list_push(tmpfiles, outputfile); 65 | } 66 | } 67 | if (!strcmp(outputfile, "-")) 68 | return stdout; 69 | FILE *fp = fopen(outputfile, "w"); 70 | if (!fp) 71 | perror("fopen"); 72 | return fp; 73 | } 74 | 75 | static void parse_debug_arg(char *s) { 76 | char *tok, *save; 77 | while ((tok = strtok_r(s, ",", &save)) != NULL) { 78 | s = NULL; 79 | if (!strcmp(tok, "cpp")) 80 | debug_cpp = true; 81 | else 82 | error("Unknown debug parameter: %s", tok); 83 | } 84 | } 85 | 86 | static void parseopt(int argc, char **argv) { 87 | cppdefs = make_string(); 88 | for (;;) { 89 | int opt = getopt(argc, argv, "I:ED:SU:acd:o:h"); 90 | if (opt == -1) 91 | break; 92 | switch (opt) { 93 | case 'I': 94 | add_include_path(optarg); 95 | break; 96 | case 'E': 97 | cpponly = true; 98 | break; 99 | case 'D': { 100 | char *p = strchr(optarg, '='); 101 | if (p) 102 | *p = ' '; 103 | string_appendf(cppdefs, "#define %s\n", optarg); 104 | break; 105 | } 106 | case 'S': 107 | dontasm = true; 108 | break; 109 | case 'U': 110 | string_appendf(cppdefs, "#undef %s\n", optarg); 111 | break; 112 | case 'a': 113 | wantast = true; 114 | break; 115 | case 'c': 116 | dontlink = true; 117 | break; 118 | case 'd': 119 | parse_debug_arg(optarg); 120 | break; 121 | case 'o': 122 | outputfile = optarg; 123 | break; 124 | case 'h': 125 | default: 126 | usage(); 127 | } 128 | } 129 | if (optind != argc - 1) 130 | usage(); 131 | 132 | if (!wantast && !cpponly && !dontasm && !dontlink) 133 | error("One of -a, -c, -E or -S must be specified"); 134 | inputfile = argv[optind]; 135 | } 136 | 137 | static void preprocess(void) { 138 | for (;;) { 139 | Token *tok = read_token(); 140 | if (!tok) 141 | break; 142 | if (tok->bol) 143 | printf("\n"); 144 | if (tok->nspace) 145 | printf("%*c", tok->nspace, ' '); 146 | printf("%s", t2s(tok)); 147 | } 148 | printf("\n"); 149 | exit(0); 150 | } 151 | 152 | int main(int argc, char **argv) { 153 | setbuf(stdout, NULL); 154 | if (atexit(delete_temp_files)) 155 | perror("atexit"); 156 | cpp_init(); 157 | parse_init(); 158 | parseopt(argc, argv); 159 | if (string_len(cppdefs) > 0) 160 | cpp_eval(get_cstring(cppdefs)); 161 | lex_init(inputfile); 162 | set_output_file(open_output_file()); 163 | 164 | if (cpponly) 165 | preprocess(); 166 | 167 | if (wantast) 168 | suppress_warning = true; 169 | List *toplevels = read_toplevels(); 170 | for (Iter *i = list_iter(toplevels); !iter_end(i);) { 171 | Node *v = iter_next(i); 172 | if (wantast) 173 | printf("%s", a2s(v)); 174 | else 175 | emit_toplevel(v); 176 | } 177 | 178 | close_output_file(); 179 | 180 | if (!wantast && !dontasm) { 181 | char *objfile = replace_suffix(inputfile, 'o'); 182 | pid_t pid = fork(); 183 | if (pid < 0) perror("fork"); 184 | if (pid == 0) { 185 | execlp("as", "as", "-o", objfile, "-c", outputfile, (char *)NULL); 186 | perror("execl failed"); 187 | } 188 | int status; 189 | waitpid(pid, &status, 0); 190 | if (status < 0) 191 | error("as failed"); 192 | } 193 | return 0; 194 | } 195 | -------------------------------------------------------------------------------- /test/struct.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void t1(void) { 7 | struct { int a; } x; 8 | x.a = 61; 9 | expect(61, x.a); 10 | } 11 | 12 | void t2(void) { 13 | struct { int a; int b; } x; 14 | x.a = 61; 15 | x.b = 2; 16 | expect(63, x.a + x.b); 17 | } 18 | 19 | void t3(void) { 20 | struct { int a; struct { char b; int c; } y; } x; 21 | x.a = 61; 22 | x.y.b = 3; 23 | x.y.c = 3; 24 | expect(67, x.a + x.y.b + x.y.c); 25 | } 26 | 27 | void t4(void) { 28 | struct tag4 { int a; struct { char b; int c; } y; } x; 29 | struct tag4 s; 30 | s.a = 61; 31 | s.y.b = 3; 32 | s.y.c = 3; 33 | expect(67, s.a + s.y.b + s.y.c); 34 | } 35 | 36 | void t5(void) { 37 | struct tag5 { int a; } x; 38 | struct tag5 *p = &x; 39 | x.a = 68; 40 | expect(68, (*p).a); 41 | } 42 | 43 | void t6(void) { 44 | struct tag6 { int a; } x; 45 | struct tag6 *p = &x; 46 | (*p).a = 69; 47 | expect(69, x.a); 48 | } 49 | 50 | void t7(void) { 51 | struct tag7 { int a; int b; } x; 52 | struct tag7 *p = &x; 53 | x.b = 71; 54 | expect(71, (*p).b); 55 | } 56 | 57 | void t8(void) { 58 | struct tag8 { int a; int b; } x; 59 | struct tag8 *p = &x; 60 | (*p).b = 72; 61 | expect(72, x.b); 62 | } 63 | 64 | void t9(void) { 65 | struct tag9 { int a[3]; int b[3]; } x; 66 | x.a[0] = 73; 67 | expect(73, x.a[0]); 68 | x.b[1] = 74; 69 | expect(74, x.b[1]); 70 | expect(74, x.a[4]); 71 | } 72 | 73 | struct tag10 { 74 | int a; 75 | struct tag10a { 76 | char b; 77 | int c; 78 | } y; 79 | } v10; 80 | void t10(void) { 81 | v10.a = 71; 82 | v10.y.b = 3; 83 | v10.y.c = 3; 84 | expect(77, v10.a + v10.y.b + v10.y.c); 85 | } 86 | 87 | struct tag11 { int a; } v11; 88 | void t11(void) { 89 | struct tag11 *p = &v11; 90 | v11.a = 78; 91 | expect(78, (*p).a); 92 | expect(78, v11.a); 93 | expect(78, p->a); 94 | p->a = 79; 95 | expect(79, (*p).a); 96 | expect(79, v11.a); 97 | expect(79, p->a); 98 | } 99 | 100 | struct tag12 { 101 | int a; 102 | int b; 103 | } x; 104 | void t12(void) { 105 | struct tag12 a[3]; 106 | a[0].a = 83; 107 | expect(83, a[0].a); 108 | a[0].b = 84; 109 | expect(84, a[0].b); 110 | a[1].b = 85; 111 | expect(85, a[1].b); 112 | int *p = a; 113 | expect(85, p[3]); 114 | } 115 | 116 | void t13(void) { 117 | struct { char c; } v = { 'a' }; 118 | expect('a', v.c); 119 | } 120 | 121 | void t14(void) { 122 | struct { int a[3]; } v = { { 1, 2, 3 } }; 123 | expect(2, v.a[1]); 124 | } 125 | 126 | void unnamed(void) { 127 | struct { 128 | union { 129 | struct { int x; int y; }; 130 | struct { char c[8]; }; 131 | }; 132 | } v; 133 | v.x = 1; 134 | v.y = 7; 135 | expect(1, v.c[0]); 136 | expect(7, v.c[4]); 137 | } 138 | 139 | void assign(void) { 140 | struct { int a, b, c; short d; char f; } v1, v2; 141 | v1.a = 3; 142 | v1.b = 5; 143 | v1.c = 7; 144 | v1.d = 9; 145 | v1.f = 11; 146 | v2 = v1; 147 | expect(3, v2.a); 148 | expect(5, v2.b); 149 | expect(7, v2.c); 150 | expect(9, v2.d); 151 | expect(11, v2.f); 152 | } 153 | 154 | void arrow(void) { 155 | struct cell { int val; struct cell *next; }; 156 | struct cell v1 = { 5, NULL }; 157 | struct cell v2 = { 6, &v1 }; 158 | struct cell v3 = { 7, &v2 }; 159 | struct cell *p = &v3; 160 | expect(7, v3.val); 161 | expect(7, p->val); 162 | expect(6, p->next->val); 163 | expect(5, p->next->next->val); 164 | 165 | p->val = 10; 166 | p->next->val = 11; 167 | p->next->next->val = 12; 168 | expect(10, p->val); 169 | expect(11, p->next->val); 170 | expect(12, p->next->next->val); 171 | } 172 | 173 | void address(void) { 174 | struct tag { int a; struct { int b; } y; } x = { 6, 7 }; 175 | int *p1 = &x.a; 176 | int *p2 = &x.y.b; 177 | expect(6, *p1); 178 | expect(7, *p2); 179 | expect(6, *&x.a); 180 | expect(7, *&x.y.b); 181 | 182 | struct tag *xp = &x; 183 | int *p3 = &xp->a; 184 | int *p4 = &xp->y.b; 185 | expect(6, *p3); 186 | expect(7, *p4); 187 | expect(6, *&xp->a); 188 | expect(7, *&xp->y.b); 189 | } 190 | 191 | void incomplete(void) { 192 | struct tag1; 193 | struct tag2 { struct tag1 *p; }; 194 | struct tag1 { int x; }; 195 | 196 | struct tag1 v1 = { 3 }; 197 | struct tag2 v2 = { &v1 }; 198 | expect(3, v2.p->x); 199 | } 200 | 201 | void bitfield_basic(void) { 202 | union { 203 | int i; 204 | struct { int a:5; int b:5; }; 205 | } x; 206 | x.a = 10; 207 | x.b = 11; 208 | expect(10, x.a); 209 | expect(11, x.b); 210 | expect(362, x.i); // 11 << 5 + 10 == 362 211 | } 212 | 213 | void bitfield_mix(void) { 214 | union { 215 | int i; 216 | struct { char a:5; int b:5; }; 217 | } x; 218 | x.a = 10; 219 | x.b = 11; 220 | expect(10, x.a); 221 | expect(11, x.b); 222 | expect(362, x.i); 223 | } 224 | 225 | void bitfield_union(void) { 226 | union { int a : 10; char b: 5; char c: 5; } x; 227 | x.a = 2; 228 | expect(2, x.a); 229 | expect(2, x.b); 230 | expect(2, x.c); 231 | } 232 | 233 | void bitfield_unnamed(void) { 234 | union { 235 | int i; 236 | struct { char a:4; char b:4; }; 237 | } x = { 0 }; 238 | x.i = 0; 239 | x.a = 2; 240 | x.b = 4; 241 | expect(2, x.a); 242 | expect(4, x.b); 243 | expect(66, x.i); 244 | 245 | union { 246 | int i; 247 | struct { char a:4; char :0; char b:4; }; 248 | } y = { 0 }; 249 | y.a = 2; 250 | y.b = 4; 251 | expect(2, y.a); 252 | expect(4, y.b); 253 | expect(1026, y.i); 254 | } 255 | 256 | struct { char a:4; char b:4; } inittest = { 2, 4 }; 257 | 258 | void bitfield_initializer(void) { 259 | expect(2, inittest.a); 260 | expect(4, inittest.b); 261 | 262 | struct { char a:4; char b:4; } x = { 2, 4 }; 263 | expect(2, x.a); 264 | expect(4, x.b); 265 | } 266 | 267 | void test_offsetof(void) { 268 | struct tag10 { int a, b; }; 269 | expect(0, offsetof(struct tag10, a)); 270 | expect(4, offsetof(struct tag10, b)); 271 | } 272 | 273 | void flexible_member(void) { 274 | struct { int a, b[]; } x; 275 | expect(4, sizeof(x)); 276 | struct { int a, b[0]; } y; 277 | expect(4, sizeof(y)); 278 | struct { int a[0]; } z; 279 | expect(0, sizeof(z)); 280 | } 281 | 282 | void empty_struct(void) { 283 | struct tag15 {}; 284 | expect(0, sizeof(struct tag15)); 285 | union tag16 {}; 286 | expect(0, sizeof(struct tag16)); 287 | } 288 | 289 | void testmain(void) { 290 | print("struct"); 291 | t1(); 292 | t2(); 293 | t3(); 294 | t4(); 295 | t5(); 296 | t6(); 297 | t7(); 298 | t8(); 299 | t9(); 300 | t10(); 301 | t11(); 302 | t12(); 303 | t13(); 304 | t14(); 305 | unnamed(); 306 | assign(); 307 | arrow(); 308 | incomplete(); 309 | bitfield_basic(); 310 | bitfield_mix(); 311 | bitfield_union(); 312 | bitfield_unnamed(); 313 | bitfield_initializer(); 314 | test_offsetof(); 315 | flexible_member(); 316 | empty_struct(); 317 | } 318 | -------------------------------------------------------------------------------- /8cc.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #ifndef EIGHTCC_H 5 | #define EIGHTCC_H 6 | 7 | #include 8 | #include 9 | #include "dict.h" 10 | #include "list.h" 11 | #include "error.h" 12 | 13 | typedef struct { 14 | char *body; 15 | int nalloc; 16 | int len; 17 | } String; 18 | 19 | extern bool suppress_warning; 20 | 21 | extern String *make_string(void); 22 | extern char *format(char *fmt, ...); 23 | extern char *vformat(char *fmt, va_list args); 24 | extern char *get_cstring(String *s); 25 | extern int string_len(String *s); 26 | extern void string_append(String *s, char c); 27 | extern void string_appendf(String *s, char *fmt, ...); 28 | 29 | #define STRING(x) \ 30 | (String){ .body = (x), .nalloc = sizeof(x), .len = sizeof(x) + 1 } 31 | 32 | enum { 33 | TIDENT, 34 | TPUNCT, 35 | TNUMBER, 36 | TCHAR, 37 | TSTRING, 38 | // Only in CPP 39 | TNEWLINE, 40 | TSPACE, 41 | TMACRO_PARAM, 42 | }; 43 | 44 | typedef struct { 45 | int type; 46 | int nspace; 47 | bool bol; 48 | bool is_vararg; 49 | char *file; 50 | int line; 51 | int column; 52 | Dict *hideset; 53 | union { 54 | char *sval; 55 | int punct; 56 | char c; 57 | int position; 58 | }; 59 | } Token; 60 | 61 | enum { 62 | AST_LITERAL = 256, 63 | AST_STRING, 64 | AST_LVAR, 65 | AST_GVAR, 66 | AST_TYPEDEF, 67 | AST_FUNCALL, 68 | AST_FUNCPTR_CALL, 69 | AST_FUNCDESG, 70 | AST_FUNC, 71 | AST_DECL, 72 | AST_INIT, 73 | AST_CONV, 74 | AST_ADDR, 75 | AST_DEREF, 76 | AST_IF, 77 | AST_TERNARY, 78 | AST_FOR, 79 | AST_WHILE, 80 | AST_DO, 81 | AST_SWITCH, 82 | AST_CASE, 83 | AST_DEFAULT, 84 | AST_RETURN, 85 | AST_BREAK, 86 | AST_CONTINUE, 87 | AST_COMPOUND_STMT, 88 | AST_STRUCT_REF, 89 | AST_GOTO, 90 | AST_COMPUTED_GOTO, 91 | AST_LABEL, 92 | AST_VA_START, 93 | AST_VA_ARG, 94 | OP_UMINUS, 95 | OP_SIZEOF, 96 | OP_CAST, 97 | OP_SHR, 98 | OP_SHL, 99 | OP_A_SHR, 100 | OP_A_SHL, 101 | OP_PRE_INC, 102 | OP_PRE_DEC, 103 | OP_POST_INC, 104 | OP_POST_DEC, 105 | OP_LABEL_ADDR, 106 | #define punct(name, _1) name, 107 | #define keyword(name, _1, _2) name, 108 | #include "keyword.h" 109 | #undef keyword 110 | #undef punct 111 | }; 112 | 113 | enum { 114 | CTYPE_VOID, 115 | CTYPE_BOOL, 116 | CTYPE_CHAR, 117 | CTYPE_SHORT, 118 | CTYPE_INT, 119 | CTYPE_LONG, 120 | CTYPE_LLONG, 121 | CTYPE_FLOAT, 122 | CTYPE_DOUBLE, 123 | CTYPE_LDOUBLE, 124 | CTYPE_ARRAY, 125 | CTYPE_PTR, 126 | CTYPE_STRUCT, 127 | CTYPE_FUNC, 128 | // used only in parser 129 | CTYPE_STUB, 130 | }; 131 | 132 | typedef struct Ctype { 133 | int type; 134 | int size; 135 | // true if signed 136 | bool sig; 137 | bool isstatic; 138 | // pointer or array 139 | struct Ctype *ptr; 140 | // array length 141 | int len; 142 | // struct 143 | Dict *fields; 144 | int offset; 145 | bool is_struct; // true if struct, false if union 146 | // bitfield 147 | int bitoff; 148 | int bitsize; 149 | // function 150 | struct Ctype *rettype; 151 | List *params; 152 | bool hasva; 153 | } Ctype; 154 | 155 | typedef struct Node { 156 | int type; 157 | Ctype *ctype; 158 | union { 159 | // Char, int, or long 160 | long ival; 161 | // Float or double 162 | struct { 163 | double fval; 164 | char *flabel; 165 | }; 166 | // String 167 | struct { 168 | char *sval; 169 | char *slabel; 170 | }; 171 | // Local/global variable 172 | struct { 173 | char *varname; 174 | // local 175 | int loff; 176 | List *lvarinit; 177 | // global 178 | char *glabel; 179 | }; 180 | // Typedef 181 | char *typedefname; 182 | // Binary operator 183 | struct { 184 | struct Node *left; 185 | struct Node *right; 186 | }; 187 | // Unary operator 188 | struct { 189 | struct Node *operand; 190 | }; 191 | // Function call or function declaration 192 | struct { 193 | char *fname; 194 | // Function call 195 | struct List *args; 196 | struct Ctype *ftype; 197 | // Functoin pointer or function designator 198 | struct Node *fptr; 199 | // Function declaration 200 | struct List *params; 201 | struct List *localvars; 202 | struct Node *body; 203 | }; 204 | // Declaration 205 | struct { 206 | struct Node *declvar; 207 | struct List *declinit; 208 | }; 209 | // Initializer 210 | struct { 211 | struct Node *initval; 212 | int initoff; 213 | Ctype *totype; 214 | }; 215 | // If statement or ternary operator 216 | struct { 217 | struct Node *cond; 218 | struct Node *then; 219 | struct Node *els; 220 | }; 221 | // For statement 222 | struct { 223 | struct Node *forinit; 224 | struct Node *forcond; 225 | struct Node *forstep; 226 | struct Node *forbody; 227 | }; 228 | // Switch statement 229 | struct { 230 | struct Node *switchexpr; 231 | struct Node *switchbody; 232 | }; 233 | // Switch-case label 234 | struct { 235 | int casebeg; 236 | int caseend; 237 | }; 238 | // Goto and label 239 | struct { 240 | char *label; 241 | char *newlabel; 242 | }; 243 | // Return statement 244 | struct Node *retval; 245 | // Compound statement 246 | struct List *stmts; 247 | // Struct reference 248 | struct { 249 | struct Node *struc; 250 | char *field; 251 | Ctype *fieldtype; 252 | }; 253 | // Builtin functions for varargs 254 | struct Node *ap; 255 | }; 256 | } Node; 257 | 258 | typedef struct { 259 | void *first; 260 | void *second; 261 | } Pair; 262 | 263 | extern void *make_pair(void *first, void *second); 264 | 265 | extern Ctype *ctype_char; 266 | extern Ctype *ctype_short; 267 | extern Ctype *ctype_int; 268 | extern Ctype *ctype_long; 269 | extern Ctype *ctype_float; 270 | extern Ctype *ctype_double; 271 | 272 | extern void cpp_init(void); 273 | extern void lex_init(char *filename); 274 | extern char *read_error_directive(void); 275 | extern void unget_cpp_token(Token *tok); 276 | extern Token *peek_cpp_token(void); 277 | extern Token *read_cpp_token(void); 278 | extern void set_input_buffer(List *tokens); 279 | extern List *get_input_buffer(void); 280 | extern void skip_cond_incl(void); 281 | extern char *read_header_file_name(bool *std); 282 | extern void push_input_file(char *displayname, char *realname, FILE *input); 283 | extern void set_input_file(char *displayname, char *realname, FILE *input); 284 | extern char *input_position(void); 285 | extern char *get_current_file(void); 286 | extern int get_current_line(void); 287 | extern char *get_current_displayname(void); 288 | extern void set_current_displayname(char *name); 289 | extern void set_current_line(int line); 290 | extern void cpp_eval(char *buf); 291 | extern void add_include_path(char *path); 292 | 293 | extern void parse_init(void); 294 | extern void unget_token(Token *tok); 295 | extern Token *peek_token(void); 296 | extern Token *read_token(void); 297 | extern void expect_newline(void); 298 | 299 | extern char *t2s(Token *tok); 300 | extern bool is_punct(Token *tok, int c); 301 | extern bool is_ident(Token *tok, char *s); 302 | extern char *a2s(Node *node); 303 | extern char *c2s(Ctype *ctype); 304 | extern void print_asm_header(void); 305 | extern char *make_label(void); 306 | extern List *read_toplevels(void); 307 | extern Node *read_expr(void); 308 | extern int eval_intexpr(Node *node); 309 | extern bool is_inttype(Ctype *ctype); 310 | extern bool is_flotype(Ctype *ctype); 311 | 312 | extern void emit_toplevel(Node *v); 313 | extern void set_output_file(FILE *fp); 314 | extern void close_output_file(void); 315 | 316 | extern bool debug_cpp; 317 | 318 | #endif /* EIGHTCC_H */ 319 | -------------------------------------------------------------------------------- /test/macro.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "test.h" 5 | 6 | void special(void) { 7 | expect_string("test/macro.c", __FILE__); 8 | expect(8, __LINE__); 9 | expect(11, strlen(__DATE__)); 10 | expect(8, strlen(__TIME__)); 11 | } 12 | 13 | void include(void) { 14 | #include "macro1.h" 15 | expect_string("macro1", MACRO_1); 16 | 17 | #define MACRO_2_FILE "macro2.h" 18 | #include MACRO_2_FILE 19 | expect_string("macro2", MACRO_2); 20 | 21 | #define STDBOOL_H_FILE 22 | #ifdef __STDBOOL_H 23 | # error test failed 24 | #endif 25 | #include STDBOOL_H_FILE 26 | #ifndef __STDBOOL_H 27 | # error test failed 28 | #endif 29 | } 30 | 31 | void predefined(void) { 32 | expect(1, __8cc__); 33 | expect(1, __amd64); 34 | expect(1, __amd64__); 35 | expect(1, __x86_64); 36 | expect(1, __x86_64__); 37 | expect(1, linux); 38 | expect(1, __linux); 39 | expect(1, __linux__); 40 | expect(1, __gnu_linux__); 41 | expect(1, __unix); 42 | expect(1, __unix__); 43 | expect(1, _LP64); 44 | expect(1, __LP64__); 45 | expect(1, __ELF__); 46 | expect(1, __STDC__); 47 | expect(1, __STDC_HOSTED__); 48 | expect(199901, __STDC_VERSION__); 49 | 50 | expect(2, __SIZEOF_SHORT__); 51 | expect(4, __SIZEOF_INT__); 52 | expect(8, __SIZEOF_LONG__); 53 | expect(8, __SIZEOF_LONG_LONG__); 54 | expect(4, __SIZEOF_FLOAT__); 55 | expect(8, __SIZEOF_DOUBLE__); 56 | expect(8, __SIZEOF_POINTER__); 57 | expect(8, __SIZEOF_PTRDIFF_T__); 58 | expect(8, __SIZEOF_SIZE_T__); 59 | expect(16, __SIZEOF_LONG_DOUBLE__); 60 | 61 | expect(sizeof(short), __SIZEOF_SHORT__); 62 | expect(sizeof(int), __SIZEOF_INT__); 63 | expect(sizeof(long), __SIZEOF_LONG__); 64 | expect(sizeof(long long), __SIZEOF_LONG_LONG__); 65 | expect(sizeof(float), __SIZEOF_FLOAT__); 66 | expect(sizeof(double), __SIZEOF_DOUBLE__); 67 | expect(sizeof(void *), __SIZEOF_POINTER__); 68 | expect(sizeof(ptrdiff_t), __SIZEOF_PTRDIFF_T__); 69 | expect(sizeof(size_t), __SIZEOF_SIZE_T__); 70 | expect(sizeof(long double), __SIZEOF_LONG_DOUBLE__); 71 | } 72 | 73 | #define ZERO 0 74 | #define ONE 1 75 | #define TWO ONE + ONE 76 | #define LOOP LOOP 77 | 78 | void simple(void) { 79 | expect(1, ONE); 80 | expect(2, TWO); 81 | } 82 | 83 | #define VAR1 VAR2 84 | #define VAR2 VAR1 85 | 86 | void loop(void) { 87 | int VAR1 = 1; 88 | int VAR2 = 2; 89 | expect(1, VAR1); 90 | expect(2, VAR2); 91 | } 92 | 93 | void undef(void) { 94 | int a = 3; 95 | #define a 10 96 | expect(10, a); 97 | #undef a 98 | expect(3, a); 99 | #define a 16 100 | expect(16, a); 101 | #undef a 102 | } 103 | 104 | void cond_incl(void) { 105 | int a = 1; 106 | #if 0 107 | a = 2; 108 | #endif 109 | expect(1, a); 110 | 111 | #if 0 112 | #elif 1 113 | a = 2; 114 | #endif 115 | expect(2, a); 116 | 117 | #if 1 118 | a = 3; 119 | #elif 1 120 | a = 4; 121 | #endif 122 | expect(3, a); 123 | 124 | #if 1 125 | a = 5; 126 | #endif 127 | expect(5, a); 128 | 129 | #if 1 130 | a = 10; 131 | #else 132 | a = 12; 133 | #endif 134 | expect(10, a); 135 | 136 | #if 0 137 | a = 11; 138 | #else 139 | a = 12; 140 | #endif 141 | expect(12, a); 142 | 143 | #if 0 144 | # if 1 145 | # endif 146 | #else 147 | a = 150; 148 | #endif 149 | expect(150, a); 150 | } 151 | 152 | void const_expr(void) { 153 | int a = 1; 154 | #if 0 + 1 155 | a = 2; 156 | #else 157 | a = 3; 158 | #endif 159 | expect(2, a); 160 | 161 | #if 0 + 1 * 2 + 4 / 2 ^ 3 & ~1 % 5 162 | a = 4; 163 | #else 164 | a = 5; 165 | #endif 166 | expect(4, a); 167 | 168 | #if 1 && 0 169 | #else 170 | a = 100; 171 | #endif 172 | expect(100, a); 173 | 174 | #if 1 && 1 175 | a = 101; 176 | #else 177 | #endif 178 | expect(101, a); 179 | 180 | #if 1 || 0 181 | a = 102; 182 | #else 183 | #endif 184 | expect(102, a); 185 | 186 | #if 0 || 0 187 | #else 188 | a = 103; 189 | #endif 190 | expect(103, a); 191 | 192 | #if 0 193 | #elif !0 194 | a = 104; 195 | #endif 196 | expect(104, a); 197 | 198 | #if 0 + 0 199 | a = 6; 200 | #else 201 | a = 7; 202 | #endif 203 | expect(7, a); 204 | 205 | #if ZERO 206 | a = 8; 207 | #else 208 | a = 9; 209 | #endif 210 | expect(9, a); 211 | 212 | #if LOOP 213 | a = 10; 214 | #else 215 | a = 11; 216 | #endif 217 | expect(10, a); 218 | 219 | #if LOOP - 1 220 | a = 12; 221 | #else 222 | a = 13; 223 | #endif 224 | expect(13, a); 225 | } 226 | 227 | void defined(void) { 228 | int a = 0; 229 | #if defined ZERO 230 | a = 1; 231 | #endif 232 | expect(1, a); 233 | #if defined(ZERO) 234 | a = 2; 235 | #endif 236 | expect(2, a); 237 | #if defined(NO_SUCH_MACRO) 238 | a = 3; 239 | #else 240 | a = 4; 241 | #endif 242 | expect(4, a); 243 | } 244 | 245 | void ifdef(void) { 246 | int a = 0; 247 | #ifdef ONE 248 | a = 1; 249 | #else 250 | a = 2; 251 | # 252 | #1234 253 | #endif 254 | expect(a, 1); 255 | 256 | #ifdef NO_SUCH_MACRO 257 | a = 3; 258 | #else 259 | a = 4; 260 | #endif 261 | expect(a, 4); 262 | 263 | #ifndef ONE 264 | a = 5; 265 | #else 266 | a = 6; 267 | #endif 268 | expect(a, 6); 269 | 270 | #ifndef NO_SUCH_MACRO 271 | a = 7; 272 | #else 273 | a = 8; 274 | #endif 275 | expect(a, 7); 276 | } 277 | 278 | int plus(int a, int b) { 279 | return a + b; 280 | } 281 | 282 | int minus(int a, int b) { 283 | return a - b; 284 | } 285 | 286 | void funclike(void) { 287 | #define stringify(x) #x 288 | expect_string("5", stringify(5)); 289 | expect_string("x", stringify(x)); 290 | expect_string("x y", stringify(x y)); 291 | expect_string("x y", stringify( x y )); 292 | expect_string("x + y", stringify( x + y )); 293 | expect_string("x + y", stringify(/**/x/**/+/**//**/ /**/y/**/)); 294 | expect_string("x+y", stringify( x+y )); 295 | expect_string("'a'", stringify('a')); 296 | expect_string("'\\''", stringify('\'')); 297 | expect_string("\"abc\"", stringify("abc")); 298 | expect_string("ZERO", stringify(ZERO)); 299 | 300 | #define m1(x) x 301 | expect(5, m1(5)); 302 | expect(7, m1((5 + 2))); 303 | expect(8, m1(plus(5, 3))); 304 | expect(10, m1() 10); 305 | expect(14, m1(2 + 306 | 2 +) 10); 307 | 308 | #define m2(x) x + x 309 | expect(10, m2(5)); 310 | 311 | #define m3(x, y) x * y 312 | expect(50, m3(5, 10)); 313 | expect(11, m3(2 + 2, 3 + 3)); 314 | 315 | #define m4(x, y) x + y + TWO 316 | expect(17, m4(5, 10)); 317 | 318 | #define m6(x, ...) x + __VA_ARGS__ 319 | expect(20, m6(2, 18)); 320 | expect(25, plus(m6(2, 18, 5))); 321 | 322 | #define plus(x, y) x * y + plus(x, y) 323 | expect(11, plus(2, 3)); 324 | #undef plus 325 | 326 | #define plus(x, y) minus(x, y) 327 | #define minus(x, y) plus(x, y) 328 | expect(31, plus(30, 1)); 329 | expect(29, minus(30, 1)); 330 | 331 | // This is not a function-like macro. 332 | #define m7 (0) + 1 333 | expect(1, m7); 334 | 335 | #define m8(x, y) x ## y 336 | expect(2, m8(TW, O)); 337 | expect(0, m8(ZERO,)); 338 | 339 | #define m9(x, y, z) x y + z 340 | expect(8, m9(1,, 7)); 341 | 342 | #define m10(x) x ## x 343 | expect_string("a", "a" m10()); 344 | 345 | #define hash_hash # ## # 346 | #define mkstr(a) # a 347 | #define in_between(a) mkstr(a) 348 | #define join(c, d) in_between(c hash_hash d) 349 | expect_string("x ## y", join(x, y)); 350 | 351 | int m14 = 67; 352 | #define m14(x) x 353 | expect(67, m14); 354 | expect(67, m14(m14)); 355 | 356 | int a = 68; 357 | #define glue(x, y) x ## y 358 | glue(a+, +); 359 | expect(69, a); 360 | 361 | #define identity(x) stringify(x) 362 | expect_string("aa A B aa C", identity(m10(a) A B m10(a) C)); 363 | 364 | #define identity2(x) stringify(z ## x) 365 | expect_string("zA aa A B aa C", identity2(A m10(a) A B m10(a) C)); 366 | 367 | #define m15(x) x x 368 | expect_string("a a", identity(m15(a))); 369 | 370 | #define m16(x) (x,x) 371 | expect_string("(a,a)", identity(m16(a))); 372 | } 373 | 374 | void empty(void) { 375 | #define EMPTY 376 | expect(1, 1 EMPTY); 377 | #define EMPTY2(x) 378 | expect(2, 2 EMPTY2(foo)); 379 | expect(2, 2 EMPTY2(foo bar)); 380 | expect(2, 2 EMPTY2(((())))); 381 | } 382 | 383 | void noarg(void) { 384 | #define NOARG() 55 385 | expect(55, NOARG()); 386 | } 387 | 388 | void line(void) { 389 | #line 99 390 | expect(99, __LINE__); 391 | #line 199 "foo" 392 | expect(199, __LINE__); 393 | expect_string("foo", __FILE__); 394 | } 395 | 396 | void null(void) { 397 | # 398 | } 399 | 400 | void counter(void) { 401 | expect(0, __COUNTER__); 402 | expect(1, __COUNTER__); 403 | expect(2, __COUNTER__); 404 | } 405 | 406 | void gnuext(void) { 407 | #define m11(x, y...) stringify(x + y) 408 | expect_string("2 + 18", m11(2, 18)); 409 | expect_string("2 +", m11(2)); 410 | 411 | #define m12(x, y...) stringify((x, ## y)) 412 | expect_string("(1)", m12(1)); 413 | expect_string("(1, 2)", m12(1, 2)); 414 | 415 | #define m13(x, y) stringify((x, ## y)) 416 | expect_string("(1,)", m13(1,)); 417 | expect_string("(1,2)", m13(1, 2)); 418 | } 419 | 420 | void testmain(void) { 421 | print("macros"); 422 | 423 | special(); 424 | include(); 425 | predefined(); 426 | simple(); 427 | loop(); 428 | undef(); 429 | cond_incl(); 430 | const_expr(); 431 | defined(); 432 | ifdef(); 433 | funclike(); 434 | empty(); 435 | noarg(); 436 | line(); 437 | null(); 438 | counter(); 439 | gnuext(); 440 | } 441 | -------------------------------------------------------------------------------- /debug.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include "8cc.h" 5 | 6 | static char *maybe_add_bitfield(char *name, Ctype *ctype) { 7 | if (ctype->bitsize > 0) 8 | return format("%s:%d:%d", name, ctype->bitoff, ctype->bitoff + ctype->bitsize); 9 | return name; 10 | } 11 | 12 | static char *c2s_int(Dict *dict, Ctype *ctype) { 13 | if (!ctype) 14 | return "(nil)"; 15 | switch (ctype->type) { 16 | case CTYPE_VOID: return "void"; 17 | case CTYPE_BOOL: return "_Bool"; 18 | case CTYPE_CHAR: return maybe_add_bitfield("char", ctype); 19 | case CTYPE_SHORT: return maybe_add_bitfield("short", ctype); 20 | case CTYPE_INT: return maybe_add_bitfield("int", ctype); 21 | case CTYPE_LONG: return maybe_add_bitfield("long", ctype); 22 | case CTYPE_LLONG: return maybe_add_bitfield("long long", ctype); 23 | case CTYPE_FLOAT: return "float"; 24 | case CTYPE_DOUBLE: return "double"; 25 | case CTYPE_LDOUBLE: return "long double"; 26 | case CTYPE_PTR: 27 | return format("*%s", c2s_int(dict, ctype->ptr)); 28 | case CTYPE_ARRAY: 29 | return format("[%d]%s", ctype->len, c2s_int(dict, ctype->ptr)); 30 | case CTYPE_STRUCT: { 31 | char *type = ctype->is_struct ? "struct" : "union"; 32 | if (dict_get(dict, format("%p", ctype))) 33 | return format("(%s)", type); 34 | dict_put(dict, format("%p", ctype), (void *)1); 35 | String *s = make_string(); 36 | string_appendf(s, "(%s", type); 37 | for (Iter *i = list_iter(dict_values(ctype->fields)); !iter_end(i);) { 38 | Ctype *fieldtype = iter_next(i); 39 | string_appendf(s, " (%s)", c2s_int(dict, fieldtype)); 40 | } 41 | string_appendf(s, ")"); 42 | return get_cstring(s); 43 | } 44 | case CTYPE_FUNC: { 45 | String *s = make_string(); 46 | string_appendf(s, "("); 47 | for (Iter *i = list_iter(ctype->params); !iter_end(i);) { 48 | Ctype *t = iter_next(i); 49 | string_appendf(s, "%s", c2s_int(dict, t)); 50 | if (!iter_end(i)) 51 | string_append(s, ','); 52 | } 53 | string_appendf(s, ")=>%s", c2s_int(dict, ctype->rettype)); 54 | return get_cstring(s); 55 | } 56 | default: 57 | return format("(Unknown ctype: %d)", ctype->type); 58 | } 59 | } 60 | 61 | char *c2s(Ctype *ctype) { 62 | return c2s_int(make_dict(NULL), ctype); 63 | } 64 | 65 | static void uop_to_string(String *buf, char *op, Node *node) { 66 | string_appendf(buf, "(%s %s)", op, a2s(node->operand)); 67 | } 68 | 69 | static void binop_to_string(String *buf, char *op, Node *node) { 70 | string_appendf(buf, "(%s %s %s)", 71 | op, a2s(node->left), a2s(node->right)); 72 | } 73 | 74 | static void a2s_declinit(String *buf, List *initlist) { 75 | for (Iter *i = list_iter(initlist); !iter_end(i);) { 76 | Node *init = iter_next(i); 77 | string_appendf(buf, "%s", a2s(init)); 78 | if (!iter_end(i)) 79 | string_appendf(buf, " "); 80 | } 81 | } 82 | 83 | static void a2s_int(String *buf, Node *node) { 84 | if (!node) { 85 | string_appendf(buf, "(nil)"); 86 | return; 87 | } 88 | switch (node->type) { 89 | case AST_LITERAL: 90 | switch (node->ctype->type) { 91 | case CTYPE_CHAR: 92 | if (node->ival == '\n') string_appendf(buf, "'\n'"); 93 | else if (node->ival == '\\') string_appendf(buf, "'\\\\'"); 94 | else if (node->ival == '\0') string_appendf(buf, "'\\0'"); 95 | else string_appendf(buf, "'%c'", node->ival); 96 | break; 97 | case CTYPE_INT: 98 | string_appendf(buf, "%d", node->ival); 99 | break; 100 | case CTYPE_LONG: 101 | string_appendf(buf, "%ldL", node->ival); 102 | break; 103 | case CTYPE_FLOAT: 104 | case CTYPE_DOUBLE: 105 | string_appendf(buf, "%f", node->fval); 106 | break; 107 | default: 108 | error("internal error"); 109 | } 110 | break; 111 | case AST_STRING: 112 | string_appendf(buf, "\"%s\"", quote_cstring(node->sval)); 113 | break; 114 | case AST_LVAR: 115 | string_appendf(buf, "lv=%s", node->varname); 116 | if (node->lvarinit) { 117 | string_appendf(buf, "("); 118 | a2s_declinit(buf, node->lvarinit); 119 | string_appendf(buf, ")"); 120 | } 121 | break; 122 | case AST_GVAR: 123 | string_appendf(buf, "gv=%s", node->varname); 124 | break; 125 | case AST_FUNCALL: 126 | case AST_FUNCPTR_CALL: { 127 | string_appendf(buf, "(%s)%s(", c2s(node->ctype), 128 | node->type == AST_FUNCALL ? node->fname : a2s(node)); 129 | for (Iter *i = list_iter(node->args); !iter_end(i);) { 130 | string_appendf(buf, "%s", a2s(iter_next(i))); 131 | if (!iter_end(i)) 132 | string_appendf(buf, ","); 133 | } 134 | string_appendf(buf, ")"); 135 | break; 136 | } 137 | case AST_FUNCDESG: { 138 | string_appendf(buf, "(funcdesg %s)", a2s(node->fptr)); 139 | break; 140 | } 141 | case AST_FUNC: { 142 | string_appendf(buf, "(%s)%s(", c2s(node->ctype), node->fname); 143 | for (Iter *i = list_iter(node->params); !iter_end(i);) { 144 | Node *param = iter_next(i); 145 | string_appendf(buf, "%s %s", c2s(param->ctype), a2s(param)); 146 | if (!iter_end(i)) 147 | string_appendf(buf, ","); 148 | } 149 | string_appendf(buf, ")"); 150 | a2s_int(buf, node->body); 151 | break; 152 | } 153 | case AST_DECL: 154 | string_appendf(buf, "(decl %s %s", 155 | c2s(node->declvar->ctype), 156 | node->declvar->varname); 157 | if (node->declinit) { 158 | string_appendf(buf, " "); 159 | a2s_declinit(buf, node->declinit); 160 | } 161 | string_appendf(buf, ")"); 162 | break; 163 | case AST_INIT: 164 | string_appendf(buf, "%s@%d", a2s(node->initval), node->initoff, c2s(node->totype)); 165 | break; 166 | case AST_CONV: 167 | string_appendf(buf, "(conv %s=>%s)", a2s(node->operand), c2s(node->ctype)); 168 | break; 169 | case AST_IF: 170 | string_appendf(buf, "(if %s %s", 171 | a2s(node->cond), 172 | a2s(node->then)); 173 | if (node->els) 174 | string_appendf(buf, " %s", a2s(node->els)); 175 | string_appendf(buf, ")"); 176 | break; 177 | case AST_TERNARY: 178 | string_appendf(buf, "(? %s %s %s)", 179 | a2s(node->cond), 180 | a2s(node->then), 181 | a2s(node->els)); 182 | break; 183 | case AST_FOR: 184 | string_appendf(buf, "(for %s %s %s %s)", 185 | a2s(node->forinit), 186 | a2s(node->forcond), 187 | a2s(node->forstep), 188 | a2s(node->forbody)); 189 | break; 190 | case AST_WHILE: 191 | string_appendf(buf, "(while %s %s)", 192 | a2s(node->forcond), 193 | a2s(node->forbody)); 194 | break; 195 | case AST_DO: 196 | string_appendf(buf, "(do %s %s)", 197 | a2s(node->forcond), 198 | a2s(node->forbody)); 199 | break; 200 | case AST_RETURN: 201 | string_appendf(buf, "(return %s)", a2s(node->retval)); 202 | break; 203 | case AST_COMPOUND_STMT: { 204 | string_appendf(buf, "{"); 205 | for (Iter *i = list_iter(node->stmts); !iter_end(i);) { 206 | a2s_int(buf, iter_next(i)); 207 | string_appendf(buf, ";"); 208 | } 209 | string_appendf(buf, "}"); 210 | break; 211 | } 212 | case AST_STRUCT_REF: 213 | a2s_int(buf, node->struc); 214 | string_appendf(buf, "."); 215 | string_appendf(buf, node->field); 216 | break; 217 | case AST_ADDR: uop_to_string(buf, "addr", node); break; 218 | case AST_DEREF: uop_to_string(buf, "deref", node); break; 219 | case OP_UMINUS: uop_to_string(buf, "-", node); break; 220 | case OP_SAL: binop_to_string(buf, "<<", node); break; 221 | case OP_SAR: 222 | case OP_SHR: binop_to_string(buf, ">>", node); break; 223 | case OP_GE: binop_to_string(buf, ">=", node); break; 224 | case OP_LE: binop_to_string(buf, "<=", node); break; 225 | case OP_NE: binop_to_string(buf, "!=", node); break; 226 | case OP_PRE_INC: uop_to_string(buf, "pre++", node); break; 227 | case OP_PRE_DEC: uop_to_string(buf, "pre--", node); break; 228 | case OP_POST_INC: uop_to_string(buf, "post++", node); break; 229 | case OP_POST_DEC: uop_to_string(buf, "post--", node); break; 230 | case OP_LOGAND: binop_to_string(buf, "and", node); break; 231 | case OP_LOGOR: binop_to_string(buf, "or", node); break; 232 | case OP_A_ADD: binop_to_string(buf, "+=", node); break; 233 | case OP_A_SUB: binop_to_string(buf, "-=", node); break; 234 | case OP_A_MUL: binop_to_string(buf, "*=", node); break; 235 | case OP_A_DIV: binop_to_string(buf, "/=", node); break; 236 | case OP_A_MOD: binop_to_string(buf, "%=", node); break; 237 | case OP_A_AND: binop_to_string(buf, "&=", node); break; 238 | case OP_A_OR: binop_to_string(buf, "|=", node); break; 239 | case OP_A_XOR: binop_to_string(buf, "^=", node); break; 240 | case OP_A_SAL: binop_to_string(buf, "<<=", node); break; 241 | case OP_A_SAR: 242 | case OP_A_SHR: binop_to_string(buf, ">>=", node); break; 243 | case '!': uop_to_string(buf, "!", node); break; 244 | case '&': binop_to_string(buf, "&", node); break; 245 | case '|': binop_to_string(buf, "|", node); break; 246 | case OP_CAST: { 247 | string_appendf(buf, "((%s)=>(%s) %s)", 248 | c2s(node->operand->ctype), 249 | c2s(node->ctype), 250 | a2s(node->operand)); 251 | break; 252 | } 253 | case OP_LABEL_ADDR: 254 | string_appendf(buf, "&&%s", node->label); 255 | break; 256 | default: { 257 | char *left = a2s(node->left); 258 | char *right = a2s(node->right); 259 | if (node->type == OP_EQ) 260 | string_appendf(buf, "(== "); 261 | else 262 | string_appendf(buf, "(%c ", node->type); 263 | string_appendf(buf, "%s %s)", left, right); 264 | } 265 | } 266 | } 267 | 268 | char *a2s(Node *node) { 269 | String *s = make_string(); 270 | a2s_int(s, node); 271 | return get_cstring(s); 272 | } 273 | 274 | char *t2s(Token *tok) { 275 | if (!tok) 276 | return "(null)"; 277 | switch (tok->type) { 278 | case TIDENT: 279 | return tok->sval; 280 | case TPUNCT: 281 | switch (tok->punct) { 282 | #define punct(ident, str) \ 283 | case ident: return str; 284 | #define keyword(ident, str, _) \ 285 | case ident: return str; 286 | #include "keyword.h" 287 | #undef keyword 288 | #undef punct 289 | default: return format("%c", tok->c); 290 | } 291 | case TCHAR: 292 | return quote_char(tok->c); 293 | case TNUMBER: 294 | return tok->sval; 295 | case TSTRING: 296 | return format("\"%s\"", quote_cstring(tok->sval)); 297 | case TNEWLINE: 298 | return "(newline)"; 299 | case TSPACE: 300 | return "(space)"; 301 | case TMACRO_PARAM: 302 | return "(macro-param)"; 303 | } 304 | error("internal error: unknown token type: %d", tok->type); 305 | } 306 | -------------------------------------------------------------------------------- /lex.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "8cc.h" 9 | 10 | static bool at_bol = true; 11 | 12 | typedef struct { 13 | char *displayname; 14 | char *realname; 15 | int line; 16 | int column; 17 | FILE *fp; 18 | } File; 19 | 20 | static List *buffer = &EMPTY_LIST; 21 | static List *altbuffer = NULL; 22 | static List *file_stack = &EMPTY_LIST; 23 | static File *file; 24 | static int line_mark = -1; 25 | static int column_mark = -1; 26 | static int ungotten = -1; 27 | 28 | static Token *newline_token = &(Token){ .type = TNEWLINE, .nspace = 0 }; 29 | 30 | static void skip_block_comment(void); 31 | 32 | static File *make_file(char *displayname, char *realname, FILE *fp) { 33 | File *r = malloc(sizeof(File)); 34 | r->displayname = displayname; 35 | r->realname = realname; 36 | r->line = 1; 37 | r->column = 0; 38 | r->fp = fp; 39 | return r; 40 | } 41 | 42 | void lex_init(char *filename) { 43 | if (!strcmp(filename, "-")) { 44 | set_input_file("(stdin)", NULL, stdin); 45 | return; 46 | } 47 | FILE *fp = fopen(filename, "r"); 48 | if (!fp) { 49 | char buf[128]; 50 | strerror_r(errno, buf, sizeof(buf)); 51 | error("Cannot open %s: %s", filename, buf); 52 | } 53 | set_input_file(filename, filename, fp); 54 | } 55 | 56 | static Token *make_token(Token *tmpl) { 57 | Token *r = malloc(sizeof(Token)); 58 | *r = *tmpl; 59 | r->hideset = make_dict(NULL); 60 | r->file = file->displayname; 61 | r->line = (line_mark < 0) ? file->line : line_mark; 62 | r->column = (column_mark < 0) ? file->column : column_mark; 63 | line_mark = -1; 64 | column_mark = -1; 65 | return r; 66 | } 67 | 68 | static Token *make_ident(char *p) { 69 | return make_token(&(Token){ TIDENT, .nspace = 0, .sval = p }); 70 | } 71 | 72 | static Token *make_strtok(char *s) { 73 | return make_token(&(Token){ TSTRING, .nspace = 0, .sval = s }); 74 | } 75 | 76 | static Token *make_punct(int punct) { 77 | return make_token(&(Token){ TPUNCT, .nspace = 0, .punct = punct }); 78 | } 79 | 80 | static Token *make_number(char *s) { 81 | return make_token(&(Token){ TNUMBER, .nspace = 0, .sval = s }); 82 | } 83 | 84 | static Token *make_char(char c) { 85 | return make_token(&(Token){ TCHAR, .nspace = 0, .c = c }); 86 | } 87 | 88 | static Token *make_space(int nspace) { 89 | return make_token(&(Token){ TSPACE, .nspace = nspace }); 90 | } 91 | 92 | void push_input_file(char *displayname, char *realname, FILE *fp) { 93 | list_push(file_stack, file); 94 | file = make_file(displayname, realname, fp); 95 | at_bol = true; 96 | } 97 | 98 | void set_input_file(char *displayname, char *realname, FILE *fp) { 99 | file = make_file(displayname, realname, fp); 100 | at_bol = true; 101 | } 102 | 103 | char *input_position(void) { 104 | if (!file) 105 | return "(unknown)"; 106 | return format("%s:%d:%d", file->displayname, file->line, file->column); 107 | } 108 | 109 | char *get_current_file(void) { 110 | return file->realname; 111 | } 112 | 113 | int get_current_line(void) { 114 | return file->line; 115 | } 116 | 117 | void set_current_line(int line) { 118 | file->line = line; 119 | } 120 | 121 | char *get_current_displayname(void) { 122 | return file->displayname; 123 | } 124 | 125 | void set_current_displayname(char *name) { 126 | file->displayname = name; 127 | } 128 | 129 | static void mark_input(void) { 130 | line_mark = file->line; 131 | column_mark = file->column; 132 | } 133 | 134 | static void unget(int c) { 135 | if (c == '\n') 136 | file->line--; 137 | if (ungotten >= 0) 138 | ungetc(ungotten, file->fp); 139 | ungotten = c; 140 | file->column--; 141 | } 142 | 143 | static bool skip_newline(int c) { 144 | if (c == '\n') 145 | return true; 146 | if (c == '\r') { 147 | int c2 = getc(file->fp); 148 | if (c2 == '\n') 149 | return true; 150 | ungetc(c2, file->fp); 151 | return true; 152 | } 153 | return false; 154 | } 155 | 156 | static int get(void) { 157 | int c = (ungotten >= 0) ? ungotten : getc(file->fp); 158 | file->column++; 159 | ungotten = -1; 160 | if (c == '\\') { 161 | c = getc(file->fp); 162 | file->column++; 163 | if (skip_newline(c)) { 164 | file->line++; 165 | file->column = 1; 166 | return get(); 167 | } 168 | unget(c); 169 | at_bol = false; 170 | return '\\'; 171 | } 172 | if (skip_newline(c)) { 173 | file->line++; 174 | file->column = 1; 175 | at_bol = true; 176 | } else { 177 | at_bol = false; 178 | } 179 | return c; 180 | } 181 | 182 | static int peek(void) { 183 | int r = get(); 184 | unget(r); 185 | return r; 186 | } 187 | 188 | static bool next(int expect) { 189 | int c = get(); 190 | if (c == expect) 191 | return true; 192 | unget(c); 193 | return false; 194 | } 195 | 196 | static void skip_line(void) { 197 | for (;;) { 198 | int c = get(); 199 | if (c == EOF) 200 | return; 201 | if (c == '\n' || c == '\r') { 202 | unget(c); 203 | return; 204 | } 205 | } 206 | } 207 | 208 | static bool iswhitespace(int c) { 209 | return c == ' ' || c == '\t' || c == '\f' || c == '\v'; 210 | } 211 | 212 | static int skip_space(void) { 213 | int nspace = 0; 214 | for (;;) { 215 | int c = get(); 216 | if (c == EOF) break; 217 | if (iswhitespace(c)) { 218 | nspace++; 219 | continue; 220 | } 221 | if (c == '\t'){ 222 | nspace += 4; 223 | continue; 224 | } 225 | if (c == '/') { 226 | if (next('*')) { 227 | skip_block_comment(); 228 | nspace++; 229 | continue; 230 | } else if (next('/')) { 231 | skip_line(); 232 | nspace++; 233 | continue; 234 | } 235 | unget('/'); 236 | break; 237 | } 238 | unget(c); 239 | break; 240 | } 241 | return nspace; 242 | } 243 | 244 | void skip_cond_incl(void) { 245 | int nest = 0; 246 | for (;;) { 247 | skip_space(); 248 | int c = get(); 249 | if (c == EOF) 250 | return; 251 | if (c == '\n' || c == '\r') 252 | continue; 253 | if (c != '#') { 254 | skip_line(); 255 | continue; 256 | } 257 | skip_space(); 258 | Token *tok = read_cpp_token(); 259 | if (tok->type == TNEWLINE) 260 | continue; 261 | if (tok->type != TIDENT) { 262 | skip_line(); 263 | continue; 264 | } 265 | if (!nest && (is_ident(tok, "else") || is_ident(tok, "elif") || is_ident(tok, "endif"))) { 266 | unget_cpp_token(tok); 267 | Token *sharp = make_punct('#'); 268 | sharp->bol = true; 269 | unget_cpp_token(sharp); 270 | return; 271 | } 272 | if (is_ident(tok, "if") || is_ident(tok, "ifdef") || is_ident(tok, "ifndef")) 273 | nest++; 274 | else if (nest && is_ident(tok, "endif")) 275 | nest--; 276 | skip_line(); 277 | } 278 | } 279 | 280 | static Token *read_number(char c) { 281 | String *s = make_string(); 282 | string_append(s, c); 283 | for (;;) { 284 | int c = get(); 285 | if (!isdigit(c) && !isalpha(c) && c != '.') { 286 | unget(c); 287 | return make_number(get_cstring(s)); 288 | } 289 | string_append(s, c); 290 | } 291 | } 292 | 293 | static int read_octal_char(int c) { 294 | int r = c - '0'; 295 | c = get(); 296 | if ('0' <= c && c <= '7') { 297 | r = (r << 3) | (c - '0'); 298 | c = get(); 299 | if ('0' <= c && c <= '7') 300 | r = (r << 3) | (c - '0'); 301 | else 302 | unget(c); 303 | } else { 304 | unget(c); 305 | } 306 | return r; 307 | } 308 | 309 | static int read_hex_char(void) { 310 | int c = get(); 311 | int r = 0; 312 | if (!isxdigit(c)) 313 | error("\\x is not followed by a hexadecimal character: %c", c); 314 | for (;; c = get()) { 315 | switch (c) { 316 | case '0' ... '9': r = (r << 4) | (c - '0'); continue; 317 | case 'a' ... 'f': r = (r << 4) | (c - 'a' + 10); continue; 318 | case 'A' ... 'F': r = (r << 4) | (c - 'A' + 10); continue; 319 | default: unget(c); return r; 320 | } 321 | } 322 | } 323 | 324 | static int read_escaped_char(void) { 325 | int c = get(); 326 | switch (c) { 327 | case '\'': case '"': case '?': case '\\': 328 | return c; 329 | case 'a': return '\a'; 330 | case 'b': return '\b'; 331 | case 'f': return '\f'; 332 | case 'n': return '\n'; 333 | case 'r': return '\r'; 334 | case 't': return '\t'; 335 | case 'v': return '\v'; 336 | case 'e': return '\033'; // '\e' is GNU extension 337 | case '0' ... '7': 338 | return read_octal_char(c); 339 | case 'x': 340 | return read_hex_char(); 341 | case EOF: 342 | error("premature end of input"); 343 | default: 344 | warn("unknown escape character: \\%c", c); 345 | return c; 346 | } 347 | } 348 | 349 | static Token *read_char(void) { 350 | int c = get(); 351 | char r = (c == '\\') ? read_escaped_char() : c; 352 | int c2 = get(); 353 | if (c2 != '\'') 354 | error("unterminated string: %c", c2); 355 | return make_char(r); 356 | } 357 | 358 | static Token *read_string(void) { 359 | String *s = make_string(); 360 | for (;;) { 361 | int c = get(); 362 | if (c == EOF) 363 | error("Unterminated string"); 364 | if (c == '"') 365 | break; 366 | if (c == '\\') 367 | c = read_escaped_char(); 368 | string_append(s, c); 369 | } 370 | return make_strtok(get_cstring(s)); 371 | } 372 | 373 | static Token *read_ident(char c) { 374 | String *s = make_string(); 375 | string_append(s, c); 376 | for (;;) { 377 | int c2 = get(); 378 | if (isalnum(c2) || c2 == '_' || c2 == '$') { 379 | string_append(s, c2); 380 | } else { 381 | unget(c2); 382 | return make_ident(get_cstring(s)); 383 | } 384 | } 385 | } 386 | 387 | static void skip_block_comment(void) { 388 | enum { in_comment, asterisk_read } state = in_comment; 389 | for (;;) { 390 | int c = get(); 391 | if (c == EOF) 392 | error("premature end of block comment"); 393 | if (c == '*') 394 | state = asterisk_read; 395 | else if (state == asterisk_read && c == '/') 396 | return; 397 | else 398 | state = in_comment; 399 | } 400 | } 401 | 402 | static Token *read_rep(char expect, int t1, int els) { 403 | if (next(expect)) 404 | return make_punct(t1); 405 | return make_punct(els); 406 | } 407 | 408 | static Token *read_rep2(char expect1, int t1, char expect2, int t2, char els) { 409 | if (next(expect1)) 410 | return make_punct(t1); 411 | if (next(expect2)) 412 | return make_punct(t2); 413 | return make_punct(els); 414 | } 415 | 416 | static Token *read_token_int(void) { 417 | mark_input(); 418 | int c = get(); 419 | switch (c) { 420 | case ' ': case '\v': case '\f': 421 | return make_space(skip_space() + 1); 422 | case '\t': 423 | return make_space(skip_space() + 4); 424 | case '\n': case '\r': 425 | skip_newline(c); 426 | return newline_token; 427 | case 'L': 428 | if (next('"')) return read_string(); 429 | if (next('\'')) return read_char(); 430 | return read_ident('L'); 431 | case '0' ... '9': 432 | return read_number(c); 433 | case 'a' ... 'z': case 'A' ... 'K': case 'M' ... 'Z': case '_': case '$': 434 | return read_ident(c); 435 | case '/': { 436 | if (next('/')) { 437 | skip_line(); 438 | return make_space(1); 439 | } 440 | if (next('*')) { 441 | skip_block_comment(); 442 | return make_space(1); 443 | } 444 | if (next('=')) 445 | return make_punct(OP_A_DIV); 446 | return make_punct('/'); 447 | } 448 | case '.': 449 | if (isdigit(peek())) 450 | return read_number(c); 451 | if (next('.')) 452 | return make_ident(format("..%c", get())); 453 | return make_punct('.'); 454 | case '(': case ')': case ',': case ';': case '[': case ']': case '{': 455 | case '}': case '?': case '~': 456 | return make_punct(c); 457 | case ':': 458 | return make_punct(next('>') ? ']' : ':'); 459 | case '#': { 460 | if (next('#')) 461 | return make_ident("##"); 462 | return make_punct('#'); 463 | } 464 | case '+': return read_rep2('+', OP_INC, '=', OP_A_ADD, '+'); 465 | case '-': { 466 | if (next('-')) 467 | return make_punct(OP_DEC); 468 | if (next('>')) 469 | return make_punct(OP_ARROW); 470 | if (next('=')) 471 | return make_punct(OP_A_SUB); 472 | return make_punct('-'); 473 | } 474 | case '*': return read_rep('=', OP_A_MUL, '*'); 475 | case '%': 476 | if (next('>')) 477 | return make_punct('}'); 478 | if (next(':')) { 479 | if (next('%')) { 480 | if (next(':')) 481 | return make_ident("##"); 482 | unget('%'); 483 | } 484 | return make_punct('#'); 485 | } 486 | return read_rep('=', OP_A_MOD, '%'); 487 | case '=': return read_rep('=', OP_EQ, '='); 488 | case '!': return read_rep('=', OP_NE, '!'); 489 | case '&': return read_rep2('&', OP_LOGAND, '=', OP_A_AND, '&'); 490 | case '|': return read_rep2('|', OP_LOGOR, '=', OP_A_OR, '|'); 491 | case '^': return read_rep('=', OP_A_XOR, '^'); 492 | case '<': { 493 | if (next('<')) return read_rep('=', OP_A_SAL, OP_SAL); 494 | if (next('=')) return make_punct(OP_LE); 495 | if (next(':')) return make_punct('['); 496 | if (next('%')) return make_punct('{'); 497 | return make_punct('<'); 498 | } 499 | case '>': { 500 | if (next('=')) 501 | return make_punct(OP_GE); 502 | if (next('>')) 503 | return read_rep('=', OP_A_SAR, OP_SAR); 504 | return make_punct('>'); 505 | } 506 | case '"': return read_string(); 507 | case '\'': return read_char(); 508 | case EOF: 509 | return NULL; 510 | default: 511 | error("Unexpected character: '%c'", c); 512 | } 513 | } 514 | 515 | char *read_header_file_name(bool *std) { 516 | skip_space(); 517 | char close; 518 | if (next('"')) { 519 | *std = false; 520 | close = '"'; 521 | } else if (next('<')) { 522 | *std = true; 523 | close = '>'; 524 | } else { 525 | return NULL; 526 | } 527 | String *s = make_string(); 528 | for (;;) { 529 | int c = get(); 530 | if (c == EOF || c == '\n' || c == '\r') 531 | error("premature end of header name"); 532 | if (c == close) 533 | break; 534 | string_append(s, c); 535 | } 536 | if (get_cstring(s)[0] == '\0') 537 | error("header name should not be empty"); 538 | return get_cstring(s); 539 | } 540 | 541 | bool is_punct(Token *tok, int c) { 542 | return tok && (tok->type == TPUNCT) && (tok->punct == c); 543 | } 544 | 545 | void set_input_buffer(List *tokens) { 546 | altbuffer = tokens ? list_reverse(tokens) : NULL; 547 | } 548 | 549 | List *get_input_buffer(void) { 550 | return altbuffer ? list_reverse(altbuffer) : NULL; 551 | } 552 | 553 | char *read_error_directive(void) { 554 | String *s = make_string(); 555 | bool bol = true; 556 | for (;;) { 557 | int c = get(); 558 | if (c == EOF) break; 559 | if (c == '\n' || c == '\r') { 560 | unget(c); 561 | break; 562 | } 563 | if (bol && iswhitespace(c)) continue; 564 | bol = false; 565 | string_append(s, c); 566 | } 567 | return get_cstring(s); 568 | } 569 | 570 | void unget_cpp_token(Token *tok) { 571 | if (!tok) return; 572 | list_push(altbuffer ? altbuffer : buffer, tok); 573 | } 574 | 575 | Token *peek_cpp_token(void) { 576 | Token *tok = read_token(); 577 | unget_cpp_token(tok); 578 | return tok; 579 | } 580 | 581 | static Token *read_cpp_token_int(void) { 582 | if (altbuffer) 583 | return list_pop(altbuffer); 584 | if (list_len(buffer) > 0) 585 | return list_pop(buffer); 586 | bool bol = at_bol; 587 | Token *tok = read_token_int(); 588 | while (tok && tok->type == TSPACE) { 589 | Token *tok2 = read_token_int(); 590 | if (tok2) 591 | tok2->nspace += tok->nspace; 592 | tok = tok2; 593 | } 594 | if (!tok && list_len(file_stack) > 0) { 595 | fclose(file->fp); 596 | file = list_pop(file_stack); 597 | at_bol = true; 598 | return newline_token; 599 | } 600 | if (tok) tok->bol = bol; 601 | return tok; 602 | } 603 | 604 | Token *read_cpp_token(void) { 605 | return read_cpp_token_int(); 606 | } 607 | -------------------------------------------------------------------------------- /cpp.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | /* 5 | * This implements Dave Prosser's C Preprocessing algorithm, described 6 | * in this document: https://github.com/rui314/8cc/wiki/cpp.algo.pdf 7 | */ 8 | 9 | // For fmemopen() 10 | #define _POSIX_C_SOURCE 200809L 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "8cc.h" 18 | 19 | bool debug_cpp; 20 | static Dict *macros = &EMPTY_DICT; 21 | static List *cond_incl_stack = &EMPTY_LIST; 22 | static List *std_include_path = &EMPTY_LIST; 23 | static Token *cpp_token_zero = &(Token){ .type = TNUMBER, .sval = "0" }; 24 | static Token *cpp_token_one = &(Token){ .type = TNUMBER, .sval = "1" }; 25 | static struct tm *current_time; 26 | static int macro_counter; 27 | 28 | typedef void special_macro_handler(Token *tok); 29 | typedef enum { IN_THEN, IN_ELSE } CondInclCtx; 30 | typedef enum { MACRO_OBJ, MACRO_FUNC, MACRO_SPECIAL } MacroType; 31 | typedef struct { CondInclCtx ctx; bool wastrue; } CondIncl; 32 | 33 | typedef struct { 34 | MacroType type; 35 | int nargs; 36 | List *body; 37 | bool is_varg; 38 | special_macro_handler *fn; 39 | } Macro; 40 | 41 | static Macro *make_obj_macro(List *body); 42 | static Macro *make_func_macro(List *body, int nargs, bool is_varg); 43 | static Macro *make_special_macro(special_macro_handler *fn); 44 | static Token *read_token_sub(bool return_at_eol); 45 | static Token *read_expand(void); 46 | 47 | /*---------------------------------------------------------------------- 48 | * Eval 49 | */ 50 | 51 | void cpp_eval(char *buf) { 52 | FILE *fp = fmemopen(buf, strlen(buf), "r"); 53 | set_input_file("(eval)", NULL, fp); 54 | List *toplevels = read_toplevels(); 55 | for (Iter *i = list_iter(toplevels); !iter_end(i);) 56 | emit_toplevel(iter_next(i)); 57 | } 58 | 59 | /*---------------------------------------------------------------------- 60 | * Constructors 61 | */ 62 | 63 | static CondIncl *make_cond_incl(CondInclCtx ctx, bool wastrue) { 64 | CondIncl *r = malloc(sizeof(CondIncl)); 65 | r->ctx = ctx; 66 | r->wastrue = wastrue; 67 | return r; 68 | } 69 | 70 | static Macro *make_macro(Macro *tmpl) { 71 | Macro *r = malloc(sizeof(Macro)); 72 | *r = *tmpl; 73 | return r; 74 | } 75 | 76 | static Macro *make_obj_macro(List *body) { 77 | return make_macro(&(Macro){ MACRO_OBJ, .body = body }); 78 | } 79 | 80 | static Macro *make_func_macro(List *body, int nargs, bool is_varg) { 81 | return make_macro(&(Macro){ 82 | MACRO_FUNC, .nargs = nargs, .body = body, .is_varg = is_varg }); 83 | } 84 | 85 | static Macro *make_special_macro(special_macro_handler *fn) { 86 | return make_macro(&(Macro){ MACRO_SPECIAL, .fn = fn }); 87 | } 88 | 89 | static Token *make_macro_token(int position, bool is_vararg) { 90 | Token *r = malloc(sizeof(Token)); 91 | r->type = TMACRO_PARAM; 92 | r->is_vararg = is_vararg; 93 | r->hideset = make_dict(NULL); 94 | r->position = position; 95 | r->nspace = 0; 96 | r->bol = false; 97 | return r; 98 | } 99 | 100 | static Token *copy_token(Token *tok) { 101 | Token *r = malloc(sizeof(Token)); 102 | *r = *tok; 103 | return r; 104 | } 105 | 106 | static Token *make_number(char *s) { 107 | Token *r = malloc(sizeof(Token)); 108 | *r = (Token){ TNUMBER, .sval = s }; 109 | return r; 110 | } 111 | 112 | static void expect(char punct) { 113 | Token *tok = read_cpp_token(); 114 | if (!tok || !is_punct(tok, punct)) 115 | error("%c expected, but got %s", t2s(tok)); 116 | } 117 | 118 | /*---------------------------------------------------------------------- 119 | * Utility functions 120 | */ 121 | 122 | bool is_ident(Token *tok, char *s) { 123 | return tok->type == TIDENT && !strcmp(tok->sval, s); 124 | } 125 | 126 | static bool next(int punct) { 127 | Token *tok = read_cpp_token(); 128 | if (is_punct(tok, punct)) 129 | return true; 130 | unget_token(tok); 131 | return false; 132 | } 133 | 134 | static void set_list_nspace(List *tokens, Token *tmpl) { 135 | if (list_len(tokens) == 0) 136 | return; 137 | Token *tok = list_head(tokens); 138 | tok->nspace = tmpl->nspace; 139 | } 140 | 141 | /*---------------------------------------------------------------------- 142 | * Macro expander 143 | */ 144 | 145 | static Token *read_ident(void) { 146 | Token *r = read_cpp_token(); 147 | if (r->type != TIDENT) 148 | error("identifier expected, but got %s", t2s(r)); 149 | return r; 150 | } 151 | 152 | void expect_newline(void) { 153 | Token *tok = read_cpp_token(); 154 | if (!tok || tok->type != TNEWLINE) 155 | error("Newline expected, but got %s", t2s(tok)); 156 | } 157 | 158 | static List *read_args_int(Macro *macro) { 159 | List *r = make_list(); 160 | List *arg = make_list(); 161 | int depth = 0; 162 | for (;;) { 163 | Token *tok = read_cpp_token(); 164 | if (!tok) 165 | error("unterminated macro argument list"); 166 | if (tok->type == TNEWLINE) 167 | continue; 168 | if (is_punct(tok, '(')) { 169 | depth++; 170 | } else if (depth) { 171 | if (is_punct(tok, ')')) 172 | depth--; 173 | list_push(arg, tok); 174 | continue; 175 | } 176 | if (is_punct(tok, ')')) { 177 | unget_token(tok); 178 | list_push(r, arg); 179 | return r; 180 | } 181 | bool in_threedots = macro->is_varg && list_len(r) + 1 == macro->nargs; 182 | if (is_punct(tok, ',') && !in_threedots) { 183 | list_push(r, arg); 184 | arg = make_list(); 185 | continue; 186 | } 187 | list_push(arg, tok); 188 | } 189 | } 190 | 191 | static List *read_args(Macro *macro) { 192 | List *args = read_args_int(macro); 193 | if (macro->is_varg) { 194 | if (list_len(args) == macro->nargs - 1) 195 | list_push(args, make_list()); 196 | else if (list_len(args) < macro->nargs) 197 | error("Macro argument number is less than expected"); 198 | return args; 199 | } 200 | if (!macro->is_varg && list_len(args) != macro->nargs) { 201 | if (macro->nargs == 0 && 202 | list_len(args) == 1 && 203 | list_len(list_get(args, 0)) == 0) 204 | return &EMPTY_LIST; 205 | error("Macro argument number does not match"); 206 | } 207 | return args; 208 | } 209 | 210 | static Dict *dict_union(Dict *a, Dict *b) { 211 | Dict *r = make_dict(NULL); 212 | for (Iter *i = list_iter(dict_keys(a)); !iter_end(i);) { 213 | char *key = iter_next(i); 214 | dict_put(r, key, dict_get(a, key)); 215 | } 216 | for (Iter *i = list_iter(dict_keys(b)); !iter_end(i);) { 217 | char *key = iter_next(i); 218 | dict_put(r, key, dict_get(b, key)); 219 | } 220 | return r; 221 | } 222 | 223 | static Dict *dict_intersection(Dict *a, Dict *b) { 224 | Dict *r = make_dict(NULL); 225 | for (Iter *i = list_iter(dict_keys(a)); !iter_end(i);) { 226 | char *key = iter_next(i); 227 | if (dict_get(b, key)) 228 | dict_put(r, key, (void *)1); 229 | } 230 | return r; 231 | } 232 | 233 | static Dict *dict_append(Dict *dict, char *s) { 234 | Dict *r = make_dict(dict); 235 | dict_put(r, s, (void *)1); 236 | return r; 237 | } 238 | 239 | static List *add_hide_set(List *tokens, Dict *hideset) { 240 | List *r = make_list(); 241 | for (Iter *i = list_iter(tokens); !iter_end(i);) { 242 | Token *t = copy_token(iter_next(i)); 243 | t->hideset = dict_union(t->hideset, hideset); 244 | list_push(r, t); 245 | } 246 | return r; 247 | } 248 | 249 | static void paste(String *s, Token *tok) { 250 | switch (tok->type) { 251 | case TIDENT: 252 | case TNUMBER: 253 | string_appendf(s, "%s", tok->sval); 254 | return; 255 | case TPUNCT: 256 | string_appendf(s, "%s", t2s(tok)); 257 | return; 258 | default: 259 | error("can't paste: %s", t2s(tok)); 260 | } 261 | } 262 | 263 | static Token *glue_tokens(Token *t0, Token *t1) { 264 | String *s = make_string(); 265 | paste(s, t0); 266 | paste(s, t1); 267 | Token *r = copy_token(t0); 268 | r->type = isdigit(get_cstring(s)[0]) ? TNUMBER : TIDENT; 269 | r->sval = get_cstring(s); 270 | return r; 271 | } 272 | 273 | static void glue_push(List *tokens, Token *tok) { 274 | assert(list_len(tokens) > 0); 275 | Token *last = list_pop(tokens); 276 | list_push(tokens, glue_tokens(last, tok)); 277 | } 278 | 279 | static char *join_tokens(List *args, bool sep) { 280 | String *s = make_string(); 281 | for (Iter *i = list_iter(args); !iter_end(i);) { 282 | Token *tok = iter_next(i); 283 | if (sep && string_len(s) && tok->nspace) 284 | string_appendf(s, " "); 285 | switch (tok->type) { 286 | case TIDENT: 287 | case TNUMBER: 288 | string_appendf(s, "%s", tok->sval); 289 | break; 290 | case TPUNCT: 291 | string_appendf(s, "%s", t2s(tok)); 292 | break; 293 | case TCHAR: 294 | string_appendf(s, "%s", quote_char(tok->c)); 295 | break; 296 | case TSTRING: 297 | string_appendf(s, "\"%s\"", quote_cstring(tok->sval)); 298 | break; 299 | default: 300 | error("internal error"); 301 | } 302 | } 303 | return get_cstring(s); 304 | } 305 | 306 | static Token *stringize(Token *tmpl, List *args) { 307 | Token *r = copy_token(tmpl); 308 | r->type = TSTRING; 309 | r->sval = join_tokens(args, true); 310 | return r; 311 | } 312 | 313 | static List *expand_all(List *tokens, Token *tmpl) { 314 | List *r = make_list(); 315 | List *orig = get_input_buffer(); 316 | set_input_buffer(tokens); 317 | Token *tok; 318 | while ((tok = read_expand()) != NULL) 319 | list_push(r, tok); 320 | set_list_nspace(r, tmpl); 321 | set_input_buffer(orig); 322 | return r; 323 | } 324 | 325 | static List *subst(Macro *macro, List *args, Dict *hideset) { 326 | List *r = make_list(); 327 | for (int i = 0; i < list_len(macro->body); i++) { 328 | bool islast = (i == list_len(macro->body) - 1); 329 | Token *t0 = list_get(macro->body, i); 330 | Token *t1 = islast ? NULL : list_get(macro->body, i + 1); 331 | bool t0_param = (t0->type == TMACRO_PARAM); 332 | bool t1_param = (!islast && t1->type == TMACRO_PARAM); 333 | 334 | if (is_punct(t0, '#') && t1_param) { 335 | list_push(r, stringize(t0, list_get(args, t1->position))); 336 | i++; 337 | continue; 338 | } 339 | if (is_ident(t0, "##") && t1_param) { 340 | List *arg = list_get(args, t1->position); 341 | if (t1->is_vararg && list_len(r) > 0 && is_punct(list_tail(r), ',')) { 342 | if (list_len(arg) > 0) 343 | list_append(r, expand_all(arg, t1)); 344 | else 345 | list_pop(r); 346 | } else if (list_len(arg) > 0) { 347 | glue_push(r, list_head(arg)); 348 | List *tmp = list_copy(arg); 349 | list_shift(tmp); 350 | list_append(r, expand_all(tmp, t1)); 351 | } 352 | i++; 353 | continue; 354 | } 355 | if (is_ident(t0, "##") && !islast) { 356 | hideset = t1->hideset; 357 | glue_push(r, t1); 358 | i++; 359 | continue; 360 | } 361 | if (t0_param && !islast && is_ident(t1, "##")) { 362 | hideset = t1->hideset; 363 | List *arg = list_get(args, t0->position); 364 | if (list_len(arg) == 0) 365 | i++; 366 | else 367 | list_append(r, arg); 368 | continue; 369 | } 370 | if (t0_param) { 371 | List *arg = list_get(args, t0->position); 372 | list_append(r, expand_all(arg, t0)); 373 | continue; 374 | } 375 | list_push(r, t0); 376 | } 377 | return add_hide_set(r, hideset); 378 | } 379 | 380 | static void unget_all(List *tokens) { 381 | for (Iter *i = list_iter(list_reverse(tokens)); !iter_end(i);) 382 | unget_token(iter_next(i)); 383 | } 384 | 385 | static Token *read_expand(void) { 386 | Token *tok = read_cpp_token(); 387 | if (!tok) return NULL; 388 | if (tok->type == TNEWLINE) 389 | return read_expand(); 390 | if (tok->type != TIDENT) 391 | return tok; 392 | char *name = tok->sval; 393 | Macro *macro = dict_get(macros, name); 394 | if (!macro || dict_get(tok->hideset, name)) 395 | return tok; 396 | 397 | switch (macro->type) { 398 | case MACRO_OBJ: { 399 | Dict *hideset = dict_append(tok->hideset, name); 400 | List *tokens = subst(macro, make_list(), hideset); 401 | set_list_nspace(tokens, tok); 402 | unget_all(tokens); 403 | return read_expand(); 404 | } 405 | case MACRO_FUNC: { 406 | if (!next('(')) 407 | return tok; 408 | List *args = read_args(macro); 409 | Token *rparen = read_cpp_token(); 410 | if (!is_punct(rparen, ')')) 411 | error("internal error: %s", t2s(rparen)); 412 | Dict *hideset = dict_append(dict_intersection(tok->hideset, rparen->hideset), name); 413 | List *tokens = subst(macro, args, hideset); 414 | set_list_nspace(tokens, tok); 415 | unget_all(tokens); 416 | return read_expand(); 417 | } 418 | case MACRO_SPECIAL: { 419 | macro->fn(tok); 420 | return read_expand(); 421 | } 422 | default: 423 | error("internal error"); 424 | } 425 | } 426 | 427 | static bool read_funclike_macro_params(Dict *param) { 428 | int pos = 0; 429 | for (;;) { 430 | Token *tok = read_cpp_token(); 431 | if (is_punct(tok, ')')) 432 | return false; 433 | if (pos) { 434 | if (!is_punct(tok, ',')) 435 | error("',' expected, but got '%s'", t2s(tok)); 436 | tok = read_cpp_token(); 437 | } 438 | if (!tok || tok->type == TNEWLINE) 439 | error("missing ')' in macro parameter list"); 440 | if (is_ident(tok, "...")) { 441 | dict_put(param, "__VA_ARGS__", make_macro_token(pos++, true)); 442 | expect(')'); 443 | return true; 444 | } 445 | if (tok->type != TIDENT) 446 | error("identifier expected, but got '%s'", t2s(tok)); 447 | char *arg = tok->sval; 448 | tok = read_cpp_token(); 449 | if (is_ident(tok, "...")) { 450 | expect(')'); 451 | dict_put(param, arg, make_macro_token(pos++, true)); 452 | return true; 453 | } 454 | unget_token(tok); 455 | dict_put(param, arg, make_macro_token(pos++, false)); 456 | } 457 | } 458 | 459 | static List *read_funclike_macro_body(Dict *param) { 460 | List *r = make_list(); 461 | for (;;) { 462 | Token *tok = read_cpp_token(); 463 | if (!tok || tok->type == TNEWLINE) 464 | return r; 465 | if (tok->type == TIDENT) { 466 | Token *subst = dict_get(param, tok->sval); 467 | if (subst) { 468 | subst = copy_token(subst); 469 | subst->nspace = tok->nspace; 470 | list_push(r, subst); 471 | continue; 472 | } 473 | } 474 | list_push(r, tok); 475 | } 476 | return r; 477 | } 478 | 479 | static void read_funclike_macro(char *name) { 480 | Dict *param = make_dict(NULL); 481 | bool is_varg = read_funclike_macro_params(param); 482 | List *body = read_funclike_macro_body(param); 483 | Macro *macro = make_func_macro(body, list_len(dict_keys(param)), is_varg); 484 | dict_put(macros, name, macro); 485 | } 486 | 487 | static void read_obj_macro(char *name) { 488 | List *body = make_list(); 489 | for (;;) { 490 | Token *tok = read_cpp_token(); 491 | if (!tok || tok->type == TNEWLINE) 492 | break; 493 | list_push(body, tok); 494 | } 495 | dict_put(macros, name, make_obj_macro(body)); 496 | } 497 | 498 | /*---------------------------------------------------------------------- 499 | * #define 500 | */ 501 | 502 | static void read_define(void) { 503 | Token *name = read_ident(); 504 | Token *tok = read_cpp_token(); 505 | if (tok && is_punct(tok, '(') && !tok->nspace) { 506 | read_funclike_macro(name->sval); 507 | return; 508 | } 509 | unget_token(tok); 510 | read_obj_macro(name->sval); 511 | } 512 | 513 | /*---------------------------------------------------------------------- 514 | * #undef 515 | */ 516 | 517 | static void read_undef(void) { 518 | Token *name = read_ident(); 519 | expect_newline(); 520 | dict_remove(macros, name->sval); 521 | } 522 | 523 | /*---------------------------------------------------------------------- 524 | * defined() 525 | */ 526 | 527 | static Token *read_defined_op(void) { 528 | Token *tok = read_cpp_token(); 529 | if (is_punct(tok, '(')) { 530 | tok = read_cpp_token(); 531 | expect(')'); 532 | } 533 | if (tok->type != TIDENT) 534 | error("Identifier expected, but got %s", t2s(tok)); 535 | return dict_get(macros, tok->sval) ? 536 | cpp_token_one : cpp_token_zero; 537 | } 538 | 539 | /*---------------------------------------------------------------------- 540 | * #if and the like 541 | */ 542 | 543 | static List *read_intexpr_line(void) { 544 | List *r = make_list(); 545 | for (;;) { 546 | Token *tok = read_token_sub(true); 547 | if (!tok) return r; 548 | if (is_ident(tok, "defined")) 549 | list_push(r, read_defined_op()); 550 | else if (tok->type == TIDENT) 551 | list_push(r, cpp_token_one); 552 | else 553 | list_push(r, tok); 554 | } 555 | } 556 | 557 | static bool read_constexpr(void) { 558 | List *orig = get_input_buffer(); 559 | set_input_buffer(read_intexpr_line()); 560 | Node *expr = read_expr(); 561 | List *buf = get_input_buffer(); 562 | if (list_len(buf) > 0) 563 | error("Stray token: %s", t2s(list_shift(buf))); 564 | set_input_buffer(orig); 565 | return eval_intexpr(expr); 566 | } 567 | 568 | static void read_if_generic(bool cond) { 569 | list_push(cond_incl_stack, make_cond_incl(IN_THEN, cond)); 570 | if (!cond) 571 | skip_cond_incl(); 572 | } 573 | 574 | static void read_if(void) { 575 | read_if_generic(read_constexpr()); 576 | } 577 | 578 | static void read_ifdef_generic(bool is_ifdef) { 579 | Token *tok = read_cpp_token(); 580 | if (!tok || tok->type != TIDENT) 581 | error("identifier expected, but got %s", t2s(tok)); 582 | bool cond = dict_get(macros, tok->sval); 583 | expect_newline(); 584 | read_if_generic(is_ifdef ? cond : !cond); 585 | } 586 | 587 | static void read_ifdef(void) { 588 | read_ifdef_generic(true); 589 | } 590 | 591 | static void read_ifndef(void) { 592 | read_ifdef_generic(false); 593 | } 594 | 595 | static void read_else(void) { 596 | if (list_len(cond_incl_stack) == 0) 597 | error("stray #else"); 598 | CondIncl *ci = list_tail(cond_incl_stack); 599 | if (ci->ctx == IN_ELSE) 600 | error("#else appears in #else"); 601 | expect_newline(); 602 | if (ci->wastrue) 603 | skip_cond_incl(); 604 | } 605 | 606 | static void read_elif(void) { 607 | if (list_len(cond_incl_stack) == 0) 608 | error("stray #elif"); 609 | CondIncl *ci = list_tail(cond_incl_stack); 610 | if (ci->ctx == IN_ELSE) 611 | error("#elif after #else"); 612 | if (ci->wastrue) 613 | skip_cond_incl(); 614 | else if (read_constexpr()) 615 | ci->wastrue = true; 616 | } 617 | 618 | static void read_endif(void) { 619 | if (list_len(cond_incl_stack) == 0) 620 | error("stray #endif"); 621 | list_pop(cond_incl_stack); 622 | expect_newline(); 623 | } 624 | 625 | /*---------------------------------------------------------------------- 626 | * #error 627 | */ 628 | 629 | static void read_error(void) { 630 | error("#error: %s", read_error_directive()); 631 | } 632 | 633 | /*---------------------------------------------------------------------- 634 | * #include 635 | */ 636 | 637 | static char *read_cpp_header_name(bool *std) { 638 | if (!get_input_buffer()) { 639 | char *r = read_header_file_name(std); 640 | if (r) 641 | return r; 642 | } 643 | Token *tok = read_expand(); 644 | if (!tok || tok->type == TNEWLINE) 645 | error("expected file name, but got %s", t2s(tok)); 646 | if (tok->type == TSTRING) { 647 | *std = false; 648 | return tok->sval; 649 | } 650 | if (!is_punct(tok, '<')) 651 | error("'<' expected, but got %s", t2s(tok)); 652 | List *tokens = make_list(); 653 | for (;;) { 654 | Token *tok = read_expand(); 655 | if (!tok || tok->type == TNEWLINE) 656 | error("premature end of header name"); 657 | if (is_punct(tok, '>')) 658 | break; 659 | list_push(tokens, tok); 660 | } 661 | *std = true; 662 | return join_tokens(tokens, false); 663 | } 664 | 665 | static bool try_include(char *dir, char *filename) { 666 | char *path = format("%s/%s", dir, filename); 667 | FILE *fp = fopen(path, "r"); 668 | if (!fp) 669 | return false; 670 | push_input_file(path, path, fp); 671 | return true; 672 | } 673 | 674 | static void read_include(void) { 675 | bool std; 676 | char *filename = read_cpp_header_name(&std); 677 | expect_newline(); 678 | if (!std) { 679 | if (get_current_file()) { 680 | char *buf = format("%s", get_current_file()); 681 | if (try_include(dirname(buf), filename)) 682 | return; 683 | } else if (try_include(".", filename)) { 684 | return; 685 | } 686 | } 687 | for (Iter *i = list_iter(std_include_path); !iter_end(i);) { 688 | if (try_include(iter_next(i), filename)) 689 | return; 690 | } 691 | error("Cannot find header file: %s", filename); 692 | } 693 | 694 | /*---------------------------------------------------------------------- 695 | * #line 696 | */ 697 | 698 | static bool is_digit_sequence(char *p) { 699 | for (; *p; p++) 700 | if (!isdigit(*p)) 701 | return false; 702 | return true; 703 | } 704 | 705 | static void read_line(void) { 706 | Token *tok = read_cpp_token(); 707 | if (!tok || tok->type != TNUMBER || !is_digit_sequence(tok->sval)) 708 | error("number expected after #line, but got %s", t2s(tok)); 709 | int line = atoi(tok->sval); 710 | tok = read_cpp_token(); 711 | char *filename = NULL; 712 | if (tok && tok->type == TSTRING) { 713 | filename = tok->sval; 714 | expect_newline(); 715 | } else if (tok->type != TNEWLINE) { 716 | error("newline or a source name are expected, but got %s", t2s(tok)); 717 | } 718 | set_current_line(line); 719 | if (filename) 720 | set_current_displayname(filename); 721 | } 722 | 723 | /*---------------------------------------------------------------------- 724 | * #-directive 725 | */ 726 | 727 | static void read_directive(void) { 728 | Token *tok = read_cpp_token(); 729 | if (is_ident(tok, "define")) read_define(); 730 | else if (is_ident(tok, "undef")) read_undef(); 731 | else if (is_ident(tok, "if")) read_if(); 732 | else if (is_ident(tok, "ifdef")) read_ifdef(); 733 | else if (is_ident(tok, "ifndef")) read_ifndef(); 734 | else if (is_ident(tok, "else")) read_else(); 735 | else if (is_ident(tok, "elif")) read_elif(); 736 | else if (is_ident(tok, "endif")) read_endif(); 737 | else if (is_ident(tok, "error")) read_error(); 738 | else if (is_ident(tok, "include")) read_include(); 739 | else if (is_ident(tok, "line")) read_line(); 740 | else if (tok->type != TNEWLINE) 741 | error("unsupported preprocessor directive: %s", t2s(tok)); 742 | } 743 | 744 | /*---------------------------------------------------------------------- 745 | * Special macros 746 | */ 747 | 748 | static struct tm *gettime(void) { 749 | if (current_time) 750 | return current_time; 751 | time_t timet = time(NULL); 752 | current_time = malloc(sizeof(struct tm)); 753 | localtime_r(&timet, current_time); 754 | return current_time; 755 | } 756 | 757 | static void handle_date_macro(Token *tmpl) { 758 | Token *tok = copy_token(tmpl); 759 | tok->type = TSTRING; 760 | char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; 761 | struct tm *now = gettime(); 762 | tok->sval = format("%s %02d %04d", month[now->tm_mon], now->tm_mday, 1900 + now->tm_year); 763 | unget_token(tok); 764 | } 765 | 766 | static void handle_time_macro(Token *tmpl) { 767 | Token *tok = copy_token(tmpl); 768 | tok->type = TSTRING; 769 | struct tm *now = gettime(); 770 | tok->sval = format("%02d:%02d:%02d", now->tm_hour, now->tm_min, now->tm_sec); 771 | unget_token(tok); 772 | } 773 | 774 | static void handle_file_macro(Token *tmpl) { 775 | Token *tok = copy_token(tmpl); 776 | tok->type = TSTRING; 777 | tok->sval = get_current_displayname(); 778 | unget_token(tok); 779 | } 780 | 781 | static void handle_line_macro(Token *tmpl) { 782 | Token *tok = copy_token(tmpl); 783 | tok->type = TNUMBER; 784 | tok->sval = format("%d", get_current_line()); 785 | unget_token(tok); 786 | } 787 | 788 | static void handle_pragma_macro(Token *ignore) { 789 | error("No pragmas supported"); 790 | } 791 | 792 | static void handle_counter_macro(Token *tmpl) { 793 | Token *tok = copy_token(tmpl); 794 | tok->type = TNUMBER; 795 | tok->sval = format("%d", macro_counter++); 796 | unget_token(tok); 797 | } 798 | 799 | /*---------------------------------------------------------------------- 800 | * Initializer 801 | */ 802 | 803 | static char *drop_last_slash(char *s) { 804 | char *r = format("%s", s); 805 | char *p = r + strlen(r) - 1; 806 | if (*p == '/') 807 | *p = '\0'; 808 | return r; 809 | } 810 | 811 | void add_include_path(char *path) { 812 | list_unshift(std_include_path, drop_last_slash(path)); 813 | } 814 | 815 | /*---------------------------------------------------------------------- 816 | * Initializer 817 | */ 818 | 819 | static void define_obj_macro(char *name, Token *value) { 820 | dict_put(macros, name, make_obj_macro(make_list1(value))); 821 | } 822 | 823 | static void define_special_macro(char *name, special_macro_handler *fn) { 824 | dict_put(macros, name, make_special_macro(fn)); 825 | } 826 | 827 | void cpp_init(void) { 828 | list_unshift(std_include_path, "/usr/include/x86_64-linux-gnu"); 829 | list_unshift(std_include_path, "/usr/include/linux"); 830 | list_unshift(std_include_path, "/usr/include"); 831 | list_unshift(std_include_path, "/usr/local/include"); 832 | list_unshift(std_include_path, "./include"); 833 | 834 | define_special_macro("__DATE__", handle_date_macro); 835 | define_special_macro("__TIME__", handle_time_macro); 836 | define_special_macro("__FILE__", handle_file_macro); 837 | define_special_macro("__LINE__", handle_line_macro); 838 | define_special_macro("_Pragma", handle_pragma_macro); 839 | define_special_macro("__COUNTER__", handle_counter_macro); 840 | 841 | char *predefined[] = { 842 | "__8cc__", "__amd64", "__amd64__", "__x86_64", "__x86_64__", 843 | "linux", "__linux", "__linux__", "__gnu_linux__", "__unix", "__unix__", 844 | "_LP64", "__LP64__", "__ELF__", "__STDC__", "__STDC_HOSTED__" }; 845 | 846 | for (int i = 0; i < sizeof(predefined) / sizeof(*predefined); i++) 847 | define_obj_macro(predefined[i], cpp_token_one); 848 | 849 | define_obj_macro("__STDC_VERSION__", make_number("199901L")); 850 | define_obj_macro("__SIZEOF_SHORT__", make_number("2")); 851 | define_obj_macro("__SIZEOF_INT__", make_number("4")); 852 | define_obj_macro("__SIZEOF_LONG__", make_number("8")); 853 | define_obj_macro("__SIZEOF_LONG_LONG__", make_number("8")); 854 | define_obj_macro("__SIZEOF_FLOAT__", make_number("4")); 855 | define_obj_macro("__SIZEOF_DOUBLE__", make_number("8")); 856 | define_obj_macro("__SIZEOF_LONG_DOUBLE__", make_number("16")); 857 | define_obj_macro("__SIZEOF_POINTER__", make_number("8")); 858 | define_obj_macro("__SIZEOF_PTRDIFF_T__", make_number("8")); 859 | define_obj_macro("__SIZEOF_SIZE_T__", make_number("8")); 860 | } 861 | 862 | /*---------------------------------------------------------------------- 863 | * Keyword 864 | */ 865 | 866 | static Token *convert_punct(Token *tmpl, int punct) { 867 | Token *r = copy_token(tmpl); 868 | r->type = TPUNCT; 869 | r->punct = punct; 870 | return r; 871 | } 872 | 873 | static Token *maybe_convert_keyword(Token *tok) { 874 | if (!tok) 875 | return NULL; 876 | if (tok->type != TIDENT) 877 | return tok; 878 | #define punct(ident, str) \ 879 | if (!strcmp(str, tok->sval)) \ 880 | return convert_punct(tok, ident); 881 | #define keyword(ident, str, _) \ 882 | if (!strcmp(str, tok->sval)) \ 883 | return convert_punct(tok, ident); 884 | #include "keyword.h" 885 | #undef keyword 886 | #undef punct 887 | return tok; 888 | } 889 | 890 | /*---------------------------------------------------------------------- 891 | * Public intefaces 892 | */ 893 | 894 | void unget_token(Token *tok) { 895 | unget_cpp_token(tok); 896 | } 897 | 898 | Token *peek_token(void) { 899 | Token *r = read_token(); 900 | unget_token(r); 901 | return r; 902 | } 903 | 904 | static Token *read_token_sub(bool return_at_eol) { 905 | for (;;) { 906 | Token *tok = read_cpp_token(); 907 | if (!tok) 908 | return NULL; 909 | if (tok && tok->type == TNEWLINE) { 910 | if (return_at_eol) 911 | return NULL; 912 | continue; 913 | } 914 | if (tok->bol && is_punct(tok, '#')) { 915 | read_directive(); 916 | continue; 917 | } 918 | unget_token(tok); 919 | Token *r = read_expand(); 920 | if (r && r->bol && is_punct(r, '#') && dict_empty(r->hideset)) { 921 | read_directive(); 922 | continue; 923 | } 924 | return r; 925 | } 926 | } 927 | 928 | Token *read_token(void) { 929 | Token *tok; 930 | for (;;) { 931 | tok = read_token_sub(false); 932 | if (!tok) return NULL; 933 | assert(tok->type != TNEWLINE); 934 | assert(tok->type != TSPACE); 935 | assert(tok->type != TMACRO_PARAM); 936 | if (tok->type != TSTRING) 937 | break; 938 | Token *tok2 = read_token_sub(false); 939 | if (!tok2 || tok2->type != TSTRING) { 940 | unget_token(tok2); 941 | break; 942 | } 943 | Token *conc = copy_token(tok); 944 | conc->sval = format("%s%s", tok->sval, tok2->sval); 945 | unget_token(conc); 946 | } 947 | Token *r = maybe_convert_keyword(tok); 948 | if (debug_cpp) 949 | fprintf(stderr, " token=%s\n", t2s(r)); 950 | return r; 951 | } 952 | -------------------------------------------------------------------------------- /gen.c: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Rui Ueyama 2 | // This program is free software licensed under the MIT license. 3 | 4 | #include 5 | #include 6 | #include 7 | #include "8cc.h" 8 | #include "list.h" 9 | 10 | static char *REGS[] = {"rdi", "rsi", "rdx", "rcx", "r8", "r9"}; 11 | static char *SREGS[] = {"dil", "sil", "dl", "cl", "r8b", "r9b"}; 12 | static char *MREGS[] = {"edi", "esi", "edx", "ecx", "r8d", "r9d"}; 13 | static int TAB = 8; 14 | static List *functions = &EMPTY_LIST; 15 | static char *lbreak; 16 | static char *lcontinue; 17 | static char *lswitch; 18 | static int stackpos; 19 | static int numgp; 20 | static int numfp; 21 | static FILE *outputfp; 22 | 23 | static void emit_expr(Node *node); 24 | static void emit_decl_init(List *inits, int off); 25 | static void emit_data_int(List *inits, int size, int off, int depth); 26 | static void emit_data(Node *v, int off, int depth); 27 | 28 | #define REGAREA_SIZE 304 29 | 30 | #define emit(...) emitf(__LINE__, "\t" __VA_ARGS__) 31 | #define emit_noindent(...) emitf(__LINE__, __VA_ARGS__) 32 | 33 | #ifdef __GNUC__ 34 | #define SAVE \ 35 | int save_hook __attribute__((unused, cleanup(pop_function))); \ 36 | list_push(functions, (void *)__func__) 37 | 38 | static void pop_function(void *ignore) { 39 | list_pop(functions); 40 | } 41 | #else 42 | #define SAVE 43 | #endif 44 | 45 | static char *get_caller_list(void) { 46 | String *s = make_string(); 47 | for (Iter *i = list_iter(functions); !iter_end(i);) { 48 | string_appendf(s, "%s", iter_next(i)); 49 | if (!iter_end(i)) 50 | string_appendf(s, " -> "); 51 | } 52 | return get_cstring(s); 53 | } 54 | 55 | void set_output_file(FILE *fp) { 56 | outputfp = fp; 57 | } 58 | 59 | void close_output_file(void) { 60 | fclose(outputfp); 61 | } 62 | 63 | static void emitf(int line, char *fmt, ...) { 64 | va_list args; 65 | va_start(args, fmt); 66 | int col = vfprintf(outputfp, fmt, args); 67 | va_end(args); 68 | 69 | for (char *p = fmt; *p; p++) 70 | if (*p == '\t') 71 | col += TAB - 1; 72 | int space = (28 - col) > 0 ? (30 - col) : 2; 73 | fprintf(outputfp, "%*c %s:%d\n", space, '#', get_caller_list(), line); 74 | } 75 | 76 | static char *get_int_reg(Ctype *ctype, char r) { 77 | assert(r == 'a' || r == 'c'); 78 | switch (ctype->size) { 79 | case 1: return (r == 'a') ? "al" : "cl"; 80 | case 2: return (r == 'a') ? "ax" : "cx"; 81 | case 4: return (r == 'a') ? "eax" : "ecx"; 82 | case 8: return (r == 'a') ? "rax" : "rcx"; 83 | default: 84 | error("Unknown data size: %s: %d", c2s(ctype), ctype->size); 85 | } 86 | } 87 | 88 | static char *get_load_inst(Ctype *ctype) { 89 | switch (ctype->size) { 90 | case 1: return "movsbq"; 91 | case 2: return "movswq"; 92 | case 4: return "movslq"; 93 | case 8: return "mov"; 94 | default: 95 | error("Unknown data size: %s: %d", c2s(ctype), ctype->size); 96 | } 97 | } 98 | 99 | static void push_xmm(int reg) { 100 | SAVE; 101 | emit("sub $8, %%rsp"); 102 | emit("movsd %%xmm%d, (%%rsp)", reg); 103 | stackpos += 8; 104 | } 105 | 106 | static void pop_xmm(int reg) { 107 | SAVE; 108 | emit("movsd (%%rsp), %%xmm%d", reg); 109 | emit("add $8, %%rsp"); 110 | stackpos -= 8; 111 | assert(stackpos >= 0); 112 | } 113 | 114 | static void push(char *reg) { 115 | SAVE; 116 | emit("push %%%s", reg); 117 | stackpos += 8; 118 | } 119 | 120 | static void pop(char *reg) { 121 | SAVE; 122 | emit("pop %%%s", reg); 123 | stackpos -= 8; 124 | assert(stackpos >= 0); 125 | } 126 | 127 | static void maybe_emit_bitshift_load(Ctype *ctype) { 128 | SAVE; 129 | if (ctype->bitsize <= 0) 130 | return; 131 | emit("shr $%d, %%rax", ctype->bitoff); 132 | push("rcx"); 133 | emit("mov $0x%lx, %%rcx", (1 << (long)ctype->bitsize) - 1); 134 | emit("and %%rcx, %%rax"); 135 | pop("rcx"); 136 | } 137 | 138 | static void maybe_emit_bitshift_save(Ctype *ctype, char *addr) { 139 | SAVE; 140 | if (ctype->bitsize <= 0) 141 | return; 142 | push("rcx"); 143 | push("rdi"); 144 | emit("mov $0x%lx, %%rdi", (1 << (long)ctype->bitsize) - 1); 145 | emit("and %%rdi, %%rax"); 146 | emit("shl $%d, %%rax", ctype->bitoff); 147 | emit("mov %s, %%%s", addr, get_int_reg(ctype, 'c')); 148 | emit("mov $0x%lx, %%rdi", ~(((1 << (long)ctype->bitsize) - 1) << ctype->bitoff)); 149 | emit("and %%rdi, %%rcx"); 150 | emit("or %%rcx, %%rax"); 151 | pop("rdi"); 152 | pop("rcx"); 153 | } 154 | 155 | static void emit_gload(Ctype *ctype, char *label, int off) { 156 | SAVE; 157 | if (ctype->type == CTYPE_ARRAY) { 158 | if (off) 159 | emit("lea %s+%d(%%rip), %%rax", label, off); 160 | else 161 | emit("lea %s(%%rip), %%rax", label); 162 | return; 163 | } 164 | char *inst = get_load_inst(ctype); 165 | emit("%s %s+%d(%%rip), %%rax", inst, label, off); 166 | maybe_emit_bitshift_load(ctype); 167 | } 168 | 169 | static void emit_toint(Ctype *ctype) { 170 | SAVE; 171 | if (ctype->type == CTYPE_FLOAT) 172 | emit("cvttss2si %%xmm0, %%eax"); 173 | else if (ctype->type == CTYPE_FLOAT) 174 | emit("cvttsd2si %%xmm0, %%eax"); 175 | } 176 | 177 | static void emit_lload(Ctype *ctype, char *base, int off) { 178 | SAVE; 179 | if (ctype->type == CTYPE_ARRAY) { 180 | emit("lea %d(%%%s), %%rax", off, base); 181 | } else if (ctype->type == CTYPE_FLOAT) { 182 | emit("movss %d(%%%s), %%xmm0", off, base); 183 | } else if (ctype->type == CTYPE_DOUBLE || ctype->type == CTYPE_LDOUBLE) { 184 | emit("movsd %d(%%%s), %%xmm0", off, base); 185 | } else { 186 | char *inst = get_load_inst(ctype); 187 | emit("%s %d(%%%s), %%rax", inst, off, base); 188 | maybe_emit_bitshift_load(ctype); 189 | } 190 | } 191 | 192 | static void maybe_convert_bool(Ctype *ctype) { 193 | if (ctype->type == CTYPE_BOOL) { 194 | emit("test %%rax, %%rax"); 195 | emit("setne %%al"); 196 | } 197 | } 198 | 199 | static void emit_gsave(char *varname, Ctype *ctype, int off) { 200 | SAVE; 201 | assert(ctype->type != CTYPE_ARRAY); 202 | maybe_convert_bool(ctype); 203 | char *reg = get_int_reg(ctype, 'a'); 204 | char *addr = format("%s+%d(%%rip)", varname, off); 205 | maybe_emit_bitshift_save(ctype, addr); 206 | emit("mov %%%s, %s", reg, addr); 207 | } 208 | 209 | static void emit_lsave(Ctype *ctype, int off) { 210 | SAVE; 211 | if (ctype->type == CTYPE_FLOAT) { 212 | emit("movss %%xmm0, %d(%%rbp)", off); 213 | } else if (ctype->type == CTYPE_DOUBLE) { 214 | emit("movsd %%xmm0, %d(%%rbp)", off); 215 | } else { 216 | maybe_convert_bool(ctype); 217 | char *reg = get_int_reg(ctype, 'a'); 218 | char *addr = format("%d(%%rbp)", off); 219 | maybe_emit_bitshift_save(ctype, addr); 220 | emit("mov %%%s, %s", reg, addr); 221 | } 222 | } 223 | 224 | static void emit_assign_deref_int(Ctype *ctype, int off) { 225 | SAVE; 226 | emit("mov (%%rsp), %%rcx"); 227 | char *reg = get_int_reg(ctype, 'c'); 228 | if (off) 229 | emit("mov %%%s, %d(%%rax)", reg, off); 230 | else 231 | emit("mov %%%s, (%%rax)", reg); 232 | pop("rax"); 233 | } 234 | 235 | static void emit_assign_deref(Node *var) { 236 | SAVE; 237 | push("rax"); 238 | emit_expr(var->operand); 239 | emit_assign_deref_int(var->operand->ctype->ptr, 0); 240 | } 241 | 242 | static void emit_pointer_arith(char type, Node *left, Node *right) { 243 | SAVE; 244 | emit_expr(left); 245 | push("rcx"); 246 | push("rax"); 247 | emit_expr(right); 248 | int size = left->ctype->ptr->size; 249 | if (size > 1) 250 | emit("imul $%d, %%rax", size); 251 | emit("mov %%rax, %%rcx"); 252 | pop("rax"); 253 | switch (type) { 254 | case '+': emit("add %%rcx, %%rax"); break; 255 | case '-': emit("sub %%rcx, %%rax"); break; 256 | default: error("invalid operator '%d'", type); 257 | } 258 | pop("rcx"); 259 | } 260 | 261 | static void emit_zero_filler(int start, int end) { 262 | for (; start <= end - 4; start += 4) 263 | emit("movl $0, %d(%%rbp)", start); 264 | for (; start < end; start++) 265 | emit("movb $0, %d(%%rbp)", start); 266 | } 267 | 268 | static void ensure_lvar_init(Node *node) { 269 | assert(node->type == AST_LVAR); 270 | if (node->lvarinit) { 271 | emit_zero_filler(node->loff, node->loff + node->ctype->size); 272 | emit_decl_init(node->lvarinit, node->loff); 273 | } 274 | node->lvarinit = NULL; 275 | } 276 | 277 | static void emit_assign_struct_ref(Node *struc, Ctype *field, int off) { 278 | SAVE; 279 | switch (struc->type) { 280 | case AST_LVAR: 281 | ensure_lvar_init(struc); 282 | emit_lsave(field, struc->loff + field->offset + off); 283 | break; 284 | case AST_GVAR: 285 | emit_gsave(struc->varname, field, field->offset + off); 286 | break; 287 | case AST_STRUCT_REF: 288 | emit_assign_struct_ref(struc->struc, field, off + struc->ctype->offset); 289 | break; 290 | case AST_DEREF: 291 | push("rax"); 292 | emit_expr(struc->operand); 293 | emit_assign_deref_int(field, field->offset + off); 294 | break; 295 | default: 296 | error("internal error: %s", a2s(struc)); 297 | } 298 | } 299 | 300 | static void emit_load_struct_ref(Node *struc, Ctype *field, int off) { 301 | SAVE; 302 | switch (struc->type) { 303 | case AST_LVAR: 304 | ensure_lvar_init(struc); 305 | emit_lload(field, "rbp", struc->loff + field->offset + off); 306 | break; 307 | case AST_GVAR: 308 | emit_gload(field, struc->varname, field->offset + off); 309 | break; 310 | case AST_STRUCT_REF: 311 | emit_load_struct_ref(struc->struc, field, struc->ctype->offset + off); 312 | break; 313 | case AST_DEREF: 314 | emit_expr(struc->operand); 315 | emit_lload(field, "rax", field->offset + off); 316 | break; 317 | default: 318 | error("internal error: %s", a2s(struc)); 319 | } 320 | } 321 | 322 | static void emit_store(Node *var) { 323 | SAVE; 324 | switch (var->type) { 325 | case AST_DEREF: emit_assign_deref(var); break; 326 | case AST_STRUCT_REF: emit_assign_struct_ref(var->struc, var->ctype, 0); break; 327 | case AST_LVAR: 328 | ensure_lvar_init(var); 329 | emit_lsave(var->ctype, var->loff); 330 | break; 331 | case AST_GVAR: emit_gsave(var->varname, var->ctype, 0); break; 332 | default: error("internal error"); 333 | } 334 | } 335 | 336 | static void emit_to_bool(Ctype *ctype) { 337 | SAVE; 338 | if (is_flotype(ctype)) { 339 | push_xmm(1); 340 | emit("xorpd %%xmm1, %%xmm1"); 341 | emit("%s %%xmm1, %%xmm0", (ctype->type == CTYPE_FLOAT) ? "ucomiss" : "ucomisd"); 342 | emit("setne %%al"); 343 | pop_xmm(1); 344 | } else { 345 | emit("cmp $0, %%rax"); 346 | emit("setne %%al"); 347 | } 348 | emit("movzb %%al, %%eax"); 349 | } 350 | 351 | static void emit_comp(char *inst, Node *node) { 352 | SAVE; 353 | if (is_flotype(node->left->ctype)) { 354 | emit_expr(node->left); 355 | push_xmm(0); 356 | emit_expr(node->right); 357 | pop_xmm(1); 358 | if (node->left->ctype->type == CTYPE_FLOAT) 359 | emit("ucomiss %%xmm0, %%xmm1"); 360 | else 361 | emit("ucomisd %%xmm0, %%xmm1"); 362 | } else { 363 | emit_expr(node->left); 364 | push("rax"); 365 | emit_expr(node->right); 366 | pop("rcx"); 367 | int type = node->left->ctype->type; 368 | if (type == CTYPE_LONG || type == CTYPE_LLONG) 369 | emit("cmp %%rax, %%rcx"); 370 | else 371 | emit("cmp %%eax, %%ecx"); 372 | } 373 | emit("%s %%al", inst); 374 | emit("movzb %%al, %%eax"); 375 | } 376 | 377 | static void emit_binop_int_arith(Node *node) { 378 | SAVE; 379 | char *op = NULL; 380 | switch (node->type) { 381 | case '+': op = "add"; break; 382 | case '-': op = "sub"; break; 383 | case '*': op = "imul"; break; 384 | case '^': op = "xor"; break; 385 | case OP_SAL: op = "sal"; break; 386 | case OP_SAR: op = "sar"; break; 387 | case OP_SHR: op = "shr"; break; 388 | case '/': case '%': break; 389 | default: error("invalid operator '%d'", node->type); 390 | } 391 | emit_expr(node->left); 392 | push("rax"); 393 | emit_expr(node->right); 394 | emit("mov %%rax, %%rcx"); 395 | pop("rax"); 396 | if (node->type == '/' || node->type == '%') { 397 | emit("cqto"); 398 | emit("idiv %%rcx"); 399 | if (node->type == '%') 400 | emit("mov %%edx, %%eax"); 401 | } else if (node->type == OP_SAL || node->type == OP_SAR || node->type == OP_SHR) { 402 | emit("%s %%cl, %%%s", op, get_int_reg(node->left->ctype, 'a')); 403 | } else { 404 | emit("%s %%rcx, %rax", op); 405 | } 406 | } 407 | 408 | static void emit_binop_float_arith(Node *node) { 409 | SAVE; 410 | char *op; 411 | bool isdouble = (node->ctype->type == CTYPE_DOUBLE); 412 | switch (node->type) { 413 | case '+': op = (isdouble ? "addsd" : "addss"); break; 414 | case '-': op = (isdouble ? "subsd" : "subss"); break; 415 | case '*': op = (isdouble ? "mulsd" : "mulss"); break; 416 | case '/': op = (isdouble ? "divsd" : "divss"); break; 417 | default: error("invalid operator '%d'", node->type); 418 | } 419 | emit_expr(node->left); 420 | push_xmm(0); 421 | emit_expr(node->right); 422 | emit("%s %%xmm0, %%xmm1", (isdouble ? "movsd" : "movss")); 423 | pop_xmm(0); 424 | emit("%s %%xmm1, %%xmm0", op); 425 | } 426 | 427 | static void emit_load_convert(Ctype *to, Ctype *from) { 428 | SAVE; 429 | if (is_inttype(from) && to->type == CTYPE_FLOAT) 430 | emit("cvtsi2ss %%eax, %%xmm0"); 431 | else if (is_inttype(from) && to->type == CTYPE_DOUBLE) 432 | emit("cvtsi2sd %%eax, %%xmm0"); 433 | else if (from->type == CTYPE_FLOAT && to->type == CTYPE_DOUBLE) 434 | emit("cvtps2pd %%xmm0, %%xmm0"); 435 | else if (from->type == CTYPE_DOUBLE && to->type == CTYPE_FLOAT) 436 | emit("cvtpd2ps %%xmm0, %%xmm0"); 437 | else if (to->type == CTYPE_BOOL) 438 | emit_to_bool(from); 439 | else if (is_inttype(to)) 440 | emit_toint(from); 441 | } 442 | 443 | static void emit_ret(void) { 444 | SAVE; 445 | emit("leave"); 446 | emit("ret"); 447 | } 448 | 449 | static void emit_binop(Node *node) { 450 | SAVE; 451 | if (node->ctype->type == CTYPE_PTR) { 452 | emit_pointer_arith(node->type, node->left, node->right); 453 | return; 454 | } 455 | switch (node->type) { 456 | case '<': emit_comp("setl", node); return; 457 | case '>': emit_comp("setg", node); return; 458 | case OP_EQ: emit_comp("sete", node); return; 459 | case OP_GE: emit_comp("setge", node); return; 460 | case OP_LE: emit_comp("setle", node); return; 461 | case OP_NE: emit_comp("setne", node); return; 462 | } 463 | if (is_inttype(node->ctype)) 464 | emit_binop_int_arith(node); 465 | else if (is_flotype(node->ctype)) 466 | emit_binop_float_arith(node); 467 | else 468 | error("internal error"); 469 | } 470 | 471 | static void emit_save_literal(Node *node, Ctype *totype, int off) { 472 | switch (totype->type) { 473 | case CTYPE_BOOL: emit("movb $%d, %d(%%rbp)", !!node->ival, off); break; 474 | case CTYPE_CHAR: emit("movb $%d, %d(%%rbp)", node->ival, off); break; 475 | case CTYPE_SHORT: emit("movw $%d, %d(%%rbp)", node->ival, off); break; 476 | case CTYPE_INT: emit("movl $%d, %d(%%rbp)", node->ival, off); break; 477 | case CTYPE_LONG: 478 | case CTYPE_LLONG: 479 | case CTYPE_PTR: { 480 | unsigned long ival = node->ival; 481 | emit("movl $%lu, %d(%%rbp)", ival & ((1L << 32) - 1), off); 482 | emit("movl $%lu, %d(%%rbp)", ival >> 32, off + 4); 483 | break; 484 | } 485 | case CTYPE_FLOAT: { 486 | float fval = node->fval; 487 | int *p = (int *)&fval; 488 | emit("movl $%u, %d(%%rbp)", *p, off); 489 | break; 490 | } 491 | case CTYPE_DOUBLE: { 492 | long *p = (long *)&node->fval; 493 | emit("movl $%lu, %d(%%rbp)", *p & ((1L << 32) - 1), off); 494 | emit("movl $%lu, %d(%%rbp)", *p >> 32, off + 4); 495 | break; 496 | } 497 | default: 498 | error("internal error: <%s> <%s> <%d>", a2s(node), c2s(totype), off); 499 | } 500 | } 501 | 502 | static void emit_addr(Node *node) { 503 | switch (node->type) { 504 | case AST_LVAR: 505 | ensure_lvar_init(node); 506 | emit("lea %d(%%rbp), %%rax", node->loff); 507 | break; 508 | case AST_GVAR: 509 | emit("lea %s(%%rip), %%rax", node->glabel); 510 | break; 511 | case AST_DEREF: 512 | emit_expr(node->operand); 513 | break; 514 | case AST_STRUCT_REF: 515 | emit_addr(node->struc); 516 | emit("add $%d, %%rax", node->ctype->offset); 517 | break; 518 | default: 519 | error("internal error: %s", a2s(node)); 520 | } 521 | } 522 | 523 | static void emit_copy_struct(Node *left, Node *right) { 524 | push("rcx"); 525 | push("r11"); 526 | emit_addr(right); 527 | emit("mov %%rax, %%rcx"); 528 | emit_addr(left); 529 | int i = 0; 530 | for (; i < left->ctype->size; i += 8) { 531 | emit("movq %d(%%rcx), %%r11", i); 532 | emit("movq %%r11, %d(%%rax)", i); 533 | } 534 | for (; i < left->ctype->size; i += 4) { 535 | emit("movl %d(%%rcx), %%r11", i); 536 | emit("movl %%r11, %d(%%rax)", i); 537 | } 538 | for (; i < left->ctype->size; i++) { 539 | emit("movb %d(%%rcx), %%r11", i); 540 | emit("movb %%r11, %d(%%rax)", i); 541 | } 542 | pop("r11"); 543 | pop("rcx"); 544 | } 545 | 546 | static void emit_decl_init(List *inits, int off) { 547 | Iter *iter = list_iter(inits); 548 | while (!iter_end(iter)) { 549 | Node *node = iter_next(iter); 550 | assert(node->type == AST_INIT); 551 | if (node->initval->type == AST_LITERAL && 552 | node->totype->bitsize <= 0) { 553 | emit_save_literal(node->initval, node->totype, node->initoff + off); 554 | } else { 555 | emit_expr(node->initval); 556 | emit_lsave(node->totype, node->initoff + off); 557 | } 558 | } 559 | } 560 | 561 | static void emit_uminus(Node *node) { 562 | emit_expr(node->operand); 563 | if (is_flotype(node->ctype)) { 564 | push_xmm(1); 565 | emit("xorpd %%xmm1, %%xmm1"); 566 | emit("%s %%xmm1, %%xmm0", (node->ctype->type == CTYPE_DOUBLE ? "subsd" : "subss")); 567 | pop_xmm(1); 568 | } else { 569 | emit("neg %%rax"); 570 | } 571 | } 572 | 573 | static void emit_pre_inc_dec(Node *node, char *op) { 574 | emit_expr(node->operand); 575 | emit("%s $1, %%rax", op); 576 | emit_store(node->operand); 577 | } 578 | 579 | static void emit_post_inc_dec(Node *node, char *op) { 580 | SAVE; 581 | emit_expr(node->operand); 582 | push("rax"); 583 | emit("%s $1, %%rax", op); 584 | emit_store(node->operand); 585 | pop("rax"); 586 | } 587 | 588 | static void set_reg_nums(List *args) { 589 | numgp = numfp = 0; 590 | for (Iter *i = list_iter(args); !iter_end(i);) { 591 | Node *arg = iter_next(i); 592 | if (is_flotype(arg->ctype)) 593 | numfp++; 594 | else 595 | numgp++; 596 | } 597 | } 598 | 599 | static void emit_je(char *label) { 600 | emit("test %%rax, %%rax"); 601 | emit("je %s", label); 602 | } 603 | 604 | static void emit_label(char *label) { 605 | emit("%s:", label); 606 | } 607 | 608 | static void emit_jmp(char *label) { 609 | emit("jmp %s", label); 610 | } 611 | 612 | static void emit_literal(Node *node) { 613 | SAVE; 614 | switch (node->ctype->type) { 615 | case CTYPE_BOOL: 616 | case CTYPE_CHAR: 617 | emit("mov $%d, %%rax", node->ival); 618 | break; 619 | case CTYPE_INT: 620 | emit("mov $%d, %%rax", node->ival); 621 | break; 622 | case CTYPE_LONG: 623 | case CTYPE_LLONG: { 624 | emit("mov $%lu, %%rax", node->ival); 625 | break; 626 | } 627 | case CTYPE_FLOAT: { 628 | if (!node->flabel) { 629 | node->flabel = make_label(); 630 | float fval = node->fval; 631 | int *p = (int *)&fval; 632 | emit_noindent(".data"); 633 | emit_label(node->flabel); 634 | emit(".long %d", *p); 635 | emit_noindent(".text"); 636 | } 637 | emit("movss %s(%%rip), %%xmm0", node->flabel); 638 | break; 639 | } 640 | case CTYPE_DOUBLE: 641 | case CTYPE_LDOUBLE: { 642 | if (!node->flabel) { 643 | node->flabel = make_label(); 644 | int *fval = (int *)&node->fval; 645 | emit_noindent(".data"); 646 | emit_label(node->flabel); 647 | emit(".long %d", fval[0]); 648 | emit(".long %d", fval[1]); 649 | emit_noindent(".text"); 650 | } 651 | emit("movsd %s(%%rip), %%xmm0", node->flabel); 652 | break; 653 | } 654 | default: 655 | error("internal error"); 656 | } 657 | } 658 | 659 | static void emit_literal_string(Node *node) { 660 | SAVE; 661 | if (!node->slabel) { 662 | node->slabel = make_label(); 663 | emit_noindent(".data"); 664 | emit_label(node->slabel); 665 | emit(".string \"%s\"", quote_cstring(node->sval)); 666 | emit_noindent(".text"); 667 | } 668 | emit("lea %s(%%rip), %%rax", node->slabel); 669 | } 670 | 671 | static void emit_lvar(Node *node) { 672 | SAVE; 673 | ensure_lvar_init(node); 674 | emit_lload(node->ctype, "rbp", node->loff); 675 | } 676 | 677 | static void emit_gvar(Node *node) { 678 | SAVE; 679 | emit_gload(node->ctype, node->glabel, 0); 680 | } 681 | 682 | static void classify_args(List *ints, List *floats, List *rest, List *args) { 683 | SAVE; 684 | int ireg = 0, xreg = 0; 685 | int imax = 6, xmax = 8; 686 | Iter *iter = list_iter(args); 687 | while (!iter_end(iter)) { 688 | Node *v = iter_next(iter); 689 | if (is_flotype(v->ctype)) 690 | list_push((xreg++ < xmax) ? floats : rest, v); 691 | else 692 | list_push((ireg++ < imax) ? ints : rest, v); 693 | } 694 | } 695 | 696 | static void save_arg_regs(int nints, int nfloats) { 697 | SAVE; 698 | assert(nints <= 6); 699 | assert(nfloats <= 8); 700 | for (int i = 0; i < nints; i++) 701 | push(REGS[i]); 702 | for (int i = 1; i < nfloats; i++) 703 | push_xmm(i); 704 | } 705 | 706 | static void restore_arg_regs(int nints, int nfloats) { 707 | SAVE; 708 | for (int i = nfloats - 1; i > 0; i--) 709 | pop_xmm(i); 710 | for (int i = nints - 1; i >= 0; i--) 711 | pop(REGS[i]); 712 | } 713 | 714 | static void emit_args(List *vals) { 715 | SAVE; 716 | Iter *iter = list_iter(vals); 717 | while (!iter_end(iter)) { 718 | Node *v = iter_next(iter); 719 | emit_expr(v); 720 | if (is_flotype(v->ctype)) 721 | push_xmm(0); 722 | else 723 | push("rax"); 724 | } 725 | } 726 | 727 | static void pop_int_args(int nints) { 728 | SAVE; 729 | for (int i = nints - 1; i >= 0; i--) 730 | pop(REGS[i]); 731 | } 732 | 733 | static void pop_float_args(int nfloats) { 734 | SAVE; 735 | for (int i = nfloats - 1; i >= 0; i--) 736 | pop_xmm(i); 737 | } 738 | 739 | static void maybe_booleanize_retval(Ctype *ctype) { 740 | if (ctype->type == CTYPE_BOOL) { 741 | emit("movzx %%al, %%rax"); 742 | } 743 | } 744 | 745 | static void emit_func_call(Node *node) { 746 | SAVE; 747 | int opos = stackpos; 748 | bool isptr = (node->type == AST_FUNCPTR_CALL); 749 | Ctype *ftype = isptr ? node->fptr->ctype->ptr : node->ftype; 750 | 751 | List *ints = make_list(); 752 | List *floats = make_list(); 753 | List *rest = make_list(); 754 | classify_args(ints, floats, rest, node->args); 755 | save_arg_regs(list_len(ints), list_len(floats)); 756 | 757 | bool padding = stackpos % 16; 758 | if (padding) { 759 | emit("sub $8, %%rsp"); 760 | stackpos += 8; 761 | } 762 | 763 | emit_args(list_reverse(rest)); 764 | if (isptr) { 765 | emit_expr(node->fptr); 766 | push("rax"); 767 | } 768 | emit_args(ints); 769 | emit_args(floats); 770 | pop_float_args(list_len(floats)); 771 | pop_int_args(list_len(ints)); 772 | 773 | if (isptr) pop("r11"); 774 | if (ftype->hasva) 775 | emit("mov $%d, %%eax", list_len(floats)); 776 | 777 | if (isptr) 778 | emit("call *%%r11"); 779 | else 780 | emit("call %s", node->fname); 781 | maybe_booleanize_retval(node->ctype); 782 | if (list_len(rest) > 0) { 783 | emit("add $%d, %%rsp", list_len(rest) * 8); 784 | stackpos -= list_len(rest) * 8; 785 | } 786 | if (padding) { 787 | emit("add $8, %%rsp"); 788 | stackpos -= 8; 789 | } 790 | restore_arg_regs(list_len(ints), list_len(floats)); 791 | assert(opos == stackpos); 792 | } 793 | 794 | static void emit_decl(Node *node) { 795 | SAVE; 796 | if (!node->declinit) 797 | return; 798 | emit_zero_filler(node->declvar->loff, 799 | node->declvar->loff + node->declvar->ctype->size); 800 | emit_decl_init(node->declinit, node->declvar->loff); 801 | } 802 | 803 | static void emit_conv(Node *node) { 804 | SAVE; 805 | emit_expr(node->operand); 806 | emit_load_convert(node->ctype, node->operand->ctype); 807 | } 808 | 809 | static void emit_deref(Node *node) { 810 | SAVE; 811 | emit_expr(node->operand); 812 | emit_lload(node->operand->ctype->ptr, "rax", 0); 813 | emit_load_convert(node->ctype, node->operand->ctype->ptr); 814 | } 815 | 816 | static void emit_ternary(Node *node) { 817 | SAVE; 818 | emit_expr(node->cond); 819 | char *ne = make_label(); 820 | emit_je(ne); 821 | if (node->then) 822 | emit_expr(node->then); 823 | if (node->els) { 824 | char *end = make_label(); 825 | emit_jmp(end); 826 | emit_label(ne); 827 | emit_expr(node->els); 828 | emit_label(end); 829 | } else { 830 | emit_label(ne); 831 | } 832 | } 833 | 834 | #define SET_JUMP_LABELS(brk, cont) \ 835 | char *obreak = lbreak; \ 836 | char *ocontinue = lcontinue; \ 837 | lbreak = brk; \ 838 | lcontinue = cont 839 | #define RESTORE_JUMP_LABELS() \ 840 | lbreak = obreak; \ 841 | lcontinue = ocontinue 842 | 843 | static void emit_for(Node *node) { 844 | SAVE; 845 | if (node->forinit) 846 | emit_expr(node->forinit); 847 | char *begin = make_label(); 848 | char *step = make_label(); 849 | char *end = make_label(); 850 | SET_JUMP_LABELS(end, step); 851 | emit_label(begin); 852 | if (node->forcond) { 853 | emit_expr(node->forcond); 854 | emit_je(end); 855 | } 856 | if (node->forbody) 857 | emit_expr(node->forbody); 858 | emit_label(step); 859 | if (node->forstep) 860 | emit_expr(node->forstep); 861 | emit_jmp(begin); 862 | emit_label(end); 863 | RESTORE_JUMP_LABELS(); 864 | } 865 | 866 | static void emit_while(Node *node) { 867 | SAVE; 868 | char *begin = make_label(); 869 | char *end = make_label(); 870 | SET_JUMP_LABELS(end, begin); 871 | emit_label(begin); 872 | emit_expr(node->forcond); 873 | emit_je(end); 874 | if (node->forbody) 875 | emit_expr(node->forbody); 876 | emit_jmp(begin); 877 | emit_label(end); 878 | RESTORE_JUMP_LABELS(); 879 | } 880 | 881 | static void emit_do(Node *node) { 882 | SAVE; 883 | char *begin = make_label(); 884 | char *end = make_label(); 885 | SET_JUMP_LABELS(end, begin); 886 | emit_label(begin); 887 | if (node->forbody) 888 | emit_expr(node->forbody); 889 | emit_expr(node->forcond); 890 | emit_je(end); 891 | emit_jmp(begin); 892 | emit_label(end); 893 | RESTORE_JUMP_LABELS(); 894 | } 895 | 896 | #undef SET_JUMP_LABELS 897 | #undef RESTORE_JUMP_LABELS 898 | 899 | static void emit_switch(Node *node) { 900 | SAVE; 901 | char *oswitch = lswitch, *obreak = lbreak; 902 | emit_expr(node->switchexpr); 903 | lswitch = make_label(); 904 | lbreak = make_label(); 905 | emit_jmp(lswitch); 906 | if (node->switchbody) 907 | emit_expr(node->switchbody); 908 | emit_label(lswitch); 909 | emit_label(lbreak); 910 | lswitch = oswitch; 911 | lbreak = obreak; 912 | } 913 | 914 | static void emit_case(Node *node) { 915 | SAVE; 916 | if (!lswitch) 917 | error("stray case label"); 918 | char *skip = make_label(); 919 | emit_jmp(skip); 920 | emit_label(lswitch); 921 | lswitch = make_label(); 922 | emit("cmp $%d, %%eax", node->casebeg); 923 | if (node->casebeg == node->caseend) { 924 | emit("jne %s", lswitch); 925 | } else { 926 | emit("jl %s", lswitch); 927 | emit("cmp $%d, %%eax", node->caseend); 928 | emit("jg %s", lswitch); 929 | } 930 | emit_label(skip); 931 | } 932 | 933 | static void emit_default(Node *node) { 934 | SAVE; 935 | if (!lswitch) 936 | error("stray case label"); 937 | emit_label(lswitch); 938 | lswitch = make_label(); 939 | } 940 | 941 | static void emit_goto(Node *node) { 942 | SAVE; 943 | assert(node->newlabel); 944 | emit_jmp(node->newlabel); 945 | } 946 | 947 | static void emit_return(Node *node) { 948 | SAVE; 949 | if (node->retval) { 950 | emit_expr(node->retval); 951 | maybe_booleanize_retval(node->retval->ctype); 952 | } 953 | emit_ret(); 954 | } 955 | 956 | static void emit_break(Node *node) { 957 | SAVE; 958 | if (!lbreak) 959 | error("stray break statement"); 960 | emit_jmp(lbreak); 961 | } 962 | 963 | static void emit_continue(Node *node) { 964 | SAVE; 965 | if (!lcontinue) 966 | error("stray continue statement"); 967 | emit_jmp(lcontinue); 968 | } 969 | 970 | static void emit_compound_stmt(Node *node) { 971 | SAVE; 972 | for (Iter *i = list_iter(node->stmts); !iter_end(i);) 973 | emit_expr(iter_next(i)); 974 | } 975 | 976 | static void emit_va_start(Node *node) { 977 | SAVE; 978 | emit_expr(node->ap); 979 | push("rcx"); 980 | emit("movl $%d, (%%rax)", numgp * 8); 981 | emit("movl $%d, 4(%%rax)", 48 + numfp * 16); 982 | emit("lea %d(%%rbp), %%rcx", -REGAREA_SIZE); 983 | emit("mov %%rcx, 16(%%rax)"); 984 | pop("rcx"); 985 | } 986 | 987 | static void emit_va_arg(Node *node) { 988 | SAVE; 989 | emit_expr(node->ap); 990 | emit("nop"); 991 | push("rcx"); 992 | push("rbx"); 993 | emit("mov 16(%%rax), %%rcx"); 994 | if (is_flotype(node->ctype)) { 995 | emit("mov 4(%%rax), %%ebx"); 996 | emit("add %%rbx, %%rcx"); 997 | emit("add $16, %%ebx"); 998 | emit("mov %%ebx, 4(%%rax)"); 999 | emit("movsd (%%rcx), %%xmm0"); 1000 | if (node->ctype->type == CTYPE_FLOAT) 1001 | emit("cvtpd2ps %%xmm0, %%xmm0"); 1002 | } else { 1003 | emit("mov (%%rax), %%ebx"); 1004 | emit("add %%rbx, %%rcx"); 1005 | emit("add $8, %%ebx"); 1006 | emit("mov %%rbx, (%%rax)"); 1007 | emit("mov (%%rcx), %%rax"); 1008 | } 1009 | pop("rbx"); 1010 | pop("rcx"); 1011 | } 1012 | 1013 | static void emit_logand(Node *node) { 1014 | SAVE; 1015 | char *end = make_label(); 1016 | emit_expr(node->left); 1017 | emit("test %%rax, %%rax"); 1018 | emit("mov $0, %%rax"); 1019 | emit("je %s", end); 1020 | emit_expr(node->right); 1021 | emit("test %%rax, %%rax"); 1022 | emit("mov $0, %%rax"); 1023 | emit("je %s", end); 1024 | emit("mov $1, %%rax"); 1025 | emit_label(end); 1026 | } 1027 | 1028 | static void emit_logor(Node *node) { 1029 | SAVE; 1030 | char *end = make_label(); 1031 | emit_expr(node->left); 1032 | emit("test %%rax, %%rax"); 1033 | emit("mov $1, %%rax"); 1034 | emit("jne %s", end); 1035 | emit_expr(node->right); 1036 | emit("test %%rax, %%rax"); 1037 | emit("mov $1, %%rax"); 1038 | emit("jne %s", end); 1039 | emit("mov $0, %%rax"); 1040 | emit_label(end); 1041 | } 1042 | 1043 | static void emit_lognot(Node *node) { 1044 | SAVE; 1045 | emit_expr(node->operand); 1046 | emit("cmp $0, %%rax"); 1047 | emit("sete %%al"); 1048 | emit("movzb %%al, %%eax"); 1049 | } 1050 | 1051 | static void emit_bitand(Node *node) { 1052 | SAVE; 1053 | emit_expr(node->left); 1054 | push("rax"); 1055 | emit_expr(node->right); 1056 | pop("rcx"); 1057 | emit("and %%rcx, %%rax"); 1058 | } 1059 | 1060 | static void emit_bitor(Node *node) { 1061 | SAVE; 1062 | emit_expr(node->left); 1063 | push("rax"); 1064 | emit_expr(node->right); 1065 | pop("rcx"); 1066 | emit("or %%rcx, %%rax"); 1067 | } 1068 | 1069 | static void emit_bitnot(Node *node) { 1070 | SAVE; 1071 | emit_expr(node->left); 1072 | emit("not %%rax"); 1073 | } 1074 | 1075 | static void emit_cast(Node *node) { 1076 | SAVE; 1077 | emit_expr(node->operand); 1078 | emit_load_convert(node->ctype, node->operand->ctype); 1079 | return; 1080 | } 1081 | 1082 | static void emit_comma(Node *node) { 1083 | SAVE; 1084 | emit_expr(node->left); 1085 | emit_expr(node->right); 1086 | } 1087 | 1088 | static void emit_assign(Node *node) { 1089 | SAVE; 1090 | if (node->left->ctype->type == CTYPE_STRUCT && 1091 | node->left->ctype->size > 8) { 1092 | emit_copy_struct(node->left, node->right); 1093 | } else { 1094 | emit_expr(node->right); 1095 | emit_load_convert(node->ctype, node->right->ctype); 1096 | emit_store(node->left); 1097 | } 1098 | } 1099 | 1100 | static void emit_label_addr(Node *node) { 1101 | SAVE; 1102 | emit("mov $%s, %%rax", node->newlabel); 1103 | } 1104 | 1105 | static void emit_computed_goto(Node *node) { 1106 | SAVE; 1107 | emit_expr(node->operand); 1108 | emit("jmp *%%rax"); 1109 | } 1110 | 1111 | static void emit_expr(Node *node) { 1112 | SAVE; 1113 | switch (node->type) { 1114 | case AST_LITERAL: emit_literal(node); return; 1115 | case AST_STRING: emit_literal_string(node); return; 1116 | case AST_LVAR: emit_lvar(node); return; 1117 | case AST_GVAR: emit_gvar(node); return; 1118 | case AST_FUNCALL: 1119 | case AST_FUNCPTR_CALL: 1120 | emit_func_call(node); 1121 | return; 1122 | case AST_DECL: emit_decl(node); return; 1123 | case AST_CONV: emit_conv(node); return; 1124 | case AST_ADDR: emit_addr(node->operand); return; 1125 | case AST_DEREF: emit_deref(node); return; 1126 | case AST_IF: 1127 | case AST_TERNARY: 1128 | emit_ternary(node); 1129 | return; 1130 | case AST_FOR: emit_for(node); return; 1131 | case AST_WHILE: emit_while(node); return; 1132 | case AST_DO: emit_do(node); return; 1133 | case AST_SWITCH: emit_switch(node); return; 1134 | case AST_CASE: emit_case(node); return; 1135 | case AST_DEFAULT: emit_default(node); return; 1136 | case AST_GOTO: emit_goto(node); return; 1137 | case AST_LABEL: 1138 | if (node->newlabel) 1139 | emit_label(node->newlabel); 1140 | return; 1141 | case AST_RETURN: emit_return(node); return; 1142 | case AST_BREAK: emit_break(node); return; 1143 | case AST_CONTINUE: emit_continue(node); return; 1144 | case AST_COMPOUND_STMT: emit_compound_stmt(node); return; 1145 | case AST_STRUCT_REF: 1146 | emit_load_struct_ref(node->struc, node->ctype, 0); 1147 | return; 1148 | case AST_VA_START: emit_va_start(node); return; 1149 | case AST_VA_ARG: emit_va_arg(node); return; 1150 | case OP_UMINUS: emit_uminus(node); return; 1151 | case OP_PRE_INC: emit_pre_inc_dec(node, "add"); return; 1152 | case OP_PRE_DEC: emit_pre_inc_dec(node, "sub"); return; 1153 | case OP_POST_INC: emit_post_inc_dec(node, "add"); return; 1154 | case OP_POST_DEC: emit_post_inc_dec(node, "sub"); return; 1155 | case '!': emit_lognot(node); return; 1156 | case '&': emit_bitand(node); return; 1157 | case '|': emit_bitor(node); return; 1158 | case '~': emit_bitnot(node); return; 1159 | case OP_LOGAND: emit_logand(node); return; 1160 | case OP_LOGOR: emit_logor(node); return; 1161 | case OP_CAST: emit_cast(node); return; 1162 | case ',': emit_comma(node); return; 1163 | case '=': emit_assign(node); return; 1164 | case OP_LABEL_ADDR: emit_label_addr(node); return; 1165 | case AST_COMPUTED_GOTO: emit_computed_goto(node); return; 1166 | default: 1167 | emit_binop(node); 1168 | } 1169 | } 1170 | 1171 | static void emit_zero(int size) { 1172 | SAVE; 1173 | for (; size >= 8; size -= 8) emit(".quad 0"); 1174 | for (; size >= 4; size -= 4) emit(".long 0"); 1175 | for (; size > 0; size--) emit(".byte 0"); 1176 | } 1177 | 1178 | static void emit_padding(Node *node, int off) { 1179 | SAVE; 1180 | int diff = node->initoff - off; 1181 | assert(diff >= 0); 1182 | emit_zero(diff); 1183 | } 1184 | 1185 | static void emit_data_addr(Node *operand, int depth) { 1186 | switch (operand->type) { 1187 | case AST_LVAR: { 1188 | char *label = make_label(); 1189 | emit(".data %d", depth + 1); 1190 | emit_label(label); 1191 | emit_data_int(operand->lvarinit, operand->ctype->size, 0, depth + 1); 1192 | emit(".data %d", depth); 1193 | emit(".quad %s", label); 1194 | return; 1195 | } 1196 | case AST_GVAR: 1197 | emit(".quad %s", operand->varname); 1198 | return; 1199 | default: 1200 | error("internal error"); 1201 | } 1202 | } 1203 | 1204 | static void emit_data_charptr(char *s, int depth) { 1205 | char *label = make_label(); 1206 | emit(".data %d", depth + 1); 1207 | emit_label(label); 1208 | emit(".string \"%s\"", quote_cstring(s)); 1209 | emit(".data %d", depth); 1210 | emit(".quad %s", label); 1211 | } 1212 | 1213 | static void emit_data_primtype(Ctype *ctype, Node *val) { 1214 | switch (ctype->type) { 1215 | case CTYPE_FLOAT: { 1216 | float v = val->fval; 1217 | emit(".long %d", *(int *)&v); 1218 | break; 1219 | } 1220 | case CTYPE_DOUBLE: 1221 | emit(".quad %ld", *(long *)&val->fval); 1222 | break; 1223 | case CTYPE_BOOL: 1224 | emit(".byte %d", !!eval_intexpr(val)); 1225 | break; 1226 | case CTYPE_CHAR: 1227 | emit(".byte %d", eval_intexpr(val)); 1228 | break; 1229 | case CTYPE_SHORT: 1230 | emit(".short %d", eval_intexpr(val)); 1231 | break; 1232 | case CTYPE_INT: 1233 | emit(".long %d", eval_intexpr(val)); 1234 | break; 1235 | case CTYPE_LONG: 1236 | case CTYPE_LLONG: 1237 | case CTYPE_PTR: 1238 | if (val->type == AST_GVAR) 1239 | emit(".quad %s", val->varname); 1240 | else 1241 | emit(".quad %d", eval_intexpr(val)); 1242 | break; 1243 | default: 1244 | error("don't know how to handle\n <%s>\n <%s>", c2s(ctype), a2s(val)); 1245 | } 1246 | } 1247 | 1248 | static void emit_data_int(List *inits, int size, int off, int depth) { 1249 | SAVE; 1250 | Iter *iter = list_iter(inits); 1251 | while (!iter_end(iter) && 0 < size) { 1252 | Node *node = iter_next(iter); 1253 | Node *v = node->initval; 1254 | emit_padding(node, off); 1255 | if (node->totype->bitsize > 0) { 1256 | assert(node->totype->bitoff == 0); 1257 | long data = eval_intexpr(v); 1258 | Ctype *totype = node->totype; 1259 | while (!iter_end(iter)) { 1260 | node = iter_next(iter); 1261 | if (node->totype->bitsize <= 0) { 1262 | break; 1263 | } 1264 | v = node->initval; 1265 | totype = node->totype; 1266 | data |= ((((long)1 << totype->bitsize) - 1) & eval_intexpr(v)) << totype->bitoff; 1267 | } 1268 | emit_data_primtype(totype, &(Node){ AST_LITERAL, totype, .ival = data }); 1269 | off += totype->size; 1270 | size -= totype->size; 1271 | if (iter_end(iter)) 1272 | break; 1273 | } else { 1274 | off += node->totype->size; 1275 | size -= node->totype->size; 1276 | } 1277 | if (v->type == AST_ADDR) { 1278 | emit_data_addr(v->operand, depth); 1279 | continue; 1280 | } 1281 | if (v->type == AST_LVAR && v->lvarinit) { 1282 | emit_data_int(v->lvarinit, v->ctype->size, 0, depth); 1283 | continue; 1284 | } 1285 | bool is_char_ptr = (v->ctype->type == CTYPE_ARRAY && v->ctype->ptr->type == CTYPE_CHAR); 1286 | if (is_char_ptr) { 1287 | emit_data_charptr(v->sval, depth); 1288 | continue; 1289 | } 1290 | emit_data_primtype(node->totype, node->initval); 1291 | } 1292 | emit_zero(size); 1293 | } 1294 | 1295 | static void emit_data(Node *v, int off, int depth) { 1296 | SAVE; 1297 | emit(".data %d", depth); 1298 | if (!v->declvar->ctype->isstatic) 1299 | emit_noindent(".global %s", v->declvar->varname); 1300 | emit_noindent("%s:", v->declvar->varname); 1301 | emit_data_int(v->declinit, v->declvar->ctype->size, off, depth); 1302 | } 1303 | 1304 | static void emit_bss(Node *v) { 1305 | SAVE; 1306 | emit(".data"); 1307 | if (!v->declvar->ctype->isstatic) 1308 | emit(".global %s", v->declvar->varname); 1309 | emit(".lcomm %s, %d", v->declvar->varname, v->declvar->ctype->size); 1310 | } 1311 | 1312 | static void emit_global_var(Node *v) { 1313 | SAVE; 1314 | if (v->declinit) 1315 | emit_data(v, 0, 0); 1316 | else 1317 | emit_bss(v); 1318 | } 1319 | 1320 | static int align(int n, int m) { 1321 | int rem = n % m; 1322 | return (rem == 0) ? n : n - rem + m; 1323 | } 1324 | 1325 | static int emit_regsave_area(void) { 1326 | int pos = -REGAREA_SIZE; 1327 | emit("mov %%rdi, %d(%%rsp)", pos); 1328 | emit("mov %%rsi, %d(%%rsp)", (pos += 8)); 1329 | emit("mov %%rdx, %d(%%rsp)", (pos += 8)); 1330 | emit("mov %%rcx, %d(%%rsp)", (pos += 8)); 1331 | emit("mov %%r8, %d(%%rsp)", (pos += 8)); 1332 | emit("mov %%r9, %d(%%rsp)", pos + 8); 1333 | char *end = make_label(); 1334 | for (int i = 0; i < 16; i++) { 1335 | emit("test %%al, %%al"); 1336 | emit("jz %s", end); 1337 | emit("movsd %%xmm%d, %d(%%rsp)", i, (pos += 16)); 1338 | emit("sub $1, %%al"); 1339 | } 1340 | emit_label(end); 1341 | emit("sub $%d, %%rsp", REGAREA_SIZE); 1342 | return REGAREA_SIZE; 1343 | } 1344 | 1345 | static void push_func_params(List *params, int off) { 1346 | int ireg = 0; 1347 | int xreg = 0; 1348 | int arg = 2; 1349 | for (Iter *i = list_iter(params); !iter_end(i);) { 1350 | Node *v = iter_next(i); 1351 | if (is_flotype(v->ctype)) { 1352 | if (xreg >= 8) { 1353 | emit("mov %d(%%rbp), %%rax", arg++ * 8); 1354 | push("rax"); 1355 | } else { 1356 | push_xmm(xreg++); 1357 | } 1358 | } else { 1359 | if (ireg >= 6) { 1360 | if (v->ctype->type == CTYPE_BOOL) { 1361 | emit("mov %d(%%rbp), %%al", arg++ * 8); 1362 | emit("movzb %%al, %%eax"); 1363 | } else { 1364 | emit("mov %d(%%rbp), %%rax", arg++ * 8); 1365 | } 1366 | push("rax"); 1367 | } else { 1368 | if (v->ctype->type == CTYPE_BOOL) 1369 | emit("movzb %%%s, %%%s", SREGS[ireg], MREGS[ireg]); 1370 | push(REGS[ireg++]); 1371 | } 1372 | } 1373 | off -= 8; 1374 | v->loff = off; 1375 | } 1376 | } 1377 | 1378 | static void emit_func_prologue(Node *func) { 1379 | SAVE; 1380 | emit(".text"); 1381 | if (!func->ctype->isstatic) 1382 | emit_noindent(".global %s", func->fname); 1383 | emit_noindent("%s:", func->fname); 1384 | emit("nop"); 1385 | push("rbp"); 1386 | emit("mov %%rsp, %%rbp"); 1387 | int off = 0; 1388 | if (func->ctype->hasva) { 1389 | set_reg_nums(func->params); 1390 | off -= emit_regsave_area(); 1391 | } 1392 | push_func_params(func->params, off); 1393 | off -= list_len(func->params) * 8; 1394 | 1395 | int localarea = 0; 1396 | for (Iter *i = list_iter(func->localvars); !iter_end(i);) { 1397 | Node *v = iter_next(i); 1398 | int size = align(v->ctype->size, 8); 1399 | assert(size % 8 == 0); 1400 | off -= size; 1401 | v->loff = off; 1402 | localarea += size; 1403 | } 1404 | if (localarea) { 1405 | emit("sub $%d, %%rsp", localarea); 1406 | stackpos += localarea; 1407 | } 1408 | } 1409 | 1410 | void emit_toplevel(Node *v) { 1411 | stackpos = 8; 1412 | if (v->type == AST_FUNC) { 1413 | emit_func_prologue(v); 1414 | emit_expr(v->body); 1415 | emit_ret(); 1416 | } else if (v->type == AST_DECL) { 1417 | emit_global_var(v); 1418 | } else { 1419 | error("internal error"); 1420 | } 1421 | } 1422 | --------------------------------------------------------------------------------