Cthulhu  0.2.10
Cthulhu compiler collection
main.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPL-3.0-only
2 
3 #include "base/util.h"
4 #include "setup/setup.h"
5 #include "format/colour.h"
6 #include "base/log.h"
9 
10 #include "arena/arena.h"
11 #include "io/console.h"
12 #include "memory/memory.h"
13 #include "notify/notify.h"
14 #include "format/notify.h"
15 #include "scan/node.h"
16 #include "support/loader.h"
17 
18 #include "cthulhu/check/check.h"
19 
20 #include "cthulhu/ssa/ssa.h"
21 
22 #include "base/panic.h"
23 #include "core/macros.h"
24 
25 #include "std/map.h"
26 #include "std/str.h"
27 #include "std/vector.h"
28 
29 #include "fs/fs.h"
30 #include "io/io.h"
31 #include "os/os.h"
32 
33 #include "argparse/argparse.h"
34 #include "support/support.h"
35 
36 #include <stddef.h>
37 #include <stdlib.h> // for malloc, free, system
38 
39 #define CHECK_REPORTS(reports, msg) \
40  do \
41  { \
42  int err = end_reports(reports, msg, kReportConfig); \
43  if (err != 0) \
44  { \
45  return err; \
46  } \
47  } while (0)
48 
49 static const frontend_t kFrontendHarness = {
50  .info = {
51  .id = "frontend-harness",
52  .name = "Test Harness",
53  .version = {
54  .license = "GPLv3",
55  .desc = "End to end test harness",
56  .author = "Elliot Haisley",
57  .version = CT_NEW_VERSION(0, 0, 2),
58  },
59  }
60 };
61 
62 static io_t *make_file(const char *path, os_access_t flags, arena_t *arena)
63 {
64  io_t *io = io_file(path, flags, arena);
65  os_error_t err = io_error(io);
66  CTASSERTF(err == 0, "failed to open file `%s` (%s)", path, os_error_string(err, arena));
67  return io;
68 }
69 
70 typedef struct user_ptr_t
71 {
72  uint32_t size;
73  uint32_t pad0;
74  uint32_t pad1;
75  uint32_t pad2;
76 
77  STA_FIELD_SIZE(size) char data[];
78 } user_ptr_t;
79 
80 CT_STATIC_ASSERT(sizeof(user_ptr_t) == 16, "user_ptr_t must be 16 byte aligned");
81 
82 typedef struct user_arena_t
83 {
84  char *memory_start;
86  char *memory_end;
87 
88  size_t alloc_count;
89  size_t realloc_count;
90  size_t free_count;
91 } user_arena_t;
92 
93 static user_ptr_t *get_memory(user_arena_t *arena, size_t size)
94 {
95  size_t aligned = CT_ALIGN_POW2(size, 16);
96  size_t space = aligned + sizeof(user_ptr_t); // required space
97 
98  CTASSERTF(arena->memory_cursor + space < arena->memory_end,
99  "out of memory (size: %zu)", size);
100 
101  user_ptr_t *ptr = (user_ptr_t *)arena->memory_cursor;
102 
103  // align the pointer itself
104  ptr = (user_ptr_t *)CT_ALIGN_POW2((uintptr_t)ptr, 16);
105  ptr->size = (uint32_t)size;
106  arena->memory_cursor = (char *)ptr + aligned + sizeof(user_ptr_t);
107 
108  arena->alloc_count++;
109 
110  return ptr;
111 }
112 
113 static user_ptr_t *get_ptr(void *ptr)
114 {
115  char *data = (char *)ptr;
116  return (user_ptr_t *)(data - sizeof(user_ptr_t));
117 }
118 
119 static void *user_malloc(size_t size, void *user)
120 {
121  user_arena_t *data = (user_arena_t *)user;
122 
123  user_ptr_t *ptr = get_memory(data, size);
124  return ptr->data;
125 }
126 
127 static void *user_realloc(void *ptr, size_t new_size, size_t old_size, void *user)
128 {
129  CT_UNUSED(old_size);
130 
131  user_arena_t *data = (user_arena_t *)user;
132 
133  user_ptr_t *old = get_ptr(ptr);
134 
135  if (old->size >= new_size) return old->data;
136 
137  user_ptr_t *new = get_memory(data, new_size);
138  ctu_memcpy(new->data, old->data, old->size);
139 
140  data->realloc_count++;
141 
142  return new->data;
143 }
144 
145 static void user_free(void *ptr, size_t size, void *user)
146 {
147  CT_UNUSED(ptr);
148  CT_UNUSED(size);
149 
150  user_arena_t *data = (user_arena_t *)user;
151 
152  data->free_count++;
153 }
154 
155 static user_arena_t new_user_arena(size_t size)
156 {
157  char *memory = malloc(size);
158  CTASSERT(memory != NULL);
159 
160  user_arena_t arena = {
161  .memory_start = memory,
162  .memory_cursor = memory,
163  .memory_end = memory + size,
164 
165  .alloc_count = 0,
166  .realloc_count = 0,
167  .free_count = 0,
168  };
169 
170  return arena;
171 }
172 
173 typedef struct arena_user_wrap_t
174 {
178 
179 static arena_t new_alloc(user_arena_t *user)
180 {
181  arena_t arena = {
182  .name = "user",
183  .fn_malloc = user_malloc,
184  .fn_realloc = user_realloc,
185  .fn_free = user_free,
186  .user = user,
187  };
188 
189  return arena;
190 }
191 
192 static int check_reports(logger_t *logger, report_config_t config, const char *title)
193 {
194  int err = text_report(logger_get_events(logger), config, title);
195  logger_reset(logger);
196  return err;
197 }
198 
199 #define CHECK_LOG(logger, fmt) \
200  do \
201  { \
202  int log_ok = check_reports(logger, report_config, fmt); \
203  if (log_ok != CT_EXIT_OK) \
204  { \
205  return log_ok; \
206  } \
207  } while (0)
208 
209 int run_test_harness(int argc, const char **argv, arena_t *arena)
210 {
211  // harness.exe <name> [files...]
212  CTASSERT(argc > 2);
213 
214  broker_t *broker = broker_new(&kFrontendHarness, arena);
215  loader_t *loader = loader_new(arena);
216  support_t *support = support_new(broker, loader, arena);
217 
218  char *cwd = os_cwd_string(arena);
219  CTASSERTF(ctu_strlen(cwd), "failed to get cwd");
220 
221  // test name
222  const char *name = argv[1];
223  int start = 2;
224 
225 #if CT_BUILD_SHARED
226  start = 3;
227  loaded_module_t mod = {0};
228  CTASSERTF(support_load_module(support, eModLanguage, argv[2], &mod), "failed to load module `%s` (%s: %s)", argv[2], load_error_string(mod.error), os_error_string(mod.os, arena));
229 #else
231 #endif
232 
233  logger_t *logger = broker_get_logger(broker);
234  const node_t *node = broker_get_node(broker);
235 
236  io_t *msg_buffer = io_stdout();
237 
238  text_config_t text_config = {
239  .config = {
240  .zeroth_line = false,
241  .max_columns = 80,
242  },
243  .colours = &kColourNone,
244  .io = msg_buffer,
245  };
246 
247  report_config_t report_config = {
249  .text_config = text_config,
250  };
251 
252  CHECK_LOG(logger, "adding languages");
253 
254  broker_init(broker);
255 
256  CTASSERTF(start < argc, "no files to parse");
257 
258  for (int i = start; i < argc; i++)
259  {
260  const char *path = argv[i];
261  const char *ext = str_ext(path, arena);
262  CTASSERTF(ext != NULL, "no extension for file `%s`", path);
263  language_runtime_t *lang = support_get_lang(support, ext);
264  CTASSERTF(lang != NULL, "no language for extension `%s`", ext);
265 
266  io_t *io = make_file(path, eOsAccessRead, arena);
267 
268  broker_parse(lang, io);
269 
270  CHECK_LOG(logger, "parsing source");
271  }
272 
273  for (size_t stage = 0; stage < ePassCount; stage++)
274  {
275  broker_run_pass(broker, stage);
276 
277  char *msg = str_format(arena, "running stage %s", broker_pass_name(stage));
278  CHECK_LOG(logger, msg);
279  }
280 
281  broker_resolve(broker);
282  CHECK_LOG(logger, "resolving symbols");
283 
284  vector_t *mods = broker_get_modules(broker);
285 
286  check_tree(logger, mods, arena);
287  CHECK_LOG(logger, "validation");
288 
289  ssa_result_t ssa = ssa_compile(mods, arena);
290  CHECK_LOG(logger, "generating ssa");
291 
292  ssa_opt(logger, ssa, arena);
293  CHECK_LOG(logger, "optimizing ssa");
294 
295  fs_t *fs = fs_virtual("out", arena);
296 
297  target_runtime_t *debug = support_get_target(support, "debug");
298  target_runtime_t *cfamily = support_get_target(support, "cfamily");
299 
300  CTASSERT(debug != NULL);
301  CTASSERT(cfamily != NULL);
302 
303  target_emit_t emit = {
304  .layout = eFileLayoutFlat,
305  .fs = fs,
306  };
307 
308  target_emit_ssa(debug, &ssa, &emit);
309  CHECK_LOG(logger, "emitting debug ssa");
310 
311  emit_result_t cfamily_result = target_emit_ssa(cfamily, &ssa, &emit);
312  CHECK_LOG(logger, "emitting cfamily ssa");
313 
314 #if 0
315  emit_options_t base_options = {
316  .arena = arena,
317  .reports = logger,
318  .fs = fs,
319 
320  .modules = ssa.modules,
321  .deps = ssa.deps,
322  };
323 
324  ssa_emit_options_t emit_options = {.opts = base_options};
325 
326  ssa_emit_result_t ssa_emit_result = emit_ssa(&emit_options);
327  CHECK_LOG(logger, "emitting ssa");
328  CT_UNUSED(ssa_emit_result); // TODO: check for errors
329 
330 
331  c89_emit_options_t c89_emit_options = {.opts = base_options};
332 
333  c89_emit_result_t c89_emit_result = emit_c89(&c89_emit_options);
334  CHECK_LOG(logger, "emitting c89");
335 #endif
336  const char *test_dir = str_format(arena, "%s" CT_NATIVE_PATH_SEPARATOR "test-out", cwd);
337  const char *run_dir = str_format(arena, "%s" CT_NATIVE_PATH_SEPARATOR "%s", test_dir, name);
338 
339  fs_t *out = fs_physical(run_dir, arena);
340  if (out == NULL)
341  {
342  msg_notify(logger, &kEvent_FailedToCreateOutputDirectory, node,
343  "failed to create output directory");
344  }
345  CHECK_LOG(logger, "creating output directory");
346 
347  sync_result_t result = fs_sync(out, fs);
348  if (result.path != NULL)
349  {
350  msg_notify(logger, &kEvent_FailedToWriteOutputFile, node, "failed to sync %s",
351  result.path);
352  }
353  CHECK_LOG(logger, "syncing output directory");
354 
355  size_t len = vector_len(cfamily_result.files);
356  vector_t *sources = vector_of(len, arena);
357  for (size_t i = 0; i < len; i++)
358  {
359  const char *part = vector_get(cfamily_result.files, i);
360  char *path = str_format(arena, "%s" CT_NATIVE_PATH_SEPARATOR "%s", run_dir, part);
361  vector_set(sources, i, path);
362  }
363 
364 #if CT_OS_WINDOWS
365  const char *lib_dir = str_format(arena, "%s" CT_NATIVE_PATH_SEPARATOR "lib", run_dir);
366 
367  os_error_t cwd_err = os_dir_create(lib_dir);
368  CTASSERTF(cwd_err == eOsExists || cwd_err == eOsSuccess, "failed to create dir `%s` %s", lib_dir, os_error_string(cwd_err, arena));
369 
370  char *cmd = str_format(arena, "cl /nologo /WX /W2 /c %s /I%s\\include /Fo%s\\", str_join(" ", sources, arena), run_dir, lib_dir);
371  int status = system(cmd); // NOLINT
372  if (status != 0)
373  {
374  msg_notify(logger, &kEvent_FailedToWriteOutputFile, node,
375  "compilation failed `%d`", status);
376  }
377 #else
378 # define CC_FLAGS "-Werror -Wno-format-contains-nul -Wno-unused-variable -Wno-unused-function"
379  char *cmd = str_format(arena, "cd %s && cc %s -c -Iinclude " CC_FLAGS, run_dir, str_join(" ", sources, arena));
380  int status = system(cmd); // NOLINT
381  if (WEXITSTATUS(status) != CT_EXIT_OK)
382  {
383  msg_notify(logger, &kEvent_FailedToWriteOutputFile, node,
384  "compilation failed %d", WEXITSTATUS(status));
385  }
386 #endif
387 
388  broker_deinit(broker);
389 
390  CHECK_LOG(logger, "compiling");
391 
392  return 0;
393 }
394 
395 int main(int argc, const char **argv)
396 {
397  setup_default(NULL);
398 
399  size_t size = (size_t)(1024U * 1024U * 64U);
400  user_arena_t arena = new_user_arena(size);
401  arena_t user = new_alloc(&arena);
402  init_global_arena(&user);
403  init_gmp_arena(&user);
404 
405  ctu_log_update(true);
406 
407  int result = run_test_harness(argc, argv, &user);
408 
409  free(arena.memory_start);
410 
411  return result;
412 }
CT_NODISCARD size_t size
Definition: scan.h:128
CT_OS_API char * os_cwd_string(arena_t *arena)
get the current working directory
Definition: os.c:72
CT_NODISCARD STA_RET_STRING CT_OS_API char * os_error_string(os_error_t error, arena_t *arena)
convert an os error code to a string
Definition: os.c:56
os_access_t
file access mode
Definition: core.h:38
int main(int argc, const char **argv)
Definition: main.c:168
#define STA_FIELD_SIZE(of)
annotate a field as being an array of of elements
CT_NOALIAS CT_BASE_API void * ctu_memcpy(STA_WRITES(size) void *CT_RESTRICT dst, STA_READS(size) const void *CT_RESTRICT src, size_t size)
copy memory from one location to another equivalent to memcpy but with safety checks
CT_NODISCARD CT_PUREFN CT_BASE_API size_t ctu_strlen(const char *str)
get the length of a string not including the null terminator equivalent to strlen but with safety che...
Definition: util.c:87
CT_BROKER_API const node_t * broker_get_node(broker_t *broker)
Definition: broker.c:333
CT_BROKER_API emit_result_t target_emit_ssa(target_runtime_t *runtime, const ssa_result_t *ssa, target_emit_t *emit)
Definition: broker.c:579
CT_BROKER_API void broker_parse(language_runtime_t *runtime, io_t *io)
Definition: broker.c:376
CT_BROKER_API void broker_run_pass(broker_t *broker, broker_pass_t pass)
Definition: broker.c:419
CT_BROKER_API void broker_resolve(broker_t *broker)
Definition: broker.c:450
CT_BROKER_API void broker_deinit(broker_t *broker)
Definition: broker.c:295
CT_BROKER_API broker_t * broker_new(const frontend_t *frontend, arena_t *arena)
Definition: broker.c:130
CT_CONSTFN CT_BROKER_API const char * broker_pass_name(broker_pass_t pass)
extra stuff
Definition: broker.c:597
CT_BROKER_API void broker_init(broker_t *broker)
Definition: broker.c:265
CT_BROKER_API vector_t * broker_get_modules(broker_t *broker)
get all the modules in the broker this does not include the root module
Definition: broker.c:362
CT_BROKER_API logger_t * broker_get_logger(broker_t *broker)
Definition: broker.c:325
@ ePassCount
Definition: broker.h:60
CT_BEGIN_API CT_CHECK_API void check_tree(logger_t *reports, vector_t *mods, arena_t *arena)
check the tree form IR all found errors are reported to the reports object
Definition: tree.c:1071
CT_FORMAT_API const colour_pallete_t kColourNone
a colour pallete that applies no colours
Definition: colour.c:10
#define CT_NATIVE_PATH_SEPARATOR
Definition: compiler.h:88
#define CT_NEW_VERSION(major, minor, patch)
creates a new ctu_version_t from major, minor and patch
Definition: version_def.h:20
#define CT_EXIT_OK
no user errors or internal errors
Definition: macros.h:91
CT_NODISCARD CT_FS_API fs_t * fs_virtual(const char *name, arena_t *arena)
create a virtual filesystem interface
Definition: virtual.c:273
CT_NODISCARD CT_FS_API fs_t * fs_physical(const char *root, arena_t *arena)
create a filesystem interface to a physical location on disk
Definition: physical.c:215
CT_FS_API sync_result_t fs_sync(fs_t *dst, fs_t *src)
synchronize 2 filesystems copies all folders and files from src to dst
Definition: fs.c:511
CT_MEMORY_API void init_gmp_arena(arena_t *arena)
initialize gmp with a custom allocator
Definition: memory.c:52
CT_MEMORY_API void init_global_arena(arena_t *arena)
initialize the global memory arena
Definition: memory.c:21
CT_NODISCARD CT_IO_API io_t * io_file(const char *path, os_access_t mode, arena_t *arena)
create an IO object from a file
Definition: file.c:125
CT_NODISCARD CT_IO_API os_error_t io_error(const io_t *io)
get the last error from the io object
Definition: io.c:144
CT_IO_API io_t * io_stdout(void)
get the global stdout IO object
Definition: console.c:60
CT_SUPPORT_API language_runtime_t * support_get_lang(support_t *support, const char *ext)
Definition: support.c:176
CT_SUPPORT_API void support_load_default_modules(support_t *support)
load all default modules
Definition: support.c:139
CT_SUPPORT_API bool support_load_module(support_t *support, module_type_t mask, const char *name, loaded_module_t *out)
Definition: support.c:153
CT_SUPPORT_API target_runtime_t * support_get_target(support_t *support, const char *name)
Definition: support.c:192
CT_SUPPORT_API support_t * support_new(broker_t *broker, loader_t *loader, arena_t *arena)
create a support instance from an existing loader and broker configures the broker with the modules i...
Definition: support.c:113
CT_BASE_API void ctu_log_update(bool enable)
update the verbosity of the logging system
Definition: log.c:15
#define CT_ALIGN_POW2(X, ALIGN)
Definition: macros.h:42
#define CT_UNUSED(x)
mark a variable as unused
Definition: macros.h:46
CT_NOTIFY_API event_builder_t msg_notify(INOUT_NOTNULL logger_t *logs, const diagnostic_t *diagnostic, const node_t *node, STA_FORMAT_STRING const char *fmt,...)
notify the logger of a new message
CT_NOTIFY_API void logger_reset(logger_t *logs)
reset the loggers messages
Definition: notify.c:89
CT_NODISCARD CT_NOTIFY_API typevec_t * logger_get_events(const logger_t *logs)
get the events from the logger
Definition: notify.c:36
#define CTASSERT(expr)
assert a condition, prints the condition as a message
Definition: panic.h:130
#define CTASSERTF(expr,...)
assert a condition with a message and optional format arguments
Definition: panic.h:116
CT_SETUP_API void setup_default(arena_t *arena)
initialise the runtime with default options
Definition: setup.c:188
CT_SSA_API void ssa_opt(logger_t *reports, ssa_result_t mod, arena_t *arena)
Optimize a given module.
Definition: opt.c:359
CT_SSA_API ssa_result_t ssa_compile(vector_t *mods, arena_t *arena)
compile a set of trees into their ssa form
Definition: ssa.c:911
CT_NODISCARD CT_STD_API char * str_ext(const char *path, arena_t *arena)
get the last file extension from a path
Definition: str.c:173
CT_NODISCARD CT_STD_API char * str_join(const char *sep, const vector_t *parts, arena_t *arena)
join strings
Definition: str.c:274
CT_NODISCARD CT_PUREFN CT_STD_API void * vector_get(const vector_t *vector, size_t index)
get a value from a vector
Definition: vector.c:134
CT_NODISCARD CT_STD_API vector_t * vector_of(size_t len, arena_t *arena)
create a new vector with a specified length
Definition: vector.c:71
CT_STD_API void vector_set(vector_t *vector, size_t index, void *value)
set a value in a vector
Definition: vector.c:125
CT_NODISCARD CT_PUREFN CT_STD_API size_t vector_len(const vector_t *vector)
get the length of a vector
Definition: vector.c:152
CT_SUPPORT_API loader_t * loader_new(arena_t *arena)
Definition: loader.c:41
CT_SUPPORT_API const char * load_error_string(load_error_t error)
Definition: loader.c:131
CT_OS_API os_error_t os_dir_create(const char *path)
check if a directory exists
Definition: fs.c:29
@ eOsExists
Definition: posix.h:26
@ eOsSuccess
Definition: posix.h:24
STA_DECL char * str_format(arena_t *arena, const char *fmt,...)
Definition: str.c:97
an allocator object
Definition: arena.h:86
const char * name
the name of the allocator
Definition: arena.h:88
arena_t arena
Definition: main.c:175
user_arena_t user
Definition: main.c:176
vector_t * files
Definition: broker.h:224
bool zeroth_line
the zeroth line of a file is the first line
Definition: notify.h:51
the frontend running the mediator
Definition: broker.h:251
module_info_t info
information about the frontend
Definition: broker.h:253
Definition: common.h:72
io object implementation
Definition: impl.h:122
load_error_t error
Definition: loader.h:55
os_error_t os
Definition: loader.h:56
Definition: common.h:8
a logging sink
Definition: notify.c:14
const char * id
unique id for the module
Definition: broker.h:101
a position in a source file
Definition: node.h:23
text_format_t report_format
Definition: notify.h:92
vector_t * modules
Definition: ssa.h:363
map_t * deps
Definition: ssa.h:364
the result of a fs_sync call this is here because we cant use Compiler message notification in the fs...
Definition: fs.h:116
const char * path
the file that failed to sync
Definition: fs.h:119
file_layout_t layout
Definition: broker.h:309
file_config_t config
Definition: notify.h:75
size_t free_count
Definition: main.c:90
char * memory_cursor
Definition: main.c:85
size_t realloc_count
Definition: main.c:89
char * memory_start
Definition: main.c:84
char * memory_end
Definition: main.c:86
size_t alloc_count
Definition: main.c:88
uint32_t pad0
Definition: main.c:73
uint32_t size
Definition: main.c:72
uint32_t pad2
Definition: main.c:75
uint32_t pad1
Definition: main.c:74
a generic vector of pointers
Definition: vector.c:16
CT_FORMAT_API int text_report(typevec_t *events, report_config_t config, const char *title)
Definition: common_extra.c:416
@ eTextSimple
simple text reporting
Definition: notify.h:63
CT_STATIC_ASSERT(sizeof(user_ptr_t)==16, "user_ptr_t must be 16 byte aligned")
#define CHECK_LOG(logger, fmt)
Definition: main.c:199
int run_test_harness(int argc, const char **argv, arena_t *arena)
Definition: main.c:209
#define CC_FLAGS