├── LICENSE
├── Makefile
├── README.md
├── codegen.c
├── examples
├── asm
├── helloworld
├── print
├── test
└── x11window
├── hashmap.c
├── include
├── float.h
├── stdalign.h
├── stdarg.h
├── stdatomic.h
├── stdbool.h
├── stddef.h
└── stdnoreturn.h
├── main.c
├── parse.c
├── preprocess.c
├── strings.c
├── test
├── alignof.c
├── alloca.c
├── arith.c
├── asm.c
├── atomic.c
├── attribute.c
├── bitfield.c
├── builtin.c
├── cast.c
├── common
├── commonsym.c
├── compat.c
├── complit.c
├── const.c
├── constexpr.c
├── control.c
├── decl.c
├── driver.sh
├── enum.c
├── extern.c
├── float.c
├── function.c
├── generic.c
├── include1.h
├── include2.h
├── include3.h
├── include4.h
├── initializer.c
├── line.c
├── literal.c
├── macro.c
├── offsetof.c
├── pointer.c
├── pragma-once.c
├── sizeof.c
├── stdhdr.c
├── string.c
├── struct.c
├── test.h
├── thirdparty
│ ├── common
│ ├── cpython.sh
│ ├── git.sh
│ ├── libpng.sh
│ ├── sqlite.sh
│ └── tinycc.sh
├── tls.c
├── typedef.c
├── typeof.c
├── unicode.c
├── union.c
├── usualconv.c
├── varargs.c
├── variable.c
└── vla.c
├── tokenize.c
├── type.c
├── unicode.c
├── xc
└── xcc.h
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 x3ric
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | CFLAGS=-std=c11 -g -lcurl -fno-common -Wall -Wno-switch
2 | SRCS=$(wildcard *.c)
3 | OBJS=$(SRCS:.c=.o)
4 |
5 | TEST_SRCS=$(wildcard test/*.c)
6 | TESTS=$(TEST_SRCS:.c=.exe)
7 |
8 | PREFIX=/usr/local
9 | BINDIR=$(PREFIX)/bin/xcc
10 |
11 | # Stage 1
12 | xcc: $(OBJS)
13 | $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
14 |
15 | $(OBJS): xcc.h
16 |
17 | test/%.exe: xcc test/%.c
18 | ./xcc -Iinclude -Itest -c -o test/$*.o test/$*.c
19 | $(CC) -pthread -o $@ test/$*.o -xc test/common
20 |
21 | test: $(TESTS)
22 | for i in $^; do echo $$i; ./$$i || exit 1; echo; done
23 | test/driver.sh ./xcc
24 |
25 | test-all: test test-stage2
26 |
27 | # Stage 2
28 | stage2/xcc: $(OBJS:%=stage2/%)
29 | $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
30 |
31 | stage2/%.o: xcc %.c
32 | mkdir -p stage2/test
33 | ./xcc -c -o $(@D)/$*.o $*.c
34 |
35 | stage2/test/%.exe: stage2/xcc test/%.c
36 | mkdir -p stage2/test
37 | ./stage2/xcc -Iinclude -Itest -c -o stage2/test/$*.o test/$*.c
38 | $(CC) -pthread -o $@ stage2/test/$*.o -xc test/common
39 |
40 | test-stage2: $(TESTS:test/%=stage2/test/%)
41 | for i in $^; do echo $$i; ./$$i || exit 1; echo; done
42 | test/driver.sh ./stage2/xcc
43 |
44 | # Stage 3
45 | .SILENT: uninstall
46 | install: xcc
47 | @if [ ! -d $(BINDIR) ]; then \
48 | install -d $(BINDIR); \
49 | fi
50 | install -m 755 xcc $(BINDIR)
51 | sudo install -m 755 ./xc /usr/local/bin/
52 | @if [ ! -d /usr/local/include/xcc/ ]; then \
53 | sudo mkdir -p /usr/local/include/xcc/; \
54 | fi
55 | @sudo chmod +x xc
56 | @sudo cp -r ./include/* /usr/local/include/xcc/
57 | @rm -rf xcc tmp* $(TESTS) test/*.s test/*.exe stage2
58 | @find * -type f '(' -name '*~' -o -name '*.o' ')' -exec rm {} ';'
59 |
60 | .SILENT: clean
61 | uninstall:
62 | sudo rm -rf /usr/local/bin/xcc /usr/local/bin/xc /usr/local/include/xcc/
63 |
64 | clean:
65 | rm -rf xcc tmp* $(TESTS) test/*.s test/*.exe stage2
66 | find * -type f '(' -name '*~' -o -name '*.o' ')' -exec rm {} ';'
67 |
68 | .PHONY: test clean test-stage2 install uninstall
69 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ### XCC
4 |
5 | XCC is a simple C compiler based on chibic, featuring support for including files from websites using curl and shebang support.
6 |
7 | #### Installation
8 |
9 |
10 | git clone https://github.com/x3ric/xcc
11 | cd xcc
12 | sudo make install
13 |
14 |
15 | #### Usage
16 |
17 |
18 | xc
19 |
20 |
21 | #### Features
22 |
23 | **Chibic Based:** Built upon chibic, XCC supports most C11 features.
24 |
25 | **GCC Backend:** Runs gcc with Web Includes & Shebang Support through XCC.
26 |
27 | **Web Includes:** Includes files from websites using curl.
28 |
29 | **Shebang Support:** Supports executing C scripts directly.
30 |
31 | #### For examples:
32 |
33 |
34 | ./examples/x11window
35 |
36 |
37 | #### Internals
38 |
39 | XCC comprises the following stages:
40 |
41 | **Tokenize:** Breaks input into tokens.
42 |
43 | **Preprocess:** Expands macros and interprets preprocessor directives.
44 |
45 | **Parse:** Constructs abstract syntax trees and assigns types.
46 |
47 | **Codegen:** Generates assembly text for AST nodes.
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/examples/asm:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/xc -run
2 | #include
3 | #include
4 | #include
5 |
6 | void assert(int expected, int actual, char *code) {
7 | if (expected == actual) {
8 | printf("%s => %d\n", code, actual);
9 | } else {
10 | printf("%s => %d expected but got %d\n", code, expected, actual);
11 | exit(1);
12 | }
13 | }
14 | #define ASSERT(x, y) assert(x, y, #y)
15 |
16 | char *asm_fn1(void) {
17 | asm("mov $50, %rax\n\t"
18 | "mov %rbp, %rsp\n\t"
19 | "pop %rbp\n\t"
20 | "ret");
21 | }
22 |
23 | char *asm_fn2(void) {
24 | asm inline volatile("mov $55, %rax\n\t"
25 | "mov %rbp, %rsp\n\t"
26 | "pop %rbp\n\t"
27 | "ret");
28 | }
29 |
30 | int main() {
31 | ASSERT(50, asm_fn1());
32 | ASSERT(55, asm_fn2());
33 |
34 | printf("OK\n");
35 | return 0;
36 | }
37 |
--------------------------------------------------------------------------------
/examples/helloworld:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/xc -run
2 | #include
3 |
4 | int main() {
5 | printf("Hello World!\n");
6 | return 0;
7 | }
8 |
--------------------------------------------------------------------------------
/examples/print:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/xc -run
2 | #include
3 |
4 | int main(int argc, char **argv) {
5 | printf("%s %s\n",argv[1],argv[2]);
6 | return 0;
7 | }
8 |
--------------------------------------------------------------------------------
/examples/test:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/xc -run -O1
2 | #include
3 | #include
4 | #include
5 |
6 | #define ARRAY_SIZE 10000
7 | #define NUM_ITERATIONS 10000
8 |
9 | // Function to calculate the sum of an array
10 | int sum_array(int arr[], int size) {
11 | int sum = 0;
12 | for (int i = 0; i < size; i++) {
13 | sum += arr[i];
14 | }
15 | return sum;
16 | }
17 |
18 | // Function to calculate the product of an array
19 | long long product_array(int arr[], int size) {
20 | long long product = 1;
21 | for (int i = 0; i < size; i++) {
22 | product *= arr[i];
23 | }
24 | return product;
25 | }
26 |
27 | int main() {
28 | int array[ARRAY_SIZE];
29 | srand(time(NULL));
30 |
31 | // Fill array with random numbers
32 | for (int i = 0; i < ARRAY_SIZE; i++) {
33 | array[i] = rand() % 100;
34 | }
35 |
36 | clock_t start_time, end_time;
37 | double total_time;
38 |
39 | // Benchmarking sum_array function
40 | start_time = clock();
41 | for (int i = 0; i < NUM_ITERATIONS; i++) {
42 | int result = sum_array(array, ARRAY_SIZE);
43 | }
44 | end_time = clock();
45 | total_time = (double)(end_time - start_time) / CLOCKS_PER_SEC;
46 | printf("Time taken for sum_array function: %.6f seconds\n", total_time);
47 |
48 | // Benchmarking product_array function
49 | start_time = clock();
50 | for (int i = 0; i < NUM_ITERATIONS; i++) {
51 | long long result = product_array(array, ARRAY_SIZE);
52 | }
53 | end_time = clock();
54 | total_time = (double)(end_time - start_time) / CLOCKS_PER_SEC;
55 | printf("Time taken for product_array function: %.6f seconds\n", total_time);
56 |
57 | return 0;
58 | }
59 |
--------------------------------------------------------------------------------
/examples/x11window:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/xc -run -lX11
2 | #include
3 | #include
4 | #include
5 |
6 | int main() {
7 | Display *display;
8 | Window window;
9 | XEvent event;
10 | display = XOpenDisplay(NULL);
11 | if (display == NULL) {
12 | fprintf(stderr, "Cannot open display\n");
13 | exit(1);
14 | }
15 | int screen = DefaultScreen(display);
16 | window = XCreateSimpleWindow(display, RootWindow(display, screen), 100, 100, 500, 500, 1,BlackPixel(display, screen), WhitePixel(display, screen));
17 | XSelectInput(display, window, ExposureMask | KeyPressMask);
18 | XMapWindow(display, window);
19 | while (1) {
20 | XNextEvent(display, &event);
21 | if (event.type == Expose) {
22 | printf("Window exposed\n");
23 | }
24 | if (event.type == KeyPress) {
25 | break;
26 | }
27 | }
28 | XDestroyWindow(display, window);
29 | XCloseDisplay(display);
30 | return 0;
31 | }
32 |
--------------------------------------------------------------------------------
/hashmap.c:
--------------------------------------------------------------------------------
1 | // This is an implementation of the open-addressing hash table.
2 |
3 | #include "xcc.h"
4 |
5 | // Initial hash bucket size
6 | #define INIT_SIZE 16
7 |
8 | // Rehash if the usage exceeds 70%.
9 | #define HIGH_WATERMARK 70
10 |
11 | // We'll keep the usage below 50% after rehashing.
12 | #define LOW_WATERMARK 50
13 |
14 | // Represents a deleted hash entry
15 | #define TOMBSTONE ((void *)-1)
16 |
17 | static uint64_t fnv_hash(char *s, int len) {
18 | uint64_t hash = 0xcbf29ce484222325;
19 | for (int i = 0; i < len; i++) {
20 | hash *= 0x100000001b3;
21 | hash ^= (unsigned char)s[i];
22 | }
23 | return hash;
24 | }
25 |
26 | // Make room for new entires in a given hashmap by removing
27 | // tombstones and possibly extending the bucket size.
28 | static void rehash(HashMap *map) {
29 | // Compute the size of the new hashmap.
30 | int nkeys = 0;
31 | for (int i = 0; i < map->capacity; i++)
32 | if (map->buckets[i].key && map->buckets[i].key != TOMBSTONE)
33 | nkeys++;
34 |
35 | int cap = map->capacity;
36 | while ((nkeys * 100) / cap >= LOW_WATERMARK)
37 | cap = cap * 2;
38 | assert(cap > 0);
39 |
40 | // Create a new hashmap and copy all key-values.
41 | HashMap map2 = {};
42 | map2.buckets = calloc(cap, sizeof(HashEntry));
43 | map2.capacity = cap;
44 |
45 | for (int i = 0; i < map->capacity; i++) {
46 | HashEntry *ent = &map->buckets[i];
47 | if (ent->key && ent->key != TOMBSTONE)
48 | hashmap_put2(&map2, ent->key, ent->keylen, ent->val);
49 | }
50 |
51 | assert(map2.used == nkeys);
52 | *map = map2;
53 | }
54 |
55 | static bool match(HashEntry *ent, char *key, int keylen) {
56 | return ent->key && ent->key != TOMBSTONE &&
57 | ent->keylen == keylen && memcmp(ent->key, key, keylen) == 0;
58 | }
59 |
60 | static HashEntry *get_entry(HashMap *map, char *key, int keylen) {
61 | if (!map->buckets)
62 | return NULL;
63 |
64 | uint64_t hash = fnv_hash(key, keylen);
65 |
66 | for (int i = 0; i < map->capacity; i++) {
67 | HashEntry *ent = &map->buckets[(hash + i) % map->capacity];
68 | if (match(ent, key, keylen))
69 | return ent;
70 | if (ent->key == NULL)
71 | return NULL;
72 | }
73 | unreachable();
74 | }
75 |
76 | static HashEntry *get_or_insert_entry(HashMap *map, char *key, int keylen) {
77 | if (!map->buckets) {
78 | map->buckets = calloc(INIT_SIZE, sizeof(HashEntry));
79 | map->capacity = INIT_SIZE;
80 | } else if ((map->used * 100) / map->capacity >= HIGH_WATERMARK) {
81 | rehash(map);
82 | }
83 |
84 | uint64_t hash = fnv_hash(key, keylen);
85 |
86 | for (int i = 0; i < map->capacity; i++) {
87 | HashEntry *ent = &map->buckets[(hash + i) % map->capacity];
88 |
89 | if (match(ent, key, keylen))
90 | return ent;
91 |
92 | if (ent->key == TOMBSTONE) {
93 | ent->key = key;
94 | ent->keylen = keylen;
95 | return ent;
96 | }
97 |
98 | if (ent->key == NULL) {
99 | ent->key = key;
100 | ent->keylen = keylen;
101 | map->used++;
102 | return ent;
103 | }
104 | }
105 | unreachable();
106 | }
107 |
108 | void *hashmap_get(HashMap *map, char *key) {
109 | return hashmap_get2(map, key, strlen(key));
110 | }
111 |
112 | void *hashmap_get2(HashMap *map, char *key, int keylen) {
113 | HashEntry *ent = get_entry(map, key, keylen);
114 | return ent ? ent->val : NULL;
115 | }
116 |
117 | void hashmap_put(HashMap *map, char *key, void *val) {
118 | hashmap_put2(map, key, strlen(key), val);
119 | }
120 |
121 | void hashmap_put2(HashMap *map, char *key, int keylen, void *val) {
122 | HashEntry *ent = get_or_insert_entry(map, key, keylen);
123 | ent->val = val;
124 | }
125 |
126 | void hashmap_delete(HashMap *map, char *key) {
127 | hashmap_delete2(map, key, strlen(key));
128 | }
129 |
130 | void hashmap_delete2(HashMap *map, char *key, int keylen) {
131 | HashEntry *ent = get_entry(map, key, keylen);
132 | if (ent)
133 | ent->key = TOMBSTONE;
134 | }
135 |
136 | void hashmap_test(void) {
137 | HashMap *map = calloc(1, sizeof(HashMap));
138 |
139 | for (int i = 0; i < 5000; i++)
140 | hashmap_put(map, format("key %d", i), (void *)(size_t)i);
141 | for (int i = 1000; i < 2000; i++)
142 | hashmap_delete(map, format("key %d", i));
143 | for (int i = 1500; i < 1600; i++)
144 | hashmap_put(map, format("key %d", i), (void *)(size_t)i);
145 | for (int i = 6000; i < 7000; i++)
146 | hashmap_put(map, format("key %d", i), (void *)(size_t)i);
147 |
148 | for (int i = 0; i < 1000; i++)
149 | assert((size_t)hashmap_get(map, format("key %d", i)) == i);
150 | for (int i = 1000; i < 1500; i++)
151 | assert(hashmap_get(map, "no such key") == NULL);
152 | for (int i = 1500; i < 1600; i++)
153 | assert((size_t)hashmap_get(map, format("key %d", i)) == i);
154 | for (int i = 1600; i < 2000; i++)
155 | assert(hashmap_get(map, "no such key") == NULL);
156 | for (int i = 2000; i < 5000; i++)
157 | assert((size_t)hashmap_get(map, format("key %d", i)) == i);
158 | for (int i = 5000; i < 6000; i++)
159 | assert(hashmap_get(map, "no such key") == NULL);
160 | for (int i = 6000; i < 7000; i++)
161 | hashmap_put(map, format("key %d", i), (void *)(size_t)i);
162 |
163 | assert(hashmap_get(map, "no such key") == NULL);
164 | printf("OK\n");
165 | }
166 |
--------------------------------------------------------------------------------
/include/float.h:
--------------------------------------------------------------------------------
1 | #ifndef __STDFLOAT_H
2 | #define __STDFLOAT_H
3 |
4 | #define DECIMAL_DIG 21
5 | #define FLT_EVAL_METHOD 0 // C11 5.2.4.2.2p9
6 | #define FLT_RADIX 2
7 | #define FLT_ROUNDS 1 // C11 5.2.4.2.2p8: to nearest
8 |
9 | #define FLT_DIG 6
10 | #define FLT_EPSILON 0x1p-23
11 | #define FLT_MANT_DIG 24
12 | #define FLT_MAX 0x1.fffffep+127
13 | #define FLT_MAX_10_EXP 38
14 | #define FLT_MAX_EXP 128
15 | #define FLT_MIN 0x1p-126
16 | #define FLT_MIN_10_EXP -37
17 | #define FLT_MIN_EXP -125
18 | #define FLT_TRUE_MIN 0x1p-149
19 |
20 | #define DBL_DIG 15
21 | #define DBL_EPSILON 0x1p-52
22 | #define DBL_MANT_DIG 53
23 | #define DBL_MAX 0x1.fffffffffffffp+1023
24 | #define DBL_MAX_10_EXP 308
25 | #define DBL_MAX_EXP 1024
26 | #define DBL_MIN 0x1p-1022
27 | #define DBL_MIN_10_EXP -307
28 | #define DBL_MIN_EXP -1021
29 | #define DBL_TRUE_MIN 0x0.0000000000001p-1022
30 |
31 | #define LDBL_DIG 15
32 | #define LDBL_EPSILON 0x1p-52
33 | #define LDBL_MANT_DIG 53
34 | #define LDBL_MAX 0x1.fffffffffffffp+1023
35 | #define LDBL_MAX_10_EXP 308
36 | #define LDBL_MAX_EXP 1024
37 | #define LDBL_MIN 0x1p-1022
38 | #define LDBL_MIN_10_EXP -307
39 | #define LDBL_MIN_EXP -1021
40 | #define LDBL_TRUE_MIN 0x0.0000000000001p-1022
41 |
42 | #endif
43 |
--------------------------------------------------------------------------------
/include/stdalign.h:
--------------------------------------------------------------------------------
1 | #ifndef __STDALIGN_H
2 | #define __STDALIGN_H
3 |
4 | #define alignas _Alignas
5 | #define alignof _Alignof
6 | #define __alignas_is_defined 1
7 | #define __alignof_is_defined 1
8 |
9 | #endif
10 |
--------------------------------------------------------------------------------
/include/stdarg.h:
--------------------------------------------------------------------------------
1 | #ifndef __STDARG_H
2 | #define __STDARG_H
3 |
4 | typedef struct {
5 | unsigned int gp_offset;
6 | unsigned int fp_offset;
7 | void *overflow_arg_area;
8 | void *reg_save_area;
9 | } __va_elem;
10 |
11 | typedef __va_elem va_list[1];
12 |
13 | #define va_start(ap, last) \
14 | do { *(ap) = *(__va_elem *)__va_area__; } while (0)
15 |
16 | #define va_end(ap)
17 |
18 | static void *__va_arg_mem(__va_elem *ap, int sz, int align) {
19 | void *p = ap->overflow_arg_area;
20 | if (align > 8)
21 | p = (p + 15) / 16 * 16;
22 | ap->overflow_arg_area = ((unsigned long)p + sz + 7) / 8 * 8;
23 | return p;
24 | }
25 |
26 | static void *__va_arg_gp(__va_elem *ap, int sz, int align) {
27 | if (ap->gp_offset >= 48)
28 | return __va_arg_mem(ap, sz, align);
29 |
30 | void *r = ap->reg_save_area + ap->gp_offset;
31 | ap->gp_offset += 8;
32 | return r;
33 | }
34 |
35 | static void *__va_arg_fp(__va_elem *ap, int sz, int align) {
36 | if (ap->fp_offset >= 112)
37 | return __va_arg_mem(ap, sz, align);
38 |
39 | void *r = ap->reg_save_area + ap->fp_offset;
40 | ap->fp_offset += 8;
41 | return r;
42 | }
43 |
44 | #define va_arg(ap, ty) \
45 | ({ \
46 | int klass = __builtin_reg_class(ty); \
47 | *(ty *)(klass == 0 ? __va_arg_gp(ap, sizeof(ty), _Alignof(ty)) : \
48 | klass == 1 ? __va_arg_fp(ap, sizeof(ty), _Alignof(ty)) : \
49 | __va_arg_mem(ap, sizeof(ty), _Alignof(ty))); \
50 | })
51 |
52 | #define va_copy(dest, src) ((dest)[0] = (src)[0])
53 |
54 | #define __GNUC_VA_LIST 1
55 | typedef va_list __gnuc_va_list;
56 |
57 | #endif
58 |
--------------------------------------------------------------------------------
/include/stdatomic.h:
--------------------------------------------------------------------------------
1 | #ifndef __STDATOMIC_H
2 | #define __STDATOMIC_H
3 |
4 | #define ATOMIC_BOOL_LOCK_FREE 1
5 | #define ATOMIC_CHAR_LOCK_FREE 1
6 | #define ATOMIC_CHAR16_T_LOCK_FREE 1
7 | #define ATOMIC_CHAR32_T_LOCK_FREE 1
8 | #define ATOMIC_WCHAR_T_LOCK_FREE 1
9 | #define ATOMIC_SHORT_LOCK_FREE 1
10 | #define ATOMIC_INT_LOCK_FREE 1
11 | #define ATOMIC_LONG_LOCK_FREE 1
12 | #define ATOMIC_LLONG_LOCK_FREE 1
13 | #define ATOMIC_POINTER_LOCK_FREE 1
14 |
15 | typedef enum {
16 | memory_order_relaxed,
17 | memory_order_consume,
18 | memory_order_acquire,
19 | memory_order_release,
20 | memory_order_acq_rel,
21 | memory_order_seq_cst,
22 | } memory_order;
23 |
24 | #define ATOMIC_FLAG_INIT(x) (x)
25 | #define atomic_init(addr, val) (*(addr) = (val))
26 | #define kill_dependency(x) (x)
27 | #define atomic_thread_fence(order)
28 | #define atomic_signal_fence(order)
29 | #define atomic_is_lock_free(x) 1
30 |
31 | #define atomic_load(addr) (*(addr))
32 | #define atomic_store(addr, val) (*(addr) = (val))
33 |
34 | #define atomic_load_explicit(addr, order) (*(addr))
35 | #define atomic_store_explicit(addr, val, order) (*(addr) = (val))
36 |
37 | #define atomic_fetch_add(obj, val) (*(obj) += (val))
38 | #define atomic_fetch_sub(obj, val) (*(obj) -= (val))
39 | #define atomic_fetch_or(obj, val) (*(obj) |= (val))
40 | #define atomic_fetch_xor(obj, val) (*(obj) ^= (val))
41 | #define atomic_fetch_and(obj, val) (*(obj) &= (val))
42 |
43 | #define atomic_fetch_add_explicit(obj, val, order) (*(obj) += (val))
44 | #define atomic_fetch_sub_explicit(obj, val, order) (*(obj) -= (val))
45 | #define atomic_fetch_or_explicit(obj, val, order) (*(obj) |= (val))
46 | #define atomic_fetch_xor_explicit(obj, val, order) (*(obj) ^= (val))
47 | #define atomic_fetch_and_explicit(obj, val, order) (*(obj) &= (val))
48 |
49 | #define atomic_compare_exchange_weak(p, old, new) \
50 | __builtin_compare_and_swap((p), (old), (new))
51 |
52 | #define atomic_compare_exchange_strong(p, old, new) \
53 | __builtin_compare_and_swap((p), (old), (new))
54 |
55 | #define atomic_exchange(obj, val) __builtin_atomic_exchange((obj), (val))
56 | #define atomic_exchange_explicit(obj, val, order) __builtin_atomic_exchange((obj), (val))
57 |
58 | #define atomic_flag_test_and_set(obj) atomic_exchange((obj), 1)
59 | #define atomic_flag_test_and_set_explicit(obj, order) atomic_exchange((obj), 1)
60 | #define atomic_flag_clear(obj) (*(obj) = 0)
61 | #define atomic_flag_clear_explicit(obj, order) (*(obj) = 0)
62 |
63 | typedef _Atomic _Bool atomic_flag;
64 | typedef _Atomic _Bool atomic_bool;
65 | typedef _Atomic char atomic_char;
66 | typedef _Atomic signed char atomic_schar;
67 | typedef _Atomic unsigned char atomic_uchar;
68 | typedef _Atomic short atomic_short;
69 | typedef _Atomic unsigned short atomic_ushort;
70 | typedef _Atomic int atomic_int;
71 | typedef _Atomic unsigned int atomic_uint;
72 | typedef _Atomic long atomic_long;
73 | typedef _Atomic unsigned long atomic_ulong;
74 | typedef _Atomic long long atomic_llong;
75 | typedef _Atomic unsigned long long atomic_ullong;
76 | typedef _Atomic unsigned short atomic_char16_t;
77 | typedef _Atomic unsigned atomic_char32_t;
78 | typedef _Atomic unsigned atomic_wchar_t;
79 | typedef _Atomic signed char atomic_int_least8_t;
80 | typedef _Atomic unsigned char atomic_uint_least8_t;
81 | typedef _Atomic short atomic_int_least16_t;
82 | typedef _Atomic unsigned short atomic_uint_least16_t;
83 | typedef _Atomic int atomic_int_least32_t;
84 | typedef _Atomic unsigned int atomic_uint_least32_t;
85 | typedef _Atomic long atomic_int_least64_t;
86 | typedef _Atomic unsigned long atomic_uint_least64_t;
87 | typedef _Atomic signed char atomic_int_fast8_t;
88 | typedef _Atomic unsigned char atomic_uint_fast8_t;
89 | typedef _Atomic short atomic_int_fast16_t;
90 | typedef _Atomic unsigned short atomic_uint_fast16_t;
91 | typedef _Atomic int atomic_int_fast32_t;
92 | typedef _Atomic unsigned int atomic_uint_fast32_t;
93 | typedef _Atomic long atomic_int_fast64_t;
94 | typedef _Atomic unsigned long atomic_uint_fast64_t;
95 | typedef _Atomic long atomic_intptr_t;
96 | typedef _Atomic unsigned long atomic_uintptr_t;
97 | typedef _Atomic unsigned long atomic_size_t;
98 | typedef _Atomic long atomic_ptrdiff_t;
99 | typedef _Atomic long atomic_intmax_t;
100 | typedef _Atomic unsigned long atomic_uintmax_t;
101 |
102 | #endif
103 |
--------------------------------------------------------------------------------
/include/stdbool.h:
--------------------------------------------------------------------------------
1 | #ifndef __STDBOOL_H
2 | #define __STDBOOL_H
3 |
4 | #define bool _Bool
5 | #define true 1
6 | #define false 0
7 | #define __bool_true_false_are_defined 1
8 |
9 | #endif
10 |
--------------------------------------------------------------------------------
/include/stddef.h:
--------------------------------------------------------------------------------
1 | #ifndef __STDDEF_H
2 | #define __STDDEF_H
3 |
4 | #define NULL ((void *)0)
5 |
6 | typedef unsigned long size_t;
7 | typedef long ptrdiff_t;
8 | typedef unsigned int wchar_t;
9 | typedef long max_align_t;
10 |
11 | #define offsetof(type, member) ((size_t)&(((type *)0)->member))
12 |
13 | #endif
14 |
--------------------------------------------------------------------------------
/include/stdnoreturn.h:
--------------------------------------------------------------------------------
1 | #ifndef __STDNORETURN_H
2 | #define __STDNORETURN_H
3 |
4 | #define noreturn _Noreturn
5 |
6 | #endif
7 |
--------------------------------------------------------------------------------
/main.c:
--------------------------------------------------------------------------------
1 | #include "xcc.h"
2 | #include
3 | #include
4 |
5 | typedef enum {
6 | FILE_NONE, FILE_C, FILE_ASM, FILE_OBJ, FILE_AR, FILE_DSO,
7 | } FileType;
8 |
9 | StringArray include_paths;
10 | int opt_O = 0;
11 | bool opt_fcommon = true;
12 | bool opt_run;
13 | bool opt_asm;
14 | bool opt_fpic;
15 |
16 | char *opt_gccarg;
17 | char *opt_rarg;
18 |
19 | static FileType opt_x;
20 | static StringArray opt_include;
21 | static bool opt_E;
22 | static bool opt_M;
23 | static bool opt_MD;
24 | static bool opt_MMD;
25 | static bool opt_MP;
26 | static bool opt_S;
27 | static bool opt_c;
28 | static bool opt_cc1;
29 | static bool opt_gcc;
30 | static bool opt_hash_hash_hash;
31 | static bool opt_static;
32 | static bool opt_shared;
33 | static char *opt_MF;
34 | static char *opt_MT;
35 | static char *opt_o;
36 |
37 | static StringArray ld_extra_args;
38 | static StringArray std_include_paths;
39 |
40 | char *base_file;
41 | static char *output_file;
42 |
43 | static StringArray input_paths;
44 | static StringArray tmpfiles;
45 |
46 | static void usage(int status) {
47 | fprintf(stderr, "XCompiler %s - a simple C compiler\n", VERSION);
48 | fprintf(stderr, "Usage: xc [options] file...\n");
49 | fprintf(stderr, "Options:\n");
50 | fprintf(stderr, "Compilation Options:\n");
51 | fprintf(stderr, " %-20s %s\n", "-o ", "Output to specified file");
52 | fprintf(stderr, " %-20s %s\n", "-S", "Compile only; do not assemble or link");
53 | fprintf(stderr, " %-20s %s\n", "-c", "Compile source files without linking");
54 | fprintf(stderr, " %-20s %s\n", "-E", "Preprocess only; do not compile, assemble, or link");
55 | fprintf(stderr, " %-20s %s\n", "-I", "Add directory to the include search path");
56 | fprintf(stderr, " %-20s %s\n", "-D", "Define macro");
57 | fprintf(stderr, " %-20s %s\n", "-U", "Undefine macro");
58 | fprintf(stderr, " %-20s %s\n", "-include ", "Process file as if included");
59 | fprintf(stderr, " %-20s %s\n", "-x ", "Specify source language");
60 | fprintf(stderr, " %-20s %s\n", "-O", "Compile and Optimize using gcc backend");
61 | fprintf(stderr, " %-20s %s\n", "-fpic", "Generate position-independent code (PIC)");
62 | fprintf(stderr, " %-20s %s\n", "-fPIC", "Generate PIC suitable for use in shared libraries");
63 | fprintf(stderr, " %-20s %s\n", "-idirafter ", "Add directory to include search path after system directories");
64 | fprintf(stderr, " %-20s %s\n", "-static", "Link statically");
65 | fprintf(stderr, " %-20s %s\n", "-shared", "Create shared object");
66 | fprintf(stderr, " %-20s %s\n", "-L ", "Add directory to the library search path");
67 | fprintf(stderr, " %-20s %s\n", "--help", "Display help message");
68 | fprintf(stderr, "\nLinking Options:\n");
69 | fprintf(stderr, " %-20s %s\n", "-l", "Link with specified library");
70 | fprintf(stderr, " %-20s %s\n", "-Wl,", "Pass option to the linker");
71 | fprintf(stderr, " %-20s %s\n", "-Xlinker ", "Pass option to the linker");
72 | fprintf(stderr, "\nDependency Generation Options:\n");
73 | fprintf(stderr, " %-20s %s\n", "-M", "Generate Makefile-style dependency");
74 | fprintf(stderr, " %-20s %s\n", "-MF ", "Write dependency information to specified file");
75 | fprintf(stderr, " %-20s %s\n", "-MP", "Add phony target for each dependency");
76 | fprintf(stderr, " %-20s %s\n", "-MT ", "Specify target for dependency");
77 | fprintf(stderr, " %-20s %s\n", "-MD", "Write dependency information and compile");
78 | fprintf(stderr, " %-20s %s\n", "-MQ ", "Specify target for dependency");
79 | fprintf(stderr, " %-20s %s\n", "-MMD", "Write dependency information and compile if necessary");
80 | fprintf(stderr, "\nOther Options:\n");
81 | fprintf(stderr, " %-20s %s\n", "-run", "Run the program");
82 | //fprintf(stderr, " %-20s %s\n", "-rarg", "Arguments of the runned program."); // built in used in xc bash shebang fixer
83 | fprintf(stderr, " %-20s %s\n", "-asm", "Print generated asm");
84 | fprintf(stderr, " %-20s %s\n", "-gcc", "Use gcc backend to compile.");
85 | fprintf(stderr, " %-20s %s\n", "-gccarg", "Specify gcc args to compile.");
86 | fprintf(stderr, " %-20s %s\n", "-###", "Print commands executed by the compiler");
87 | fprintf(stderr, " %-20s %s\n", "-cc1", "Invoke the C compiler driver");
88 | fprintf(stderr, " %-20s %s\n", "-fcommon", "Allow multiple definitions of a global variable");
89 | fprintf(stderr, " %-20s %s\n", "-fno-common", "Do not allow multiple definitions of a global variable");
90 | fprintf(stderr, " %-20s %s\n", "-s", "Remove all symbol table and relocation information");
91 | fprintf(stderr, " %-20s %s\n", "-cc1-input ", "Specify input file for C compiler driver");
92 | fprintf(stderr, " %-20s %s\n", "-cc1-output ", "Specify output file for C compiler driver");
93 | fprintf(stderr, "For more information, see the documentation.\n");
94 | exit(status);
95 | }
96 |
97 | static void version() {
98 | fprintf(stderr, "xc %s\n", VERSION);
99 | exit(0);
100 | }
101 |
102 | static bool take_arg(char *arg) {
103 | char *x[] = {
104 | "-O", "-o", "-I", "-idirafter", "-include", "-x", "-MF", "-MT", "-Xlinker", "-soname", "-gccarg", "-rarg"
105 | };
106 | for (int i = 0; i < sizeof(x) / sizeof(*x); i++)
107 | if (!strcmp(arg, x[i])) {
108 | return true;
109 | } else {
110 | return false;
111 | }
112 | return false;
113 | }
114 |
115 | static void add_default_include_paths(char *argv0) {
116 | // We expect that xcc-specific include files are installed
117 | // to ./include relative to argv[0].
118 | strarray_push(&include_paths, format("%s/include", dirname(strdup(argv0))));
119 |
120 | // Add standard include paths.
121 |
122 | strarray_push(&include_paths, "/usr/local/include/xcc/");
123 | strarray_push(&include_paths, "/usr/local/include");
124 | strarray_push(&include_paths, "/usr/include/x86_64-linux-gnu");
125 |
126 | strarray_push(&include_paths, "/usr/include");
127 | strarray_push(&include_paths, "/usr/include/linux");
128 |
129 | // Keep a copy of the standard include paths for -MMD option.
130 | for (int i = 0; i < include_paths.len; i++)
131 | strarray_push(&std_include_paths, include_paths.data[i]);
132 | }
133 |
134 | static void define(char *str) {
135 | char *eq = strchr(str, '=');
136 | if (eq)
137 | define_macro(strndup(str, eq - str), eq + 1);
138 | else
139 | define_macro(str, "1");
140 | }
141 |
142 | static FileType parse_opt_x(char *s) {
143 | if (!strcmp(s, "c"))
144 | return FILE_C;
145 | if (!strcmp(s, "assembler"))
146 | return FILE_ASM;
147 | if (!strcmp(s, "none"))
148 | return FILE_NONE;
149 | error(": unknown argument for -x: %s", s);
150 | }
151 |
152 | static char *quote_makefile(char *s) {
153 | char *buf = calloc(1, strlen(s) * 2 + 1);
154 |
155 | for (int i = 0, j = 0; s[i]; i++) {
156 | switch (s[i]) {
157 | case '$':
158 | buf[j++] = '$';
159 | buf[j++] = '$';
160 | break;
161 | case '#':
162 | buf[j++] = '\\';
163 | buf[j++] = '#';
164 | break;
165 | case ' ':
166 | case '\t':
167 | for (int k = i - 1; k >= 0 && s[k] == '\\'; k--)
168 | buf[j++] = '\\';
169 | buf[j++] = '\\';
170 | buf[j++] = s[i];
171 | break;
172 | default:
173 | buf[j++] = s[i];
174 | break;
175 | }
176 | }
177 | return buf;
178 | }
179 |
180 | static void parse_args(int argc, char **argv) {
181 | // Make sure that all command line options that take an argument
182 | // have an argument.
183 | for (int i = 1; i < argc; i++)
184 | if (take_arg(argv[i]))
185 | if (!argv[++i])
186 | usage(1);
187 |
188 | StringArray idirafter = {};
189 |
190 | for (int i = 1; i < argc; i++) {
191 |
192 | if (!strcmp(argv[i], "-###")) {
193 | opt_hash_hash_hash = true;
194 | continue;
195 | }
196 |
197 | if (!strcmp(argv[i], "-run")) {
198 | opt_run = true;
199 | continue;
200 | }
201 |
202 | if (!strcmp(argv[i], "-asm")) {
203 | opt_asm = true;
204 | continue;
205 | }
206 |
207 | if (!strcmp(argv[i], "-gcc")) {
208 | opt_gcc = true;
209 | continue;
210 | }
211 |
212 | if (!strcmp(argv[i], "-cc1")) {
213 | opt_cc1 = true;
214 | continue;
215 | }
216 |
217 | if (!strcmp(argv[i], "--help")||!strcmp(argv[i], "-h"))
218 | usage(0);
219 |
220 |
221 | if (!strcmp(argv[i], "--version"))
222 | version();
223 |
224 | if (!strcmp(argv[i], "-o")) {
225 | opt_o = argv[++i];
226 | continue;
227 | }
228 |
229 | if (!strncmp(argv[i], "-o", 2)) {
230 | opt_o = argv[i] + 2;
231 | continue;
232 | }
233 |
234 | if (!strncmp(argv[i], "-Os", 3)) {
235 | opt_O = -1;
236 | continue;
237 | }
238 |
239 | if (!strncmp(argv[i], "-O", 2)) {
240 | int level = argv[i][2] - '0';
241 | if (level >= 0 && level <= 3) {
242 | opt_O = level;
243 | }
244 | continue;
245 | }
246 |
247 | if (!strcmp(argv[i], "-soname")) {
248 | strarray_push(&ld_extra_args, argv[i]);
249 | strarray_push(&ld_extra_args, argv[++i]);
250 | continue;
251 | }
252 |
253 | if (!strcmp(argv[i], "-S")) {
254 | opt_S = true;
255 | continue;
256 | }
257 |
258 | if (!strcmp(argv[i], "-fcommon")) {
259 | opt_fcommon = true;
260 | continue;
261 | }
262 |
263 | if (!strcmp(argv[i], "-fno-common")) {
264 | opt_fcommon = false;
265 | continue;
266 | }
267 |
268 | if (!strcmp(argv[i], "-c")) {
269 | opt_c = true;
270 | continue;
271 | }
272 |
273 | if (!strcmp(argv[i], "-E")) {
274 | opt_E = true;
275 | continue;
276 | }
277 |
278 | if (!strncmp(argv[i], "-I", 2)) {
279 | strarray_push(&include_paths, argv[i] + 2);
280 | continue;
281 | }
282 |
283 | if (!strcmp(argv[i], "-D")) {
284 | define(argv[++i]);
285 | continue;
286 | }
287 |
288 | if (!strncmp(argv[i], "-D", 2)) {
289 | define(argv[i] + 2);
290 | continue;
291 | }
292 |
293 | if (!strcmp(argv[i], "-U")) {
294 | undef_macro(argv[++i]);
295 | continue;
296 | }
297 |
298 | if (!strncmp(argv[i], "-U", 2)) {
299 | undef_macro(argv[i] + 2);
300 | continue;
301 | }
302 |
303 | if (!strcmp(argv[i], "-include")) {
304 | strarray_push(&opt_include, argv[++i]);
305 | continue;
306 | }
307 |
308 | if (!strcmp(argv[i], "-x")) {
309 | opt_x = parse_opt_x(argv[++i]);
310 | continue;
311 | }
312 |
313 | if (!strncmp(argv[i], "-x", 2)) {
314 | opt_x = parse_opt_x(argv[i] + 2);
315 | continue;
316 | }
317 |
318 | if (!strncmp(argv[i], "-l", 2) || !strncmp(argv[i], "-Wl,", 4)) {
319 | strarray_push(&input_paths, argv[i]);
320 | continue;
321 | }
322 |
323 | if (!strcmp(argv[i], "-Xlinker")) {
324 | strarray_push(&ld_extra_args, argv[++i]);
325 | continue;
326 | }
327 |
328 | if (!strcmp(argv[i], "-s")) {
329 | strarray_push(&ld_extra_args, "-s");
330 | continue;
331 | }
332 |
333 | if (!strcmp(argv[i], "-M")) {
334 | opt_M = true;
335 | continue;
336 | }
337 |
338 | if (!strcmp(argv[i], "-MF")) {
339 | opt_MF = argv[++i];
340 | continue;
341 | }
342 |
343 | if (!strcmp(argv[i], "-MP")) {
344 | opt_MP = true;
345 | continue;
346 | }
347 |
348 | if (!strcmp(argv[i], "-MT")) {
349 | if (opt_MT == NULL)
350 | opt_MT = argv[++i];
351 | else
352 | opt_MT = format("%s %s", opt_MT, argv[++i]);
353 | continue;
354 | }
355 |
356 | if (!strcmp(argv[i], "-MD")) {
357 | opt_MD = true;
358 | continue;
359 | }
360 |
361 | if (!strcmp(argv[i], "-MQ")) {
362 | if (opt_MT == NULL)
363 | opt_MT = quote_makefile(argv[++i]);
364 | else
365 | opt_MT = format("%s %s", opt_MT, quote_makefile(argv[++i]));
366 | continue;
367 | }
368 |
369 | if (!strcmp(argv[i], "-MMD")) {
370 | opt_MD = opt_MMD = true;
371 | continue;
372 | }
373 |
374 | if (!strcmp(argv[i], "-fpic") || !strcmp(argv[i], "-fPIC")) {
375 | opt_fpic = true;
376 | continue;
377 | }
378 |
379 | if (!strcmp(argv[i], "-cc1-input")) {
380 | base_file = argv[++i];
381 | continue;
382 | }
383 |
384 | if (!strcmp(argv[i], "-cc1-output")) {
385 | output_file = argv[++i];
386 | continue;
387 | }
388 |
389 | if (!strcmp(argv[i], "-idirafter")) {
390 | strarray_push(&idirafter, argv[i++]);
391 | continue;
392 | }
393 |
394 | if (!strcmp(argv[i], "-gccarg")) {
395 | opt_gcc = true;
396 | opt_gccarg = argv[++i];
397 | continue;
398 | }
399 |
400 | if (!strcmp(argv[i], "-rarg")) {
401 | opt_rarg = argv[++i];
402 | continue;
403 | }
404 |
405 | if (!strcmp(argv[i], "-static")) {
406 | opt_static = true;
407 | strarray_push(&ld_extra_args, "-static");
408 | continue;
409 | }
410 |
411 | if (!strcmp(argv[i], "-shared")) {
412 | opt_shared = true;
413 | strarray_push(&ld_extra_args, "-shared");
414 | continue;
415 | }
416 |
417 | if (!strcmp(argv[i], "-L")) {
418 | strarray_push(&ld_extra_args, "-L");
419 | strarray_push(&ld_extra_args, argv[++i]);
420 | continue;
421 | }
422 |
423 | if (!strncmp(argv[i], "-L", 2)) {
424 | strarray_push(&ld_extra_args, "-L");
425 | strarray_push(&ld_extra_args, argv[i] + 2);
426 | continue;
427 | }
428 |
429 | if (!strcmp(argv[i], "-hashmap-test")) {
430 | hashmap_test();
431 | exit(0);
432 | }
433 |
434 | // These options are ignored for now.
435 | if (!strncmp(argv[i], "-W", 2) ||
436 | !strncmp(argv[i], "-g", 2) ||
437 | !strncmp(argv[i], "-std=", 5) ||
438 | !strcmp(argv[i], "-ffreestanding") ||
439 | !strcmp(argv[i], "-fno-builtin") ||
440 | !strcmp(argv[i], "-fno-omit-frame-pointer") ||
441 | !strcmp(argv[i], "-fno-stack-protector") ||
442 | !strcmp(argv[i], "-fno-strict-aliasing") ||
443 | !strcmp(argv[i], "-m64") ||
444 | !strcmp(argv[i], "-mno-red-zone") ||
445 | !strcmp(argv[i], "-w"))
446 | continue;
447 |
448 | if (argv[i][0] == '-' && argv[i][1] != '\0')
449 | error("unknown argument: %s", argv[i]);
450 |
451 | strarray_push(&input_paths, argv[i]);
452 | }
453 |
454 | for (int i = 0; i < idirafter.len; i++)
455 | strarray_push(&include_paths, idirafter.data[i]);
456 |
457 | if (input_paths.len == 0) {
458 | printf("Please input file to use it\n");
459 | usage(1);
460 | }
461 |
462 | // -E implies that the input is the C macro language.
463 | if (opt_E)
464 | opt_x = FILE_C;
465 | }
466 |
467 | static FILE *open_file(char *path) {
468 | if (!path || strcmp(path, "-") == 0)
469 | return stdout;
470 |
471 | FILE *out = fopen(path, "w");
472 | if (!out)
473 | error("cannot open output file: %s: %s", path, strerror(errno));
474 | return out;
475 | }
476 |
477 | static bool endswith(char *p, char *q) {
478 | int len1 = strlen(p);
479 | int len2 = strlen(q);
480 | return (len1 >= len2) && !strcmp(p + len1 - len2, q);
481 | }
482 |
483 | // Replace file extension
484 | static char *replace_extn(char *tmpl, char *extn) {
485 | char *filename = basename(strdup(tmpl));
486 | char *dot = strrchr(filename, '.');
487 | if (dot)
488 | *dot = '\0';
489 | return format("%s%s", filename, extn);
490 | }
491 |
492 | static void cleanup(void) {
493 | for (int i = 0; i < tmpfiles.len; i++)
494 | unlink(tmpfiles.data[i]);
495 | }
496 |
497 | static char *create_tmpfile(void) {
498 | char *path = strdup("/tmp/xcc-XXXXXX");
499 | int fd = mkstemp(path);
500 | if (fd == -1)
501 | error("mkstemp failed: %s", strerror(errno));
502 | close(fd);
503 |
504 | strarray_push(&tmpfiles, path);
505 | return path;
506 | }
507 |
508 | static void run_subprocess(char **argv) {
509 |
510 | // If -### is given, dump the subprocess's command line.
511 | if (opt_hash_hash_hash) {
512 | fprintf(stderr, "%s", argv[0]);
513 | for (int i = 1; argv[i]; i++)
514 | fprintf(stderr, " %s", argv[i]);
515 | fprintf(stderr, "\n");
516 | }
517 |
518 | if (fork() == 0) {
519 | // Child process. Run a new command.
520 | execvp(argv[0], argv);
521 | fprintf(stderr, "exec failed: %s: %s\n", argv[0], strerror(errno));
522 | _exit(1);
523 | }
524 |
525 | // Wait for the child process to finish.
526 | int status;
527 | while (wait(&status) > 0);
528 | if (status != 0)
529 | exit(1);
530 | }
531 |
532 | static void run_cc1(int argc, char **argv, char *input, char *output) {
533 | char **args = calloc(argc + 10, sizeof(char *));
534 | memcpy(args, argv, argc * sizeof(char *));
535 | args[argc++] = "-cc1";
536 |
537 | if (input) {
538 | args[argc++] = "-cc1-input";
539 | args[argc++] = input;
540 | }
541 |
542 | if (output) {
543 | args[argc++] = "-cc1-output";
544 | args[argc++] = output;
545 | }
546 |
547 | run_subprocess(args);
548 | }
549 |
550 | // Print tokens to stdout. Used for -E.
551 | static void print_tokens(Token *tok) {
552 | FILE *out = open_file(opt_o ? opt_o : "-");
553 |
554 | int line = 1;
555 | for (; tok->kind != TK_EOF; tok = tok->next) {
556 | if (line > 1 && tok->at_bol)
557 | fprintf(out, "\n");
558 | if (tok->has_space && !tok->at_bol)
559 | fprintf(out, " ");
560 | fprintf(out, "%.*s", tok->len, tok->loc);
561 | line++;
562 | }
563 | fprintf(out, "\n");
564 | }
565 |
566 | static bool in_std_include_path(char *path) {
567 | for (int i = 0; i < std_include_paths.len; i++) {
568 | char *dir = std_include_paths.data[i];
569 | int len = strlen(dir);
570 | if (strncmp(dir, path, len) == 0 && path[len] == '/')
571 | return true;
572 | }
573 | return false;
574 | }
575 |
576 | // If -M options is given, the compiler write a list of input files to
577 | // stdout in a format that "make" command can read. This feature is
578 | // used to automate file dependency management.
579 | static void print_dependencies(void) {
580 | char *path;
581 | if (opt_MF)
582 | path = opt_MF;
583 | else if (opt_MD)
584 | path = replace_extn(opt_o ? opt_o : base_file, ".d");
585 | else if (opt_o)
586 | path = opt_o;
587 | else
588 | path = "-";
589 |
590 | FILE *out = open_file(path);
591 | if (opt_MT)
592 | fprintf(out, "%s:", opt_MT);
593 | else
594 | fprintf(out, "%s:", quote_makefile(replace_extn(base_file, ".o")));
595 |
596 | File **files = get_input_files();
597 |
598 | for (int i = 0; files[i]; i++) {
599 | if (opt_MMD && in_std_include_path(files[i]->name))
600 | continue;
601 | fprintf(out, " \\\n %s", files[i]->name);
602 | }
603 |
604 | fprintf(out, "\n\n");
605 |
606 | if (opt_MP) {
607 | for (int i = 1; files[i]; i++) {
608 | if (opt_MMD && in_std_include_path(files[i]->name))
609 | continue;
610 | fprintf(out, "%s:\n\n", quote_makefile(files[i]->name));
611 | }
612 | }
613 | }
614 |
615 | static Token *must_tokenize_file(char *path) {
616 | Token *tok = tokenize_file(path);
617 | if (!tok)
618 | error("%s: %s", path, strerror(errno));
619 | return tok;
620 | }
621 |
622 | static Token *append_tokens(Token *tok1, Token *tok2) {
623 | if (!tok1 || tok1->kind == TK_EOF)
624 | return tok2;
625 |
626 | Token *t = tok1;
627 | while (t->next->kind != TK_EOF)
628 | t = t->next;
629 | t->next = tok2;
630 | return tok1;
631 | }
632 |
633 | #define MAX_URL_LENGTH 2048
634 |
635 | #define PATH_MAX 4096 // Define the maximum path length
636 |
637 | char *get_launch_directory() {
638 | char *path = malloc(PATH_MAX);
639 | if (path == NULL) {
640 | perror("Memory allocation error");
641 | exit(EXIT_FAILURE);
642 | }
643 |
644 | if (getcwd(path, PATH_MAX) == NULL) {
645 | perror("getcwd() error");
646 | free(path);
647 | exit(EXIT_FAILURE);
648 | }
649 |
650 | return path;
651 | }
652 |
653 | char* process_gcc(const char *base_file) {
654 | FILE *input = fopen(base_file, "r");
655 | if (input == NULL) {
656 | fprintf(stderr, "Error: Could not open file %s\n", base_file);
657 | exit(EXIT_FAILURE);
658 | }
659 |
660 | // Read the first line
661 | char buffer[4096];
662 | if (fgets(buffer, sizeof(buffer), input) == NULL) {
663 | fclose(input);
664 | return strdup(base_file);
665 | }
666 |
667 | // Check if the first line starts with "#!"
668 | if (strncmp(buffer, "#!", 2) == 0) {
669 | if (fgets(buffer, sizeof(buffer), input) == NULL) {
670 | rewind(input);
671 | }
672 | } else {
673 | rewind(input);
674 | }
675 |
676 | // Create a temporary file
677 | char temp_file[] = "/tmp/ccx-gccXXXXXX";
678 | int temp_fd = mkstemp(temp_file);
679 | if (temp_fd == -1) {
680 | fclose(input);
681 | exit(EXIT_FAILURE);
682 | }
683 | FILE *temp = fdopen(temp_fd, "w");
684 | if (temp == NULL) {
685 | fclose(input);
686 | close(temp_fd);
687 | exit(EXIT_FAILURE);
688 | }
689 |
690 | // Write the modified content to the temporary file
691 | fputs(buffer, temp); // Write the first line
692 | while (fgets(buffer, sizeof(buffer), input) != NULL) {
693 | if (strncmp(buffer, "#include \"https://", 17) == 0) {
694 | // Extract the URL from the line
695 | char *url_start = strchr(buffer, '"') + 1;
696 | char *url_end = strchr(url_start, '"');
697 | size_t url_len = url_end - url_start;
698 |
699 | // Copy the URL into a string
700 | char url[MAX_URL_LENGTH];
701 | strncpy(url, url_start, url_len);
702 | url[url_len] = '\0';
703 |
704 | // Extract filename from URL
705 | char *filename_start = strrchr(url, '/') + 1;
706 | if (filename_start == NULL) {
707 | error("Error: Unable to extract filename from URL");
708 | }
709 |
710 | // Construct the string
711 | char include_string[MAX_URL_LENGTH * 2]; // Maximum size of dirname + filename_start
712 | snprintf(include_string, sizeof(include_string), "%s/%s", get_launch_directory(), filename_start);
713 |
714 | // Check if the file is already downloaded
715 | if (!file_exists(include_string)) {
716 | CURL *curl = curl_easy_init();
717 | if (curl == NULL) {
718 | error("Could not initialize cURL context");
719 | }
720 |
721 | FILE *download_file = fopen(include_string, "w");
722 | if (download_file == NULL) {
723 | fprintf(stderr, "Could not open file '%s'\n", include_string);
724 | }
725 |
726 | curl_easy_setopt(curl, CURLOPT_URL, url);
727 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, download_file);
728 | CURLcode res = curl_easy_perform(curl);
729 | if (res != CURLE_OK) {
730 | fprintf(stderr, "Could not download '%s': %s\n", url, curl_easy_strerror(res));
731 | }
732 |
733 | curl_easy_cleanup(curl);
734 | fclose(download_file);
735 | }
736 |
737 | char include_buffer[1024];
738 | snprintf(include_buffer, sizeof(include_buffer), "#include \"%s/%s\"\n", get_launch_directory(), filename_start);
739 | fputs(include_buffer, temp);
740 | } else {
741 | fputs(buffer, temp);
742 | }
743 | }
744 |
745 | fclose(input);
746 | fclose(temp);
747 | return strdup(temp_file);
748 | }
749 |
750 | static void cc1(void) {
751 | if (opt_O > 0 || opt_gcc) {
752 | // Open a temporary output buffer
753 | char *buf;
754 | size_t buflen;
755 | FILE *output_buf = open_memstream(&buf, &buflen);
756 | if (output_buf == NULL) {
757 | error("Failed to open memory stream");
758 | }
759 |
760 | // Use popen to execute GCC and redirect its output to our buffer
761 | char cmd[4096];
762 | char include_cmd[1024];
763 | for (int i = 0; i < include_paths.len; i++) {
764 | if (strcmp(include_paths.data[i], "/usr/local/bin/xcc/include") == 0 || strcmp(include_paths.data[i], "/usr/local/include/xcc/") == 0 || strcmp(include_paths.data[i], "/usr/local/include") == 0 || strcmp(include_paths.data[i], "/usr/include/x86_64-linux-gnu") == 0 || strcmp(include_paths.data[i], "/usr/include") == 0 || strcmp(include_paths.data[i], "/usr/include/linux") == 0) {
765 | continue;
766 | }
767 | strcat(include_cmd, "-I");
768 | strcat(include_cmd, include_paths.data[i]);
769 | strcat(include_cmd, " ");
770 | }
771 |
772 | char *tmpfile = process_gcc(base_file);
773 | if(!file_exists(tmpfile)){
774 | error("Failed to gen tmp gcc c file.");
775 | }
776 |
777 |
778 | if(opt_gccarg == NULL){
779 | if (opt_O == -1) {
780 | snprintf(cmd, sizeof(cmd), "gcc %s-S -Os -o \"%s\" -x c \"%s\"", include_cmd, output_file, tmpfile);
781 | } else {
782 | snprintf(cmd, sizeof(cmd), "gcc %s-S -O%d -o \"%s\" -x c \"%s\"", include_cmd, opt_O, output_file, tmpfile);
783 | }
784 | } else {
785 | if (opt_O == -1) {
786 | snprintf(cmd, sizeof(cmd), "gcc %s %s-S -Os -o \"%s\" -x c \"%s\"", opt_gccarg, include_cmd, output_file, tmpfile);
787 | } else {
788 | snprintf(cmd, sizeof(cmd), "gcc %s %s-S -O%d -o \"%s\" -x c \"%s\"", opt_gccarg, include_cmd, opt_O, output_file, tmpfile);
789 | }
790 | }
791 |
792 | printf("%s\n",cmd);
793 |
794 | FILE *gcc_output = popen(cmd, "r");
795 | if (gcc_output == NULL || !file_exists(tmpfile)) {
796 | printf("%s\n",cmd);
797 | error("Failed to execute GCC");
798 | }
799 |
800 | // Read the output of GCC and write it to the output buffer
801 | char buffer[4096];
802 | size_t bytes_read;
803 | while ((bytes_read = fread(buffer, 1, sizeof(buffer), gcc_output)) > 0) {
804 | fwrite(buffer, 1, bytes_read, output_buf);
805 | }
806 |
807 | // Close the pipes and free resources
808 | pclose(gcc_output);
809 | fclose(output_buf);
810 | } else {
811 | Token *tok = NULL;
812 |
813 | // Process -include option
814 | for (int i = 0; i < opt_include.len; i++) {
815 | char *incl = opt_include.data[i];
816 |
817 | char *path;
818 | if (file_exists(incl)) {
819 | path = incl;
820 | } else {
821 | path = search_include_paths(incl);
822 | if (!path)
823 | error("-include: %s: %s", incl, strerror(errno));
824 | }
825 |
826 | Token *tok2 = must_tokenize_file(path);
827 | tok = append_tokens(tok, tok2);
828 | }
829 |
830 | // Tokenize and parse.
831 | Token *tok2 = must_tokenize_file(base_file);
832 | tok = append_tokens(tok, tok2);
833 | tok = preprocess(tok);
834 |
835 | // If -M or -MD are given, print file dependencies.
836 | if (opt_M || opt_MD) {
837 | print_dependencies();
838 | if (opt_M)
839 | return;
840 | }
841 |
842 | // If -E is given, print out preprocessed C code as a result.
843 | if (opt_E) {
844 | print_tokens(tok);
845 | return;
846 | }
847 |
848 | Obj *prog = parse(tok);
849 |
850 | // Open a temporary output buffer
851 | char *buf;
852 | size_t buflen;
853 | FILE *output_buf = open_memstream(&buf, &buflen);
854 | if (output_buf == NULL) {
855 | error("Failed to open memory stream");
856 | }
857 |
858 | // Traverse the AST to emit assembly.
859 | codegen(prog, output_buf);
860 | fclose(output_buf);
861 |
862 | // Write the assembly text to a file.
863 | FILE *out = open_file(output_file);
864 | fwrite(buf, buflen, 1, out);
865 | fclose(out);
866 | }
867 | }
868 |
869 | static void assemble(char *input, char *output) {
870 | char *cmd[] = {"as", "-c", input, "-o", output, NULL};
871 | run_subprocess(cmd);
872 | }
873 |
874 | static char *find_file(char *pattern) {
875 | char *path = NULL;
876 | glob_t buf = {};
877 | glob(pattern, 0, NULL, &buf);
878 | if (buf.gl_pathc > 0)
879 | path = strdup(buf.gl_pathv[buf.gl_pathc - 1]);
880 | globfree(&buf);
881 | return path;
882 | }
883 |
884 | // Returns true if a given file exists.
885 | bool file_exists(char *path) {
886 | struct stat st;
887 | return !stat(path, &st);
888 | }
889 |
890 | static char *find_libpath(void) {
891 | if (file_exists("/usr/lib/x86_64-linux-gnu/crti.o"))
892 | return "/usr/lib/x86_64-linux-gnu";
893 | if (file_exists("/usr/lib64/crti.o"))
894 | return "/usr/lib64";
895 | error("library path is not found");
896 | }
897 |
898 | static char *find_gcc_libpath(void) {
899 | char *paths[] = {
900 | "/usr/lib/gcc/x86_64-linux-gnu/*/crtbegin.o",
901 | "/usr/lib/gcc/x86_64-pc-linux-gnu/*/crtbegin.o", // For Gentoo
902 | "/usr/lib/gcc/x86_64-redhat-linux/*/crtbegin.o", // For Fedora
903 | };
904 |
905 | for (int i = 0; i < sizeof(paths) / sizeof(*paths); i++) {
906 | char *path = find_file(paths[i]);
907 | if (path)
908 | return dirname(path);
909 | }
910 |
911 | error("gcc library path is not found");
912 | }
913 |
914 | static void run_linker(StringArray *inputs, char *output) {
915 |
916 | StringArray arr = {};
917 |
918 | strarray_push(&arr, "ld");
919 | strarray_push(&arr, "-o");
920 | strarray_push(&arr, output);
921 | strarray_push(&arr, "-m");
922 | strarray_push(&arr, "elf_x86_64");
923 |
924 | char *libpath = find_libpath();
925 | char *gcc_libpath = find_gcc_libpath();
926 |
927 | if (opt_shared) {
928 | strarray_push(&arr, format("%s/crti.o", libpath));
929 | strarray_push(&arr, format("%s/crtbeginS.o", gcc_libpath));
930 | } else {
931 | strarray_push(&arr, format("%s/crt1.o", libpath));
932 | strarray_push(&arr, format("%s/crti.o", libpath));
933 | strarray_push(&arr, format("%s/crtbegin.o", gcc_libpath));
934 | }
935 |
936 | strarray_push(&arr, format("-L%s", gcc_libpath));
937 | strarray_push(&arr, "-L/usr/lib/x86_64-linux-gnu");
938 | strarray_push(&arr, "-L/usr/lib64");
939 | strarray_push(&arr, "-L/lib64");
940 |
941 | strarray_push(&arr, "-L/usr/lib/x86_64-linux-gnu");
942 | strarray_push(&arr, "-L/usr/lib/x86_64-pc-linux-gnu");
943 | strarray_push(&arr, "-L/usr/lib/x86_64-redhat-linux");
944 | strarray_push(&arr, "-L/usr/lib");
945 | strarray_push(&arr, "-L/lib");
946 |
947 | if (!opt_static) {
948 | strarray_push(&arr, "-dynamic-linker");
949 | strarray_push(&arr, "/lib64/ld-linux-x86-64.so.2");
950 | }
951 |
952 | for (int i = 0; i < ld_extra_args.len; i++)
953 | strarray_push(&arr, ld_extra_args.data[i]);
954 |
955 | for (int i = 0; i < inputs->len; i++)
956 | strarray_push(&arr, inputs->data[i]);
957 |
958 | if (opt_static) {
959 | strarray_push(&arr, "--start-group");
960 | strarray_push(&arr, "-lgcc");
961 | strarray_push(&arr, "-lgcc_eh");
962 | strarray_push(&arr, "-lc");
963 | strarray_push(&arr, "--end-group");
964 | } else {
965 | strarray_push(&arr, "-lc");
966 | strarray_push(&arr, "-lgcc");
967 | strarray_push(&arr, "--as-needed");
968 | strarray_push(&arr, "-lgcc_s");
969 | strarray_push(&arr, "--no-as-needed");
970 | }
971 |
972 | if (opt_shared)
973 | strarray_push(&arr, format("%s/crtendS.o", gcc_libpath));
974 | else
975 | strarray_push(&arr, format("%s/crtend.o", gcc_libpath));
976 |
977 | strarray_push(&arr, format("%s/crtn.o", libpath));
978 | strarray_push(&arr, NULL);
979 |
980 | run_subprocess(arr.data);
981 | }
982 |
983 | static FileType get_file_type(char *filename) {
984 | if (opt_x != FILE_NONE)
985 | return opt_x;
986 | if (endswith(filename, ".a"))
987 | return FILE_AR;
988 | if (endswith(filename, ".so"))
989 | return FILE_DSO;
990 | if (endswith(filename, ".o"))
991 | return FILE_OBJ;
992 | if (endswith(filename, ".c") || endswith(filename, ""))
993 | return FILE_C;
994 | if (endswith(filename, ".s") || endswith(filename, ".S") || endswith(filename, ".asm"))
995 | return FILE_ASM;
996 | if(opt_run){
997 | return FILE_C;
998 | }
999 | error(": unknown file extension: %s", filename);
1000 | }
1001 |
1002 | void executeIfExists(char *file, int argc, char **argv) {
1003 | if (file_exists(file)) {
1004 | char exec[256];
1005 | char rm[100];
1006 | snprintf(exec, sizeof(exec), "./%s", file);
1007 | //printf("\nArgs: %s\n",opt_rarg);
1008 | if (opt_rarg != NULL) {
1009 | strcat(exec, " ");
1010 | strcat(exec, opt_rarg);
1011 | }
1012 | system(exec);
1013 | snprintf(rm, sizeof(rm), "rm -rf ./%s", file);
1014 | system(rm);
1015 | }
1016 | }
1017 |
1018 | int main(int argc, char **argv) {
1019 | atexit(cleanup);
1020 | init_macros();
1021 | parse_args(argc, argv);
1022 |
1023 | if (curl_global_init(CURL_GLOBAL_DEFAULT) != 0) {
1024 | error("Could not initialize Global cURL context.");
1025 | }
1026 |
1027 | if (opt_cc1) {
1028 | add_default_include_paths(argv[0]);
1029 | cc1();
1030 | return 0;
1031 | }
1032 |
1033 | if (input_paths.len > 1 && opt_o && (opt_c || opt_S || opt_E))
1034 | error("cannot specify '-o' with '-c,' '-S' or '-E' with multiple files");
1035 |
1036 | StringArray ld_args = {};
1037 |
1038 | for (int i = 0; i < input_paths.len; i++) {
1039 | char *input = input_paths.data[i];
1040 |
1041 | if (!strncmp(input, "-l", 2)) {
1042 | strarray_push(&ld_args, input);
1043 | continue;
1044 | }
1045 |
1046 | if (!strncmp(input, "-Wl,", 4)) {
1047 | char *s = strdup(input + 4);
1048 | char *arg = strtok(s, ",");
1049 | while (arg) {
1050 | strarray_push(&ld_args, arg);
1051 | arg = strtok(NULL, ",");
1052 | }
1053 | continue;
1054 | }
1055 |
1056 | char *output;
1057 | if (opt_o)
1058 | output = opt_o;
1059 | else if (opt_S)
1060 | output = replace_extn(input, ".s");
1061 | else
1062 | output = replace_extn(input, ".o");
1063 |
1064 | FileType type = get_file_type(input);
1065 |
1066 | // Handle .o or .a
1067 | if (type == FILE_OBJ || type == FILE_AR || type == FILE_DSO) {
1068 | strarray_push(&ld_args, input);
1069 | continue;
1070 | }
1071 |
1072 | // Handle .s, -S, .asm
1073 | if (type == FILE_ASM) {
1074 | if (!opt_S)
1075 | assemble(input, output);
1076 | continue;
1077 | }
1078 |
1079 | assert(type == FILE_C);
1080 |
1081 | // Just preprocess
1082 | if (opt_E || opt_M) {
1083 | run_cc1(argc, argv, input, NULL);
1084 | continue;
1085 | }
1086 |
1087 | // Compile
1088 | if (opt_S) {
1089 | run_cc1(argc, argv, input, output);
1090 | continue;
1091 | }
1092 |
1093 | // Compile and assemble
1094 | if (opt_c) {
1095 | char *tmp = create_tmpfile();
1096 | run_cc1(argc, argv, input, tmp);
1097 | assemble(tmp, output);
1098 | continue;
1099 | }
1100 |
1101 | // Compile, assemble and link
1102 | char *tmp1 = create_tmpfile();
1103 | char *tmp2 = create_tmpfile();
1104 | run_cc1(argc, argv, input, tmp1);
1105 | assemble(tmp1, tmp2);
1106 | strarray_push(&ld_args, tmp2);
1107 | continue;
1108 | }
1109 |
1110 | curl_global_cleanup();
1111 |
1112 | if(opt_run) {
1113 | char *output_file_elf = opt_o ? opt_o : "a.out";
1114 | run_linker(&ld_args, output_file_elf);
1115 | executeIfExists(output_file_elf,argc,argv);
1116 | } else if (ld_args.len > 0) {
1117 | run_linker(&ld_args, opt_o ? opt_o : "a.out");
1118 | }
1119 |
1120 | return 0;
1121 | }
1122 |
--------------------------------------------------------------------------------
/strings.c:
--------------------------------------------------------------------------------
1 | #include "xcc.h"
2 |
3 | void strarray_push(StringArray *arr, char *s) {
4 | if (!arr->data) {
5 | arr->data = calloc(8, sizeof(char *));
6 | arr->capacity = 8;
7 | }
8 |
9 | if (arr->capacity == arr->len) {
10 | arr->data = realloc(arr->data, sizeof(char *) * arr->capacity * 2);
11 | arr->capacity *= 2;
12 | for (int i = arr->len; i < arr->capacity; i++)
13 | arr->data[i] = NULL;
14 | }
15 |
16 | arr->data[arr->len++] = s;
17 | }
18 |
19 | // Takes a printf-style format string and returns a formatted string.
20 | char *format(char *fmt, ...) {
21 | char *buf;
22 | size_t buflen;
23 | FILE *out = open_memstream(&buf, &buflen);
24 |
25 | va_list ap;
26 | va_start(ap, fmt);
27 | vfprintf(out, fmt, ap);
28 | va_end(ap);
29 | fclose(out);
30 | return buf;
31 | }
32 |
--------------------------------------------------------------------------------
/test/alignof.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int _Alignas(512) g1;
4 | int _Alignas(512) g2;
5 | char g3;
6 | int g4;
7 | long g5;
8 | char g6;
9 |
10 | int main() {
11 | ASSERT(1, _Alignof(char));
12 | ASSERT(2, _Alignof(short));
13 | ASSERT(4, _Alignof(int));
14 | ASSERT(8, _Alignof(long));
15 | ASSERT(8, _Alignof(long long));
16 | ASSERT(1, _Alignof(char[3]));
17 | ASSERT(4, _Alignof(int[3]));
18 | ASSERT(1, _Alignof(struct {char a; char b;}[2]));
19 | ASSERT(8, _Alignof(struct {char a; long b;}[2]));
20 |
21 | ASSERT(1, ({ _Alignas(char) char x, y; &y-&x; }));
22 | ASSERT(8, ({ _Alignas(long) char x, y; &y-&x; }));
23 | ASSERT(32, ({ _Alignas(32) char x, y; &y-&x; }));
24 | ASSERT(32, ({ _Alignas(32) int *x, *y; ((char *)&y)-((char *)&x); }));
25 | ASSERT(16, ({ struct { _Alignas(16) char x, y; } a; &a.y-&a.x; }));
26 | ASSERT(8, ({ struct T { _Alignas(8) char a; }; _Alignof(struct T); }));
27 |
28 | ASSERT(0, (long)(char *)&g1 % 512);
29 | ASSERT(0, (long)(char *)&g2 % 512);
30 | ASSERT(0, (long)(char *)&g4 % 4);
31 | ASSERT(0, (long)(char *)&g5 % 8);
32 |
33 | ASSERT(1, ({ char x; _Alignof(x); }));
34 | ASSERT(4, ({ int x; _Alignof(x); }));
35 | ASSERT(1, ({ char x; _Alignof x; }));
36 | ASSERT(4, ({ int x; _Alignof x; }));
37 |
38 | ASSERT(1, _Alignof(char) << 31 >> 31);
39 | ASSERT(1, _Alignof(char) << 63 >> 63);
40 | ASSERT(1, ({ char x; _Alignof(x) << 63 >> 63; }));
41 |
42 | ASSERT(0, ({ char x[16]; (unsigned long)&x % 16; }));
43 | ASSERT(0, ({ char x[17]; (unsigned long)&x % 16; }));
44 | ASSERT(0, ({ char x[100]; (unsigned long)&x % 16; }));
45 | ASSERT(0, ({ char x[101]; (unsigned long)&x % 16; }));
46 |
47 | printf("OK\n");
48 | return 0;
49 | }
50 |
--------------------------------------------------------------------------------
/test/alloca.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | void *fn(int x, void *p, int y) { return p; }
4 |
5 | int main() {
6 | int i = 0;
7 |
8 | char *p1 = alloca(16);
9 | char *p2 = alloca(16);
10 | char *p3 = 1 + (char *)alloca(3) + 1;
11 | p3 -= 2;
12 | char *p4 = fn(1, alloca(16), 3);
13 |
14 | ASSERT(16, p1 - p2);
15 | ASSERT(16, p2 - p3);
16 | ASSERT(16, p3 - p4);
17 |
18 | memcpy(p1, "0123456789abcdef", 16);
19 | memcpy(p2, "ghijklmnopqrstuv", 16);
20 | memcpy(p3, "wxy", 3);
21 |
22 | ASSERT(0, memcmp(p1, "0123456789abcdef", 16));
23 | ASSERT(0, memcmp(p2, "ghijklmnopqrstuv", 16));
24 | ASSERT(0, memcmp(p3, "wxy", 3));
25 |
26 | printf("OK\n");
27 | return 0;
28 | }
29 |
--------------------------------------------------------------------------------
/test/arith.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | ASSERT(0, 0);
5 | ASSERT(42, 42);
6 | ASSERT(21, 5+20-4);
7 | ASSERT(41, 12 + 34 - 5 );
8 | ASSERT(47, 5+6*7);
9 | ASSERT(15, 5*(9-6));
10 | ASSERT(4, (3+5)/2);
11 | ASSERT(10, -10+20);
12 | ASSERT(10, - -10);
13 | ASSERT(10, - - +10);
14 |
15 | ASSERT(0, 0==1);
16 | ASSERT(1, 42==42);
17 | ASSERT(1, 0!=1);
18 | ASSERT(0, 42!=42);
19 |
20 | ASSERT(1, 0<1);
21 | ASSERT(0, 1<1);
22 | ASSERT(0, 2<1);
23 | ASSERT(1, 0<=1);
24 | ASSERT(1, 1<=1);
25 | ASSERT(0, 2<=1);
26 |
27 | ASSERT(1, 1>0);
28 | ASSERT(0, 1>1);
29 | ASSERT(0, 1>2);
30 | ASSERT(1, 1>=0);
31 | ASSERT(1, 1>=1);
32 | ASSERT(0, 1>=2);
33 |
34 | ASSERT(0, 1073741824 * 100 / 100);
35 |
36 | ASSERT(7, ({ int i=2; i+=5; i; }));
37 | ASSERT(7, ({ int i=2; i+=5; }));
38 | ASSERT(3, ({ int i=5; i-=2; i; }));
39 | ASSERT(3, ({ int i=5; i-=2; }));
40 | ASSERT(6, ({ int i=3; i*=2; i; }));
41 | ASSERT(6, ({ int i=3; i*=2; }));
42 | ASSERT(3, ({ int i=6; i/=2; i; }));
43 | ASSERT(3, ({ int i=6; i/=2; }));
44 |
45 | ASSERT(3, ({ int i=2; ++i; }));
46 | ASSERT(2, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; ++*p; }));
47 | ASSERT(0, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; --*p; }));
48 |
49 | ASSERT(2, ({ int i=2; i++; }));
50 | ASSERT(2, ({ int i=2; i--; }));
51 | ASSERT(3, ({ int i=2; i++; i; }));
52 | ASSERT(1, ({ int i=2; i--; i; }));
53 | ASSERT(1, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; *p++; }));
54 | ASSERT(1, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; *p--; }));
55 |
56 | ASSERT(0, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; (*p++)--; a[0]; }));
57 | ASSERT(0, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; (*(p--))--; a[1]; }));
58 | ASSERT(2, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; (*p)--; a[2]; }));
59 | ASSERT(2, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; (*p)--; p++; *p; }));
60 |
61 | ASSERT(0, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; (*p++)--; a[0]; }));
62 | ASSERT(0, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; (*p++)--; a[1]; }));
63 | ASSERT(2, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; (*p++)--; a[2]; }));
64 | ASSERT(2, ({ int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p=a+1; (*p++)--; *p; }));
65 |
66 | ASSERT(0, !1);
67 | ASSERT(0, !2);
68 | ASSERT(1, !0);
69 | ASSERT(1, !(char)0);
70 | ASSERT(0, !(long)3);
71 | ASSERT(4, sizeof(!(char)0));
72 | ASSERT(4, sizeof(!(long)0));
73 |
74 | ASSERT(-1, ~0);
75 | ASSERT(0, ~-1);
76 |
77 | ASSERT(5, 17%6);
78 | ASSERT(5, ((long)17)%6);
79 | ASSERT(2, ({ int i=10; i%=4; i; }));
80 | ASSERT(2, ({ long i=10; i%=4; i; }));
81 |
82 | ASSERT(0, 0&1);
83 | ASSERT(1, 3&1);
84 | ASSERT(3, 7&3);
85 | ASSERT(10, -1&10);
86 |
87 | ASSERT(1, 0|1);
88 | ASSERT(0b10011, 0b10000|0b00011);
89 |
90 | ASSERT(0, 0^0);
91 | ASSERT(0, 0b1111^0b1111);
92 | ASSERT(0b110100, 0b111000^0b001100);
93 |
94 | ASSERT(2, ({ int i=6; i&=3; i; }));
95 | ASSERT(7, ({ int i=6; i|=3; i; }));
96 | ASSERT(10, ({ int i=15; i^=5; i; }));
97 |
98 | ASSERT(1, 1<<0);
99 | ASSERT(8, 1<<3);
100 | ASSERT(10, 5<<1);
101 | ASSERT(2, 5>>1);
102 | ASSERT(-1, -1>>1);
103 | ASSERT(1, ({ int i=1; i<<=0; i; }));
104 | ASSERT(8, ({ int i=1; i<<=3; i; }));
105 | ASSERT(10, ({ int i=5; i<<=1; i; }));
106 | ASSERT(2, ({ int i=5; i>>=1; i; }));
107 | ASSERT(-1, -1);
108 | ASSERT(-1, ({ int i=-1; i; }));
109 | ASSERT(-1, ({ int i=-1; i>>=1; i; }));
110 |
111 | ASSERT(2, 0?1:2);
112 | ASSERT(1, 1?1:2);
113 | ASSERT(-1, 0?-2:-1);
114 | ASSERT(-2, 1?-2:-1);
115 | ASSERT(4, sizeof(0?1:2));
116 | ASSERT(8, sizeof(0?(long)1:(long)2));
117 | ASSERT(-1, 0?(long)-2:-1);
118 | ASSERT(-1, 0?-2:(long)-1);
119 | ASSERT(-2, 1?(long)-2:-1);
120 | ASSERT(-2, 1?-2:(long)-1);
121 |
122 | 1 ? -2 : (void)-1;
123 |
124 | ASSERT(20, ({ int x; int *p=&x; p+20-p; }));
125 | ASSERT(1, ({ int x; int *p=&x; p+20-p>0; }));
126 | ASSERT(-20, ({ int x; int *p=&x; p-20-p; }));
127 | ASSERT(1, ({ int x; int *p=&x; p-20-p<0; }));
128 |
129 | ASSERT(15, (char *)0xffffffffffffffff - (char *)0xfffffffffffffff0);
130 | ASSERT(-15, (char *)0xfffffffffffffff0 - (char *)0xffffffffffffffff);
131 | ASSERT(1, (void *)0xffffffffffffffff > (void *)0);
132 |
133 | ASSERT(3, 3?:5);
134 | ASSERT(5, 0?:5);
135 | ASSERT(4, ({ int i = 3; ++i?:10; }));
136 |
137 | ASSERT(3, (long double)3);
138 | ASSERT(5, (long double)3+2);
139 | ASSERT(6, (long double)3*2);
140 | ASSERT(5, (long double)3+2.0);
141 |
142 | printf("OK\n");
143 | return 0;
144 | }
145 |
--------------------------------------------------------------------------------
/test/asm.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | char *asm_fn1(void) {
4 | asm("mov $50, %rax\n\t"
5 | "mov %rbp, %rsp\n\t"
6 | "pop %rbp\n\t"
7 | "ret");
8 | }
9 |
10 | char *asm_fn2(void) {
11 | asm inline volatile("mov $55, %rax\n\t"
12 | "mov %rbp, %rsp\n\t"
13 | "pop %rbp\n\t"
14 | "ret");
15 | }
16 |
17 | int main() {
18 | ASSERT(50, asm_fn1());
19 | ASSERT(55, asm_fn2());
20 |
21 | printf("OK\n");
22 | return 0;
23 | }
24 |
--------------------------------------------------------------------------------
/test/atomic.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 | #include
3 | #include
4 |
5 | static int incr(_Atomic int *p) {
6 | int oldval = *p;
7 | int newval;
8 | do {
9 | newval = oldval + 1;
10 | } while (!atomic_compare_exchange_weak(p, &oldval, newval));
11 | return newval;
12 | }
13 |
14 | static int add1(void *arg) {
15 | _Atomic int *x = arg;
16 | for (int i = 0; i < 1000*1000; i++)
17 | incr(x);
18 | return 0;
19 | }
20 |
21 | static int add2(void *arg) {
22 | _Atomic int *x = arg;
23 | for (int i = 0; i < 1000*1000; i++)
24 | (*x)++;
25 | return 0;
26 | }
27 |
28 | static int add3(void *arg) {
29 | _Atomic int *x = arg;
30 | for (int i = 0; i < 1000*1000; i++)
31 | *x += 5;
32 | return 0;
33 | }
34 |
35 | static int add_millions(void) {
36 | _Atomic int x = 0;
37 |
38 | pthread_t thr1;
39 | pthread_t thr2;
40 | pthread_t thr3;
41 |
42 | pthread_create(&thr1, NULL, add1, &x);
43 | pthread_create(&thr2, NULL, add2, &x);
44 | pthread_create(&thr3, NULL, add3, &x);
45 |
46 | for (int i = 0; i < 1000*1000; i++)
47 | x--;
48 |
49 | pthread_join(thr1, NULL);
50 | pthread_join(thr2, NULL);
51 | pthread_join(thr3, NULL);
52 | return x;
53 | }
54 |
55 | int main() {
56 | ASSERT(6*1000*1000, add_millions());
57 |
58 | ASSERT(3, ({ int x=3; atomic_exchange(&x, 5); }));
59 | ASSERT(5, ({ int x=3; atomic_exchange(&x, 5); x; }));
60 |
61 | printf("OK\n");
62 | return 0;
63 | }
64 |
--------------------------------------------------------------------------------
/test/attribute.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 | #include "stddef.h"
3 |
4 | int main() {
5 | ASSERT(5, ({ struct { char a; int b; } __attribute__((packed)) x; sizeof(x); }));
6 | ASSERT(0, offsetof(struct __attribute__((packed)) { char a; int b; }, a));
7 | ASSERT(1, offsetof(struct __attribute__((packed)) { char a; int b; }, b));
8 |
9 | ASSERT(5, ({ struct __attribute__((packed)) { char a; int b; } x; sizeof(x); }));
10 | ASSERT(0, offsetof(struct { char a; int b; } __attribute__((packed)), a));
11 | ASSERT(1, offsetof(struct { char a; int b; } __attribute__((packed)), b));
12 |
13 | ASSERT(9, ({ typedef struct { char a; int b[2]; } __attribute__((packed)) T; sizeof(T); }));
14 | ASSERT(9, ({ typedef struct __attribute__((packed)) { char a; int b[2]; } T; sizeof(T); }));
15 |
16 | ASSERT(1, offsetof(struct __attribute__((packed)) T { char a; int b[2]; }, b));
17 | ASSERT(1, _Alignof(struct __attribute__((packed)) { char a; int b[2]; }));
18 |
19 | ASSERT(8, ({ struct __attribute__((aligned(8))) { int a; } x; _Alignof(x); }));
20 | ASSERT(8, ({ struct { int a; } __attribute__((aligned(8))) x; _Alignof(x); }));
21 |
22 | ASSERT(8, ({ struct __attribute__((aligned(8), packed)) { char a; int b; } x; _Alignof(x); }));
23 | ASSERT(8, ({ struct { char a; int b; } __attribute__((aligned(8), packed)) x; _Alignof(x); }));
24 | ASSERT(1, offsetof(struct __attribute__((aligned(8), packed)) { char a; int b; }, b));
25 | ASSERT(1, offsetof(struct { char a; int b; } __attribute__((aligned(8), packed)), b));
26 |
27 | ASSERT(8, ({ struct __attribute__((aligned(8))) __attribute__((packed)) { char a; int b; } x; _Alignof(x); }));
28 | ASSERT(8, ({ struct { char a; int b; } __attribute__((aligned(8))) __attribute__((packed)) x; _Alignof(x); }));
29 | ASSERT(1, offsetof(struct __attribute__((aligned(8))) __attribute__((packed)) { char a; int b; }, b));
30 | ASSERT(1, offsetof(struct { char a; int b; } __attribute__((aligned(8))) __attribute__((packed)), b));
31 |
32 | ASSERT(8, ({ struct __attribute__((aligned(8))) { char a; int b; } __attribute__((packed)) x; _Alignof(x); }));
33 | ASSERT(1, offsetof(struct __attribute__((aligned(8))) { char a; int b; } __attribute__((packed)), b));
34 |
35 | ASSERT(16, ({ struct __attribute__((aligned(8+8))) { char a; int b; } x; _Alignof(x); }));
36 |
37 | printf("OK\n");
38 | return 0;
39 | }
40 |
--------------------------------------------------------------------------------
/test/bitfield.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | struct {
4 | char a;
5 | int b : 5;
6 | int c : 10;
7 | } g45 = {1, 2, 3}, g46={};
8 |
9 | int main() {
10 | ASSERT(4, sizeof(struct {int x:1; }));
11 | ASSERT(8, sizeof(struct {long x:1; }));
12 |
13 | struct bit1 {
14 | short a;
15 | char b;
16 | int c : 2;
17 | int d : 3;
18 | int e : 3;
19 | };
20 |
21 | ASSERT(4, sizeof(struct bit1));
22 | ASSERT(1, ({ struct bit1 x; x.a=1; x.b=2; x.c=3; x.d=4; x.e=5; x.a; }));
23 | ASSERT(1, ({ struct bit1 x={1,2,3,4,5}; x.a; }));
24 | ASSERT(2, ({ struct bit1 x={1,2,3,4,5}; x.b; }));
25 | ASSERT(-1, ({ struct bit1 x={1,2,3,4,5}; x.c; }));
26 | ASSERT(-4, ({ struct bit1 x={1,2,3,4,5}; x.d; }));
27 | ASSERT(-3, ({ struct bit1 x={1,2,3,4,5}; x.e; }));
28 |
29 | ASSERT(1, g45.a);
30 | ASSERT(2, g45.b);
31 | ASSERT(3, g45.c);
32 |
33 | ASSERT(0, g46.a);
34 | ASSERT(0, g46.b);
35 | ASSERT(0, g46.c);
36 |
37 | typedef struct {
38 | int a : 10;
39 | int b : 10;
40 | int c : 10;
41 | } T3;
42 |
43 | ASSERT(1, ({ T3 x={1,2,3}; x.a++; }));
44 | ASSERT(2, ({ T3 x={1,2,3}; x.b++; }));
45 | ASSERT(3, ({ T3 x={1,2,3}; x.c++; }));
46 |
47 | ASSERT(2, ({ T3 x={1,2,3}; ++x.a; }));
48 | ASSERT(3, ({ T3 x={1,2,3}; ++x.b; }));
49 | ASSERT(4, ({ T3 x={1,2,3}; ++x.c; }));
50 |
51 | ASSERT(4, sizeof(struct {int a:3; int c:1; int c:5;}));
52 | ASSERT(8, sizeof(struct {int a:3; int:0; int c:5;}));
53 | ASSERT(4, sizeof(struct {int a:3; int:0;}));
54 |
55 | printf("OK\n");
56 | return 0;
57 | }
58 |
--------------------------------------------------------------------------------
/test/builtin.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | ASSERT(1, __builtin_types_compatible_p(int, int));
5 | ASSERT(1, __builtin_types_compatible_p(double, double));
6 | ASSERT(0, __builtin_types_compatible_p(int, long));
7 | ASSERT(0, __builtin_types_compatible_p(long, float));
8 | ASSERT(1, __builtin_types_compatible_p(int *, int *));
9 | ASSERT(0, __builtin_types_compatible_p(short *, int *));
10 | ASSERT(0, __builtin_types_compatible_p(int **, int *));
11 | ASSERT(1, __builtin_types_compatible_p(const int, int));
12 | ASSERT(0, __builtin_types_compatible_p(unsigned, int));
13 | ASSERT(1, __builtin_types_compatible_p(signed, int));
14 | ASSERT(0, __builtin_types_compatible_p(struct {int a;}, struct {int a;}));
15 |
16 | ASSERT(1, __builtin_types_compatible_p(int (*)(void), int (*)(void)));
17 | ASSERT(1, __builtin_types_compatible_p(void (*)(int), void (*)(int)));
18 | ASSERT(1, __builtin_types_compatible_p(void (*)(int, double), void (*)(int, double)));
19 | ASSERT(1, __builtin_types_compatible_p(int (*)(float, double), int (*)(float, double)));
20 | ASSERT(0, __builtin_types_compatible_p(int (*)(float, double), int));
21 | ASSERT(0, __builtin_types_compatible_p(int (*)(float, double), int (*)(float)));
22 | ASSERT(0, __builtin_types_compatible_p(int (*)(float, double), int (*)(float, double, int)));
23 | ASSERT(1, __builtin_types_compatible_p(double (*)(...), double (*)(...)));
24 | ASSERT(0, __builtin_types_compatible_p(double (*)(...), double (*)(void)));
25 |
26 | ASSERT(1, ({ typedef struct {int a;} T; __builtin_types_compatible_p(T, T); }));
27 | ASSERT(1, ({ typedef struct {int a;} T; __builtin_types_compatible_p(T, const T); }));
28 |
29 | ASSERT(1, ({ struct {int a; int b;} x; __builtin_types_compatible_p(typeof(x.a), typeof(x.b)); }));
30 |
31 | printf("OK\n");
32 | return 0;
33 | }
34 |
--------------------------------------------------------------------------------
/test/cast.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | ASSERT(131585, (int)8590066177);
5 | ASSERT(513, (short)8590066177);
6 | ASSERT(1, (char)8590066177);
7 | ASSERT(1, (long)1);
8 | ASSERT(0, (long)&*(int *)0);
9 | ASSERT(513, ({ int x=512; *(char *)&x=1; x; }));
10 | ASSERT(5, ({ int x=5; long y=(long)&x; *(int*)y; }));
11 |
12 | (void)1;
13 |
14 | ASSERT(-1, (char)255);
15 | ASSERT(-1, (signed char)255);
16 | ASSERT(255, (unsigned char)255);
17 | ASSERT(-1, (short)65535);
18 | ASSERT(65535, (unsigned short)65535);
19 | ASSERT(-1, (int)0xffffffff);
20 | ASSERT(0xffffffff, (unsigned)0xffffffff);
21 |
22 | ASSERT(1, -1<1);
23 | ASSERT(0, -1<(unsigned)1);
24 | ASSERT(254, (char)127+(char)127);
25 | ASSERT(65534, (short)32767+(short)32767);
26 | ASSERT(-1, -1>>1);
27 | ASSERT(-1, (unsigned long)-1);
28 | ASSERT(2147483647, ((unsigned)-1)>>1);
29 | ASSERT(-50, (-100)/2);
30 | ASSERT(2147483598, ((unsigned)-100)/2);
31 | ASSERT(9223372036854775758, ((unsigned long)-100)/2);
32 | ASSERT(0, ((long)-1)/(unsigned)100);
33 | ASSERT(-2, (-100)%7);
34 | ASSERT(2, ((unsigned)-100)%7);
35 | ASSERT(6, ((unsigned long)-100)%9);
36 |
37 | ASSERT(65535, (int)(unsigned short)65535);
38 | ASSERT(65535, ({ unsigned short x = 65535; x; }));
39 | ASSERT(65535, ({ unsigned short x = 65535; (int)x; }));
40 |
41 | ASSERT(-1, ({ typedef short T; T x = 65535; (int)x; }));
42 | ASSERT(65535, ({ typedef unsigned short T; T x = 65535; (int)x; }));
43 |
44 | ASSERT(0, (_Bool)0.0);
45 | ASSERT(1, (_Bool)0.1);
46 | ASSERT(3, (char)3.0);
47 | ASSERT(1000, (short)1000.3);
48 | ASSERT(3, (int)3.99);
49 | ASSERT(2000000000000000, (long)2e15);
50 | ASSERT(3, (float)3.5);
51 | ASSERT(5, (double)(float)5.5);
52 | ASSERT(3, (float)3);
53 | ASSERT(3, (double)3);
54 | ASSERT(3, (float)3L);
55 | ASSERT(3, (double)3L);
56 |
57 | printf("OK\n");
58 | return 0;
59 | }
60 |
--------------------------------------------------------------------------------
/test/common:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | void assert(int expected, int actual, char *code) {
6 | if (expected == actual) {
7 | printf("%s => %d\n", code, actual);
8 | } else {
9 | printf("%s => %d expected but got %d\n", code, expected, actual);
10 | exit(1);
11 | }
12 | }
13 |
14 | static int static_fn() { return 5; }
15 | int ext1 = 5;
16 | int *ext2 = &ext1;
17 | int ext3 = 7;
18 | int ext_fn1(int x) { return x; }
19 | int ext_fn2(int x) { return x; }
20 | int common_ext2 = 3;
21 | static int common_local;
22 |
23 | int false_fn() { return 512; }
24 | int true_fn() { return 513; }
25 | int char_fn() { return (2<<8)+3; }
26 | int short_fn() { return (2<<16)+5; }
27 |
28 | int uchar_fn() { return (2<<10)-1-4; }
29 | int ushort_fn() { return (2<<20)-1-7; }
30 |
31 | int schar_fn() { return (2<<10)-1-4; }
32 | int sshort_fn() { return (2<<20)-1-7; }
33 |
34 | int add_all(int n, ...) {
35 | va_list ap;
36 | va_start(ap, n);
37 |
38 | int sum = 0;
39 | for (int i = 0; i < n; i++)
40 | sum += va_arg(ap, int);
41 | return sum;
42 | }
43 |
44 | float add_float(float x, float y) {
45 | return x + y;
46 | }
47 |
48 | double add_double(double x, double y) {
49 | return x + y;
50 | }
51 |
52 | int add10_int(int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10) {
53 | return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10;
54 | }
55 |
56 | float add10_float(float x1, float x2, float x3, float x4, float x5, float x6, float x7, float x8, float x9, float x10) {
57 | return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10;
58 | }
59 |
60 | double add10_double(double x1, double x2, double x3, double x4, double x5, double x6, double x7, double x8, double x9, double x10) {
61 | return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10;
62 | }
63 |
64 | typedef struct { int a,b; short c; char d; } Ty4;
65 | typedef struct { int a; float b; double c; } Ty5;
66 | typedef struct { unsigned char a[3]; } Ty6;
67 | typedef struct { long a, b, c; } Ty7;
68 |
69 | int struct_test4(Ty4 x, int n) {
70 | switch (n) {
71 | case 0: return x.a;
72 | case 1: return x.b;
73 | case 2: return x.c;
74 | default: return x.d;
75 | }
76 | }
77 |
78 | int struct_test5(Ty5 x, int n) {
79 | switch (n) {
80 | case 0: return x.a;
81 | case 1: return x.b;
82 | default: return x.c;
83 | }
84 | }
85 |
86 | int struct_test6(Ty6 x, int n) {
87 | return x.a[n];
88 | }
89 |
90 | int struct_test7(Ty7 x, int n) {
91 | switch (n) {
92 | case 0: return x.a;
93 | case 1: return x.b;
94 | default: return x.c;
95 | }
96 | }
97 |
98 | Ty4 struct_test24(void) {
99 | return (Ty4){10, 20, 30, 40};
100 | }
101 |
102 | Ty5 struct_test25(void) {
103 | return (Ty5){10, 20, 30};
104 | }
105 |
106 | Ty6 struct_test26(void) {
107 | return (Ty6){10, 20, 30};
108 | }
109 |
110 | typedef struct { unsigned char a[10]; } Ty20;
111 | typedef struct { unsigned char a[20]; } Ty21;
112 |
113 | Ty20 struct_test27(void) {
114 | return (Ty20){10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
115 | }
116 |
117 | Ty21 struct_test28(void) {
118 | return (Ty21){1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
119 | }
120 |
--------------------------------------------------------------------------------
/test/commonsym.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int x;
4 | int x = 5;
5 | int y = 7;
6 | int y;
7 | int common_ext1;
8 | int common_ext2;
9 | static int common_local;
10 |
11 | int main() {
12 | ASSERT(5, x);
13 | ASSERT(7, y);
14 | ASSERT(0, common_ext1);
15 | ASSERT(3, common_ext2);
16 |
17 | printf("OK\n");
18 | return 0;
19 | }
20 |
--------------------------------------------------------------------------------
/test/compat.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | _Noreturn noreturn_fn(int restrict x) {
4 | exit(0);
5 | }
6 |
7 | void funcy_type(int arg[restrict static 3]) {}
8 |
9 | int main() {
10 | { volatile x; }
11 | { int volatile x; }
12 | { volatile int x; }
13 | { volatile int volatile volatile x; }
14 | { int volatile * volatile volatile x; }
15 | { auto ** restrict __restrict __restrict__ const volatile *x; }
16 |
17 | printf("OK\n");
18 | return 0;
19 | }
20 |
--------------------------------------------------------------------------------
/test/complit.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | typedef struct Tree {
4 | int val;
5 | struct Tree *lhs;
6 | struct Tree *rhs;
7 | } Tree;
8 |
9 | Tree *tree = &(Tree){
10 | 1,
11 | &(Tree){
12 | 2,
13 | &(Tree){ 3, 0, 0 },
14 | &(Tree){ 4, 0, 0 }
15 | },
16 | 0
17 | };
18 |
19 | int main() {
20 | ASSERT(1, (int){1});
21 | ASSERT(2, ((int[]){0,1,2})[2]);
22 | ASSERT('a', ((struct {char a; int b;}){'a', 3}).a);
23 | ASSERT(3, ({ int x=3; (int){x}; }));
24 | (int){3} = 5;
25 |
26 | ASSERT(1, tree->val);
27 | ASSERT(2, tree->lhs->val);
28 | ASSERT(3, tree->lhs->lhs->val);
29 | ASSERT(4, tree->lhs->rhs->val);
30 |
31 | printf("OK\n");
32 | return 0;
33 | }
34 |
--------------------------------------------------------------------------------
/test/const.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | { const x; }
5 | { int const x; }
6 | { const int x; }
7 | { const int const const x; }
8 | ASSERT(5, ({ const x = 5; x; }));
9 | ASSERT(8, ({ const x = 8; int *const y=&x; *y; }));
10 | ASSERT(6, ({ const x = 6; *(const * const)&x; }));
11 |
12 | printf("OK\n");
13 | return 0;
14 | }
15 |
--------------------------------------------------------------------------------
/test/constexpr.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | float g40 = 1.5;
4 | double g41 = 0.0 ? 55 : (0, 1 + 1 * 5.0 / 2 * (double)2 * (int)2.0);
5 |
6 | int main() {
7 | ASSERT(10, ({ enum { ten=1+2+3+4 }; ten; }));
8 | ASSERT(1, ({ int i=0; switch(3) { case 5-2+0*3: i++; } i; }));
9 | ASSERT(8, ({ int x[1+1]; sizeof(x); }));
10 | ASSERT(6, ({ char x[8-2]; sizeof(x); }));
11 | ASSERT(6, ({ char x[2*3]; sizeof(x); }));
12 | ASSERT(3, ({ char x[12/4]; sizeof(x); }));
13 | ASSERT(2, ({ char x[12%10]; sizeof(x); }));
14 | ASSERT(0b100, ({ char x[0b110&0b101]; sizeof(x); }));
15 | ASSERT(0b111, ({ char x[0b110|0b101]; sizeof(x); }));
16 | ASSERT(0b110, ({ char x[0b111^0b001]; sizeof(x); }));
17 | ASSERT(4, ({ char x[1<<2]; sizeof(x); }));
18 | ASSERT(2, ({ char x[4>>1]; sizeof(x); }));
19 | ASSERT(2, ({ char x[(1==1)+1]; sizeof(x); }));
20 | ASSERT(1, ({ char x[(1!=1)+1]; sizeof(x); }));
21 | ASSERT(1, ({ char x[(1<1)+1]; sizeof(x); }));
22 | ASSERT(2, ({ char x[(1<=1)+1]; sizeof(x); }));
23 | ASSERT(2, ({ char x[1?2:3]; sizeof(x); }));
24 | ASSERT(3, ({ char x[0?2:3]; sizeof(x); }));
25 | ASSERT(3, ({ char x[(1,3)]; sizeof(x); }));
26 | ASSERT(2, ({ char x[!0+1]; sizeof(x); }));
27 | ASSERT(1, ({ char x[!1+1]; sizeof(x); }));
28 | ASSERT(2, ({ char x[~-3]; sizeof(x); }));
29 | ASSERT(2, ({ char x[(5||6)+1]; sizeof(x); }));
30 | ASSERT(1, ({ char x[(0||0)+1]; sizeof(x); }));
31 | ASSERT(2, ({ char x[(1&&1)+1]; sizeof(x); }));
32 | ASSERT(1, ({ char x[(1&&0)+1]; sizeof(x); }));
33 | ASSERT(3, ({ char x[(int)3]; sizeof(x); }));
34 | ASSERT(15, ({ char x[(char)0xffffff0f]; sizeof(x); }));
35 | ASSERT(0x10f, ({ char x[(short)0xffff010f]; sizeof(x); }));
36 | ASSERT(4, ({ char x[(int)0xfffffffffff+5]; sizeof(x); }));
37 | ASSERT(8, ({ char x[(int*)0+2]; sizeof(x); }));
38 | ASSERT(12, ({ char x[(int*)16-1]; sizeof(x); }));
39 | ASSERT(3, ({ char x[(int*)16-(int*)4]; sizeof(x); }));
40 |
41 | ASSERT(4, ({ char x[(-1>>31)+5]; sizeof(x); }));
42 | ASSERT(255, ({ char x[(unsigned char)0xffffffff]; sizeof(x); }));
43 | ASSERT(0x800f, ({ char x[(unsigned short)0xffff800f]; sizeof(x); }));
44 | ASSERT(1, ({ char x[(unsigned int)0xfffffffffff>>31]; sizeof(x); }));
45 | ASSERT(1, ({ char x[(long)-1/((long)1<<62)+1]; sizeof(x); }));
46 | ASSERT(4, ({ char x[(unsigned long)-1/((long)1<<62)+1]; sizeof(x); }));
47 | ASSERT(1, ({ char x[(unsigned)1<-1]; sizeof(x); }));
48 | ASSERT(1, ({ char x[(unsigned)1<=-1]; sizeof(x); }));
49 |
50 | ASSERT(1, g40==1.5);
51 | ASSERT(1, g41==11);
52 |
53 | printf("OK\n");
54 | return 0;
55 | }
56 |
--------------------------------------------------------------------------------
/test/control.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | /*
4 | * This is a block comment.
5 | */
6 |
7 | int main() {
8 | ASSERT(3, ({ int x; if (0) x=2; else x=3; x; }));
9 | ASSERT(3, ({ int x; if (1-1) x=2; else x=3; x; }));
10 | ASSERT(2, ({ int x; if (1) x=2; else x=3; x; }));
11 | ASSERT(2, ({ int x; if (2-1) x=2; else x=3; x; }));
12 |
13 | ASSERT(55, ({ int i=0; int j=0; for (i=0; i<=10; i=i+1) j=i+j; j; }));
14 |
15 | ASSERT(10, ({ int i=0; while(i<10) i=i+1; i; }));
16 |
17 | ASSERT(3, ({ 1; {2;} 3; }));
18 | ASSERT(5, ({ ;;; 5; }));
19 |
20 | ASSERT(10, ({ int i=0; while(i<10) i=i+1; i; }));
21 | ASSERT(55, ({ int i=0; int j=0; while(i<=10) {j=i+j; i=i+1;} j; }));
22 |
23 | ASSERT(3, (1,2,3));
24 | ASSERT(5, ({ int i=2, j=3; (i=5,j)=6; i; }));
25 | ASSERT(6, ({ int i=2, j=3; (i=5,j)=6; j; }));
26 |
27 | ASSERT(55, ({ int j=0; for (int i=0; i<=10; i=i+1) j=j+i; j; }));
28 | ASSERT(3, ({ int i=3; int j=0; for (int i=0; i<=10; i=i+1) j=j+i; i; }));
29 |
30 | ASSERT(1, 0||1);
31 | ASSERT(1, 0||(2-2)||5);
32 | ASSERT(0, 0||0);
33 | ASSERT(0, 0||(2-2));
34 |
35 | ASSERT(0, 0&&1);
36 | ASSERT(0, (2-2)&&5);
37 | ASSERT(1, 1&&5);
38 |
39 | ASSERT(3, ({ int i=0; goto a; a: i++; b: i++; c: i++; i; }));
40 | ASSERT(2, ({ int i=0; goto e; d: i++; e: i++; f: i++; i; }));
41 | ASSERT(1, ({ int i=0; goto i; g: i++; h: i++; i: i++; i; }));
42 |
43 | ASSERT(1, ({ typedef int foo; goto foo; foo:; 1; }));
44 |
45 | ASSERT(3, ({ int i=0; for(;i<10;i++) { if (i == 3) break; } i; }));
46 | ASSERT(4, ({ int i=0; while (1) { if (i++ == 3) break; } i; }));
47 | ASSERT(3, ({ int i=0; for(;i<10;i++) { for (;;) break; if (i == 3) break; } i; }));
48 | ASSERT(4, ({ int i=0; while (1) { while(1) break; if (i++ == 3) break; } i; }));
49 |
50 | ASSERT(10, ({ int i=0; int j=0; for (;i<10;i++) { if (i>5) continue; j++; } i; }));
51 | ASSERT(6, ({ int i=0; int j=0; for (;i<10;i++) { if (i>5) continue; j++; } j; }));
52 | ASSERT(10, ({ int i=0; int j=0; for(;!i;) { for (;j!=10;j++) continue; break; } j; }));
53 | ASSERT(11, ({ int i=0; int j=0; while (i++<10) { if (i>5) continue; j++; } i; }));
54 | ASSERT(5, ({ int i=0; int j=0; while (i++<10) { if (i>5) continue; j++; } j; }));
55 | ASSERT(11, ({ int i=0; int j=0; while(!i) { while (j++!=10) continue; break; } j; }));
56 |
57 | ASSERT(5, ({ int i=0; switch(0) { case 0:i=5;break; case 1:i=6;break; case 2:i=7;break; } i; }));
58 | ASSERT(6, ({ int i=0; switch(1) { case 0:i=5;break; case 1:i=6;break; case 2:i=7;break; } i; }));
59 | ASSERT(7, ({ int i=0; switch(2) { case 0:i=5;break; case 1:i=6;break; case 2:i=7;break; } i; }));
60 | ASSERT(0, ({ int i=0; switch(3) { case 0:i=5;break; case 1:i=6;break; case 2:i=7;break; } i; }));
61 | ASSERT(5, ({ int i=0; switch(0) { case 0:i=5;break; default:i=7; } i; }));
62 | ASSERT(7, ({ int i=0; switch(1) { case 0:i=5;break; default:i=7; } i; }));
63 | ASSERT(2, ({ int i=0; switch(1) { case 0: 0; case 1: 0; case 2: 0; i=2; } i; }));
64 | ASSERT(0, ({ int i=0; switch(3) { case 0: 0; case 1: 0; case 2: 0; i=2; } i; }));
65 |
66 | ASSERT(3, ({ int i=0; switch(-1) { case 0xffffffff: i=3; break; } i; }));
67 |
68 | ASSERT(7, ({ int i=0; int j=0; do { j++; } while (i++ < 6); j; }));
69 | ASSERT(4, ({ int i=0; int j=0; int k=0; do { if (++j > 3) break; continue; k++; } while (1); j; }));
70 |
71 | ASSERT(0, 0.0 && 0.0);
72 | ASSERT(0, 0.0 && 0.1);
73 | ASSERT(0, 0.3 && 0.0);
74 | ASSERT(1, 0.3 && 0.5);
75 | ASSERT(0, 0.0 || 0.0);
76 | ASSERT(1, 0.0 || 0.1);
77 | ASSERT(1, 0.3 || 0.0);
78 | ASSERT(1, 0.3 || 0.5);
79 | ASSERT(5, ({ int x; if (0.0) x=3; else x=5; x; }));
80 | ASSERT(3, ({ int x; if (0.1) x=3; else x=5; x; }));
81 | ASSERT(5, ({ int x=5; if (0.0) x=3; x; }));
82 | ASSERT(3, ({ int x=5; if (0.1) x=3; x; }));
83 | ASSERT(10, ({ double i=10.0; int j=0; for (; i; i--, j++); j; }));
84 | ASSERT(10, ({ double i=10.0; int j=0; do j++; while(--i); j; }));
85 |
86 | ASSERT(2, ({ int i=0; switch(7) { case 0 ... 5: i=1; break; case 6 ... 20: i=2; break; } i; }));
87 | ASSERT(1, ({ int i=0; switch(7) { case 0 ... 7: i=1; break; case 8 ... 10: i=2; break; } i; }));
88 | ASSERT(1, ({ int i=0; switch(7) { case 0: i=1; break; case 7 ... 7: i=1; break; } i; }));
89 |
90 | ASSERT(3, ({ void *p = &&v11; int i=0; goto *p; v11:i++; v12:i++; v13:i++; i; }));
91 | ASSERT(2, ({ void *p = &&v22; int i=0; goto *p; v21:i++; v22:i++; v23:i++; i; }));
92 | ASSERT(1, ({ void *p = &&v33; int i=0; goto *p; v31:i++; v32:i++; v33:i++; i; }));
93 |
94 | ASSERT(3, ({ static void *p[]={&&v41,&&v42,&&v43}; int i=0; goto *p[0]; v41:i++; v42:i++; v43:i++; i; }));
95 | ASSERT(2, ({ static void *p[]={&&v52,&&v52,&&v53}; int i=0; goto *p[1]; v51:i++; v52:i++; v53:i++; i; }));
96 | ASSERT(1, ({ static void *p[]={&&v62,&&v62,&&v63}; int i=0; goto *p[2]; v61:i++; v62:i++; v63:i++; i; }));
97 |
98 | printf("OK\n");
99 | return 0;
100 | }
101 |
--------------------------------------------------------------------------------
/test/decl.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | ASSERT(1, ({ char x; sizeof(x); }));
5 | ASSERT(2, ({ short int x; sizeof(x); }));
6 | ASSERT(2, ({ int short x; sizeof(x); }));
7 | ASSERT(4, ({ int x; sizeof(x); }));
8 | ASSERT(8, ({ long int x; sizeof(x); }));
9 | ASSERT(8, ({ int long x; sizeof(x); }));
10 |
11 | ASSERT(8, ({ long long x; sizeof(x); }));
12 |
13 | ASSERT(0, ({ _Bool x=0; x; }));
14 | ASSERT(1, ({ _Bool x=1; x; }));
15 | ASSERT(1, ({ _Bool x=2; x; }));
16 | ASSERT(1, (_Bool)1);
17 | ASSERT(1, (_Bool)2);
18 | ASSERT(0, (_Bool)(char)256);
19 |
20 | printf("OK\n");
21 | return 0;
22 | }
23 |
--------------------------------------------------------------------------------
/test/driver.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | xcc=$1
3 |
4 | tmp=`mktemp -d /tmp/xcc-test-XXXXXX`
5 | trap 'rm -rf $tmp' INT TERM HUP EXIT
6 | echo > $tmp/empty.c
7 |
8 | check() {
9 | if [ $? -eq 0 ]; then
10 | echo "testing $1 ... passed"
11 | else
12 | echo "testing $1 ... failed"
13 | exit 1
14 | fi
15 | }
16 |
17 | # -o
18 | rm -f $tmp/out
19 | ./xcc -c -o $tmp/out $tmp/empty.c
20 | [ -f $tmp/out ]
21 | check -o out
22 |
23 | rm -f $tmp/out
24 | ./xcc -c -o$tmp/out $tmp/empty.c
25 | [ -f $tmp/out ]
26 | check -oout
27 |
28 | # --help
29 | $xcc --help 2>&1 | grep -q xc
30 | check --help
31 |
32 | # -S
33 | echo 'int main() {}' | $xcc -S -o- -xc - | grep -q 'main:'
34 | check -S
35 |
36 | # Default output file
37 | rm -f $tmp/out.o $tmp/out.s
38 | echo 'int main() {}' > $tmp/out.c
39 | (cd $tmp; $OLDPWD/$xcc -c out.c)
40 | [ -f $tmp/out.o ]
41 | check 'default output file'
42 |
43 | (cd $tmp; $OLDPWD/$xcc -c -S out.c)
44 | [ -f $tmp/out.s ]
45 | check 'default output file'
46 |
47 | # Multiple input files
48 | rm -f $tmp/foo.o $tmp/bar.o
49 | echo 'int x;' > $tmp/foo.c
50 | echo 'int y;' > $tmp/bar.c
51 | (cd $tmp; $OLDPWD/$xcc -c $tmp/foo.c $tmp/bar.c)
52 | [ -f $tmp/foo.o ] && [ -f $tmp/bar.o ]
53 | check 'multiple input files'
54 |
55 | rm -f $tmp/foo.s $tmp/bar.s
56 | echo 'int x;' > $tmp/foo.c
57 | echo 'int y;' > $tmp/bar.c
58 | (cd $tmp; $OLDPWD/$xcc -c -S $tmp/foo.c $tmp/bar.c)
59 | [ -f $tmp/foo.s ] && [ -f $tmp/bar.s ]
60 | check 'multiple input files'
61 |
62 | # Run linker
63 | rm -f $tmp/foo
64 | echo 'int main() { return 0; }' | $xcc -o $tmp/foo -xc -xc -
65 | $tmp/foo
66 | check linker
67 |
68 | rm -f $tmp/foo
69 | echo 'int bar(); int main() { return bar(); }' > $tmp/foo.c
70 | echo 'int bar() { return 42; }' > $tmp/bar.c
71 | $xcc -o $tmp/foo $tmp/foo.c $tmp/bar.c
72 | $tmp/foo
73 | [ "$?" = 42 ]
74 | check linker
75 |
76 | # a.out
77 | rm -f $tmp/a.out
78 | echo 'int main() {}' > $tmp/foo.c
79 | (cd $tmp; $OLDPWD/$xcc foo.c)
80 | [ -f $tmp/a.out ]
81 | check a.out
82 |
83 | # -E
84 | echo foo > $tmp/out
85 | echo "#include \"$tmp/out\"" | $xcc -E -xc - | grep -q foo
86 | check -E
87 |
88 | echo foo > $tmp/out1
89 | echo "#include \"$tmp/out1\"" | $xcc -E -o $tmp/out2 -xc -
90 | cat $tmp/out2 | grep -q foo
91 | check '-E and -o'
92 |
93 | # -I
94 | mkdir $tmp/dir
95 | echo foo > $tmp/dir/i-option-test
96 | echo "#include \"i-option-test\"" | $xcc -I$tmp/dir -E -xc - | grep -q foo
97 | check -I
98 |
99 | # -D
100 | echo foo | $xcc -Dfoo -E -xc - | grep -q 1
101 | check -D
102 |
103 | # -D
104 | echo foo | $xcc -Dfoo=bar -E -xc - | grep -q bar
105 | check -D
106 |
107 | # -U
108 | echo foo | $xcc -Dfoo=bar -Ufoo -E -xc - | grep -q foo
109 | check -U
110 |
111 | # ignored options
112 | $xcc -c -O -Wall -g -std=c11 -ffreestanding -fno-builtin \
113 | -fno-omit-frame-pointer -fno-stack-protector -fno-strict-aliasing \
114 | -m64 -mno-red-zone -w -o /dev/null $tmp/empty.c
115 | check 'ignored options'
116 |
117 | # BOM marker
118 | printf '\xef\xbb\xbfxyz\n' | $xcc -E -o- -xc - | grep -q '^xyz'
119 | check 'BOM marker'
120 |
121 | # Inline functions
122 | echo 'inline void foo() {}' > $tmp/inline1.c
123 | echo 'inline void foo() {}' > $tmp/inline2.c
124 | echo 'int main() { return 0; }' > $tmp/inline3.c
125 | $xcc -o /dev/null $tmp/inline1.c $tmp/inline2.c $tmp/inline3.c
126 | check inline
127 |
128 | echo 'extern inline void foo() {}' > $tmp/inline1.c
129 | echo 'int foo(); int main() { foo(); }' > $tmp/inline2.c
130 | $xcc -o /dev/null $tmp/inline1.c $tmp/inline2.c
131 | check inline
132 |
133 | echo 'static inline void f1() {}' | $xcc -o- -S -xc - | grep -v -q f1:
134 | check inline
135 |
136 | echo 'static inline void f1() {} void foo() { f1(); }' | $xcc -o- -S -xc - | grep -q f1:
137 | check inline
138 |
139 | echo 'static inline void f1() {} static inline void f2() { f1(); } void foo() { f1(); }' | $xcc -o- -S -xc - | grep -q f1:
140 | check inline
141 |
142 | echo 'static inline void f1() {} static inline void f2() { f1(); } void foo() { f1(); }' | $xcc -o- -S -xc - | grep -v -q f2:
143 | check inline
144 |
145 | echo 'static inline void f1() {} static inline void f2() { f1(); } void foo() { f2(); }' | $xcc -o- -S -xc - | grep -q f1:
146 | check inline
147 |
148 | echo 'static inline void f1() {} static inline void f2() { f1(); } void foo() { f2(); }' | $xcc -o- -S -xc - | grep -q f2:
149 | check inline
150 |
151 | echo 'static inline void f2(); static inline void f1() { f2(); } static inline void f2() { f1(); } void foo() {}' | $xcc -o- -S -xc - | grep -v -q f1:
152 | check inline
153 |
154 | echo 'static inline void f2(); static inline void f1() { f2(); } static inline void f2() { f1(); } void foo() {}' | $xcc -o- -S -xc - | grep -v -q f2:
155 | check inline
156 |
157 | echo 'static inline void f2(); static inline void f1() { f2(); } static inline void f2() { f1(); } void foo() { f1(); }' | $xcc -o- -S -xc - | grep -q f1:
158 | check inline
159 |
160 | echo 'static inline void f2(); static inline void f1() { f2(); } static inline void f2() { f1(); } void foo() { f1(); }' | $xcc -o- -S -xc - | grep -q f2:
161 | check inline
162 |
163 | echo 'static inline void f2(); static inline void f1() { f2(); } static inline void f2() { f1(); } void foo() { f2(); }' | $xcc -o- -S -xc - | grep -q f1:
164 | check inline
165 |
166 | echo 'static inline void f2(); static inline void f1() { f2(); } static inline void f2() { f1(); } void foo() { f2(); }' | $xcc -o- -S -xc - | grep -q f2:
167 | check inline
168 |
169 | # -idirafter
170 | mkdir -p $tmp/dir1 $tmp/dir2
171 | echo foo > $tmp/dir1/idirafter
172 | echo bar > $tmp/dir2/idirafter
173 | echo "#include \"idirafter\"" | $xcc -I$tmp/dir1 -I$tmp/dir2 -E -xc - | grep -q foo
174 | check -idirafter
175 | echo "#include \"idirafter\"" | $xcc -idirafter $tmp/dir1 -I$tmp/dir2 -E -xc - | grep -q bar
176 | check -idirafter
177 |
178 | # -fcommon
179 | echo 'int foo;' | $xcc -S -o- -xc - | grep -q '\.comm foo'
180 | check '-fcommon (default)'
181 |
182 | echo 'int foo;' | $xcc -fcommon -S -o- -xc - | grep -q '\.comm foo'
183 | check '-fcommon'
184 |
185 | # -fno-common
186 | echo 'int foo;' | $xcc -fno-common -S -o- -xc - | grep -q '^foo:'
187 | check '-fno-common'
188 |
189 | # -include
190 | echo foo > $tmp/out.h
191 | echo bar | $xcc -include $tmp/out.h -E -o- -xc - | grep -q -z 'foo.*bar'
192 | check -include
193 | echo NULL | $xcc -Iinclude -include stdio.h -E -o- -xc - | grep -q 0
194 | check -include
195 |
196 | # -x
197 | echo 'int x;' | $xcc -c -xc -o $tmp/foo.o -
198 | check -xc
199 | echo 'x:' | $xcc -c -x assembler -o $tmp/foo.o -
200 | check '-x assembler'
201 |
202 | echo 'int x;' > $tmp/foo.c
203 | $xcc -c -x assembler -x none -o $tmp/foo.o $tmp/foo.c
204 | check '-x none'
205 |
206 | # -E
207 | echo foo | $xcc -E - | grep -q foo
208 | check -E
209 |
210 | # .a file
211 | echo 'void foo() {}' | $xcc -c -xc -o $tmp/foo.o -
212 | echo 'void bar() {}' | $xcc -c -xc -o $tmp/bar.o -
213 | ar rcs $tmp/foo.a $tmp/foo.o $tmp/bar.o
214 | echo 'void foo(); void bar(); int main() { foo(); bar(); }' > $tmp/main.c
215 | $xcc -o $tmp/foo $tmp/main.c $tmp/foo.a
216 | check '.a'
217 |
218 | # .so file
219 | echo 'void foo() {}' | cc -fPIC -c -xc -o $tmp/foo.o -
220 | echo 'void bar() {}' | cc -fPIC -c -xc -o $tmp/bar.o -
221 | cc -shared -o $tmp/foo.so $tmp/foo.o $tmp/bar.o
222 | echo 'void foo(); void bar(); int main() { foo(); bar(); }' > $tmp/main.c
223 | $xcc -o $tmp/foo $tmp/main.c $tmp/foo.so
224 | check '.so'
225 |
226 | $xcc -hashmap-test
227 | check 'hashmap'
228 |
229 | # -M
230 | echo '#include "out2.h"' > $tmp/out.c
231 | echo '#include "out3.h"' >> $tmp/out.c
232 | touch $tmp/out2.h $tmp/out3.h
233 | $xcc -M -I$tmp $tmp/out.c | grep -q -z '^out.o: .*/out\.c .*/out2\.h .*/out3\.h'
234 | check -M
235 |
236 | # -MF
237 | $xcc -MF $tmp/mf -M -I$tmp $tmp/out.c
238 | grep -q -z '^out.o: .*/out\.c .*/out2\.h .*/out3\.h' $tmp/mf
239 | check -MF
240 |
241 | # -MP
242 | $xcc -MF $tmp/mp -MP -M -I$tmp $tmp/out.c
243 | grep -q '^.*/out2.h:' $tmp/mp
244 | check -MP
245 | grep -q '^.*/out3.h:' $tmp/mp
246 | check -MP
247 |
248 | # -MT
249 | $xcc -MT foo -M -I$tmp $tmp/out.c | grep -q '^foo:'
250 | check -MT
251 | $xcc -MT foo -MT bar -M -I$tmp $tmp/out.c | grep -q '^foo bar:'
252 | check -MT
253 |
254 | # -MD
255 | echo '#include "out2.h"' > $tmp/md2.c
256 | echo '#include "out3.h"' > $tmp/md3.c
257 | (cd $tmp; $OLDPWD/$xcc -c -MD -I. md2.c md3.c)
258 | grep -q -z '^md2.o:.* md2\.c .* ./out2\.h' $tmp/md2.d
259 | check -MD
260 | grep -q -z '^md3.o:.* md3\.c .* ./out3\.h' $tmp/md3.d
261 | check -MD
262 |
263 | $xcc -c -MD -MF $tmp/md-mf.d -I. $tmp/md2.c
264 | grep -q -z '^md2.o:.*md2\.c .*/out2\.h' $tmp/md-mf.d
265 | check -MD
266 |
267 | echo 'extern int bar; int foo() { return bar; }' | $xcc -fPIC -xc -c -o $tmp/foo.o -
268 | cc -shared -o $tmp/foo.so $tmp/foo.o
269 | echo 'int foo(); int bar=3; int main() { foo(); }' > $tmp/main.c
270 | $xcc -o $tmp/foo $tmp/main.c $tmp/foo.so
271 | check -fPIC
272 |
273 | # #include_next
274 | mkdir -p $tmp/next1 $tmp/next2 $tmp/next3
275 | echo '#include "file1.h"' > $tmp/file.c
276 | echo '#include_next "file1.h"' > $tmp/next1/file1.h
277 | echo '#include_next "file2.h"' > $tmp/next2/file1.h
278 | echo 'foo' > $tmp/next3/file2.h
279 | $xcc -I$tmp/next1 -I$tmp/next2 -I$tmp/next3 -E $tmp/file.c | grep -q foo
280 | check '#include_next'
281 |
282 | # -static
283 | echo 'extern int bar; int foo() { return bar; }' > $tmp/foo.c
284 | echo 'int foo(); int bar=3; int main() { foo(); }' > $tmp/bar.c
285 | $xcc -static -o $tmp/foo $tmp/foo.c $tmp/bar.c
286 | check -static
287 | file $tmp/foo | grep -q 'statically linked'
288 | check -static
289 |
290 | # -shared
291 | echo 'extern int bar; int foo() { return bar; }' > $tmp/foo.c
292 | echo 'int foo(); int bar=3; int main() { foo(); }' > $tmp/bar.c
293 | $xcc -fPIC -shared -o $tmp/foo.so $tmp/foo.c $tmp/bar.c
294 | check -shared
295 |
296 | # -L
297 | echo 'extern int bar; int foo() { return bar; }' > $tmp/foo.c
298 | $xcc -fPIC -shared -o $tmp/libfoobar.so $tmp/foo.c
299 | echo 'int foo(); int bar=3; int main() { foo(); }' > $tmp/bar.c
300 | $xcc -o $tmp/foo $tmp/bar.c -L$tmp -lfoobar
301 | check -L
302 |
303 | # -Wl,
304 | echo 'int foo() {}' | $xcc -c -o $tmp/foo.o -xc -
305 | echo 'int foo() {}' | $xcc -c -o $tmp/bar.o -xc -
306 | echo 'int main() {}' | $xcc -c -o $tmp/baz.o -xc -
307 | cc -Wl,-z,muldefs,--gc-sections -o $tmp/foo $tmp/foo.o $tmp/bar.o $tmp/baz.o
308 | check -Wl,
309 |
310 | # -Xlinker
311 | echo 'int foo() {}' | $xcc -c -o $tmp/foo.o -xc -
312 | echo 'int foo() {}' | $xcc -c -o $tmp/bar.o -xc -
313 | echo 'int main() {}' | $xcc -c -o $tmp/baz.o -xc -
314 | cc -Xlinker -z -Xlinker muldefs -Xlinker --gc-sections -o $tmp/foo $tmp/foo.o $tmp/bar.o $tmp/baz.o
315 | check -Xlinker
316 |
317 | # -soname
318 | echo 'int main() { return 0; }' | $xcc -c -o $tmp/foo.o -xc -
319 | $xcc -shared -soname libfoo.so.0 -o $tmp/libfoo.so $tmp/foo.o
320 | check -soname
321 |
322 | echo OK
323 |
--------------------------------------------------------------------------------
/test/enum.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | ASSERT(0, ({ enum { zero, one, two }; zero; }));
5 | ASSERT(1, ({ enum { zero, one, two }; one; }));
6 | ASSERT(2, ({ enum { zero, one, two }; two; }));
7 | ASSERT(5, ({ enum { five=5, six, seven }; five; }));
8 | ASSERT(6, ({ enum { five=5, six, seven }; six; }));
9 | ASSERT(0, ({ enum { zero, five=5, three=3, four }; zero; }));
10 | ASSERT(5, ({ enum { zero, five=5, three=3, four }; five; }));
11 | ASSERT(3, ({ enum { zero, five=5, three=3, four }; three; }));
12 | ASSERT(4, ({ enum { zero, five=5, three=3, four }; four; }));
13 | ASSERT(4, ({ enum { zero, one, two } x; sizeof(x); }));
14 | ASSERT(4, ({ enum t { zero, one, two }; enum t y; sizeof(y); }));
15 |
16 | printf("OK\n");
17 | return 0;
18 | }
19 |
--------------------------------------------------------------------------------
/test/extern.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | extern int ext1;
4 | extern int *ext2;
5 |
6 | inline int inline_fn(void) {
7 | return 3;
8 | }
9 |
10 | int main() {
11 | ASSERT(5, ext1);
12 | ASSERT(5, *ext2);
13 |
14 | extern int ext3;
15 | ASSERT(7, ext3);
16 |
17 | int ext_fn1(int x);
18 | ASSERT(5, ext_fn1(5));
19 |
20 | extern int ext_fn2(int x);
21 | ASSERT(8, ext_fn2(8));
22 |
23 | printf("OK\n");
24 | return 0;
25 | }
26 |
--------------------------------------------------------------------------------
/test/float.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | ASSERT(35, (float)(char)35);
5 | ASSERT(35, (float)(short)35);
6 | ASSERT(35, (float)(int)35);
7 | ASSERT(35, (float)(long)35);
8 | ASSERT(35, (float)(unsigned char)35);
9 | ASSERT(35, (float)(unsigned short)35);
10 | ASSERT(35, (float)(unsigned int)35);
11 | ASSERT(35, (float)(unsigned long)35);
12 |
13 | ASSERT(35, (double)(char)35);
14 | ASSERT(35, (double)(short)35);
15 | ASSERT(35, (double)(int)35);
16 | ASSERT(35, (double)(long)35);
17 | ASSERT(35, (double)(unsigned char)35);
18 | ASSERT(35, (double)(unsigned short)35);
19 | ASSERT(35, (double)(unsigned int)35);
20 | ASSERT(35, (double)(unsigned long)35);
21 |
22 | ASSERT(35, (char)(float)35);
23 | ASSERT(35, (short)(float)35);
24 | ASSERT(35, (int)(float)35);
25 | ASSERT(35, (long)(float)35);
26 | ASSERT(35, (unsigned char)(float)35);
27 | ASSERT(35, (unsigned short)(float)35);
28 | ASSERT(35, (unsigned int)(float)35);
29 | ASSERT(35, (unsigned long)(float)35);
30 |
31 | ASSERT(35, (char)(double)35);
32 | ASSERT(35, (short)(double)35);
33 | ASSERT(35, (int)(double)35);
34 | ASSERT(35, (long)(double)35);
35 | ASSERT(35, (unsigned char)(double)35);
36 | ASSERT(35, (unsigned short)(double)35);
37 | ASSERT(35, (unsigned int)(double)35);
38 | ASSERT(35, (unsigned long)(double)35);
39 |
40 | ASSERT(-2147483648, (double)(unsigned long)(long)-1);
41 |
42 | ASSERT(1, 2e3==2e3);
43 | ASSERT(0, 2e3==2e5);
44 | ASSERT(1, 2.0==2);
45 | ASSERT(0, 5.1<5);
46 | ASSERT(0, 5.0<5);
47 | ASSERT(1, 4.9<5);
48 | ASSERT(0, 5.1<=5);
49 | ASSERT(1, 5.0<=5);
50 | ASSERT(1, 4.9<=5);
51 |
52 | ASSERT(1, 2e3f==2e3);
53 | ASSERT(0, 2e3f==2e5);
54 | ASSERT(1, 2.0f==2);
55 | ASSERT(0, 5.1f<5);
56 | ASSERT(0, 5.0f<5);
57 | ASSERT(1, 4.9f<5);
58 | ASSERT(0, 5.1f<=5);
59 | ASSERT(1, 5.0f<=5);
60 | ASSERT(1, 4.9f<=5);
61 |
62 | ASSERT(6, 2.3+3.8);
63 | ASSERT(-1, 2.3-3.8);
64 | ASSERT(-3, -3.8);
65 | ASSERT(13, 3.3*4);
66 | ASSERT(2, 5.0/2);
67 |
68 | ASSERT(6, 2.3f+3.8f);
69 | ASSERT(6, 2.3f+3.8);
70 | ASSERT(-1, 2.3f-3.8);
71 | ASSERT(-3, -3.8f);
72 | ASSERT(13, 3.3f*4);
73 | ASSERT(2, 5.0f/2);
74 |
75 | ASSERT(0, 0.0/0.0 == 0.0/0.0);
76 | ASSERT(1, 0.0/0.0 != 0.0/0.0);
77 |
78 | ASSERT(0, 0.0/0.0 < 0);
79 | ASSERT(0, 0.0/0.0 <= 0);
80 | ASSERT(0, 0.0/0.0 > 0);
81 | ASSERT(0, 0.0/0.0 >= 0);
82 |
83 | ASSERT(0, !3.);
84 | ASSERT(1, !0.);
85 | ASSERT(0, !3.f);
86 | ASSERT(1, !0.f);
87 |
88 | ASSERT(5, 0.0 ? 3 : 5);
89 | ASSERT(3, 1.2 ? 3 : 5);
90 |
91 | printf("OK\n");
92 | return 0;
93 | }
94 |
--------------------------------------------------------------------------------
/test/function.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int ret3(void) {
4 | return 3;
5 | return 5;
6 | }
7 |
8 | int add2(int x, int y) {
9 | return x + y;
10 | }
11 |
12 | int sub2(int x, int y) {
13 | return x - y;
14 | }
15 |
16 | int add6(int a, int b, int c, int d, int e, int f) {
17 | return a + b + c + d + e + f;
18 | }
19 |
20 | int addx(int *x, int y) {
21 | return *x + y;
22 | }
23 |
24 | int sub_char(char a, char b, char c) {
25 | return a - b - c;
26 | }
27 |
28 | int fib(int x) {
29 | if (x<=1)
30 | return 1;
31 | return fib(x-1) + fib(x-2);
32 | }
33 |
34 | int sub_long(long a, long b, long c) {
35 | return a - b - c;
36 | }
37 |
38 | int sub_short(short a, short b, short c) {
39 | return a - b - c;
40 | }
41 |
42 | int g1;
43 |
44 | int *g1_ptr(void) { return &g1; }
45 | char int_to_char(int x) { return x; }
46 |
47 | int div_long(long a, long b) {
48 | return a / b;
49 | }
50 |
51 | _Bool bool_fn_add(_Bool x) { return x + 1; }
52 | _Bool bool_fn_sub(_Bool x) { return x - 1; }
53 |
54 | static int static_fn(void) { return 3; }
55 |
56 | int param_decay(int x[]) { return x[0]; }
57 |
58 | int counter() {
59 | static int i;
60 | static int j = 1+1;
61 | return i++ + j++;
62 | }
63 |
64 | void ret_none() {
65 | return;
66 | }
67 |
68 | _Bool true_fn();
69 | _Bool false_fn();
70 | char char_fn();
71 | short short_fn();
72 |
73 | unsigned char uchar_fn();
74 | unsigned short ushort_fn();
75 |
76 | char schar_fn();
77 | short sshort_fn();
78 |
79 | int add_all(int n, ...);
80 |
81 | typedef struct {
82 | int gp_offset;
83 | int fp_offset;
84 | void *overflow_arg_area;
85 | void *reg_save_area;
86 | } __va_elem;
87 |
88 | typedef __va_elem va_list[1];
89 |
90 | int add_all(int n, ...);
91 | int sprintf(char *buf, char *fmt, ...);
92 | int vsprintf(char *buf, char *fmt, va_list ap);
93 |
94 | char *fmt(char *buf, char *fmt, ...) {
95 | va_list ap;
96 | *ap = *(__va_elem *)__va_area__;
97 | vsprintf(buf, fmt, ap);
98 | }
99 |
100 | double add_double(double x, double y);
101 | float add_float(float x, float y);
102 |
103 | float add_float3(float x, float y, float z) {
104 | return x + y + z;
105 | }
106 |
107 | double add_double3(double x, double y, double z) {
108 | return x + y + z;
109 | }
110 |
111 | int (*fnptr(int (*fn)(int n, ...)))(int, ...) {
112 | return fn;
113 | }
114 |
115 | int param_decay2(int x()) { return x(); }
116 |
117 | char *func_fn(void) {
118 | return __func__;
119 | }
120 |
121 | char *function_fn(void) {
122 | return __FUNCTION__;
123 | }
124 |
125 | int add10_int(int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10);
126 | float add10_float(float x1, float x2, float x3, float x4, float x5, float x6, float x7, float x8, float x9, float x10);
127 | double add10_double(double x1, double x2, double x3, double x4, double x5, double x6, double x7, double x8, double x9, double x10);
128 |
129 | int many_args1(int a, int b, int c, int d, int e, int f, int g, int h) {
130 | return g / h;
131 | }
132 |
133 | double many_args2(double a, double b, double c, double d, double e,
134 | double f, double g, double h, double i, double j) {
135 | return i / j;
136 | }
137 |
138 | int many_args3(int a, double b, int c, int d, double e, int f,
139 | double g, int h, double i, double j, double k,
140 | double l, double m, int n, int o, double p) {
141 | return o / p;
142 | }
143 |
144 | typedef struct { int a,b; short c; char d; } Ty4;
145 | typedef struct { int a; float b; double c; } Ty5;
146 | typedef struct { unsigned char a[3]; } Ty6;
147 | typedef struct { long a, b, c; } Ty7;
148 |
149 | int struct_test5(Ty5 x, int n);
150 | int struct_test4(Ty4 x, int n);
151 | int struct_test6(Ty6 x, int n);
152 | int struct_test7(Ty7 x, int n);
153 |
154 | int struct_test14(Ty4 x, int n) {
155 | switch (n) {
156 | case 0: return x.a;
157 | case 1: return x.b;
158 | case 2: return x.c;
159 | default: return x.d;
160 | }
161 | }
162 |
163 | int struct_test15(Ty5 x, int n) {
164 | switch (n) {
165 | case 0: return x.a;
166 | case 1: return x.b;
167 | default: return x.c;
168 | }
169 | }
170 |
171 | typedef struct { unsigned char a[10]; } Ty20;
172 | typedef struct { unsigned char a[20]; } Ty21;
173 |
174 | Ty4 struct_test24(void);
175 | Ty5 struct_test25(void);
176 | Ty6 struct_test26(void);
177 | Ty20 struct_test27(void);
178 | Ty21 struct_test28(void);
179 |
180 | Ty4 struct_test34(void) {
181 | return (Ty4){10, 20, 30, 40};
182 | }
183 |
184 | Ty5 struct_test35(void) {
185 | return (Ty5){10, 20, 30};
186 | }
187 |
188 | Ty6 struct_test36(void) {
189 | return (Ty6){10, 20, 30};
190 | }
191 |
192 | Ty20 struct_test37(void) {
193 | return (Ty20){10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
194 | }
195 |
196 | Ty21 struct_test38(void) {
197 | return (Ty21){1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
198 | }
199 |
200 | inline int inline_fn(void) {
201 | return 3;
202 | }
203 |
204 | double to_double(long double x) {
205 | return x;
206 | }
207 |
208 | long double to_ldouble(int x) {
209 | return x;
210 | }
211 |
212 | int main() {
213 | ASSERT(3, ret3());
214 | ASSERT(8, add2(3, 5));
215 | ASSERT(2, sub2(5, 3));
216 | ASSERT(21, add6(1,2,3,4,5,6));
217 | ASSERT(66, add6(1,2,add6(3,4,5,6,7,8),9,10,11));
218 | ASSERT(136, add6(1,2,add6(3,add6(4,5,6,7,8,9),10,11,12,13),14,15,16));
219 |
220 | ASSERT(7, add2(3,4));
221 | ASSERT(1, sub2(4,3));
222 | ASSERT(55, fib(9));
223 |
224 | ASSERT(1, ({ sub_char(7, 3, 3); }));
225 |
226 | ASSERT(1, sub_long(7, 3, 3));
227 | ASSERT(1, sub_short(7, 3, 3));
228 |
229 | g1 = 3;
230 |
231 | ASSERT(3, *g1_ptr());
232 | ASSERT(5, int_to_char(261));
233 | ASSERT(5, int_to_char(261));
234 | ASSERT(-5, div_long(-10, 2));
235 |
236 | ASSERT(1, bool_fn_add(3));
237 | ASSERT(0, bool_fn_sub(3));
238 | ASSERT(1, bool_fn_add(-3));
239 | ASSERT(0, bool_fn_sub(-3));
240 | ASSERT(1, bool_fn_add(0));
241 | ASSERT(1, bool_fn_sub(0));
242 |
243 | ASSERT(3, static_fn());
244 |
245 | ASSERT(3, ({ int x[2]; x[0]=3; param_decay(x); }));
246 |
247 | ASSERT(2, counter());
248 | ASSERT(4, counter());
249 | ASSERT(6, counter());
250 |
251 | ret_none();
252 |
253 | ASSERT(1, true_fn());
254 | ASSERT(0, false_fn());
255 | ASSERT(3, char_fn());
256 | ASSERT(5, short_fn());
257 |
258 | ASSERT(6, add_all(3,1,2,3));
259 | ASSERT(5, add_all(4,1,2,3,-1));
260 |
261 | { char buf[100]; fmt(buf, "%d %d %s", 1, 2, "foo"); printf("%s\n", buf); }
262 |
263 | ASSERT(0, ({ char buf[100]; sprintf(buf, "%d %d %s", 1, 2, "foo"); strcmp("1 2 foo", buf); }));
264 |
265 | ASSERT(0, ({ char buf[100]; fmt(buf, "%d %d %s", 1, 2, "foo"); strcmp("1 2 foo", buf); }));
266 |
267 | ASSERT(251, uchar_fn());
268 | ASSERT(65528, ushort_fn());
269 | ASSERT(-5, schar_fn());
270 | ASSERT(-8, sshort_fn());
271 |
272 | ASSERT(6, add_float(2.3, 3.8));
273 | ASSERT(6, add_double(2.3, 3.8));
274 |
275 | ASSERT(7, add_float3(2.5, 2.5, 2.5));
276 | ASSERT(7, add_double3(2.5, 2.5, 2.5));
277 |
278 | ASSERT(0, ({ char buf[100]; sprintf(buf, "%.1f", (float)3.5); strcmp(buf, "3.5"); }));
279 |
280 | ASSERT(0, ({ char buf[100]; fmt(buf, "%.1f", (float)3.5); strcmp(buf, "3.5"); }));
281 |
282 | ASSERT(5, (add2)(2,3));
283 | ASSERT(5, (&add2)(2,3));
284 | ASSERT(7, ({ int (*fn)(int,int) = add2; fn(2,5); }));
285 | ASSERT(6, fnptr(add_all)(3, 1, 2, 3));
286 |
287 | ASSERT(3, param_decay2(ret3));
288 |
289 | ASSERT(5, sizeof(__func__));
290 | ASSERT(0, strcmp("main", __func__));
291 | ASSERT(0, strcmp("func_fn", func_fn()));
292 | ASSERT(0, strcmp("main", __FUNCTION__));
293 | ASSERT(0, strcmp("function_fn", function_fn()));
294 |
295 |
296 | ASSERT(55, add10_int(1,2,3,4,5,6,7,8,9,10));
297 | ASSERT(55, add10_float(1,2,3,4,5,6,7,8,9,10));
298 | ASSERT(55, add10_double(1,2,3,4,5,6,7,8,9,10));
299 |
300 | ASSERT(0, ({ char buf[200]; sprintf(buf, "%d %.1f %.1f %.1f %d %d %.1f %d %d %d %d %.1f %d %d %.1f %.1f %.1f %.1f %d", 1, 1.0, 1.0, 1.0, 1, 1, 1.0, 1, 1, 1, 1, 1.0, 1, 1, 1.0, 1.0, 1.0, 1.0, 1); strcmp("1 1.0 1.0 1.0 1 1 1.0 1 1 1 1 1.0 1 1 1.0 1.0 1.0 1.0 1", buf); }));
301 |
302 | ASSERT(4, many_args1(1,2,3,4,5,6,40,10));
303 | ASSERT(4, many_args2(1,2,3,4,5,6,7,8,40,10));
304 | ASSERT(8, many_args3(1,2,3,4,5,6,7,8,9,10,11,12,13,14,80,10));
305 |
306 | ASSERT(10, ({ Ty4 x={10,20,30,40}; struct_test4(x, 0); }));
307 | ASSERT(20, ({ Ty4 x={10,20,30,40}; struct_test4(x, 1); }));
308 | ASSERT(30, ({ Ty4 x={10,20,30,40}; struct_test4(x, 2); }));
309 | ASSERT(40, ({ Ty4 x={10,20,30,40}; struct_test4(x, 3); }));
310 |
311 | ASSERT(10, ({ Ty5 x={10,20,30}; struct_test5(x, 0); }));
312 | ASSERT(20, ({ Ty5 x={10,20,30}; struct_test5(x, 1); }));
313 | ASSERT(30, ({ Ty5 x={10,20,30}; struct_test5(x, 2); }));
314 |
315 | ASSERT(10, ({ Ty6 x={10,20,30}; struct_test6(x, 0); }));
316 | ASSERT(20, ({ Ty6 x={10,20,30}; struct_test6(x, 1); }));
317 | ASSERT(30, ({ Ty6 x={10,20,30}; struct_test6(x, 2); }));
318 |
319 | ASSERT(10, ({ Ty7 x={10,20,30}; struct_test7(x, 0); }));
320 | ASSERT(20, ({ Ty7 x={10,20,30}; struct_test7(x, 1); }));
321 | ASSERT(30, ({ Ty7 x={10,20,30}; struct_test7(x, 2); }));
322 |
323 | ASSERT(10, ({ Ty4 x={10,20,30,40}; struct_test14(x, 0); }));
324 | ASSERT(20, ({ Ty4 x={10,20,30,40}; struct_test14(x, 1); }));
325 | ASSERT(30, ({ Ty4 x={10,20,30,40}; struct_test14(x, 2); }));
326 | ASSERT(40, ({ Ty4 x={10,20,30,40}; struct_test14(x, 3); }));
327 |
328 | ASSERT(10, ({ Ty5 x={10,20,30}; struct_test15(x, 0); }));
329 | ASSERT(20, ({ Ty5 x={10,20,30}; struct_test15(x, 1); }));
330 | ASSERT(30, ({ Ty5 x={10,20,30}; struct_test15(x, 2); }));
331 |
332 | ASSERT(10, struct_test24().a);
333 | ASSERT(20, struct_test24().b);
334 | ASSERT(30, struct_test24().c);
335 | ASSERT(40, struct_test24().d);
336 |
337 | ASSERT(10, struct_test25().a);
338 | ASSERT(20, struct_test25().b);
339 | ASSERT(30, struct_test25().c);
340 |
341 | ASSERT(10, struct_test26().a[0]);
342 | ASSERT(20, struct_test26().a[1]);
343 | ASSERT(30, struct_test26().a[2]);
344 |
345 | ASSERT(10, struct_test27().a[0]);
346 | ASSERT(60, struct_test27().a[5]);
347 | ASSERT(100, struct_test27().a[9]);
348 |
349 | ASSERT(1, struct_test28().a[0]);
350 | ASSERT(5, struct_test28().a[4]);
351 | ASSERT(10, struct_test28().a[9]);
352 | ASSERT(15, struct_test28().a[14]);
353 | ASSERT(20, struct_test28().a[19]);
354 |
355 | ASSERT(10, struct_test34().a);
356 | ASSERT(20, struct_test34().b);
357 | ASSERT(30, struct_test34().c);
358 | ASSERT(40, struct_test34().d);
359 |
360 | ASSERT(10, struct_test35().a);
361 | ASSERT(20, struct_test35().b);
362 | ASSERT(30, struct_test35().c);
363 |
364 | ASSERT(10, struct_test36().a[0]);
365 | ASSERT(20, struct_test36().a[1]);
366 | ASSERT(30, struct_test36().a[2]);
367 |
368 | ASSERT(10, struct_test37().a[0]);
369 | ASSERT(60, struct_test37().a[5]);
370 | ASSERT(100, struct_test37().a[9]);
371 |
372 | ASSERT(1, struct_test38().a[0]);
373 | ASSERT(5, struct_test38().a[4]);
374 | ASSERT(10, struct_test38().a[9]);
375 | ASSERT(15, struct_test38().a[14]);
376 | ASSERT(20, struct_test38().a[19]);
377 |
378 | ASSERT(5, (***add2)(2,3));
379 |
380 | ASSERT(3, inline_fn());
381 |
382 | ASSERT(0, ({ char buf[100]; sprintf(buf, "%Lf", (long double)12.3); strncmp(buf, "12.3", 4); }));
383 |
384 | ASSERT(1, to_double(3.5) == 3.5);
385 | ASSERT(0, to_double(3.5) == 3);
386 |
387 | ASSERT(1, (long double)5.0 == (long double)5.0);
388 | ASSERT(0, (long double)5.0 == (long double)5.2);
389 |
390 | ASSERT(1, to_ldouble(5.0) == 5.0);
391 | ASSERT(0, to_ldouble(5.0) == 5.2);
392 |
393 | printf("OK\n");
394 | }
395 |
--------------------------------------------------------------------------------
/test/generic.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | ASSERT(1, _Generic(100.0, double: 1, int *: 2, int: 3, float: 4));
5 | ASSERT(2, _Generic((int *)0, double: 1, int *: 2, int: 3, float: 4));
6 | ASSERT(2, _Generic((int[3]){}, double: 1, int *: 2, int: 3, float: 4));
7 | ASSERT(3, _Generic(100, double: 1, int *: 2, int: 3, float: 4));
8 | ASSERT(4, _Generic(100f, double: 1, int *: 2, int: 3, float: 4));
9 |
10 | printf("OK\n");
11 | return 0;
12 | }
13 |
--------------------------------------------------------------------------------
/test/include1.h:
--------------------------------------------------------------------------------
1 | #include "include2.h"
2 |
3 | char *include1_filename = __FILE__;
4 | int include1_line = __LINE__;
5 |
6 | int include1 = 5;
7 |
--------------------------------------------------------------------------------
/test/include2.h:
--------------------------------------------------------------------------------
1 | int include2 = 7;
2 |
--------------------------------------------------------------------------------
/test/include3.h:
--------------------------------------------------------------------------------
1 | #define foo 3
2 |
--------------------------------------------------------------------------------
/test/include4.h:
--------------------------------------------------------------------------------
1 | #define foo 4
2 |
--------------------------------------------------------------------------------
/test/initializer.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | char g3 = 3;
4 | short g4 = 4;
5 | int g5 = 5;
6 | long g6 = 6;
7 | int g9[3] = {0, 1, 2};
8 | struct {char a; int b;} g11[2] = {{1, 2}, {3, 4}};
9 | struct {int a[2];} g12[2] = {{{1, 2}}};
10 | union { int a; char b[8]; } g13[2] = {0x01020304, 0x05060708};
11 | char g17[] = "foobar";
12 | char g18[10] = "foobar";
13 | char g19[3] = "foobar";
14 | char *g20 = g17+0;
15 | char *g21 = g17+3;
16 | char *g22 = &g17-3;
17 | char *g23[] = {g17+0, g17+3, g17-3};
18 | int g24=3;
19 | int *g25=&g24;
20 | int g26[3] = {1, 2, 3};
21 | int *g27 = g26 + 1;
22 | int *g28 = &g11[1].a;
23 | long g29 = (long)(long)g26;
24 | struct { struct { int a[3]; } a; } g30 = {{{1,2,3}}};
25 | int *g31=g30.a.a;
26 | struct {int a[2];} g40[2] = {{1, 2}, 3, 4};
27 | struct {int a[2];} g41[2] = {1, 2, 3, 4};
28 | char g43[][4] = {'f', 'o', 'o', 0, 'b', 'a', 'r', 0};
29 | char *g44 = {"foo"};
30 | union { int a; char b[4]; } g50 = {.b[2]=0x12};
31 | union { int a; } g51[2] = {};
32 |
33 | typedef char T60[];
34 | T60 g60 = {1, 2, 3};
35 | T60 g61 = {1, 2, 3, 4, 5, 6};
36 |
37 | typedef struct { char a, b[]; } T65;
38 | T65 g65 = {'f','o','o',0};
39 | T65 g66 = {'f','o','o','b','a','r',0};
40 |
41 | int main() {
42 | ASSERT(1, ({ int x[3]={1,2,3}; x[0]; }));
43 | ASSERT(2, ({ int x[3]={1,2,3}; x[1]; }));
44 | ASSERT(3, ({ int x[3]={1,2,3}; x[2]; }));
45 | ASSERT(3, ({ int x[3]={1,2,3}; x[2]; }));
46 |
47 | ASSERT(2, ({ int x[2][3]={{1,2,3},{4,5,6}}; x[0][1]; }));
48 | ASSERT(4, ({ int x[2][3]={{1,2,3},{4,5,6}}; x[1][0]; }));
49 | ASSERT(6, ({ int x[2][3]={{1,2,3},{4,5,6}}; x[1][2]; }));
50 |
51 | ASSERT(0, ({ int x[3]={}; x[0]; }));
52 | ASSERT(0, ({ int x[3]={}; x[1]; }));
53 | ASSERT(0, ({ int x[3]={}; x[2]; }));
54 |
55 | ASSERT(2, ({ int x[2][3]={{1,2}}; x[0][1]; }));
56 | ASSERT(0, ({ int x[2][3]={{1,2}}; x[1][0]; }));
57 | ASSERT(0, ({ int x[2][3]={{1,2}}; x[1][2]; }));
58 |
59 | ASSERT('a', ({ char x[4]="abc"; x[0]; }));
60 | ASSERT('c', ({ char x[4]="abc"; x[2]; }));
61 | ASSERT(0, ({ char x[4]="abc"; x[3]; }));
62 | ASSERT('a', ({ char x[2][4]={"abc","def"}; x[0][0]; }));
63 | ASSERT(0, ({ char x[2][4]={"abc","def"}; x[0][3]; }));
64 | ASSERT('d', ({ char x[2][4]={"abc","def"}; x[1][0]; }));
65 | ASSERT('f', ({ char x[2][4]={"abc","def"}; x[1][2]; }));
66 |
67 | ASSERT(4, ({ int x[]={1,2,3,4}; x[3]; }));
68 | ASSERT(16, ({ int x[]={1,2,3,4}; sizeof(x); }));
69 | ASSERT(4, ({ char x[]="foo"; sizeof(x); }));
70 |
71 | ASSERT(4, ({ typedef char T[]; T x="foo"; T y="x"; sizeof(x); }));
72 | ASSERT(2, ({ typedef char T[]; T x="foo"; T y="x"; sizeof(y); }));
73 | ASSERT(2, ({ typedef char T[]; T x="x"; T y="foo"; sizeof(x); }));
74 | ASSERT(4, ({ typedef char T[]; T x="x"; T y="foo"; sizeof(y); }));
75 |
76 | ASSERT(1, ({ struct {int a; int b; int c;} x={1,2,3}; x.a; }));
77 | ASSERT(2, ({ struct {int a; int b; int c;} x={1,2,3}; x.b; }));
78 | ASSERT(3, ({ struct {int a; int b; int c;} x={1,2,3}; x.c; }));
79 | ASSERT(1, ({ struct {int a; int b; int c;} x={1}; x.a; }));
80 | ASSERT(0, ({ struct {int a; int b; int c;} x={1}; x.b; }));
81 | ASSERT(0, ({ struct {int a; int b; int c;} x={1}; x.c; }));
82 |
83 | ASSERT(1, ({ struct {int a; int b;} x[2]={{1,2},{3,4}}; x[0].a; }));
84 | ASSERT(2, ({ struct {int a; int b;} x[2]={{1,2},{3,4}}; x[0].b; }));
85 | ASSERT(3, ({ struct {int a; int b;} x[2]={{1,2},{3,4}}; x[1].a; }));
86 | ASSERT(4, ({ struct {int a; int b;} x[2]={{1,2},{3,4}}; x[1].b; }));
87 |
88 | ASSERT(0, ({ struct {int a; int b;} x[2]={{1,2}}; x[1].b; }));
89 |
90 | ASSERT(0, ({ struct {int a; int b;} x={}; x.a; }));
91 | ASSERT(0, ({ struct {int a; int b;} x={}; x.b; }));
92 |
93 | ASSERT(5, ({ typedef struct {int a,b,c,d,e,f;} T; T x={1,2,3,4,5,6}; T y; y=x; y.e; }));
94 | ASSERT(2, ({ typedef struct {int a,b;} T; T x={1,2}; T y, z; z=y=x; z.b; }));
95 |
96 | ASSERT(1, ({ typedef struct {int a,b;} T; T x={1,2}; T y=x; y.a; }));
97 |
98 | ASSERT(4, ({ union { int a; char b[4]; } x={0x01020304}; x.b[0]; }));
99 | ASSERT(3, ({ union { int a; char b[4]; } x={0x01020304}; x.b[1]; }));
100 |
101 | ASSERT(0x01020304, ({ union { struct { char a,b,c,d; } e; int f; } x={{4,3,2,1}}; x.f; }));
102 |
103 | ASSERT(3, g3);
104 | ASSERT(4, g4);
105 | ASSERT(5, g5);
106 | ASSERT(6, g6);
107 |
108 | ASSERT(0, g9[0]);
109 | ASSERT(1, g9[1]);
110 | ASSERT(2, g9[2]);
111 |
112 | ASSERT(1, g11[0].a);
113 | ASSERT(2, g11[0].b);
114 | ASSERT(3, g11[1].a);
115 | ASSERT(4, g11[1].b);
116 |
117 | ASSERT(1, g12[0].a[0]);
118 | ASSERT(2, g12[0].a[1]);
119 | ASSERT(0, g12[1].a[0]);
120 | ASSERT(0, g12[1].a[1]);
121 |
122 | ASSERT(4, g13[0].b[0]);
123 | ASSERT(3, g13[0].b[1]);
124 | ASSERT(8, g13[1].b[0]);
125 | ASSERT(7, g13[1].b[1]);
126 |
127 | ASSERT(7, sizeof(g17));
128 | ASSERT(10, sizeof(g18));
129 | ASSERT(3, sizeof(g19));
130 |
131 | ASSERT(0, memcmp(g17, "foobar", 7));
132 | ASSERT(0, memcmp(g18, "foobar\0\0\0", 10));
133 | ASSERT(0, memcmp(g19, "foo", 3));
134 |
135 | ASSERT(0, strcmp(g20, "foobar"));
136 | ASSERT(0, strcmp(g21, "bar"));
137 | ASSERT(0, strcmp(g22+3, "foobar"));
138 |
139 | ASSERT(0, strcmp(g23[0], "foobar"));
140 | ASSERT(0, strcmp(g23[1], "bar"));
141 | ASSERT(0, strcmp(g23[2]+3, "foobar"));
142 |
143 | ASSERT(3, g24);
144 | ASSERT(3, *g25);
145 | ASSERT(2, *g27);
146 | ASSERT(3, *g28);
147 | ASSERT(1, *(int *)g29);
148 |
149 | ASSERT(1, g31[0]);
150 | ASSERT(2, g31[1]);
151 | ASSERT(3, g31[2]);
152 |
153 | ASSERT(1, g40[0].a[0]);
154 | ASSERT(2, g40[0].a[1]);
155 | ASSERT(3, g40[1].a[0]);
156 | ASSERT(4, g40[1].a[1]);
157 |
158 | ASSERT(1, g41[0].a[0]);
159 | ASSERT(2, g41[0].a[1]);
160 | ASSERT(3, g41[1].a[0]);
161 | ASSERT(4, g41[1].a[1]);
162 |
163 | ASSERT(0, ({ int x[2][3]={0,1,2,3,4,5}; x[0][0]; }));
164 | ASSERT(3, ({ int x[2][3]={0,1,2,3,4,5}; x[1][0]; }));
165 |
166 | ASSERT(0, ({ struct {int a; int b;} x[2]={0,1,2,3}; x[0].a; }));
167 | ASSERT(2, ({ struct {int a; int b;} x[2]={0,1,2,3}; x[1].a; }));
168 |
169 | ASSERT(0, strcmp(g43[0], "foo"));
170 | ASSERT(0, strcmp(g43[1], "bar"));
171 | ASSERT(0, strcmp(g44, "foo"));
172 |
173 | ASSERT(3, ({ int a[]={1,2,3,}; a[2]; }));
174 | ASSERT(1, ({ struct {int a,b,c;} x={1,2,3,}; x.a; }));
175 | ASSERT(1, ({ union {int a; char b;} x={1,}; x.a; }));
176 | ASSERT(2, ({ enum {x,y,z,}; z; }));
177 |
178 | ASSERT(3, sizeof(g60));
179 | ASSERT(6, sizeof(g61));
180 |
181 | ASSERT(4, sizeof(g65));
182 | ASSERT(7, sizeof(g66));
183 | ASSERT(0, strcmp(g65.b, "oo"));
184 | ASSERT(0, strcmp(g66.b, "oobar"));
185 |
186 | ASSERT(4, ({ int x[3]={1, 2, 3, [0]=4, 5}; x[0]; }));
187 | ASSERT(5, ({ int x[3]={1, 2, 3, [0]=4, 5}; x[1]; }));
188 | ASSERT(3, ({ int x[3]={1, 2, 3, [0]=4, 5}; x[2]; }));
189 |
190 | ASSERT(10, ({ int x[2][3]={1,2,3,4,5,6,[0][1]=7,8,[0]=9,[0]=10,11,[1][0]=12}; x[0][0]; }));
191 | ASSERT(11, ({ int x[2][3]={1,2,3,4,5,6,[0][1]=7,8,[0]=9,[0]=10,11,[1][0]=12}; x[0][1]; }));
192 | ASSERT(8, ({ int x[2][3]={1,2,3,4,5,6,[0][1]=7,8,[0]=9,[0]=10,11,[1][0]=12}; x[0][2]; }));
193 | ASSERT(12, ({ int x[2][3]={1,2,3,4,5,6,[0][1]=7,8,[0]=9,[0]=10,11,[1][0]=12}; x[1][0]; }));
194 | ASSERT(5, ({ int x[2][3]={1,2,3,4,5,6,[0][1]=7,8,[0]=9,[0]=10,11,[1][0]=12}; x[1][1]; }));
195 | ASSERT(6, ({ int x[2][3]={1,2,3,4,5,6,[0][1]=7,8,[0]=9,[0]=10,11,[1][0]=12}; x[1][2]; }));
196 |
197 | ASSERT(7, ({ int x[2][3]={1,2,3,4,5,6,[0]={7,8},9,10}; x[0][0]; }));
198 | ASSERT(8, ({ int x[2][3]={1,2,3,4,5,6,[0]={7,8},9,10}; x[0][1]; }));
199 | ASSERT(3, ({ int x[2][3]={1,2,3,4,5,6,[0]={7,8},9,10}; x[0][2]; }));
200 | ASSERT(9, ({ int x[2][3]={1,2,3,4,5,6,[0]={7,8},9,10}; x[1][0]; }));
201 | ASSERT(10, ({ int x[2][3]={1,2,3,4,5,6,[0]={7,8},9,10}; x[1][1]; }));
202 | ASSERT(6, ({ int x[2][3]={1,2,3,4,5,6,[0]={7,8},9,10}; x[1][2]; }));
203 |
204 | ASSERT(7, ((int[10]){ [3]=7 })[3]);
205 | ASSERT(0, ((int[10]){ [3]=7 })[4]);
206 |
207 | ASSERT(10, ({ char x[]={[10-3]=1,2,3}; sizeof(x); }));
208 | ASSERT(20, ({ char x[][2]={[8][1]=1,2}; sizeof(x); }));
209 |
210 | ASSERT(3, sizeof(g60));
211 | ASSERT(6, sizeof(g61));
212 |
213 | ASSERT(4, sizeof(g65));
214 | ASSERT(7, sizeof(g66));
215 | ASSERT(0, strcmp(g65.b, "oo"));
216 | ASSERT(0, strcmp(g66.b, "oobar"));
217 |
218 | ASSERT(7, ((int[10]){ [3] 7 })[3]);
219 | ASSERT(0, ((int[10]){ [3] 7 })[4]);
220 |
221 | ASSERT(4, ({ struct { int a,b; } x={1,2,.b=3,.a=4}; x.a; }));
222 | ASSERT(3, ({ struct { int a,b; } x={1,2,.b=3,.a=4}; x.b; }));
223 |
224 | ASSERT(1, ({ struct { struct { int a,b; } c; } x={.c=1,2}; x.c.a; }));
225 | ASSERT(2, ({ struct { struct { int a,b; } c; } x={.c=1,2}; x.c.b; }));
226 |
227 | ASSERT(0, ({ struct { struct { int a,b; } c; } x={.c.b=1}; x.c.a; }));
228 | ASSERT(1, ({ struct { struct { int a,b; } c; } x={.c.b=1}; x.c.b; }));
229 |
230 | ASSERT(1, ({ struct { int a[2]; } x={.a=1,2}; x.a[0]; }));
231 | ASSERT(2, ({ struct { int a[2]; } x={.a=1,2}; x.a[1]; }));
232 |
233 | ASSERT(0, ({ struct { int a[2]; } x={.a[1]=1}; x.a[0]; }));
234 | ASSERT(1, ({ struct { int a[2]; } x={.a[1]=1}; x.a[1]; }));
235 |
236 | ASSERT(3, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[0].a; }));
237 | ASSERT(4, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[0].b; }));
238 | ASSERT(0, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[1].a; }));
239 | ASSERT(1, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[1].b; }));
240 | ASSERT(2, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[2].a; }));
241 | ASSERT(0, ({ struct { int a,b; } x[]={[1].b=1,2,[0]=3,4,}; x[2].b; }));
242 |
243 | ASSERT(1, ({ typedef struct { int a,b; } T; T x={1,2}; T y[]={x}; y[0].a; }));
244 | ASSERT(2, ({ typedef struct { int a,b; } T; T x={1,2}; T y[]={x}; y[0].b; }));
245 | ASSERT(0, ({ typedef struct { int a,b; } T; T x={1,2}; T y[]={x, [0].b=3}; y[0].a; }));
246 | ASSERT(3, ({ typedef struct { int a,b; } T; T x={1,2}; T y[]={x, [0].b=3}; y[0].b; }));
247 |
248 | ASSERT(5, ((struct { int a,b,c; }){ .c=5 }).c);
249 | ASSERT(0, ((struct { int a,b,c; }){ .c=5 }).a);
250 |
251 | ASSERT(0x00ff, ({ union { unsigned short a; char b[2]; } x={.b[0]=0xff}; x.a; }));
252 | ASSERT(0xff00, ({ union { unsigned short a; char b[2]; } x={.b[1]=0xff}; x.a; }));
253 |
254 | ASSERT(0x00120000, g50.a);
255 | ASSERT(0, g51[0].a);
256 | ASSERT(0, g51[1].a);
257 |
258 | ASSERT(1, ({ struct { struct { int a; struct { int b; }; }; int c; } x={1,2,3,.b=4,5}; x.a; }));
259 | ASSERT(4, ({ struct { struct { int a; struct { int b; }; }; int c; } x={1,2,3,.b=4,5}; x.b; }));
260 | ASSERT(5, ({ struct { struct { int a; struct { int b; }; }; int c; } x={1,2,3,.b=4,5}; x.c; }));
261 |
262 | ASSERT(16, ({ char x[]={[2 ... 10]='a', [7]='b', [15 ... 15]='c', [3 ... 5]='d'}; sizeof(x); }));
263 | ASSERT(0, ({ char x[]={[2 ... 10]='a', [7]='b', [15 ... 15]='c', [3 ... 5]='d'}; memcmp(x, "\0\0adddabaaa\0\0\0\0c", 16); }));
264 |
265 | printf("OK\n");
266 | return 0;
267 | }
268 |
--------------------------------------------------------------------------------
/test/line.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | #line 500 "foo"
5 | ASSERT(501, __LINE__);
6 | ASSERT(0, strcmp(__FILE__, "foo"));
7 |
8 | #line 800 "bar"
9 | ASSERT(801, __LINE__);
10 | ASSERT(0, strcmp(__FILE__, "bar"));
11 |
12 | #line 1
13 | ASSERT(2, __LINE__);
14 |
15 | # 200 "xyz" 2 3
16 | ASSERT(201, __LINE__);
17 | ASSERT(0, strcmp(__FILE__, "xyz"));
18 |
19 | printf("OK\n");
20 | return 0;
21 | }
22 |
--------------------------------------------------------------------------------
/test/literal.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | ASSERT(97, 'a');
5 | ASSERT(10, '\n');
6 | ASSERT(-128, '\x80');
7 |
8 | ASSERT(511, 0777);
9 | ASSERT(0, 0x0);
10 | ASSERT(10, 0xa);
11 | ASSERT(10, 0XA);
12 | ASSERT(48879, 0xbeef);
13 | ASSERT(48879, 0xBEEF);
14 | ASSERT(48879, 0XBEEF);
15 | ASSERT(0, 0b0);
16 | ASSERT(1, 0b1);
17 | ASSERT(47, 0b101111);
18 | ASSERT(47, 0B101111);
19 |
20 | ASSERT(4, sizeof(0));
21 | ASSERT(8, sizeof(0L));
22 | ASSERT(8, sizeof(0LU));
23 | ASSERT(8, sizeof(0UL));
24 | ASSERT(8, sizeof(0LL));
25 | ASSERT(8, sizeof(0LLU));
26 | ASSERT(8, sizeof(0Ull));
27 | ASSERT(8, sizeof(0l));
28 | ASSERT(8, sizeof(0ll));
29 | ASSERT(8, sizeof(0x0L));
30 | ASSERT(8, sizeof(0b0L));
31 | ASSERT(4, sizeof(2147483647));
32 | ASSERT(8, sizeof(2147483648));
33 | ASSERT(-1, 0xffffffffffffffff);
34 | ASSERT(8, sizeof(0xffffffffffffffff));
35 | ASSERT(4, sizeof(4294967295U));
36 | ASSERT(8, sizeof(4294967296U));
37 |
38 | ASSERT(3, -1U>>30);
39 | ASSERT(3, -1Ul>>62);
40 | ASSERT(3, -1ull>>62);
41 |
42 | ASSERT(1, 0xffffffffffffffffl>>63);
43 | ASSERT(1, 0xffffffffffffffffll>>63);
44 |
45 | ASSERT(-1, 18446744073709551615);
46 | ASSERT(8, sizeof(18446744073709551615));
47 | ASSERT(-1, 18446744073709551615>>63);
48 |
49 | ASSERT(-1, 0xffffffffffffffff);
50 | ASSERT(8, sizeof(0xffffffffffffffff));
51 | ASSERT(1, 0xffffffffffffffff>>63);
52 |
53 | ASSERT(-1, 01777777777777777777777);
54 | ASSERT(8, sizeof(01777777777777777777777));
55 | ASSERT(1, 01777777777777777777777>>63);
56 |
57 | ASSERT(-1, 0b1111111111111111111111111111111111111111111111111111111111111111);
58 | ASSERT(8, sizeof(0b1111111111111111111111111111111111111111111111111111111111111111));
59 | ASSERT(1, 0b1111111111111111111111111111111111111111111111111111111111111111>>63);
60 |
61 | ASSERT(8, sizeof(2147483648));
62 | ASSERT(4, sizeof(2147483647));
63 |
64 | ASSERT(8, sizeof(0x1ffffffff));
65 | ASSERT(4, sizeof(0xffffffff));
66 | ASSERT(1, 0xffffffff>>31);
67 |
68 | ASSERT(8, sizeof(040000000000));
69 | ASSERT(4, sizeof(037777777777));
70 | ASSERT(1, 037777777777>>31);
71 |
72 | ASSERT(8, sizeof(0b111111111111111111111111111111111));
73 | ASSERT(4, sizeof(0b11111111111111111111111111111111));
74 | ASSERT(1, 0b11111111111111111111111111111111>>31);
75 |
76 | ASSERT(-1, 1 << 31 >> 31);
77 | ASSERT(-1, 01 << 31 >> 31);
78 | ASSERT(-1, 0x1 << 31 >> 31);
79 | ASSERT(-1, 0b1 << 31 >> 31);
80 |
81 | 0.0;
82 | 1.0;
83 | 3e+8;
84 | 0x10.1p0;
85 | .1E4f;
86 |
87 | ASSERT(4, sizeof(8f));
88 | ASSERT(4, sizeof(0.3F));
89 | ASSERT(8, sizeof(0.));
90 | ASSERT(8, sizeof(.0));
91 | ASSERT(16, sizeof(5.l));
92 | ASSERT(16, sizeof(2.0L));
93 |
94 | assert(1, size\
95 | of(char), \
96 | "sizeof(char)");
97 |
98 | ASSERT(4, sizeof(L'\0'));
99 | ASSERT(97, L'a');
100 |
101 | printf("OK\n");
102 | return 0;
103 | }
104 |
--------------------------------------------------------------------------------
/test/macro.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 | #include "include1.h"
3 |
4 | char *main_filename1 = __FILE__;
5 | int main_line1 = __LINE__;
6 | #define LINE() __LINE__
7 | int main_line2 = LINE();
8 |
9 | #
10 |
11 | /* */ #
12 |
13 | int ret3(void) { return 3; }
14 | int dbl(int x) { return x*x; }
15 |
16 | int add2(int x, int y) {
17 | return x + y;
18 | }
19 |
20 | int add6(int a, int b, int c, int d, int e, int f) {
21 | return a + b + c + d + e + f;
22 | }
23 |
24 | int main() {
25 | ASSERT(5, include1);
26 | ASSERT(7, include2);
27 |
28 | #if 0
29 | #include "/no/such/file"
30 | ASSERT(0, 1);
31 | #if nested
32 | #endif
33 | #endif
34 |
35 | int m = 0;
36 |
37 | #if 1
38 | m = 5;
39 | #endif
40 | ASSERT(5, m);
41 |
42 | #if 1
43 | # if 0
44 | # if 1
45 | foo bar
46 | # endif
47 | # endif
48 | m = 3;
49 | #endif
50 | ASSERT(3, m);
51 |
52 | #if 1-1
53 | # if 1
54 | # endif
55 | # if 1
56 | # else
57 | # endif
58 | # if 0
59 | # else
60 | # endif
61 | m = 2;
62 | #else
63 | # if 1
64 | m = 3;
65 | # endif
66 | #endif
67 | ASSERT(3, m);
68 |
69 | #if 1
70 | m = 2;
71 | #else
72 | m = 3;
73 | #endif
74 | ASSERT(2, m);
75 |
76 | #if 1
77 | m = 2;
78 | #else
79 | m = 3;
80 | #endif
81 | ASSERT(2, m);
82 |
83 | #if 0
84 | m = 1;
85 | #elif 0
86 | m = 2;
87 | #elif 3+5
88 | m = 3;
89 | #elif 1*5
90 | m = 4;
91 | #endif
92 | ASSERT(3, m);
93 |
94 | #if 1+5
95 | m = 1;
96 | #elif 1
97 | m = 2;
98 | #elif 3
99 | m = 2;
100 | #endif
101 | ASSERT(1, m);
102 |
103 | #if 0
104 | m = 1;
105 | #elif 1
106 | # if 1
107 | m = 2;
108 | # else
109 | m = 3;
110 | # endif
111 | #else
112 | m = 5;
113 | #endif
114 | ASSERT(2, m);
115 |
116 | int M1 = 5;
117 |
118 | #define M1 3
119 | ASSERT(3, M1);
120 | #define M1 4
121 | ASSERT(4, M1);
122 |
123 | #define M1 3+4+
124 | ASSERT(12, M1 5);
125 |
126 | #define M1 3+4
127 | ASSERT(23, M1*5);
128 |
129 | #define ASSERT_ assert(
130 | #define if 5
131 | #define five "5"
132 | #define END )
133 | ASSERT_ 5, if, five END;
134 |
135 | #undef ASSERT_
136 | #undef if
137 | #undef five
138 | #undef END
139 |
140 | if (0);
141 |
142 | #define M 5
143 | #if M
144 | m = 5;
145 | #else
146 | m = 6;
147 | #endif
148 | ASSERT(5, m);
149 |
150 | #define M 5
151 | #if M-5
152 | m = 6;
153 | #elif M
154 | m = 5;
155 | #endif
156 | ASSERT(5, m);
157 |
158 | int M2 = 6;
159 | #define M2 M2 + 3
160 | ASSERT(9, M2);
161 |
162 | #define M3 M2 + 3
163 | ASSERT(12, M3);
164 |
165 | int M4 = 3;
166 | #define M4 M5 * 5
167 | #define M5 M4 + 2
168 | ASSERT(13, M4);
169 |
170 | #ifdef M6
171 | m = 5;
172 | #else
173 | m = 3;
174 | #endif
175 | ASSERT(3, m);
176 |
177 | #define M6
178 | #ifdef M6
179 | m = 5;
180 | #else
181 | m = 3;
182 | #endif
183 | ASSERT(5, m);
184 |
185 | #ifndef M7
186 | m = 3;
187 | #else
188 | m = 5;
189 | #endif
190 | ASSERT(3, m);
191 |
192 | #define M7
193 | #ifndef M7
194 | m = 3;
195 | #else
196 | m = 5;
197 | #endif
198 | ASSERT(5, m);
199 |
200 | #if 0
201 | #ifdef NO_SUCH_MACRO
202 | #endif
203 | #ifndef NO_SUCH_MACRO
204 | #endif
205 | #else
206 | #endif
207 |
208 | #define M7() 1
209 | int M7 = 5;
210 | ASSERT(1, M7());
211 | ASSERT(5, M7);
212 |
213 | #define M7 ()
214 | ASSERT(3, ret3 M7);
215 |
216 | #define M8(x,y) x+y
217 | ASSERT(7, M8(3, 4));
218 |
219 | #define M8(x,y) x*y
220 | ASSERT(24, M8(3+4, 4+5));
221 |
222 | #define M8(x,y) (x)*(y)
223 | ASSERT(63, M8(3+4, 4+5));
224 |
225 | #define M8(x,y) x y
226 | ASSERT(9, M8(, 4+5));
227 |
228 | #define M8(x,y) x*y
229 | ASSERT(20, M8((2+3), 4));
230 |
231 | #define M8(x,y) x*y
232 | ASSERT(12, M8((2,3), 4));
233 |
234 | #define dbl(x) M10(x) * x
235 | #define M10(x) dbl(x) + 3
236 | ASSERT(10, dbl(2));
237 |
238 | #define M11(x) #x
239 | ASSERT('a', M11( a!b `""c)[0]);
240 | ASSERT('!', M11( a!b `""c)[1]);
241 | ASSERT('b', M11( a!b `""c)[2]);
242 | ASSERT(' ', M11( a!b `""c)[3]);
243 | ASSERT('`', M11( a!b `""c)[4]);
244 | ASSERT('"', M11( a!b `""c)[5]);
245 | ASSERT('"', M11( a!b `""c)[6]);
246 | ASSERT('c', M11( a!b `""c)[7]);
247 | ASSERT(0, M11( a!b `""c)[8]);
248 |
249 | #define paste(x,y) x##y
250 | ASSERT(15, paste(1,5));
251 | ASSERT(255, paste(0,xff));
252 | ASSERT(3, ({ int foobar=3; paste(foo,bar); }));
253 | ASSERT(5, paste(5,));
254 | ASSERT(5, paste(,5));
255 |
256 | #define i 5
257 | ASSERT(101, ({ int i3=100; paste(1+i,3); }));
258 | #undef i
259 |
260 | #define paste2(x) x##5
261 | ASSERT(26, paste2(1+2));
262 |
263 | #define paste3(x) 2##x
264 | ASSERT(23, paste3(1+2));
265 |
266 | #define paste4(x, y, z) x##y##z
267 | ASSERT(123, paste4(1,2,3));
268 |
269 | #define M12
270 | #if defined(M12)
271 | m = 3;
272 | #else
273 | m = 4;
274 | #endif
275 | ASSERT(3, m);
276 |
277 | #define M12
278 | #if defined M12
279 | m = 3;
280 | #else
281 | m = 4;
282 | #endif
283 | ASSERT(3, m);
284 |
285 | #if defined(M12) - 1
286 | m = 3;
287 | #else
288 | m = 4;
289 | #endif
290 | ASSERT(4, m);
291 |
292 | #if defined(NO_SUCH_MACRO)
293 | m = 3;
294 | #else
295 | m = 4;
296 | #endif
297 | ASSERT(4, m);
298 |
299 | #if no_such_symbol == 0
300 | m = 5;
301 | #else
302 | m = 6;
303 | #endif
304 | ASSERT(5, m);
305 |
306 | #define STR(x) #x
307 | #define M12(x) STR(x)
308 | #define M13(x) M12(foo.x)
309 | ASSERT(0, strcmp(M13(bar), "foo.bar"));
310 |
311 | #define M13(x) M12(foo. x)
312 | ASSERT(0, strcmp(M13(bar), "foo. bar"));
313 |
314 | #define M12 foo
315 | #define M13(x) STR(x)
316 | #define M14(x) M13(x.M12)
317 | ASSERT(0, strcmp(M14(bar), "bar.foo"));
318 |
319 | #define M14(x) M13(x. M12)
320 | ASSERT(0, strcmp(M14(bar), "bar. foo"));
321 |
322 | #include "include3.h"
323 | ASSERT(3, foo);
324 |
325 | #include "include4.h"
326 | ASSERT(4, foo);
327 |
328 | #define M13 "include3.h"
329 | #include M13
330 | ASSERT(3, foo);
331 |
332 | #define M13 < include4.h
333 | #include M13 >
334 | ASSERT(4, foo);
335 |
336 | #undef foo
337 |
338 | ASSERT(1, __STDC__);
339 |
340 | ASSERT(0, strcmp(main_filename1, "test/macro.c"));
341 | ASSERT(5, main_line1);
342 | ASSERT(7, main_line2);
343 | ASSERT(0, strcmp(include1_filename, "test/include1.h"));
344 | ASSERT(4, include1_line);
345 |
346 | #define M14(...) 3
347 | ASSERT(3, M14());
348 |
349 | #define M14(...) __VA_ARGS__
350 | ASSERT(2, M14() 2);
351 | ASSERT(5, M14(5));
352 |
353 | #define M14(...) add2(__VA_ARGS__)
354 | ASSERT(8, M14(2, 6));
355 |
356 | #define M14(...) add6(1,2,__VA_ARGS__,6)
357 | ASSERT(21, M14(3,4,5));
358 |
359 | #define M14(x, ...) add6(1,2,x,__VA_ARGS__,6)
360 | ASSERT(21, M14(3,4,5));
361 |
362 | #define M14(args...) 3
363 | ASSERT(3, M14());
364 |
365 | #define M14(x, ...) x
366 | ASSERT(5, M14(5));
367 |
368 | #define M14(args...) args
369 | ASSERT(2, M14() 2);
370 | ASSERT(5, M14(5));
371 |
372 | #define M14(args...) add2(args)
373 | ASSERT(8, M14(2, 6));
374 |
375 | #define M14(args...) add6(1,2,args,6)
376 | ASSERT(21, M14(3,4,5));
377 |
378 | #define M14(x, args...) add6(1,2,x,args,6)
379 | ASSERT(21, M14(3,4,5));
380 |
381 | #define M14(x, args...) x
382 | ASSERT(5, M14(5));
383 |
384 | #define CONCAT(x,y) x##y
385 | ASSERT(5, ({ int f0zz=5; CONCAT(f,0zz); }));
386 | ASSERT(5, ({ CONCAT(4,.57) + 0.5; }));
387 |
388 | ASSERT(11, strlen(__DATE__));
389 | ASSERT(8, strlen(__TIME__));
390 |
391 | ASSERT(0, __COUNTER__);
392 | ASSERT(1, __COUNTER__);
393 | ASSERT(2, __COUNTER__);
394 |
395 | ASSERT(24, strlen(__TIMESTAMP__));
396 |
397 | ASSERT(0, strcmp(__BASE_FILE__, "test/macro.c"));
398 |
399 | #define M30(buf, fmt, ...) sprintf(buf, fmt __VA_OPT__(,) __VA_ARGS__)
400 | ASSERT(0, ({ char buf[100]; M30(buf, "foo"); strcmp(buf, "foo"); }));
401 | ASSERT(0, ({ char buf[100]; M30(buf, "foo%d", 3); strcmp(buf, "foo3"); }));
402 | ASSERT(0, ({ char buf[100]; M30(buf, "foo%d%d", 3, 5); strcmp(buf, "foo35"); }));
403 |
404 | #define M31(buf, fmt, ...) sprintf(buf, fmt, ## __VA_ARGS__)
405 | ASSERT(0, ({ char buf[100]; M31(buf, "foo"); strcmp(buf, "foo"); }));
406 | ASSERT(0, ({ char buf[100]; M31(buf, "foo%d", 3); strcmp(buf, "foo3"); }));
407 | ASSERT(0, ({ char buf[100]; M31(buf, "foo%d%d", 3, 5); strcmp(buf, "foo35"); }));
408 |
409 | #define M31(x, y) (1, ##x y)
410 | ASSERT(3, M31(, 3));
411 |
412 | printf("OK\n");
413 | return 0;
414 | }
415 |
--------------------------------------------------------------------------------
/test/offsetof.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 | #include
3 |
4 | typedef struct {
5 | int a;
6 | char b;
7 | int c;
8 | double d;
9 | } T;
10 |
11 | int main() {
12 | ASSERT(0, offsetof(T, a));
13 | ASSERT(4, offsetof(T, b));
14 | ASSERT(8, offsetof(T, c));
15 | ASSERT(16, offsetof(T, d));
16 |
17 | printf("OK\n");
18 | return 0;
19 | }
20 |
--------------------------------------------------------------------------------
/test/pointer.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | ASSERT(3, ({ int x=3; *&x; }));
5 | ASSERT(3, ({ int x=3; int *y=&x; int **z=&y; **z; }));
6 | ASSERT(5, ({ int x=3; int y=5; *(&x+1); }));
7 | ASSERT(3, ({ int x=3; int y=5; *(&y-1); }));
8 | ASSERT(5, ({ int x=3; int y=5; *(&x-(-1)); }));
9 | ASSERT(5, ({ int x=3; int *y=&x; *y=5; x; }));
10 | ASSERT(7, ({ int x=3; int y=5; *(&x+1)=7; y; }));
11 | ASSERT(7, ({ int x=3; int y=5; *(&y-2+1)=7; x; }));
12 | ASSERT(5, ({ int x=3; (&x+2)-&x+3; }));
13 | ASSERT(8, ({ int x, y; x=3; y=5; x+y; }));
14 | ASSERT(8, ({ int x=3, y=5; x+y; }));
15 |
16 | ASSERT(3, ({ int x[2]; int *y=&x; *y=3; *x; }));
17 |
18 | ASSERT(3, ({ int x[3]; *x=3; *(x+1)=4; *(x+2)=5; *x; }));
19 | ASSERT(4, ({ int x[3]; *x=3; *(x+1)=4; *(x+2)=5; *(x+1); }));
20 | ASSERT(5, ({ int x[3]; *x=3; *(x+1)=4; *(x+2)=5; *(x+2); }));
21 |
22 | ASSERT(0, ({ int x[2][3]; int *y=x; *y=0; **x; }));
23 | ASSERT(1, ({ int x[2][3]; int *y=x; *(y+1)=1; *(*x+1); }));
24 | ASSERT(2, ({ int x[2][3]; int *y=x; *(y+2)=2; *(*x+2); }));
25 | ASSERT(3, ({ int x[2][3]; int *y=x; *(y+3)=3; **(x+1); }));
26 | ASSERT(4, ({ int x[2][3]; int *y=x; *(y+4)=4; *(*(x+1)+1); }));
27 | ASSERT(5, ({ int x[2][3]; int *y=x; *(y+5)=5; *(*(x+1)+2); }));
28 |
29 | ASSERT(3, ({ int x[3]; *x=3; x[1]=4; x[2]=5; *x; }));
30 | ASSERT(4, ({ int x[3]; *x=3; x[1]=4; x[2]=5; *(x+1); }));
31 | ASSERT(5, ({ int x[3]; *x=3; x[1]=4; x[2]=5; *(x+2); }));
32 | ASSERT(5, ({ int x[3]; *x=3; x[1]=4; x[2]=5; *(x+2); }));
33 | ASSERT(5, ({ int x[3]; *x=3; x[1]=4; 2[x]=5; *(x+2); }));
34 |
35 | ASSERT(0, ({ int x[2][3]; int *y=x; y[0]=0; x[0][0]; }));
36 | ASSERT(1, ({ int x[2][3]; int *y=x; y[1]=1; x[0][1]; }));
37 | ASSERT(2, ({ int x[2][3]; int *y=x; y[2]=2; x[0][2]; }));
38 | ASSERT(3, ({ int x[2][3]; int *y=x; y[3]=3; x[1][0]; }));
39 | ASSERT(4, ({ int x[2][3]; int *y=x; y[4]=4; x[1][1]; }));
40 | ASSERT(5, ({ int x[2][3]; int *y=x; y[5]=5; x[1][2]; }));
41 |
42 | printf("OK\n");
43 | return 0;
44 | }
45 |
--------------------------------------------------------------------------------
/test/pragma-once.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | #pragma once
4 |
5 | #include "test/pragma-once.c"
6 |
7 | int main() {
8 | printf("OK\n");
9 | return 0;
10 | }
11 |
--------------------------------------------------------------------------------
/test/sizeof.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | ASSERT(1, sizeof(char));
5 | ASSERT(2, sizeof(short));
6 | ASSERT(2, sizeof(short int));
7 | ASSERT(2, sizeof(int short));
8 | ASSERT(4, sizeof(int));
9 | ASSERT(8, sizeof(long));
10 | ASSERT(8, sizeof(long int));
11 | ASSERT(8, sizeof(long int));
12 | ASSERT(8, sizeof(char *));
13 | ASSERT(8, sizeof(int *));
14 | ASSERT(8, sizeof(long *));
15 | ASSERT(8, sizeof(int **));
16 | ASSERT(8, sizeof(int(*)[4]));
17 | ASSERT(32, sizeof(int*[4]));
18 | ASSERT(16, sizeof(int[4]));
19 | ASSERT(48, sizeof(int[3][4]));
20 | ASSERT(8, sizeof(struct {int a; int b;}));
21 |
22 | ASSERT(8, sizeof(-10 + (long)5));
23 | ASSERT(8, sizeof(-10 - (long)5));
24 | ASSERT(8, sizeof(-10 * (long)5));
25 | ASSERT(8, sizeof(-10 / (long)5));
26 | ASSERT(8, sizeof((long)-10 + 5));
27 | ASSERT(8, sizeof((long)-10 - 5));
28 | ASSERT(8, sizeof((long)-10 * 5));
29 | ASSERT(8, sizeof((long)-10 / 5));
30 |
31 | ASSERT(1, ({ char i; sizeof(++i); }));
32 | ASSERT(1, ({ char i; sizeof(i++); }));
33 |
34 | ASSERT(8, sizeof(int(*)[10]));
35 | ASSERT(8, sizeof(int(*)[][10]));
36 |
37 | ASSERT(4, sizeof(struct { int x, y[]; }));
38 |
39 | ASSERT(1, sizeof(char));
40 | ASSERT(1, sizeof(signed char));
41 | ASSERT(1, sizeof(signed char signed));
42 | ASSERT(1, sizeof(unsigned char));
43 | ASSERT(1, sizeof(unsigned char unsigned));
44 |
45 | ASSERT(2, sizeof(short));
46 | ASSERT(2, sizeof(int short));
47 | ASSERT(2, sizeof(short int));
48 | ASSERT(2, sizeof(signed short));
49 | ASSERT(2, sizeof(int short signed));
50 | ASSERT(2, sizeof(unsigned short));
51 | ASSERT(2, sizeof(int short unsigned));
52 |
53 | ASSERT(4, sizeof(int));
54 | ASSERT(4, sizeof(signed int));
55 | ASSERT(4, sizeof(signed));
56 | ASSERT(4, sizeof(signed signed));
57 | ASSERT(4, sizeof(unsigned int));
58 | ASSERT(4, sizeof(unsigned));
59 | ASSERT(4, sizeof(unsigned unsigned));
60 |
61 | ASSERT(8, sizeof(long));
62 | ASSERT(8, sizeof(signed long));
63 | ASSERT(8, sizeof(signed long int));
64 | ASSERT(8, sizeof(unsigned long));
65 | ASSERT(8, sizeof(unsigned long int));
66 |
67 | ASSERT(8, sizeof(long long));
68 | ASSERT(8, sizeof(signed long long));
69 | ASSERT(8, sizeof(signed long long int));
70 | ASSERT(8, sizeof(unsigned long long));
71 | ASSERT(8, sizeof(unsigned long long int));
72 |
73 | ASSERT(1, sizeof((char)1));
74 | ASSERT(2, sizeof((short)1));
75 | ASSERT(4, sizeof((int)1));
76 | ASSERT(8, sizeof((long)1));
77 |
78 | ASSERT(4, sizeof((char)1 + (char)1));
79 | ASSERT(4, sizeof((short)1 + (short)1));
80 | ASSERT(4, sizeof(1?2:3));
81 | ASSERT(4, sizeof(1?(short)2:(char)3));
82 | ASSERT(8, sizeof(1?(long)2:(char)3));
83 |
84 | ASSERT(1, sizeof(char) << 31 >> 31);
85 | ASSERT(1, sizeof(char) << 63 >> 63);
86 |
87 | ASSERT(4, sizeof(float));
88 | ASSERT(8, sizeof(double));
89 |
90 | ASSERT(4, sizeof(1f+2));
91 | ASSERT(8, sizeof(1.0+2));
92 | ASSERT(4, sizeof(1f-2));
93 | ASSERT(8, sizeof(1.0-2));
94 | ASSERT(4, sizeof(1f*2));
95 | ASSERT(8, sizeof(1.0*2));
96 | ASSERT(4, sizeof(1f/2));
97 | ASSERT(8, sizeof(1.0/2));
98 |
99 | ASSERT(16, sizeof(long double));
100 |
101 | ASSERT(1, sizeof(main));
102 |
103 | printf("OK\n");
104 | return 0;
105 | }
106 |
--------------------------------------------------------------------------------
/test/stdhdr.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | int main() {
10 | printf("OK\n");
11 | return 0;
12 | }
13 |
--------------------------------------------------------------------------------
/test/string.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | ASSERT(0, ""[0]);
5 | ASSERT(1, sizeof(""));
6 |
7 | ASSERT(97, "abc"[0]);
8 | ASSERT(98, "abc"[1]);
9 | ASSERT(99, "abc"[2]);
10 | ASSERT(0, "abc"[3]);
11 | ASSERT(4, sizeof("abc"));
12 |
13 | ASSERT(7, "\a"[0]);
14 | ASSERT(8, "\b"[0]);
15 | ASSERT(9, "\t"[0]);
16 | ASSERT(10, "\n"[0]);
17 | ASSERT(11, "\v"[0]);
18 | ASSERT(12, "\f"[0]);
19 | ASSERT(13, "\r"[0]);
20 | ASSERT(27, "\e"[0]);
21 |
22 | ASSERT(106, "\j"[0]);
23 | ASSERT(107, "\k"[0]);
24 | ASSERT(108, "\l"[0]);
25 |
26 | ASSERT(7, "\ax\ny"[0]);
27 | ASSERT(120, "\ax\ny"[1]);
28 | ASSERT(10, "\ax\ny"[2]);
29 | ASSERT(121, "\ax\ny"[3]);
30 |
31 | ASSERT(0, "\0"[0]);
32 | ASSERT(16, "\20"[0]);
33 | ASSERT(65, "\101"[0]);
34 | ASSERT(104, "\1500"[0]);
35 | ASSERT(0, "\x00"[0]);
36 | ASSERT(119, "\x77"[0]);
37 |
38 | ASSERT(7, sizeof("abc" "def"));
39 | ASSERT(9, sizeof("abc" "d" "efgh"));
40 | ASSERT(0, strcmp("abc" "d" "\nefgh", "abcd\nefgh"));
41 | ASSERT(0, !strcmp("abc" "d", "abcd\nefgh"));
42 | ASSERT(0, strcmp("\x9" "0", "\t0"));
43 |
44 | ASSERT(16, sizeof(L"abc" ""));
45 |
46 | ASSERT(28, sizeof(L"abc" "def"));
47 | ASSERT(28, sizeof(L"abc" L"def"));
48 | ASSERT(14, sizeof(u"abc" "def"));
49 | ASSERT(14, sizeof(u"abc" u"def"));
50 |
51 | ASSERT(L'a', (L"abc" "def")[0]);
52 | ASSERT(L'd', (L"abc" "def")[3]);
53 | ASSERT(L'\0', (L"abc" "def")[6]);
54 |
55 | ASSERT(u'a', (u"abc" "def")[0]);
56 | ASSERT(u'd', (u"abc" "def")[3]);
57 | ASSERT(u'\0', (u"abc" "def")[6]);
58 |
59 | ASSERT(L'あ', ("あ" L"")[0]);
60 | ASSERT(0343, ("\343\201\202" L"")[0]);
61 | ASSERT(0201, ("\343\201\202" L"")[1]);
62 | ASSERT(0202, ("\343\201\202" L"")[2]);
63 | ASSERT(0, ("\343\201\202" L"")[3]);
64 |
65 | ASSERT(L'a', ("a" "b" L"c")[0]);
66 | ASSERT(L'b', ("a" "b" L"c")[1]);
67 | ASSERT(L'c', ("a" "b" L"c")[2]);
68 | ASSERT(0, ("a" "b" L"c")[3]);
69 |
70 | printf("OK\n");
71 | return 0;
72 | }
73 |
--------------------------------------------------------------------------------
/test/struct.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | ASSERT(1, ({ struct {int a; int b;} x; x.a=1; x.b=2; x.a; }));
5 | ASSERT(2, ({ struct {int a; int b;} x; x.a=1; x.b=2; x.b; }));
6 | ASSERT(1, ({ struct {char a; int b; char c;} x; x.a=1; x.b=2; x.c=3; x.a; }));
7 | ASSERT(2, ({ struct {char a; int b; char c;} x; x.b=1; x.b=2; x.c=3; x.b; }));
8 | ASSERT(3, ({ struct {char a; int b; char c;} x; x.a=1; x.b=2; x.c=3; x.c; }));
9 |
10 | ASSERT(0, ({ struct {char a; char b;} x[3]; char *p=x; p[0]=0; x[0].a; }));
11 | ASSERT(1, ({ struct {char a; char b;} x[3]; char *p=x; p[1]=1; x[0].b; }));
12 | ASSERT(2, ({ struct {char a; char b;} x[3]; char *p=x; p[2]=2; x[1].a; }));
13 | ASSERT(3, ({ struct {char a; char b;} x[3]; char *p=x; p[3]=3; x[1].b; }));
14 |
15 | ASSERT(6, ({ struct {char a[3]; char b[5];} x; char *p=&x; x.a[0]=6; p[0]; }));
16 | ASSERT(7, ({ struct {char a[3]; char b[5];} x; char *p=&x; x.b[0]=7; p[3]; }));
17 |
18 | ASSERT(6, ({ struct { struct { char b; } a; } x; x.a.b=6; x.a.b; }));
19 |
20 | ASSERT(4, ({ struct {int a;} x; sizeof(x); }));
21 | ASSERT(8, ({ struct {int a; int b;} x; sizeof(x); }));
22 | ASSERT(8, ({ struct {int a, b;} x; sizeof(x); }));
23 | ASSERT(12, ({ struct {int a[3];} x; sizeof(x); }));
24 | ASSERT(16, ({ struct {int a;} x[4]; sizeof(x); }));
25 | ASSERT(24, ({ struct {int a[3];} x[2]; sizeof(x); }));
26 | ASSERT(2, ({ struct {char a; char b;} x; sizeof(x); }));
27 | ASSERT(0, ({ struct {} x; sizeof(x); }));
28 | ASSERT(8, ({ struct {char a; int b;} x; sizeof(x); }));
29 | ASSERT(8, ({ struct {int a; char b;} x; sizeof(x); }));
30 |
31 | ASSERT(8, ({ struct t {int a; int b;} x; struct t y; sizeof(y); }));
32 | ASSERT(8, ({ struct t {int a; int b;}; struct t y; sizeof(y); }));
33 | ASSERT(2, ({ struct t {char a[2];}; { struct t {char a[4];}; } struct t y; sizeof(y); }));
34 | ASSERT(3, ({ struct t {int x;}; int t=1; struct t y; y.x=2; t+y.x; }));
35 |
36 | ASSERT(3, ({ struct t {char a;} x; struct t *y = &x; x.a=3; y->a; }));
37 | ASSERT(3, ({ struct t {char a;} x; struct t *y = &x; y->a=3; x.a; }));
38 |
39 | ASSERT(3, ({ struct {int a,b;} x,y; x.a=3; y=x; y.a; }));
40 | ASSERT(7, ({ struct t {int a,b;}; struct t x; x.a=7; struct t y; struct t *z=&y; *z=x; y.a; }));
41 | ASSERT(7, ({ struct t {int a,b;}; struct t x; x.a=7; struct t y, *p=&x, *q=&y; *q=*p; y.a; }));
42 | ASSERT(5, ({ struct t {char a, b;} x, y; x.a=5; y=x; y.a; }));
43 |
44 | ASSERT(3, ({ struct {int a,b;} x,y; x.a=3; y=x; y.a; }));
45 | ASSERT(7, ({ struct t {int a,b;}; struct t x; x.a=7; struct t y; struct t *z=&y; *z=x; y.a; }));
46 | ASSERT(7, ({ struct t {int a,b;}; struct t x; x.a=7; struct t y, *p=&x, *q=&y; *q=*p; y.a; }));
47 | ASSERT(5, ({ struct t {char a, b;} x, y; x.a=5; y=x; y.a; }));
48 |
49 | ASSERT(8, ({ struct t {int a; int b;} x; struct t y; sizeof(y); }));
50 | ASSERT(8, ({ struct t {int a; int b;}; struct t y; sizeof(y); }));
51 |
52 | ASSERT(16, ({ struct {char a; long b;} x; sizeof(x); }));
53 | ASSERT(4, ({ struct {char a; short b;} x; sizeof(x); }));
54 |
55 | ASSERT(8, ({ struct foo *bar; sizeof(bar); }));
56 | ASSERT(4, ({ struct T *foo; struct T {int x;}; sizeof(struct T); }));
57 | ASSERT(1, ({ struct T { struct T *next; int x; } a; struct T b; b.x=1; a.next=&b; a.next->x; }));
58 | ASSERT(4, ({ typedef struct T T; struct T { int x; }; sizeof(T); }));
59 |
60 | ASSERT(2, ({ struct {int a;} x={1}, y={2}; (x=y).a; }));
61 | ASSERT(1, ({ struct {int a;} x={1}, y={2}; (1?x:y).a; }));
62 | ASSERT(2, ({ struct {int a;} x={1}, y={2}; (0?x:y).a; }));
63 |
64 | printf("OK\n");
65 | return 0;
66 | }
67 |
--------------------------------------------------------------------------------
/test/test.h:
--------------------------------------------------------------------------------
1 | #define ASSERT(x, y) assert(x, y, #y)
2 |
3 | void assert(int expected, int actual, char *code);
4 | int printf(char *fmt, ...);
5 | int sprintf(char *buf, char *fmt, ...);
6 | int vsprintf(char *buf, char *fmt, void *ap);
7 | int strcmp(char *p, char *q);
8 | int strncmp(char *p, char *q, long n);
9 | int memcmp(char *p, char *q, long n);
10 | void exit(int n);
11 | int vsprintf();
12 | long strlen(char *s);
13 | void *memcpy(void *dest, void *src, long n);
14 | void *memset(void *s, int c, long n);
15 |
--------------------------------------------------------------------------------
/test/thirdparty/common:
--------------------------------------------------------------------------------
1 | make="make -j$(nproc)"
2 | xcc=`pwd`/xcc
3 |
4 | dir=$(basename -s .git $repo)
5 |
6 | set -e -x
7 |
8 | mkdir -p thirdparty
9 | cd thirdparty
10 | [ -d $dir ] || git clone $repo
11 | cd $dir
12 |
--------------------------------------------------------------------------------
/test/thirdparty/cpython.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | repo='git@github.com:python/cpython.git'
3 | . test/thirdparty/common
4 | git reset --hard c75330605d4795850ec74fdc4d69aa5d92f76c00
5 |
6 | # Python's './configure' command misidentifies xcc as icc
7 | # (Intel C Compiler) because icc is a substring of xcc.
8 | # Modify the configure file as a workaround.
9 | sed -i -e 1996,2011d configure.ac
10 | autoreconf
11 |
12 | CC=$xcc ./configure
13 | $make clean
14 | $make
15 | $make test
16 |
--------------------------------------------------------------------------------
/test/thirdparty/git.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | repo='git@github.com:git/git.git'
3 | . test/thirdparty/common
4 | git reset --hard 54e85e7af1ac9e9a92888060d6811ae767fea1bc
5 |
6 | $make clean
7 | $make V=1 CC=$xcc test
8 |
--------------------------------------------------------------------------------
/test/thirdparty/libpng.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | repo='git@github.com:rui314/libpng.git'
3 | . test/thirdparty/common
4 | git reset --hard dbe3e0c43e549a1602286144d94b0666549b18e6
5 |
6 | CC=$xcc ./configure
7 | sed -i 's/^wl=.*/wl=-Wl,/; s/^pic_flag=.*/pic_flag=-fPIC/' libtool
8 | $make clean
9 | $make
10 | $make test
11 |
--------------------------------------------------------------------------------
/test/thirdparty/sqlite.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | repo='git@github.com:sqlite/sqlite.git'
3 | . test/thirdparty/common
4 | git reset --hard 86f477edaa17767b39c7bae5b67cac8580f7a8c1
5 |
6 | CC=$xcc CFLAGS=-D_GNU_SOURCE ./configure
7 | sed -i 's/^wl=.*/wl=-Wl,/; s/^pic_flag=.*/pic_flag=-fPIC/' libtool
8 | $make clean
9 | $make
10 | $make test
11 |
--------------------------------------------------------------------------------
/test/thirdparty/tinycc.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | repo='git@github.com:TinyCC/tinycc.git'
3 | . test/thirdparty/common
4 | git reset --hard df67d8617b7d1d03a480a28f9f901848ffbfb7ec
5 |
6 | ./configure --cc=$xcc
7 | $make clean
8 | $make
9 | $make CC=cc test
10 |
--------------------------------------------------------------------------------
/test/tls.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 | #include
3 | #include
4 |
5 | _Thread_local int v1;
6 | _Thread_local int v2 = 5;
7 | int v3 = 7;
8 |
9 | int thread_main(void *unused) {
10 | ASSERT(0, v1);
11 | ASSERT(5, v2);
12 | ASSERT(7, v3);
13 |
14 | v1 = 1;
15 | v2 = 2;
16 | v3 = 3;
17 |
18 | ASSERT(1, v1);
19 | ASSERT(2, v2);
20 | ASSERT(3, v3);
21 |
22 | return 0;
23 | }
24 |
25 | int main() {
26 | pthread_t thr;
27 |
28 | ASSERT(0, v1);
29 | ASSERT(5, v2);
30 | ASSERT(7, v3);
31 |
32 | ASSERT(0, pthread_create(&thr, NULL, thread_main, NULL));
33 | ASSERT(0, pthread_join(thr, NULL));
34 |
35 | ASSERT(0, v1);
36 | ASSERT(5, v2);
37 | ASSERT(3, v3);
38 |
39 | printf("OK\n");
40 | return 0;
41 | }
42 |
--------------------------------------------------------------------------------
/test/typedef.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | typedef int MyInt, MyInt2[4];
4 | typedef int;
5 |
6 | int main() {
7 | ASSERT(1, ({ typedef int t; t x=1; x; }));
8 | ASSERT(1, ({ typedef struct {int a;} t; t x; x.a=1; x.a; }));
9 | ASSERT(1, ({ typedef int t; t t=1; t; }));
10 | ASSERT(2, ({ typedef struct {int a;} t; { typedef int t; } t x; x.a=2; x.a; }));
11 | ASSERT(4, ({ typedef t; t x; sizeof(x); }));
12 | ASSERT(3, ({ MyInt x=3; x; }));
13 | ASSERT(16, ({ MyInt2 x; sizeof(x); }));
14 |
15 | printf("OK\n");
16 | return 0;
17 | }
18 |
--------------------------------------------------------------------------------
/test/typeof.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | ASSERT(3, ({ typeof(int) x=3; x; }));
5 | ASSERT(3, ({ typeof(1) x=3; x; }));
6 | ASSERT(4, ({ int x; typeof(x) y; sizeof(y); }));
7 | ASSERT(8, ({ int x; typeof(&x) y; sizeof(y); }));
8 | ASSERT(4, ({ typeof("foo") x; sizeof(x); }));
9 | ASSERT(12, sizeof(typeof(struct { int a,b,c; })));
10 |
11 | printf("OK\n");
12 | return 0;
13 | }
14 |
--------------------------------------------------------------------------------
/test/unicode.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | #define STR(x) #x
4 |
5 | typedef unsigned short char16_t;
6 | typedef unsigned int char32_t;
7 | typedef int wchar_t;
8 |
9 | int π = 3;
10 |
11 | int main() {
12 | ASSERT(4, sizeof(L'\0'));
13 | ASSERT(97, L'a');
14 |
15 | ASSERT(0, strcmp("αβγ", "\u03B1\u03B2\u03B3"));
16 | ASSERT(0, strcmp("日本語", "\u65E5\u672C\u8A9E"));
17 | ASSERT(0, strcmp("日本語", "\U000065E5\U0000672C\U00008A9E"));
18 | ASSERT(0, strcmp("🌮", "\U0001F32E"));
19 |
20 | ASSERT(-1, L'\xffffffff'>>31);
21 | ASSERT(946, L'β');
22 | ASSERT(12354, L'あ');
23 | ASSERT(127843, L'🍣');
24 |
25 | ASSERT(2, sizeof(u'\0'));
26 | ASSERT(1, u'\xffff'>>15);
27 | ASSERT(97, u'a');
28 | ASSERT(946, u'β');
29 | ASSERT(12354, u'あ');
30 | ASSERT(62307, u'🍣');
31 |
32 | ASSERT(0, strcmp(STR(u'a'), "u'a'"));
33 |
34 | ASSERT(4, sizeof(U'\0'));
35 | ASSERT(1, U'\xffffffff'>>31);
36 | ASSERT(97, U'a');
37 | ASSERT(946, U'β');
38 | ASSERT(12354, U'あ');
39 | ASSERT(127843, U'🍣');
40 |
41 | ASSERT(0, strcmp(STR(U'a'), "U'a'"));
42 |
43 | ASSERT(4, sizeof(u8"abc"));
44 | ASSERT(0, strcmp(u8"abc", "abc"));
45 |
46 | ASSERT(0, strcmp(STR(u8"a"), "u8\"a\""));
47 |
48 | ASSERT(2, sizeof(u""));
49 | ASSERT(10, sizeof(u"\xffzzz"));
50 | ASSERT(0, memcmp(u"", "\0\0", 2));
51 | ASSERT(0, memcmp(u"abc", "a\0b\0c\0\0\0", 8));
52 | ASSERT(0, memcmp(u"日本語", "\345e,g\236\212\0\0", 8));
53 | ASSERT(0, memcmp(u"🍣", "<\330c\337\0\0", 6));
54 | ASSERT(u'β', u"βb"[0]);
55 | ASSERT(u'b', u"βb"[1]);
56 | ASSERT(0, u"βb"[2]);
57 |
58 | ASSERT(0, strcmp(STR(u"a"), "u\"a\""));
59 |
60 | ASSERT(4, sizeof(U""));
61 | ASSERT(20, sizeof(U"\xffzzz"));
62 | ASSERT(0, memcmp(U"", "\0\0\0\0", 4));
63 | ASSERT(0, memcmp(U"abc", "a\0\0\0b\0\0\0c\0\0\0\0\0\0\0", 16));
64 | ASSERT(0, memcmp(U"日本語", "\345e\0\0,g\0\0\236\212\0\0\0\0\0\0", 16));
65 | ASSERT(0, memcmp(U"🍣", "c\363\001\0\0\0\0\0", 8));
66 | ASSERT(u'β', U"βb"[0]);
67 | ASSERT(u'b', U"βb"[1]);
68 | ASSERT(0, U"βb"[2]);
69 | ASSERT(1, U"\xffffffff"[0] >> 31);
70 |
71 | ASSERT(0, strcmp(STR(U"a"), "U\"a\""));
72 |
73 | ASSERT(4, sizeof(L""));
74 | ASSERT(20, sizeof(L"\xffzzz"));
75 | ASSERT(0, memcmp(L"", "\0\0\0\0", 4));
76 | ASSERT(0, memcmp(L"abc", "a\0\0\0b\0\0\0c\0\0\0\0\0\0\0", 16));
77 | ASSERT(0, memcmp(L"日本語", "\345e\0\0,g\0\0\236\212\0\0\0\0\0\0", 16));
78 | ASSERT(0, memcmp(L"🍣", "c\363\001\0\0\0\0\0", 8));
79 | ASSERT(u'β', L"βb"[0]);
80 | ASSERT(u'b', L"βb"[1]);
81 | ASSERT(0, L"βb"[2]);
82 | ASSERT(-1, L"\xffffffff"[0] >> 31);
83 |
84 | ASSERT(0, strcmp(STR(L"a"), "L\"a\""));
85 |
86 | ASSERT(u'α', ({ char16_t x[] = u"αβ"; x[0]; }));
87 | ASSERT(u'β', ({ char16_t x[] = u"αβ"; x[1]; }));
88 | ASSERT(6, ({ char16_t x[] = u"αβ"; sizeof(x); }));
89 |
90 | ASSERT(U'🤔', ({ char32_t x[] = U"🤔x"; x[0]; }));
91 | ASSERT(U'x', ({ char32_t x[] = U"🤔x"; x[1]; }));
92 | ASSERT(12, ({ char32_t x[] = U"🤔x"; sizeof(x); }));
93 |
94 | ASSERT(L'🤔', ({ wchar_t x[] = L"🤔x"; x[0]; }));
95 | ASSERT(L'x', ({ wchar_t x[] = L"🤔x"; x[1]; }));
96 | ASSERT(12, ({ wchar_t x[] = L"🤔x"; sizeof(x); }));
97 |
98 | ASSERT(3, π);
99 | ASSERT(3, ({ int あβ0¾=3; あβ0¾; }));
100 | ASSERT(5, ({ int $$$=5; $$$; }));
101 |
102 | printf("OK\n");
103 | return 0;
104 | }
105 |
--------------------------------------------------------------------------------
/test/union.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | ASSERT(8, ({ union { int a; char b[6]; } x; sizeof(x); }));
5 | ASSERT(3, ({ union { int a; char b[4]; } x; x.a = 515; x.b[0]; }));
6 | ASSERT(2, ({ union { int a; char b[4]; } x; x.a = 515; x.b[1]; }));
7 | ASSERT(0, ({ union { int a; char b[4]; } x; x.a = 515; x.b[2]; }));
8 | ASSERT(0, ({ union { int a; char b[4]; } x; x.a = 515; x.b[3]; }));
9 |
10 | ASSERT(3, ({ union {int a,b;} x,y; x.a=3; y.a=5; y=x; y.a; }));
11 | ASSERT(3, ({ union {struct {int a,b;} c;} x,y; x.c.b=3; y.c.b=5; y=x; y.c.b; }));
12 |
13 | ASSERT(0xef, ({ union { struct { unsigned char a,b,c,d; }; long e; } x; x.e=0xdeadbeef; x.a; }));
14 | ASSERT(0xbe, ({ union { struct { unsigned char a,b,c,d; }; long e; } x; x.e=0xdeadbeef; x.b; }));
15 | ASSERT(0xad, ({ union { struct { unsigned char a,b,c,d; }; long e; } x; x.e=0xdeadbeef; x.c; }));
16 | ASSERT(0xde, ({ union { struct { unsigned char a,b,c,d; }; long e; } x; x.e=0xdeadbeef; x.d; }));
17 |
18 | ASSERT(3, ({struct { union { int a,b; }; union { int c,d; }; } x; x.a=3; x.b; }));
19 | ASSERT(5, ({struct { union { int a,b; }; union { int c,d; }; } x; x.d=5; x.c; }));
20 |
21 | printf("OK\n");
22 | return 0;
23 | }
24 |
--------------------------------------------------------------------------------
/test/usualconv.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | static int ret10(void) { return 10; }
4 |
5 | int main() {
6 | ASSERT((long)-5, -10 + (long)5);
7 | ASSERT((long)-15, -10 - (long)5);
8 | ASSERT((long)-50, -10 * (long)5);
9 | ASSERT((long)-2, -10 / (long)5);
10 |
11 | ASSERT(1, -2 < (long)-1);
12 | ASSERT(1, -2 <= (long)-1);
13 | ASSERT(0, -2 > (long)-1);
14 | ASSERT(0, -2 >= (long)-1);
15 |
16 | ASSERT(1, (long)-2 < -1);
17 | ASSERT(1, (long)-2 <= -1);
18 | ASSERT(0, (long)-2 > -1);
19 | ASSERT(0, (long)-2 >= -1);
20 |
21 | ASSERT(0, 2147483647 + 2147483647 + 2);
22 | ASSERT((long)-1, ({ long x; x=-1; x; }));
23 |
24 | ASSERT(1, ({ char x[3]; x[0]=0; x[1]=1; x[2]=2; char *y=x+1; y[0]; }));
25 | ASSERT(0, ({ char x[3]; x[0]=0; x[1]=1; x[2]=2; char *y=x+1; y[-1]; }));
26 | ASSERT(5, ({ struct t {char a;} x, y; x.a=5; y=x; y.a; }));
27 |
28 | ASSERT(10, (1 ? ret10 : (void *)0)());
29 |
30 | printf("OK\n");
31 | return 0;
32 | }
33 |
--------------------------------------------------------------------------------
/test/varargs.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 | #include
3 |
4 | int sum1(int x, ...) {
5 | va_list ap;
6 | va_start(ap, x);
7 |
8 | for (;;) {
9 | int y = va_arg(ap, int);
10 | if (y == 0)
11 | return x;
12 | x += y;
13 | }
14 | }
15 |
16 | int sum2(int x, ...) {
17 | va_list ap;
18 | va_start(ap, x);
19 |
20 | for (;;) {
21 | double y = va_arg(ap, double);
22 | x += y;
23 |
24 | int z = va_arg(ap, int);
25 | if (z == 0)
26 | return x;
27 | x += z;
28 | }
29 | }
30 |
31 | void fmt(char *buf, char *fmt, ...) {
32 | va_list ap;
33 | va_start(ap, fmt);
34 |
35 | va_list ap2;
36 | va_copy(ap2, ap);
37 | vsprintf(buf, fmt, ap2);
38 | va_end(buf);
39 | }
40 |
41 | int main() {
42 | ASSERT(6, sum1(1, 2, 3, 0));
43 | ASSERT(55, sum1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0));
44 | ASSERT(21, sum2(1, 2.0, 3, 4.0, 5, 6.0, 0));
45 | ASSERT(21, sum2(1, 2.0, 3, 4.0, 5, 6.0, 0));
46 | ASSERT(210, sum2(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0, 11, 12.0, 13, 14.0, 15, 16.0, 17, 18.0, 19, 20.0, 0));
47 | ASSERT(0, ({ char buf[100]; fmt(buf, "%d %d", 2, 3); strcmp(buf, "2 3"); }));
48 |
49 | printf("OK\n");
50 | return 0;
51 | }
52 |
--------------------------------------------------------------------------------
/test/variable.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int g1, g2[4];
4 | static int g3 = 3;
5 |
6 | int main() {
7 | ASSERT(3, ({ int a; a=3; a; }));
8 | ASSERT(3, ({ int a=3; a; }));
9 | ASSERT(8, ({ int a=3; int z=5; a+z; }));
10 |
11 | ASSERT(3, ({ int a=3; a; }));
12 | ASSERT(8, ({ int a=3; int z=5; a+z; }));
13 | ASSERT(6, ({ int a; int b; a=b=3; a+b; }));
14 | ASSERT(3, ({ int foo=3; foo; }));
15 | ASSERT(8, ({ int foo123=3; int bar=5; foo123+bar; }));
16 |
17 | ASSERT(4, ({ int x; sizeof(x); }));
18 | ASSERT(4, ({ int x; sizeof x; }));
19 | ASSERT(8, ({ int *x; sizeof(x); }));
20 | ASSERT(16, ({ int x[4]; sizeof(x); }));
21 | ASSERT(48, ({ int x[3][4]; sizeof(x); }));
22 | ASSERT(16, ({ int x[3][4]; sizeof(*x); }));
23 | ASSERT(4, ({ int x[3][4]; sizeof(**x); }));
24 | ASSERT(5, ({ int x[3][4]; sizeof(**x) + 1; }));
25 | ASSERT(5, ({ int x[3][4]; sizeof **x + 1; }));
26 | ASSERT(4, ({ int x[3][4]; sizeof(**x + 1); }));
27 | ASSERT(4, ({ int x=1; sizeof(x=2); }));
28 | ASSERT(1, ({ int x=1; sizeof(x=2); x; }));
29 |
30 | ASSERT(0, g1);
31 | ASSERT(3, ({ g1=3; g1; }));
32 | ASSERT(0, ({ g2[0]=0; g2[1]=1; g2[2]=2; g2[3]=3; g2[0]; }));
33 | ASSERT(1, ({ g2[0]=0; g2[1]=1; g2[2]=2; g2[3]=3; g2[1]; }));
34 | ASSERT(2, ({ g2[0]=0; g2[1]=1; g2[2]=2; g2[3]=3; g2[2]; }));
35 | ASSERT(3, ({ g2[0]=0; g2[1]=1; g2[2]=2; g2[3]=3; g2[3]; }));
36 |
37 | ASSERT(4, sizeof(g1));
38 | ASSERT(16, sizeof(g2));
39 |
40 | ASSERT(1, ({ char x=1; x; }));
41 | ASSERT(1, ({ char x=1; char y=2; x; }));
42 | ASSERT(2, ({ char x=1; char y=2; y; }));
43 |
44 | ASSERT(1, ({ char x; sizeof(x); }));
45 | ASSERT(10, ({ char x[10]; sizeof(x); }));
46 |
47 | ASSERT(2, ({ int x=2; { int x=3; } x; }));
48 | ASSERT(2, ({ int x=2; { int x=3; } int y=4; x; }));
49 | ASSERT(3, ({ int x=2; { x=3; } x; }));
50 |
51 | ASSERT(7, ({ int x; int y; char z; char *a=&y; char *b=&z; b-a; }));
52 | ASSERT(1, ({ int x; char y; int z; char *a=&y; char *b=&z; b-a; }));
53 |
54 | ASSERT(8, ({ long x; sizeof(x); }));
55 | ASSERT(2, ({ short x; sizeof(x); }));
56 |
57 | ASSERT(24, ({ char *x[3]; sizeof(x); }));
58 | ASSERT(8, ({ char (*x)[3]; sizeof(x); }));
59 | ASSERT(1, ({ char (x); sizeof(x); }));
60 | ASSERT(3, ({ char (x)[3]; sizeof(x); }));
61 | ASSERT(12, ({ char (x[3])[4]; sizeof(x); }));
62 | ASSERT(4, ({ char (x[3])[4]; sizeof(x[0]); }));
63 | ASSERT(3, ({ char *x[3]; char y; x[0]=&y; y=3; x[0][0]; }));
64 | ASSERT(4, ({ char x[3]; char (*y)[3]=x; y[0][0]=4; y[0][0]; }));
65 |
66 | { void *x; }
67 |
68 | ASSERT(3, g3);
69 |
70 | printf("OK\n");
71 | return 0;
72 | }
73 |
--------------------------------------------------------------------------------
/test/vla.c:
--------------------------------------------------------------------------------
1 | #include "test.h"
2 |
3 | int main() {
4 | ASSERT(20, ({ int n=5; int x[n]; sizeof(x); }));
5 | ASSERT((5+1)*(8*2)*4, ({ int m=5, n=8; int x[m+1][n*2]; sizeof(x); }));
6 |
7 | ASSERT(8, ({ char n=10; int (*x)[n][n+2]; sizeof(x); }));
8 | ASSERT(480, ({ char n=10; int (*x)[n][n+2]; sizeof(*x); }));
9 | ASSERT(48, ({ char n=10; int (*x)[n][n+2]; sizeof(**x); }));
10 | ASSERT(4, ({ char n=10; int (*x)[n][n+2]; sizeof(***x); }));
11 |
12 | ASSERT(60, ({ char n=3; int x[5][n]; sizeof(x); }));
13 | ASSERT(12, ({ char n=3; int x[5][n]; sizeof(*x); }));
14 |
15 | ASSERT(60, ({ char n=3; int x[n][5]; sizeof(x); }));
16 | ASSERT(20, ({ char n=3; int x[n][5]; sizeof(*x); }));
17 |
18 | ASSERT(0, ({ int n=10; int x[n+1][n+6]; int *p=x; for (int i = 0; i
28 | static void verror_at(char *filename, char *input, unsigned line_no,
29 | char *loc, char *fmt, va_list ap) {
30 | // Find a line containing `loc`.
31 | char *line = loc;
32 | while (input < line && line[-1] != '\n')
33 | line--;
34 |
35 | char *end = loc;
36 | while (*end && *end != '\n')
37 | end++;
38 |
39 | // Print out the line.
40 | int indent = fprintf(stderr, "%s:%u: ", filename, line_no);
41 | fprintf(stderr, "%.*s\n", (int)(end - line), line);
42 |
43 | // Show the error message.
44 | int pos = display_width(line, loc - line) + indent;
45 |
46 | fprintf(stderr, "%*s", pos, ""); // print pos spaces.
47 | fprintf(stderr, "^ ");
48 | vfprintf(stderr, fmt, ap);
49 | fprintf(stderr, "\n");
50 | }
51 |
52 | void error_at(char *loc, char *fmt, ...) {
53 | unsigned line_no = 1;
54 | for (char *p = current_file->contents; p < loc; p++)
55 | if (*p == '\n')
56 | line_no++;
57 |
58 | va_list ap;
59 | va_start(ap, fmt);
60 | verror_at(current_file->name, current_file->contents, line_no, loc, fmt, ap);
61 | va_end(ap);
62 | exit(1);
63 | }
64 |
65 | void error_tok(Token *tok, char *fmt, ...) {
66 | va_list ap;
67 | va_start(ap, fmt);
68 | verror_at(tok->file->name, tok->file->contents, tok->line_no, tok->loc, fmt, ap);
69 | va_end(ap);
70 | exit(1);
71 | }
72 |
73 | void warn_tok(Token *tok, char *fmt, ...) {
74 | va_list ap;
75 | va_start(ap, fmt);
76 | verror_at(tok->file->name, tok->file->contents, tok->line_no, tok->loc, fmt, ap);
77 | va_end(ap);
78 | }
79 |
80 | // Consumes the current token if it matches `op`.
81 | bool equal(Token *tok, char *op) {
82 | return memcmp(tok->loc, op, tok->len) == 0 && op[tok->len] == '\0';
83 | }
84 |
85 | // Ensure that the current token is `op`.
86 | Token *skip(Token *tok, char *op) {
87 | if (!equal(tok, op))
88 | error_tok(tok, "expected '%s'", op);
89 | return tok->next;
90 | }
91 |
92 | bool consume(Token **rest, Token *tok, char *str) {
93 | if (equal(tok, str)) {
94 | *rest = tok->next;
95 | return true;
96 | }
97 | *rest = tok;
98 | return false;
99 | }
100 |
101 | // Create a new token.
102 | static Token *new_token(TokenKind kind, char *start, char *end) {
103 | Token *tok = calloc(1, sizeof(Token));
104 | tok->kind = kind;
105 | tok->loc = start;
106 | tok->len = end - start;
107 | tok->file = current_file;
108 | tok->filename = current_file->display_name;
109 | tok->at_bol = at_bol;
110 | tok->has_space = has_space;
111 |
112 | at_bol = has_space = false;
113 | return tok;
114 | }
115 |
116 | static bool startswith(char *p, char *q) {
117 | return strncmp(p, q, strlen(q)) == 0;
118 | }
119 |
120 | // Read an identifier and returns the length of it.
121 | // If p does not point to a valid identifier, 0 is returned.
122 | static int read_ident(char *start) {
123 | char *p = start;
124 | uint32_t c = decode_utf8(&p, p);
125 | if (!is_ident1(c))
126 | return 0;
127 |
128 | for (;;) {
129 | char *q;
130 | c = decode_utf8(&q, p);
131 | if (!is_ident2(c))
132 | return p - start;
133 | p = q;
134 | }
135 | }
136 |
137 | static int from_hex(char c) {
138 | if ('0' <= c && c <= '9')
139 | return c - '0';
140 | if ('a' <= c && c <= 'f')
141 | return c - 'a' + 10;
142 | return c - 'A' + 10;
143 | }
144 |
145 | // Read a punctuator token from p and returns its length.
146 | static int read_punct(char *p) {
147 | static char *kw[] = {
148 | "<<=", ">>=", "...", "==", "!=", "<=", ">=", "->", "+=",
149 | "-=", "*=", "/=", "++", "--", "%=", "&=", "|=", "^=", "&&",
150 | "||", "<<", ">>", "##"
151 | };
152 |
153 | for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++)
154 | if (startswith(p, kw[i]))
155 | return strlen(kw[i]);
156 |
157 | return ispunct(*p) ? 1 : 0;
158 | }
159 |
160 | static bool is_keyword(Token *tok) {
161 | static HashMap map;
162 |
163 | if (map.capacity == 0) {
164 | static char *kw[] = {
165 | "return", "if", "else", "for", "while", "int", "sizeof", "char",
166 | "struct", "union", "short", "long", "void", "typedef", "_Bool",
167 | "enum", "static", "goto", "break", "continue", "switch", "case",
168 | "default", "extern", "_Alignof", "_Alignas", "do", "signed",
169 | "unsigned", "const", "volatile", "auto", "register", "restrict",
170 | "__restrict", "__restrict__", "_Noreturn", "float", "double",
171 | "typeof", "asm", "_Thread_local", "__thread", "_Atomic",
172 | "__attribute__",
173 | };
174 |
175 | for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++)
176 | hashmap_put(&map, kw[i], (void *)1);
177 | }
178 |
179 | return hashmap_get2(&map, tok->loc, tok->len);
180 | }
181 |
182 | static int read_escaped_char(char **new_pos, char *p) {
183 | if ('0' <= *p && *p <= '7') {
184 | // Read an octal number.
185 | int c = *p++ - '0';
186 | if ('0' <= *p && *p <= '7') {
187 | c = (c << 3) + (*p++ - '0');
188 | if ('0' <= *p && *p <= '7')
189 | c = (c << 3) + (*p++ - '0');
190 | }
191 | *new_pos = p;
192 | return c;
193 | }
194 |
195 | if (*p == 'x') {
196 | // Read a hexadecimal number.
197 | p++;
198 | if (!isxdigit(*p))
199 | error_at(p, "invalid hex escape sequence");
200 |
201 | int c = 0;
202 | for (; isxdigit(*p); p++)
203 | c = (c << 4) + from_hex(*p);
204 | *new_pos = p;
205 | return c;
206 | }
207 |
208 | *new_pos = p + 1;
209 |
210 | // Escape sequences are defined using themselves here. E.g.
211 | // '\n' is implemented using '\n'. This tautological definition
212 | // works because the compiler that compiles our compiler knows
213 | // what '\n' actually is. In other words, we "inherit" the ASCII
214 | // code of '\n' from the compiler that compiles our compiler,
215 | // so we don't have to teach the actual code here.
216 | //
217 | // This fact has huge implications not only for the correctness
218 | // of the compiler but also for the security of the generated code.
219 | // For more info, read "Reflections on Trusting Trust" by Ken Thompson.
220 | // https://github.com/rui314/xcc/wiki/thompson1984.pdf
221 | switch (*p) {
222 | case 'a': return '\a';
223 | case 'b': return '\b';
224 | case 't': return '\t';
225 | case 'n': return '\n';
226 | case 'v': return '\v';
227 | case 'f': return '\f';
228 | case 'r': return '\r';
229 | // [GNU] \e for the ASCII escape character is a GNU C extension.
230 | case 'e': return 27;
231 | default: return *p;
232 | }
233 | }
234 |
235 | // Find a closing double-quote.
236 | static char *string_literal_end(char *p) {
237 | char *start = p;
238 | for (; *p != '"'; p++) {
239 | if (*p == '\n' || *p == '\0')
240 | error_at(start, "unclosed string literal");
241 | if (*p == '\\')
242 | p++;
243 | }
244 | return p;
245 | }
246 |
247 | static Token *read_string_literal(char *start, char *quote) {
248 | char *end = string_literal_end(quote + 1);
249 | char *buf = calloc(1, end - quote);
250 | int len = 0;
251 |
252 | for (char *p = quote + 1; p < end;) {
253 | if (*p == '\\')
254 | buf[len++] = read_escaped_char(&p, p + 1);
255 | else
256 | buf[len++] = *p++;
257 | }
258 |
259 | Token *tok = new_token(TK_STR, start, end + 1);
260 | tok->ty = array_of(ty_char, len + 1);
261 | tok->str = buf;
262 | return tok;
263 | }
264 |
265 | // Read a UTF-8-encoded string literal and transcode it in UTF-16.
266 | //
267 | // UTF-16 is yet another variable-width encoding for Unicode. Code
268 | // points smaller than U+10000 are encoded in 2 bytes. Code points
269 | // equal to or larger than that are encoded in 4 bytes. Each 2 bytes
270 | // in the 4 byte sequence is called "surrogate", and a 4 byte sequence
271 | // is called a "surrogate pair".
272 | static Token *read_utf16_string_literal(char *start, char *quote) {
273 | char *end = string_literal_end(quote + 1);
274 | uint16_t *buf = calloc(2, end - start);
275 | int len = 0;
276 |
277 | for (char *p = quote + 1; p < end;) {
278 | if (*p == '\\') {
279 | buf[len++] = read_escaped_char(&p, p + 1);
280 | continue;
281 | }
282 |
283 | uint32_t c = decode_utf8(&p, p);
284 | if (c < 0x10000) {
285 | // Encode a code point in 2 bytes.
286 | buf[len++] = c;
287 | } else {
288 | // Encode a code point in 4 bytes.
289 | c -= 0x10000;
290 | buf[len++] = 0xd800 + ((c >> 10) & 0x3ff);
291 | buf[len++] = 0xdc00 + (c & 0x3ff);
292 | }
293 | }
294 |
295 | Token *tok = new_token(TK_STR, start, end + 1);
296 | tok->ty = array_of(ty_ushort, len + 1);
297 | tok->str = (char *)buf;
298 | return tok;
299 | }
300 |
301 | // Read a UTF-8-encoded string literal and transcode it in UTF-32.
302 | //
303 | // UTF-32 is a fixed-width encoding for Unicode. Each code point is
304 | // encoded in 4 bytes.
305 | static Token *read_utf32_string_literal(char *start, char *quote, Type *ty) {
306 | char *end = string_literal_end(quote + 1);
307 | uint32_t *buf = calloc(4, end - quote);
308 | int len = 0;
309 |
310 | for (char *p = quote + 1; p < end;) {
311 | if (*p == '\\')
312 | buf[len++] = read_escaped_char(&p, p + 1);
313 | else
314 | buf[len++] = decode_utf8(&p, p);
315 | }
316 |
317 | Token *tok = new_token(TK_STR, start, end + 1);
318 | tok->ty = array_of(ty, len + 1);
319 | tok->str = (char *)buf;
320 | return tok;
321 | }
322 |
323 | static Token *read_char_literal(char *start, char *quote, Type *ty) {
324 | char *p = quote + 1;
325 | if (*p == '\0')
326 | error_at(start, "unclosed char literal");
327 |
328 | int c;
329 | if (*p == '\\')
330 | c = read_escaped_char(&p, p + 1);
331 | else
332 | c = decode_utf8(&p, p);
333 |
334 | char *end = strchr(p, '\'');
335 | if (!end)
336 | error_at(p, "unclosed char literal");
337 |
338 | Token *tok = new_token(TK_NUM, start, end + 1);
339 | tok->val = c;
340 | tok->ty = ty;
341 | return tok;
342 | }
343 |
344 | static bool convert_pp_int(Token *tok) {
345 | char *p = tok->loc;
346 |
347 | // Read a binary, octal, decimal or hexadecimal number.
348 | int base = 10;
349 | if (!strncasecmp(p, "0x", 2) && isxdigit(p[2])) {
350 | p += 2;
351 | base = 16;
352 | } else if (!strncasecmp(p, "0b", 2) && (p[2] == '0' || p[2] == '1')) {
353 | p += 2;
354 | base = 2;
355 | } else if (*p == '0') {
356 | base = 8;
357 | }
358 |
359 | int64_t val = strtoul(p, &p, base);
360 |
361 | // Read U, L or LL suffixes.
362 | bool l = false;
363 | bool u = false;
364 |
365 | if (startswith(p, "LLU") || startswith(p, "LLu") ||
366 | startswith(p, "llU") || startswith(p, "llu") ||
367 | startswith(p, "ULL") || startswith(p, "Ull") ||
368 | startswith(p, "uLL") || startswith(p, "ull")) {
369 | p += 3;
370 | l = u = true;
371 | } else if (!strncasecmp(p, "lu", 2) || !strncasecmp(p, "ul", 2)) {
372 | p += 2;
373 | l = u = true;
374 | } else if (startswith(p, "LL") || startswith(p, "ll")) {
375 | p += 2;
376 | l = true;
377 | } else if (*p == 'L' || *p == 'l') {
378 | p++;
379 | l = true;
380 | } else if (*p == 'U' || *p == 'u') {
381 | p++;
382 | u = true;
383 | }
384 |
385 | if (p != tok->loc + tok->len)
386 | return false;
387 |
388 | // Infer a type.
389 | Type *ty;
390 | if (base == 10) {
391 | if (l && u)
392 | ty = ty_ulong;
393 | else if (l)
394 | ty = ty_long;
395 | else if (u)
396 | ty = (val >> 32) ? ty_ulong : ty_uint;
397 | else
398 | ty = (val >> 31) ? ty_long : ty_int;
399 | } else {
400 | if (l && u)
401 | ty = ty_ulong;
402 | else if (l)
403 | ty = (val >> 63) ? ty_ulong : ty_long;
404 | else if (u)
405 | ty = (val >> 32) ? ty_ulong : ty_uint;
406 | else if (val >> 63)
407 | ty = ty_ulong;
408 | else if (val >> 32)
409 | ty = ty_long;
410 | else if (val >> 31)
411 | ty = ty_uint;
412 | else
413 | ty = ty_int;
414 | }
415 |
416 | tok->kind = TK_NUM;
417 | tok->val = val;
418 | tok->ty = ty;
419 | return true;
420 | }
421 |
422 | // The definition of the numeric literal at the preprocessing stage
423 | // is more relaxed than the definition of that at the later stages.
424 | // In order to handle that, a numeric literal is tokenized as a
425 | // "pp-number" token first and then converted to a regular number
426 | // token after preprocessing.
427 | //
428 | // This function converts a pp-number token to a regular number token.
429 | static void convert_pp_number(Token *tok) {
430 | // Try to parse as an integer constant.
431 | if (convert_pp_int(tok))
432 | return;
433 |
434 | // If it's not an integer, it must be a floating point constant.
435 | char *end;
436 | long double val = strtold(tok->loc, &end);
437 |
438 | Type *ty;
439 | if (*end == 'f' || *end == 'F') {
440 | ty = ty_float;
441 | end++;
442 | } else if (*end == 'l' || *end == 'L') {
443 | ty = ty_ldouble;
444 | end++;
445 | } else {
446 | ty = ty_double;
447 | }
448 |
449 | if (tok->loc + tok->len != end)
450 | error_tok(tok, "invalid numeric constant");
451 |
452 | tok->kind = TK_NUM;
453 | tok->fval = val;
454 | tok->ty = ty;
455 | }
456 |
457 | void convert_pp_tokens(Token *tok) {
458 | for (Token *t = tok; t->kind != TK_EOF; t = t->next) {
459 | if (is_keyword(t))
460 | t->kind = TK_KEYWORD;
461 | else if (t->kind == TK_PP_NUM)
462 | convert_pp_number(t);
463 | }
464 | }
465 |
466 | // Initialize line info for all tokens.
467 | static void add_line_numbers(Token *tok) {
468 | char *p = current_file->contents;
469 | int n = 1;
470 |
471 | do {
472 | if (p == tok->loc) {
473 | tok->line_no = n;
474 | tok = tok->next;
475 | }
476 | if (*p == '\n')
477 | n++;
478 | } while (*p++);
479 | }
480 |
481 | Token *tokenize_string_literal(Token *tok, Type *basety) {
482 | Token *t;
483 | if (basety->size == 2)
484 | t = read_utf16_string_literal(tok->loc, tok->loc);
485 | else
486 | t = read_utf32_string_literal(tok->loc, tok->loc, basety);
487 | t->next = tok->next;
488 | return t;
489 | }
490 |
491 | // Tokenize a given string and returns new tokens.
492 | Token *tokenize(File *file) {
493 | current_file = file;
494 |
495 | char *p = file->contents;
496 | Token head = {};
497 | Token *cur = &head;
498 |
499 | at_bol = true;
500 | has_space = false;
501 |
502 | while (*p) {
503 |
504 | // Skip line shebang.
505 | if (startswith(p, "#!")) {
506 | p += 2;
507 | while (*p != '\n')
508 | p++;
509 | has_space = true;
510 | continue;
511 | }
512 |
513 | // Skip line comments.
514 | if (startswith(p, "//")) {
515 | p += 2;
516 | while (*p != '\n')
517 | p++;
518 | has_space = true;
519 | continue;
520 | }
521 |
522 | // Skip block comments.
523 | if (startswith(p, "/*")) {
524 | char *q = strstr(p + 2, "*/");
525 | if (!q)
526 | error_at(p, "unclosed block comment");
527 | p = q + 2;
528 | has_space = true;
529 | continue;
530 | }
531 |
532 | // Skip newline.
533 | if (*p == '\n') {
534 | p++;
535 | at_bol = true;
536 | has_space = false;
537 | continue;
538 | }
539 |
540 | // Skip whitespace characters.
541 | if (isspace(*p)) {
542 | p++;
543 | has_space = true;
544 | continue;
545 | }
546 |
547 | // Numeric literal
548 | if (isdigit(*p) || (*p == '.' && isdigit(p[1]))) {
549 | char *q = p++;
550 | for (;;) {
551 | if (p[0] && p[1] && strchr("eEpP", p[0]) && strchr("+-", p[1]))
552 | p += 2;
553 | else if (isalnum(*p) || *p == '.')
554 | p++;
555 | else
556 | break;
557 | }
558 | cur = cur->next = new_token(TK_PP_NUM, q, p);
559 | continue;
560 | }
561 |
562 | // String literal
563 | if (*p == '"') {
564 | cur = cur->next = read_string_literal(p, p);
565 | p += cur->len;
566 | continue;
567 | }
568 |
569 | // UTF-8 string literal
570 | if (startswith(p, "u8\"")) {
571 | cur = cur->next = read_string_literal(p, p + 2);
572 | p += cur->len;
573 | continue;
574 | }
575 |
576 | // UTF-16 string literal
577 | if (startswith(p, "u\"")) {
578 | cur = cur->next = read_utf16_string_literal(p, p + 1);
579 | p += cur->len;
580 | continue;
581 | }
582 |
583 | // Wide string literal
584 | if (startswith(p, "L\"")) {
585 | cur = cur->next = read_utf32_string_literal(p, p + 1, ty_int);
586 | p += cur->len;
587 | continue;
588 | }
589 |
590 | // UTF-32 string literal
591 | if (startswith(p, "U\"")) {
592 | cur = cur->next = read_utf32_string_literal(p, p + 1, ty_uint);
593 | p += cur->len;
594 | continue;
595 | }
596 |
597 | // Character literal
598 | if (*p == '\'') {
599 | cur = cur->next = read_char_literal(p, p, ty_int);
600 | cur->val = (char)cur->val;
601 | p += cur->len;
602 | continue;
603 | }
604 |
605 | // UTF-16 character literal
606 | if (startswith(p, "u'")) {
607 | cur = cur->next = read_char_literal(p, p + 1, ty_ushort);
608 | cur->val &= 0xffff;
609 | p += cur->len;
610 | continue;
611 | }
612 |
613 | // Wide character literal
614 | if (startswith(p, "L'")) {
615 | cur = cur->next = read_char_literal(p, p + 1, ty_int);
616 | p += cur->len;
617 | continue;
618 | }
619 |
620 | // UTF-32 character literal
621 | if (startswith(p, "U'")) {
622 | cur = cur->next = read_char_literal(p, p + 1, ty_uint);
623 | p += cur->len;
624 | continue;
625 | }
626 |
627 | // Identifier or keyword
628 | int ident_len = read_ident(p);
629 | if (ident_len) {
630 | cur = cur->next = new_token(TK_IDENT, p, p + ident_len);
631 | p += cur->len;
632 | continue;
633 | }
634 |
635 | // Punctuators
636 | int punct_len = read_punct(p);
637 | if (punct_len) {
638 | cur = cur->next = new_token(TK_PUNCT, p, p + punct_len);
639 | p += cur->len;
640 | continue;
641 | }
642 |
643 | error_at(p, "invalid token");
644 | }
645 |
646 | cur = cur->next = new_token(TK_EOF, p, p);
647 | add_line_numbers(head.next);
648 | return head.next;
649 | }
650 |
651 | // Returns the contents of a given file.
652 | static char *read_file(char *path) {
653 | FILE *fp;
654 |
655 | if (strcmp(path, "-") == 0) {
656 | // By convention, read from stdin if a given filename is "-".
657 | fp = stdin;
658 | } else {
659 | fp = fopen(path, "r");
660 | if (!fp)
661 | return NULL;
662 | }
663 |
664 | char *buf;
665 | size_t buflen;
666 | FILE *out = open_memstream(&buf, &buflen);
667 |
668 | // Read the entire file.
669 | for (;;) {
670 | char buf2[4096];
671 | int n = fread(buf2, 1, sizeof(buf2), fp);
672 | if (n == 0)
673 | break;
674 | fwrite(buf2, 1, n, out);
675 | }
676 |
677 | if (fp != stdin)
678 | fclose(fp);
679 |
680 | // Make sure that the last line is properly terminated with '\n'.
681 | fflush(out);
682 | if (buflen == 0 || buf[buflen - 1] != '\n')
683 | fputc('\n', out);
684 | fputc('\0', out);
685 | fclose(out);
686 | return buf;
687 | }
688 |
689 | File **get_input_files(void) {
690 | return input_files;
691 | }
692 |
693 | File *new_file(char *name, unsigned file_no, char *contents) {
694 | File *file = calloc(1, sizeof(File));
695 | file->name = name;
696 | file->display_name = name;
697 | file->file_no = file_no;
698 | file->contents = contents;
699 | return file;
700 | }
701 |
702 | // Replaces \r or \r\n with \n.
703 | static void canonicalize_newline(char *p) {
704 | int i = 0, j = 0;
705 |
706 | while (p[i]) {
707 | if (p[i] == '\r' && p[i + 1] == '\n') {
708 | i += 2;
709 | p[j++] = '\n';
710 | } else if (p[i] == '\r') {
711 | i++;
712 | p[j++] = '\n';
713 | } else {
714 | p[j++] = p[i++];
715 | }
716 | }
717 |
718 | p[j] = '\0';
719 | }
720 |
721 | // Removes backslashes followed by a newline.
722 | static void remove_backslash_newline(char *p) {
723 | int i = 0, j = 0;
724 |
725 | // We want to keep the number of newline characters so that
726 | // the logical line number matches the physical one.
727 | // This counter maintain the number of newlines we have removed.
728 | int n = 0;
729 |
730 | while (p[i]) {
731 | if (p[i] == '\\' && p[i + 1] == '\n') {
732 | i += 2;
733 | n++;
734 | } else if (p[i] == '\n') {
735 | p[j++] = p[i++];
736 | for (; n > 0; n--)
737 | p[j++] = '\n';
738 | } else {
739 | p[j++] = p[i++];
740 | }
741 | }
742 |
743 | for (; n > 0; n--)
744 | p[j++] = '\n';
745 | p[j] = '\0';
746 | }
747 |
748 | static uint32_t read_universal_char(char *p, int len) {
749 | uint32_t c = 0;
750 | for (int i = 0; i < len; i++) {
751 | if (!isxdigit(p[i]))
752 | return 0;
753 | c = (c << 4) | from_hex(p[i]);
754 | }
755 | return c;
756 | }
757 |
758 | // Replace \u or \U escape sequences with corresponding UTF-8 bytes.
759 | static void convert_universal_chars(char *p) {
760 | char *q = p;
761 |
762 | while (*p) {
763 | if (startswith(p, "\\u")) {
764 | uint32_t c = read_universal_char(p + 2, 4);
765 | if (c) {
766 | p += 6;
767 | q += encode_utf8(q, c);
768 | } else {
769 | *q++ = *p++;
770 | }
771 | } else if (startswith(p, "\\U")) {
772 | uint32_t c = read_universal_char(p + 2, 8);
773 | if (c) {
774 | p += 10;
775 | q += encode_utf8(q, c);
776 | } else {
777 | *q++ = *p++;
778 | }
779 | } else if (p[0] == '\\') {
780 | *q++ = *p++;
781 | *q++ = *p++;
782 | } else {
783 | *q++ = *p++;
784 | }
785 | }
786 |
787 | *q = '\0';
788 | }
789 |
790 | Token *tokenize_file(char *path) {
791 | char *p = read_file(path);
792 | if (!p)
793 | return NULL;
794 |
795 | // UTF-8 texts may start with a 3-byte "BOM" marker sequence.
796 | // If exists, just skip them because they are useless bytes.
797 | // (It is actually not recommended to add BOM markers to UTF-8
798 | // texts, but it's not uncommon particularly on Windows.)
799 | if (!memcmp(p, "\xef\xbb\xbf", 3))
800 | p += 3;
801 |
802 | canonicalize_newline(p);
803 | remove_backslash_newline(p);
804 | convert_universal_chars(p);
805 |
806 | // Save the filename for assembler .file directive.
807 | static int file_no;
808 | File *file = new_file(path, file_no + 1, p);
809 |
810 | // Save the filename for assembler .file directive.
811 | input_files = realloc(input_files, sizeof(char *) * (file_no + 2));
812 | input_files[file_no] = file;
813 | input_files[file_no + 1] = NULL;
814 | file_no++;
815 |
816 | return tokenize(file);
817 | }
818 |
--------------------------------------------------------------------------------
/type.c:
--------------------------------------------------------------------------------
1 | #include "xcc.h"
2 |
3 | Type *ty_void = &(Type){TY_VOID, 1, 1};
4 | Type *ty_bool = &(Type){TY_BOOL, 1, 1};
5 |
6 | Type *ty_char = &(Type){TY_CHAR, 1, 1};
7 | Type *ty_short = &(Type){TY_SHORT, 2, 2};
8 | Type *ty_int = &(Type){TY_INT, 4, 4};
9 | Type *ty_long = &(Type){TY_LONG, 8, 8};
10 |
11 | Type *ty_uchar = &(Type){TY_CHAR, 1, 1, true};
12 | Type *ty_ushort = &(Type){TY_SHORT, 2, 2, true};
13 | Type *ty_uint = &(Type){TY_INT, 4, 4, true};
14 | Type *ty_ulong = &(Type){TY_LONG, 8, 8, true};
15 |
16 | Type *ty_float = &(Type){TY_FLOAT, 4, 4};
17 | Type *ty_double = &(Type){TY_DOUBLE, 8, 8};
18 | Type *ty_ldouble = &(Type){TY_LDOUBLE, 16, 16};
19 |
20 | static Type *new_type(TypeKind kind, int size, int align) {
21 | Type *ty = calloc(1, sizeof(Type));
22 | ty->kind = kind;
23 | ty->size = size;
24 | ty->align = align;
25 | return ty;
26 | }
27 |
28 | bool is_integer(Type *ty) {
29 | TypeKind k = ty->kind;
30 | return k == TY_BOOL || k == TY_CHAR || k == TY_SHORT ||
31 | k == TY_INT || k == TY_LONG || k == TY_ENUM;
32 | }
33 |
34 | bool is_flonum(Type *ty) {
35 | return ty->kind == TY_FLOAT || ty->kind == TY_DOUBLE ||
36 | ty->kind == TY_LDOUBLE;
37 | }
38 |
39 | bool is_numeric(Type *ty) {
40 | return is_integer(ty) || is_flonum(ty);
41 | }
42 |
43 | bool is_compatible(Type *t1, Type *t2) {
44 | if (t1 == t2)
45 | return true;
46 |
47 | if (t1->origin)
48 | return is_compatible(t1->origin, t2);
49 |
50 | if (t2->origin)
51 | return is_compatible(t1, t2->origin);
52 |
53 | if (t1->kind != t2->kind)
54 | return false;
55 |
56 | switch (t1->kind) {
57 | case TY_CHAR:
58 | case TY_SHORT:
59 | case TY_INT:
60 | case TY_LONG:
61 | return t1->is_unsigned == t2->is_unsigned;
62 | case TY_FLOAT:
63 | case TY_DOUBLE:
64 | case TY_LDOUBLE:
65 | return true;
66 | case TY_PTR:
67 | return is_compatible(t1->base, t2->base);
68 | case TY_FUNC: {
69 | if (!is_compatible(t1->return_ty, t2->return_ty))
70 | return false;
71 | if (t1->is_variadic != t2->is_variadic)
72 | return false;
73 |
74 | Type *p1 = t1->params;
75 | Type *p2 = t2->params;
76 | for (; p1 && p2; p1 = p1->next, p2 = p2->next)
77 | if (!is_compatible(p1, p2))
78 | return false;
79 | return p1 == NULL && p2 == NULL;
80 | }
81 | case TY_ARRAY:
82 | if (!is_compatible(t1->base, t2->base))
83 | return false;
84 | return t1->array_len < 0 && t2->array_len < 0 &&
85 | t1->array_len == t2->array_len;
86 | }
87 | return false;
88 | }
89 |
90 | Type *copy_type(Type *ty) {
91 | Type *ret = calloc(1, sizeof(Type));
92 | *ret = *ty;
93 | ret->origin = ty;
94 | return ret;
95 | }
96 |
97 | Type *pointer_to(Type *base) {
98 | Type *ty = new_type(TY_PTR, 8, 8);
99 | ty->base = base;
100 | ty->is_unsigned = true;
101 | return ty;
102 | }
103 |
104 | Type *func_type(Type *return_ty) {
105 | // The C spec disallows sizeof(), but
106 | // GCC allows that and the expression is evaluated to 1.
107 | Type *ty = new_type(TY_FUNC, 1, 1);
108 | ty->return_ty = return_ty;
109 | return ty;
110 | }
111 |
112 | Type *array_of(Type *base, int len) {
113 | Type *ty = new_type(TY_ARRAY, base->size * len, base->align);
114 | ty->base = base;
115 | ty->array_len = len;
116 | return ty;
117 | }
118 |
119 | Type *vla_of(Type *base, Node *len) {
120 | Type *ty = new_type(TY_VLA, 8, 8);
121 | ty->base = base;
122 | ty->vla_len = len;
123 | return ty;
124 | }
125 |
126 | Type *enum_type(void) {
127 | return new_type(TY_ENUM, 4, 4);
128 | }
129 |
130 | Type *struct_type(void) {
131 | return new_type(TY_STRUCT, 0, 1);
132 | }
133 |
134 | static Type *get_common_type(Type *ty1, Type *ty2) {
135 | if (ty1->base)
136 | return pointer_to(ty1->base);
137 |
138 | if (ty1->kind == TY_FUNC)
139 | return pointer_to(ty1);
140 | if (ty2->kind == TY_FUNC)
141 | return pointer_to(ty2);
142 |
143 | if (ty1->kind == TY_LDOUBLE || ty2->kind == TY_LDOUBLE)
144 | return ty_ldouble;
145 | if (ty1->kind == TY_DOUBLE || ty2->kind == TY_DOUBLE)
146 | return ty_double;
147 | if (ty1->kind == TY_FLOAT || ty2->kind == TY_FLOAT)
148 | return ty_float;
149 |
150 | if (ty1->size < 4)
151 | ty1 = ty_int;
152 | if (ty2->size < 4)
153 | ty2 = ty_int;
154 |
155 | if (ty1->size != ty2->size)
156 | return (ty1->size < ty2->size) ? ty2 : ty1;
157 |
158 | if (ty2->is_unsigned)
159 | return ty2;
160 | return ty1;
161 | }
162 |
163 | // For many binary operators, we implicitly promote operands so that
164 | // both operands have the same type. Any integral type smaller than
165 | // int is always promoted to int. If the type of one operand is larger
166 | // than the other's (e.g. "long" vs. "int"), the smaller operand will
167 | // be promoted to match with the other.
168 | //
169 | // This operation is called the "usual arithmetic conversion".
170 | static void usual_arith_conv(Node **lhs, Node **rhs) {
171 | Type *ty = get_common_type((*lhs)->ty, (*rhs)->ty);
172 | *lhs = new_cast(*lhs, ty);
173 | *rhs = new_cast(*rhs, ty);
174 | }
175 |
176 | void add_type(Node *node) {
177 | if (!node || node->ty)
178 | return;
179 |
180 | add_type(node->lhs);
181 | add_type(node->rhs);
182 | add_type(node->cond);
183 | add_type(node->then);
184 | add_type(node->els);
185 | add_type(node->init);
186 | add_type(node->inc);
187 |
188 | for (Node *n = node->body; n; n = n->next)
189 | add_type(n);
190 | for (Node *n = node->args; n; n = n->next)
191 | add_type(n);
192 |
193 | switch (node->kind) {
194 | case ND_NUM:
195 | node->ty = ty_int;
196 | return;
197 | case ND_ADD:
198 | case ND_SUB:
199 | case ND_MUL:
200 | case ND_DIV:
201 | case ND_MOD:
202 | case ND_BITAND:
203 | case ND_BITOR:
204 | case ND_BITXOR:
205 | usual_arith_conv(&node->lhs, &node->rhs);
206 | node->ty = node->lhs->ty;
207 | return;
208 | case ND_NEG: {
209 | Type *ty = get_common_type(ty_int, node->lhs->ty);
210 | node->lhs = new_cast(node->lhs, ty);
211 | node->ty = ty;
212 | return;
213 | }
214 | case ND_ASSIGN:
215 | if (node->lhs->ty->kind == TY_ARRAY)
216 | error_tok(node->lhs->tok, "not an lvalue");
217 | if (node->lhs->ty->kind != TY_STRUCT)
218 | node->rhs = new_cast(node->rhs, node->lhs->ty);
219 | node->ty = node->lhs->ty;
220 | return;
221 | case ND_EQ:
222 | case ND_NE:
223 | case ND_LT:
224 | case ND_LE:
225 | usual_arith_conv(&node->lhs, &node->rhs);
226 | node->ty = ty_int;
227 | return;
228 | case ND_FUNCALL:
229 | node->ty = node->func_ty->return_ty;
230 | return;
231 | case ND_NOT:
232 | case ND_LOGOR:
233 | case ND_LOGAND:
234 | node->ty = ty_int;
235 | return;
236 | case ND_BITNOT:
237 | case ND_SHL:
238 | case ND_SHR:
239 | node->ty = node->lhs->ty;
240 | return;
241 | case ND_VAR:
242 | case ND_VLA_PTR:
243 | node->ty = node->var->ty;
244 | return;
245 | case ND_COND:
246 | if (node->then->ty->kind == TY_VOID || node->els->ty->kind == TY_VOID) {
247 | node->ty = ty_void;
248 | } else {
249 | usual_arith_conv(&node->then, &node->els);
250 | node->ty = node->then->ty;
251 | }
252 | return;
253 | case ND_COMMA:
254 | node->ty = node->rhs->ty;
255 | return;
256 | case ND_MEMBER:
257 | node->ty = node->member->ty;
258 | return;
259 | case ND_ADDR: {
260 | Type *ty = node->lhs->ty;
261 | if (ty->kind == TY_ARRAY)
262 | node->ty = pointer_to(ty->base);
263 | else
264 | node->ty = pointer_to(ty);
265 | return;
266 | }
267 | case ND_DEREF:
268 | if (!node->lhs->ty->base)
269 | error_tok(node->tok, "invalid pointer dereference");
270 | if (node->lhs->ty->base->kind == TY_VOID)
271 | error_tok(node->tok, "dereferencing a void pointer");
272 |
273 | node->ty = node->lhs->ty->base;
274 | return;
275 | case ND_STMT_EXPR:
276 | if (node->body) {
277 | Node *stmt = node->body;
278 | while (stmt->next)
279 | stmt = stmt->next;
280 | if (stmt->kind == ND_EXPR_STMT) {
281 | node->ty = stmt->lhs->ty;
282 | return;
283 | }
284 | }
285 | error_tok(node->tok, "statement expression returning void is not supported");
286 | return;
287 | case ND_LABEL_VAL:
288 | node->ty = pointer_to(ty_void);
289 | return;
290 | case ND_CAS:
291 | add_type(node->cas_addr);
292 | add_type(node->cas_old);
293 | add_type(node->cas_new);
294 | node->ty = ty_bool;
295 |
296 | if (node->cas_addr->ty->kind != TY_PTR)
297 | error_tok(node->cas_addr->tok, "pointer expected");
298 | if (node->cas_old->ty->kind != TY_PTR)
299 | error_tok(node->cas_old->tok, "pointer expected");
300 | return;
301 | case ND_EXCH:
302 | if (node->lhs->ty->kind != TY_PTR)
303 | error_tok(node->cas_addr->tok, "pointer expected");
304 | node->ty = node->lhs->ty->base;
305 | return;
306 | }
307 | }
308 |
--------------------------------------------------------------------------------
/unicode.c:
--------------------------------------------------------------------------------
1 | #include "xcc.h"
2 |
3 | // Encode a given character in UTF-8.
4 | int encode_utf8(char *buf, uint32_t c) {
5 | if (c <= 0x7F) {
6 | buf[0] = c;
7 | return 1;
8 | }
9 |
10 | if (c <= 0x7FF) {
11 | buf[0] = 0b11000000 | (c >> 6);
12 | buf[1] = 0b10000000 | (c & 0b00111111);
13 | return 2;
14 | }
15 |
16 | if (c <= 0xFFFF) {
17 | buf[0] = 0b11100000 | (c >> 12);
18 | buf[1] = 0b10000000 | ((c >> 6) & 0b00111111);
19 | buf[2] = 0b10000000 | (c & 0b00111111);
20 | return 3;
21 | }
22 |
23 | buf[0] = 0b11110000 | (c >> 18);
24 | buf[1] = 0b10000000 | ((c >> 12) & 0b00111111);
25 | buf[2] = 0b10000000 | ((c >> 6) & 0b00111111);
26 | buf[3] = 0b10000000 | (c & 0b00111111);
27 | return 4;
28 | }
29 |
30 | // Read a UTF-8-encoded Unicode code point from a source file.
31 | // We assume that source files are always in UTF-8.
32 | //
33 | // UTF-8 is a variable-width encoding in which one code point is
34 | // encoded in one to four bytes. One byte UTF-8 code points are
35 | // identical to ASCII. Non-ASCII characters are encoded using more
36 | // than one byte.
37 | uint32_t decode_utf8(char **new_pos, char *p) {
38 | if ((unsigned char)*p < 128) {
39 | *new_pos = p + 1;
40 | return *p;
41 | }
42 |
43 | char *start = p;
44 | int len;
45 | uint32_t c;
46 |
47 | if ((unsigned char)*p >= 0b11110000) {
48 | len = 4;
49 | c = *p & 0b111;
50 | } else if ((unsigned char)*p >= 0b11100000) {
51 | len = 3;
52 | c = *p & 0b1111;
53 | } else if ((unsigned char)*p >= 0b11000000) {
54 | len = 2;
55 | c = *p & 0b11111;
56 | } else {
57 | error_at(start, "invalid UTF-8 sequence");
58 | }
59 |
60 | for (int i = 1; i < len; i++) {
61 | if ((unsigned char)p[i] >> 6 != 0b10)
62 | error_at(start, "invalid UTF-8 sequence");
63 | c = (c << 6) | (p[i] & 0b111111);
64 | }
65 |
66 | *new_pos = p + len;
67 | return c;
68 | }
69 |
70 | static bool in_range(uint32_t *range, uint32_t c) {
71 | for (int i = 0; range[i] != -1; i += 2)
72 | if (range[i] <= c && c <= range[i + 1])
73 | return true;
74 | return false;
75 | }
76 |
77 | // [https://www.sigbus.info/n1570#D] C11 allows not only ASCII but
78 | // some multibyte characters in certan Unicode ranges to be used in an
79 | // identifier.
80 | //
81 | // This function returns true if a given character is acceptable as
82 | // the first character of an identifier.
83 | //
84 | // For example, ¾ (U+00BE) is a valid identifier because characters in
85 | // 0x00BE-0x00C0 are allowed, while neither ⟘ (U+27D8) nor ' '
86 | // (U+3000, full-width space) are allowed because they are out of range.
87 | bool is_ident1(uint32_t c) {
88 | static uint32_t range[] = {
89 | '_', '_', 'a', 'z', 'A', 'Z', '$', '$',
90 | 0x00A8, 0x00A8, 0x00AA, 0x00AA, 0x00AD, 0x00AD, 0x00AF, 0x00AF,
91 | 0x00B2, 0x00B5, 0x00B7, 0x00BA, 0x00BC, 0x00BE, 0x00C0, 0x00D6,
92 | 0x00D8, 0x00F6, 0x00F8, 0x00FF, 0x0100, 0x02FF, 0x0370, 0x167F,
93 | 0x1681, 0x180D, 0x180F, 0x1DBF, 0x1E00, 0x1FFF, 0x200B, 0x200D,
94 | 0x202A, 0x202E, 0x203F, 0x2040, 0x2054, 0x2054, 0x2060, 0x206F,
95 | 0x2070, 0x20CF, 0x2100, 0x218F, 0x2460, 0x24FF, 0x2776, 0x2793,
96 | 0x2C00, 0x2DFF, 0x2E80, 0x2FFF, 0x3004, 0x3007, 0x3021, 0x302F,
97 | 0x3031, 0x303F, 0x3040, 0xD7FF, 0xF900, 0xFD3D, 0xFD40, 0xFDCF,
98 | 0xFDF0, 0xFE1F, 0xFE30, 0xFE44, 0xFE47, 0xFFFD,
99 | 0x10000, 0x1FFFD, 0x20000, 0x2FFFD, 0x30000, 0x3FFFD, 0x40000, 0x4FFFD,
100 | 0x50000, 0x5FFFD, 0x60000, 0x6FFFD, 0x70000, 0x7FFFD, 0x80000, 0x8FFFD,
101 | 0x90000, 0x9FFFD, 0xA0000, 0xAFFFD, 0xB0000, 0xBFFFD, 0xC0000, 0xCFFFD,
102 | 0xD0000, 0xDFFFD, 0xE0000, 0xEFFFD, -1,
103 | };
104 |
105 | return in_range(range, c);
106 | }
107 |
108 | // Returns true if a given character is acceptable as a non-first
109 | // character of an identifier.
110 | bool is_ident2(uint32_t c) {
111 | static uint32_t range[] = {
112 | '0', '9', '$', '$', 0x0300, 0x036F, 0x1DC0, 0x1DFF, 0x20D0, 0x20FF,
113 | 0xFE20, 0xFE2F, -1,
114 | };
115 |
116 | return is_ident1(c) || in_range(range, c);
117 | }
118 |
119 | // Returns the number of columns needed to display a given
120 | // character in a fixed-width font.
121 | //
122 | // Based on https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
123 | static int char_width(uint32_t c) {
124 | static uint32_t range1[] = {
125 | 0x0000, 0x001F, 0x007f, 0x00a0, 0x0300, 0x036F, 0x0483, 0x0486,
126 | 0x0488, 0x0489, 0x0591, 0x05BD, 0x05BF, 0x05BF, 0x05C1, 0x05C2,
127 | 0x05C4, 0x05C5, 0x05C7, 0x05C7, 0x0600, 0x0603, 0x0610, 0x0615,
128 | 0x064B, 0x065E, 0x0670, 0x0670, 0x06D6, 0x06E4, 0x06E7, 0x06E8,
129 | 0x06EA, 0x06ED, 0x070F, 0x070F, 0x0711, 0x0711, 0x0730, 0x074A,
130 | 0x07A6, 0x07B0, 0x07EB, 0x07F3, 0x0901, 0x0902, 0x093C, 0x093C,
131 | 0x0941, 0x0948, 0x094D, 0x094D, 0x0951, 0x0954, 0x0962, 0x0963,
132 | 0x0981, 0x0981, 0x09BC, 0x09BC, 0x09C1, 0x09C4, 0x09CD, 0x09CD,
133 | 0x09E2, 0x09E3, 0x0A01, 0x0A02, 0x0A3C, 0x0A3C, 0x0A41, 0x0A42,
134 | 0x0A47, 0x0A48, 0x0A4B, 0x0A4D, 0x0A70, 0x0A71, 0x0A81, 0x0A82,
135 | 0x0ABC, 0x0ABC, 0x0AC1, 0x0AC5, 0x0AC7, 0x0AC8, 0x0ACD, 0x0ACD,
136 | 0x0AE2, 0x0AE3, 0x0B01, 0x0B01, 0x0B3C, 0x0B3C, 0x0B3F, 0x0B3F,
137 | 0x0B41, 0x0B43, 0x0B4D, 0x0B4D, 0x0B56, 0x0B56, 0x0B82, 0x0B82,
138 | 0x0BC0, 0x0BC0, 0x0BCD, 0x0BCD, 0x0C3E, 0x0C40, 0x0C46, 0x0C48,
139 | 0x0C4A, 0x0C4D, 0x0C55, 0x0C56, 0x0CBC, 0x0CBC, 0x0CBF, 0x0CBF,
140 | 0x0CC6, 0x0CC6, 0x0CCC, 0x0CCD, 0x0CE2, 0x0CE3, 0x0D41, 0x0D43,
141 | 0x0D4D, 0x0D4D, 0x0DCA, 0x0DCA, 0x0DD2, 0x0DD4, 0x0DD6, 0x0DD6,
142 | 0x0E31, 0x0E31, 0x0E34, 0x0E3A, 0x0E47, 0x0E4E, 0x0EB1, 0x0EB1,
143 | 0x0EB4, 0x0EB9, 0x0EBB, 0x0EBC, 0x0EC8, 0x0ECD, 0x0F18, 0x0F19,
144 | 0x0F35, 0x0F35, 0x0F37, 0x0F37, 0x0F39, 0x0F39, 0x0F71, 0x0F7E,
145 | 0x0F80, 0x0F84, 0x0F86, 0x0F87, 0x0F90, 0x0F97, 0x0F99, 0x0FBC,
146 | 0x0FC6, 0x0FC6, 0x102D, 0x1030, 0x1032, 0x1032, 0x1036, 0x1037,
147 | 0x1039, 0x1039, 0x1058, 0x1059, 0x1160, 0x11FF, 0x135F, 0x135F,
148 | 0x1712, 0x1714, 0x1732, 0x1734, 0x1752, 0x1753, 0x1772, 0x1773,
149 | 0x17B4, 0x17B5, 0x17B7, 0x17BD, 0x17C6, 0x17C6, 0x17C9, 0x17D3,
150 | 0x17DD, 0x17DD, 0x180B, 0x180D, 0x18A9, 0x18A9, 0x1920, 0x1922,
151 | 0x1927, 0x1928, 0x1932, 0x1932, 0x1939, 0x193B, 0x1A17, 0x1A18,
152 | 0x1B00, 0x1B03, 0x1B34, 0x1B34, 0x1B36, 0x1B3A, 0x1B3C, 0x1B3C,
153 | 0x1B42, 0x1B42, 0x1B6B, 0x1B73, 0x1DC0, 0x1DCA, 0x1DFE, 0x1DFF,
154 | 0x200B, 0x200F, 0x202A, 0x202E, 0x2060, 0x2063, 0x206A, 0x206F,
155 | 0x20D0, 0x20EF, 0x302A, 0x302F, 0x3099, 0x309A, 0xA806, 0xA806,
156 | 0xA80B, 0xA80B, 0xA825, 0xA826, 0xFB1E, 0xFB1E, 0xFE00, 0xFE0F,
157 | 0xFE20, 0xFE23, 0xFEFF, 0xFEFF, 0xFFF9, 0xFFFB, 0x10A01, 0x10A03,
158 | 0x10A05, 0x10A06, 0x10A0C, 0x10A0F, 0x10A38, 0x10A3A, 0x10A3F, 0x10A3F,
159 | 0x1D167, 0x1D169, 0x1D173, 0x1D182, 0x1D185, 0x1D18B, 0x1D1AA, 0x1D1AD,
160 | 0x1D242, 0x1D244, 0xE0001, 0xE0001, 0xE0020, 0xE007F, 0xE0100, 0xE01EF,
161 | -1,
162 | };
163 |
164 | if (in_range(range1, c))
165 | return 0;
166 |
167 | static uint32_t range2[] = {
168 | 0x1100, 0x115F, 0x2329, 0x2329, 0x232A, 0x232A, 0x2E80, 0x303E,
169 | 0x3040, 0xA4CF, 0xAC00, 0xD7A3, 0xF900, 0xFAFF, 0xFE10, 0xFE19,
170 | 0xFE30, 0xFE6F, 0xFF00, 0xFF60, 0xFFE0, 0xFFE6, 0x1F000, 0x1F644,
171 | 0x20000, 0x2FFFD, 0x30000, 0x3FFFD, -1,
172 | };
173 |
174 | if (in_range(range2, c))
175 | return 2;
176 | return 1;
177 | }
178 |
179 | // Returns the number of columns needed to display a given
180 | // string in a fixed-width font.
181 | int display_width(char *p, int len) {
182 | char *start = p;
183 | int w = 0;
184 | while (p - start < len) {
185 | uint32_t c = decode_utf8(&p, p);
186 | w += char_width(c);
187 | }
188 | return w;
189 | }
190 |
--------------------------------------------------------------------------------
/xc:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Xcompiler for C
3 | # bash patch to accept xxc as a shebang
4 | if [ "$#" -le 1 ]; then
5 | /usr/local/bin/xcc/xcc
6 | exit
7 | fi
8 | args=()
9 | split_arguments() {
10 | local arg="$1"
11 | local in_quote=false
12 | local part=""
13 | if [[ ! -z $2 ]]; then
14 | args+=("$arg")
15 | else
16 | for ((i = 0; i < ${#arg}; i++)); do
17 | char="${arg:$i:1}"
18 | if [[ $char == '"' ]]; then
19 | in_quote=!$in_quote
20 | elif [[ $char == ' ' && $in_quote == false ]]; then
21 | args+=("$part")
22 | part=""
23 | else
24 | part+="$char"
25 | fi
26 | done
27 | args+=("$part")
28 | fi
29 | }
30 | arguments=()
31 | while [ ! -f "${!#}" ]; do
32 | arg="${!#}"
33 | split_arguments "$arg" run
34 | arguments+=("\"${args[@]}\"")
35 | unset args
36 | set -- "${@:1:$(($#-1))}"
37 | done
38 | num_args=$#
39 | filename="${!num_args}"
40 | for arg in "${@:1:$((num_args - 1))}"; do
41 | split_arguments "$arg"
42 | done
43 | revrargs=()
44 | for ((i=${#arguments[@]}-1; i>=0; i--)); do
45 | revrargs+="${arguments[i]} "
46 | done
47 | if [ ${#revrargs[@]} -gt 0 ]; then
48 | /usr/local/bin/xcc/xcc "${args[@]}" -rarg "$(echo ${revrargs[@]})" "${filename}"
49 | else
50 | /usr/local/bin/xcc/xcc "${args[@]}" "${filename}"
51 | fi
--------------------------------------------------------------------------------
/xcc.h:
--------------------------------------------------------------------------------
1 | #define _POSIX_C_SOURCE 200809L
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #ifndef VERSION
22 | #define VERSION "0.1"
23 | #endif
24 |
25 | #define MAX(x, y) ((x) < (y) ? (y) : (x))
26 | #define MIN(x, y) ((x) < (y) ? (x) : (y))
27 |
28 | #ifndef __GNUC__
29 | # define __attribute__(x)
30 | #endif
31 |
32 | typedef struct Type Type;
33 | typedef struct Node Node;
34 | typedef struct Member Member;
35 | typedef struct Relocation Relocation;
36 | typedef struct Hideset Hideset;
37 |
38 | //
39 | // strings.c
40 | //
41 |
42 | typedef struct {
43 | char **data;
44 | int capacity;
45 | int len;
46 | } StringArray;
47 |
48 | void strarray_push(StringArray *arr, char *s);
49 | char *format(char *fmt, ...) __attribute__((format(printf, 1, 2)));
50 |
51 | //
52 | // tokenize.c
53 | //
54 |
55 | // Token
56 | typedef enum {
57 | TK_IDENT, // Identifiers
58 | TK_PUNCT, // Punctuators
59 | TK_KEYWORD, // Keywords
60 | TK_STR, // String literals
61 | TK_NUM, // Numeric literals
62 | TK_PP_NUM, // Preprocessing numbers
63 | TK_EOF, // End-of-file markers
64 | } TokenKind;
65 |
66 | typedef struct {
67 | char *name;
68 | unsigned file_no;
69 | char *contents;
70 |
71 | // For #line directive
72 | char *display_name;
73 | int line_delta;
74 | } File;
75 |
76 | // Token type
77 | typedef struct Token Token;
78 | struct Token {
79 | TokenKind kind; // Token kind
80 | Token *next; // Next token
81 | int64_t val; // If kind is TK_NUM, its value
82 | long double fval; // If kind is TK_NUM, its value
83 | char *loc; // Token location
84 | int len; // Token length
85 | Type *ty; // Used if TK_NUM or TK_STR
86 | char *str; // String literal contents including terminating '\0'
87 |
88 | File *file; // Source location
89 | char *filename; // Filename
90 | unsigned line_no; // Line number
91 | int line_delta; // Line number
92 | bool at_bol; // True if this token is at beginning of line
93 | bool has_space; // True if this token follows a space character
94 | Hideset *hideset; // For macro expansion
95 | Token *origin; // If this is expanded from a macro, the original token
96 | };
97 |
98 | noreturn void error(char *fmt, ...) __attribute__((format(printf, 1, 2)));
99 | noreturn void error_at(char *loc, char *fmt, ...) __attribute__((format(printf, 2, 3)));
100 | noreturn void error_tok(Token *tok, char *fmt, ...) __attribute__((format(printf, 2, 3)));
101 | void warn_tok(Token *tok, char *fmt, ...) __attribute__((format(printf, 2, 3)));
102 | bool equal(Token *tok, char *op);
103 | Token *skip(Token *tok, char *op);
104 | bool consume(Token **rest, Token *tok, char *str);
105 | void convert_pp_tokens(Token *tok);
106 | File **get_input_files(void);
107 | File *new_file(char *name, unsigned file_no, char *contents);
108 | Token *tokenize_string_literal(Token *tok, Type *basety);
109 | Token *tokenize(File *file);
110 | Token *tokenize_file(char *filename);
111 |
112 | #define unreachable() \
113 | error("internal error at %s:%u", __FILE__, __LINE__)
114 |
115 | //
116 | // preprocess.c
117 | //
118 |
119 | char *search_include_paths(char *filename);
120 | void init_macros(void);
121 | void define_macro(char *name, char *buf);
122 | void undef_macro(char *name);
123 | Token *preprocess(Token *tok);
124 |
125 | //
126 | // parse.c
127 | //
128 |
129 | // Variable or function
130 | typedef struct Obj Obj;
131 | struct Obj {
132 | Obj *next;
133 | char *name; // Variable name
134 | Type *ty; // Type
135 | Token *tok; // representative token
136 | bool is_local; // local or global/function
137 | int align; // alignment
138 |
139 | // Local variable
140 | int offset;
141 |
142 | // Global variable or function
143 | bool is_function;
144 | bool is_definition;
145 | bool is_static;
146 |
147 | // Global variable
148 | bool is_tentative;
149 | bool is_tls;
150 | char *init_data;
151 | Relocation *rel;
152 |
153 | // Function
154 | bool is_inline;
155 | Obj *params;
156 | Node *body;
157 | Obj *locals;
158 | Obj *va_area;
159 | Obj *alloca_bottom;
160 | int stack_size;
161 |
162 | // Static inline function
163 | bool is_live;
164 | bool is_root;
165 | StringArray refs;
166 | };
167 |
168 | // Global variable can be initialized either by a constant expression
169 | // or a pointer to another global variable. This struct represents the
170 | // latter.
171 | typedef struct Relocation Relocation;
172 | struct Relocation {
173 | Relocation *next;
174 | int offset;
175 | char **label;
176 | long addend;
177 | };
178 |
179 | // AST node
180 | typedef enum {
181 | ND_NULL_EXPR, // Do nothing
182 | ND_ADD, // +
183 | ND_SUB, // -
184 | ND_MUL, // *
185 | ND_DIV, // /
186 | ND_NEG, // unary -
187 | ND_MOD, // %
188 | ND_BITAND, // &
189 | ND_BITOR, // |
190 | ND_BITXOR, // ^
191 | ND_SHL, // <<
192 | ND_SHR, // >>
193 | ND_EQ, // ==
194 | ND_NE, // !=
195 | ND_LT, // <
196 | ND_LE, // <=
197 | ND_ASSIGN, // =
198 | ND_COND, // ?:
199 | ND_COMMA, // ,
200 | ND_MEMBER, // . (struct member access)
201 | ND_ADDR, // unary &
202 | ND_DEREF, // unary *
203 | ND_NOT, // !
204 | ND_BITNOT, // ~
205 | ND_LOGAND, // &&
206 | ND_LOGOR, // ||
207 | ND_RETURN, // "return"
208 | ND_IF, // "if"
209 | ND_FOR, // "for" or "while"
210 | ND_DO, // "do"
211 | ND_SWITCH, // "switch"
212 | ND_CASE, // "case"
213 | ND_BLOCK, // { ... }
214 | ND_GOTO, // "goto"
215 | ND_GOTO_EXPR, // "goto" labels-as-values
216 | ND_LABEL, // Labeled statement
217 | ND_LABEL_VAL, // [GNU] Labels-as-values
218 | ND_FUNCALL, // Function call
219 | ND_EXPR_STMT, // Expression statement
220 | ND_STMT_EXPR, // Statement expression
221 | ND_VAR, // Variable
222 | ND_VLA_PTR, // VLA designator
223 | ND_NUM, // Integer
224 | ND_CAST, // Type cast
225 | ND_MEMZERO, // Zero-clear a stack variable
226 | ND_ASM, // "asm"
227 | ND_CAS, // Atomic compare-and-swap
228 | ND_EXCH, // Atomic exchange
229 | } NodeKind;
230 |
231 | // AST node type
232 | struct Node {
233 | NodeKind kind; // Node kind
234 | Node *next; // Next node
235 | Type *ty; // Type, e.g. int or pointer to int
236 | Token *tok; // Representative token
237 |
238 | Node *lhs; // Left-hand side
239 | Node *rhs; // Right-hand side
240 |
241 | // "if" or "for" statement
242 | Node *cond;
243 | Node *then;
244 | Node *els;
245 | Node *init;
246 | Node *inc;
247 |
248 | // "break" and "continue" labels
249 | char *brk_label;
250 | char *cont_label;
251 |
252 | // Block or statement expression
253 | Node *body;
254 |
255 | // Struct member access
256 | Member *member;
257 |
258 | // Function call
259 | Type *func_ty;
260 | Node *args;
261 | bool pass_by_stack;
262 | Obj *ret_buffer;
263 |
264 | // Goto or labeled statement, or labels-as-values
265 | char *label;
266 | char *unique_label;
267 | Node *goto_next;
268 |
269 | // Switch
270 | Node *case_next;
271 | Node *default_case;
272 |
273 | // Case
274 | long begin;
275 | long end;
276 |
277 | // "asm" string literal
278 | char *asm_str;
279 |
280 | // Atomic compare-and-swap
281 | Node *cas_addr;
282 | Node *cas_old;
283 | Node *cas_new;
284 |
285 | // Atomic op= operators
286 | Obj *atomic_addr;
287 | Node *atomic_expr;
288 |
289 | // Variable
290 | Obj *var;
291 |
292 | // Numeric literal
293 | int64_t val;
294 | long double fval;
295 | };
296 |
297 | Node *new_cast(Node *expr, Type *ty);
298 | int64_t const_expr(Token **rest, Token *tok);
299 | Obj *parse(Token *tok);
300 |
301 | //
302 | // type.c
303 | //
304 |
305 | typedef enum {
306 | TY_VOID,
307 | TY_BOOL,
308 | TY_CHAR,
309 | TY_SHORT,
310 | TY_INT,
311 | TY_LONG,
312 | TY_FLOAT,
313 | TY_DOUBLE,
314 | TY_LDOUBLE,
315 | TY_ENUM,
316 | TY_PTR,
317 | TY_FUNC,
318 | TY_ARRAY,
319 | TY_VLA, // variable-length array
320 | TY_STRUCT,
321 | TY_UNION,
322 | } TypeKind;
323 |
324 | struct Type {
325 | TypeKind kind;
326 | int size; // sizeof() value
327 | int align; // alignment
328 | bool is_unsigned; // unsigned or signed
329 | bool is_atomic; // true if _Atomic
330 | Type *origin; // for type compatibility check
331 |
332 | // Pointer-to or array-of type. We intentionally use the same member
333 | // to represent pointer/array duality in C.
334 | //
335 | // In many contexts in which a pointer is expected, we examine this
336 | // member instead of "kind" member to determine whether a type is a
337 | // pointer or not. That means in many contexts "array of T" is
338 | // naturally handled as if it were "pointer to T", as required by
339 | // the C spec.
340 | Type *base;
341 |
342 | // Declaration
343 | Token *name;
344 | Token *name_pos;
345 |
346 | // Array
347 | int array_len;
348 |
349 | // Variable-length array
350 | Node *vla_len; // # of elements
351 | Obj *vla_size; // sizeof() value
352 |
353 | // Struct
354 | Member *members;
355 | bool is_flexible;
356 | bool is_packed;
357 |
358 | // Function type
359 | Type *return_ty;
360 | Type *params;
361 | bool is_variadic;
362 | Type *next;
363 | };
364 |
365 | // Struct member
366 | struct Member {
367 | Member *next;
368 | Type *ty;
369 | Token *tok; // for error message
370 | Token *name;
371 | int idx;
372 | int align;
373 | int offset;
374 |
375 | // Bitfield
376 | bool is_bitfield;
377 | int bit_offset;
378 | int bit_width;
379 | };
380 |
381 | extern Type *ty_void;
382 | extern Type *ty_bool;
383 |
384 | extern Type *ty_char;
385 | extern Type *ty_short;
386 | extern Type *ty_int;
387 | extern Type *ty_long;
388 |
389 | extern Type *ty_uchar;
390 | extern Type *ty_ushort;
391 | extern Type *ty_uint;
392 | extern Type *ty_ulong;
393 |
394 | extern Type *ty_float;
395 | extern Type *ty_double;
396 | extern Type *ty_ldouble;
397 |
398 | bool is_integer(Type *ty);
399 | bool is_flonum(Type *ty);
400 | bool is_numeric(Type *ty);
401 | bool is_compatible(Type *t1, Type *t2);
402 | Type *copy_type(Type *ty);
403 | Type *pointer_to(Type *base);
404 | Type *func_type(Type *return_ty);
405 | Type *array_of(Type *base, int size);
406 | Type *vla_of(Type *base, Node *expr);
407 | Type *enum_type(void);
408 | Type *struct_type(void);
409 | void add_type(Node *node);
410 |
411 | //
412 | // codegen.c
413 | //
414 |
415 | void codegen(Obj *prog, FILE *out);
416 | int align_to(int n, int align);
417 |
418 | //
419 | // unicode.c
420 | //
421 |
422 | int encode_utf8(char *buf, uint32_t c);
423 | uint32_t decode_utf8(char **new_pos, char *p);
424 | bool is_ident1(uint32_t c);
425 | bool is_ident2(uint32_t c);
426 | int display_width(char *p, int len);
427 |
428 | //
429 | // hashmap.c
430 | //
431 |
432 | typedef struct {
433 | char *key;
434 | int keylen;
435 | void *val;
436 | } HashEntry;
437 |
438 | typedef struct {
439 | HashEntry *buckets;
440 | int capacity;
441 | int used;
442 | } HashMap;
443 |
444 | void *hashmap_get(HashMap *map, char *key);
445 | void *hashmap_get2(HashMap *map, char *key, int keylen);
446 | void hashmap_put(HashMap *map, char *key, void *val);
447 | void hashmap_put2(HashMap *map, char *key, int keylen, void *val);
448 | void hashmap_delete(HashMap *map, char *key);
449 | void hashmap_delete2(HashMap *map, char *key, int keylen);
450 | void hashmap_test(void);
451 |
452 | //
453 | // main.c
454 | //
455 |
456 | bool file_exists(char *path);
457 |
458 | extern StringArray include_paths;
459 | extern int opt_O;
460 | extern bool opt_asm;
461 | extern bool opt_fpic;
462 | extern bool opt_fcommon;
463 | extern char *base_file;
464 |
--------------------------------------------------------------------------------