Initial commit of current C compiler sources, partly rebranded but not yet properly cleaned up!

This commit is contained in:
Zak Yani Star Fenton 2025-06-04 03:45:05 +10:00
parent 6e217b2669
commit 7f74463109
33 changed files with 18135 additions and 0 deletions

317
cc.c Normal file
View File

@ -0,0 +1,317 @@
#define TOOL_CPP
#define _CRT_SECURE_NO_WARNINGS
#ifdef _WIN32
#include <process.h>
#endif
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
//#include "compile.h"
#define CCB_IMPLEMENTATION
#include "ccb.h"
#define CCBGENERIC_IMPLEMENTATION
#include "ccbgeneric.h"
#ifdef TOOL_CPP
#define CPP_IMPLEMENTATION
#include "cpp.h"
#endif
#ifdef TOOL_MK
#define MK_IMPLEMENTATION
#include "mk.h"
#endif
int sh_main(int argc, char** argv);
void ccb_compile_error_impl(ccb_t* ccb/*, const char* fmt, ...*/) {
fprintf(stderr, "^ Around line %d, column %d of '%s'.\n", ccb->pos.uline, ccb->pos.ucol, ccb->pos.ufile);
fprintf(stderr, " [Around line %d, column %d of preprocessed input]\n", ccb->pos.line, ccb->pos.col);
/*#ifdef _ZCC
fprintf(stderr, "ERROR '%s'\n(TODO: Better formatting!)\n", fmt);
((char*)NULL)[0] = 0; // Trigger debugger
#else
va_list a;
va_start(a, fmt);
vfprintf(stderr, fmt, a);
fprintf(stderr, "\n");
va_end(a);
#endif*/
exit(EXIT_FAILURE);
}
void ccb_compile_warn_impl(ccb_t* ccb/*, const char* fmt, ...*/) {
fprintf(stderr, "^ Around line %d, column %d of '%s'.\n", ccb->pos.uline, ccb->pos.ucol, ccb->pos.ufile);
fprintf(stderr, " [Around line %d, column %d of preprocessed input]\n", ccb->pos.line, ccb->pos.col);
/*#ifdef _ZCC
fprintf(stderr, "WARNING '%s'\n(TODO: Better formatting!)\n", fmt);
#else
va_list a;
va_start(a, fmt);
vfprintf(stderr, fmt, a);
fprintf(stderr, "\n");
va_end(a);
#endif*/
}
static int startcompile(ccb_t* ccb) {
ccb_util_init(ccb);
ccb_target_init(ccb);
ccb_ast_init(ccb);
ccb_list_t* block = ccb_parse_run(ccb);
if (!ccb->dump_ast) {
if (ccb->include_data) {
ccb_target_gen_data_section(ccb);
}
}
/* First run is required to gather externs. TODO: This should be done for any extern data elements too! */
for (ccb_list_iterator_t* it = ccb_list_iterator(block); !ccb_list_iterator_end(it); ) {
if (!ccb->dump_ast) {
if (ccb->include_code) {
ccb_target_gen_declfunction(ccb, ccb_list_iterator_next(it));
}
}
else {
printf("%s", ccb_ast_string(ccb, ccb_list_iterator_next(it)));
}
}
if (ccb->knownexterns != NULL) {
for (ccb_list_iterator_t* it = ccb_list_iterator(ccb->knownexterns); !ccb_list_iterator_end(it); ) {
if (!ccb->dump_ast) {
if (ccb->include_code) {
ccb_target_gen_declextern(ccb, ccb_list_iterator_next(it));
}
}
else {
printf("%s", ccb_ast_string(ccb, ccb_list_iterator_next(it)));
}
}
}
for (ccb_list_iterator_t* it = ccb_list_iterator(block); !ccb_list_iterator_end(it); ) {
if (!ccb->dump_ast) {
if (ccb->include_code) {
ccb_target_gen_function(ccb, ccb_list_iterator_next(it));
}
}
else {
printf("%s", ccb_ast_string(ccb, ccb_list_iterator_next(it)));
}
}
return true;
}
/* Original main program from LICE:
int main(int argc, char **argv) {
argc--;
argv++;
return startcompile(!!(argc && !strcmp(*argv, "--dump-ast")))
? EXIT_SUCCESS
: EXIT_FAILURE;
}
*/
static void usage(int argc, char** argv, int arge) {
//fprintf(stderr, "TODO: Usage!\n");
char* n = argv[0];
fprintf(stderr, "USAGE:\n\n");
fprintf(stderr, " %s [--silent] [--ast-only|--code-only|--data-only|--usage] [TODO --asmfmt fasm|gas] [TODO --binfmt elf|flat] [--input <fname>] [--output|--append <fname>]\n\n", n);
fprintf(stderr, "(This is a simple core compiler designed to be invoked from a more user-friendly frontend.\nArguments must be provided in the above order, defaults to using stdin/stdout.)");
}
int preprocessormain(int argc, char** argv); // Extra definition of the C preprocessor main program
//void mk_main(int argc, char** argv); // Extra definition of the "make" tool main program
/* Zak's new new main program: (frontend) */
#include "frontend.c"
/* Zak's new main program: (backend) */
int backend_main(int argc, char** argv) {
/*int x;
for(x=0;x<argc;x++) {
printf("Got arg '%s'\n", argv[x]);
}*/
//argc = 3;
//argv = (char*[]){"test",/*"--ast-only",*/"--input","C:\\Users\\Zak\\source\\repos\\ZCC\\Debug\\test2.c"};
ccb_t ccb;
ccb.dump_ast = false;
ccb.silent = false;
ccb.input = stdin;
ccb.output = stdout;
ccb.include_code = true;
ccb.include_data = true;
ccb.pos.line = 1;
ccb.pos.col = 1;
ccb.pos.uline = 1;
ccb.pos.ucol = 1;
ccb.pos.ufile = strdup("input");
ccb.default_callconv = 0;
ccb.func_callconv = 0;
ccb.func_name = NULL;
ccb.mod_name = NULL;
ccb.mod_initstmts = NULL;
ccb.sym_prefix = strdup("");
ccb.declarednames = NULL;
ccb.knownexterns = NULL;
ccb.bsscount = 0;
int argi = 1;
if (argc > argi && (!strcmp(argv[argi], "--silent") || !strcmp(argv[argi], "--usage") || !strcmp(argv[argi], "-P") || !strcmp(argv[argi], "-B"))) { // Also skip added notices if using --usage
if (!strcmp(argv[argi], "--silent")) {
argi++;
}
ccb.silent = true;
}
else {
fprintf(stderr, "SecureLang C Compiler backend (CCb), early version\n");
fprintf(stderr, "NOTE: This program generally reads program code from standard input (until EOF) and writes assembly code to standard output.\n");
fprintf(stderr, " It would usually be used from a compiler frontend (preprocessing, assembling and linking must be done separately).\n");
fprintf(stderr, " Use --silent as the first argument to disable these notices or --usage to learn more.\n\n");
}
/* Ignore -B, used to invoke backend. */
if (argc > argi && !strcmp(argv[argi], "-B")) {
argi++;
}
if (argc > argi && !strcmp(argv[argi], "--ast-only")) {
if (!ccb.silent) {
fprintf(stderr, "NOTE: Will dump AST to output instead of assembly code.\n");
}
ccb.dump_ast = true;
argi++;
}
else if (argc > argi && !strcmp(argv[argi], "--data-only")) {
if (!ccb.silent) {
fprintf(stderr, "NOTE: Will dump data section only.\n");
}
ccb.include_code = false;
ccb.include_data = true;
argi++;
}
else if (argc > argi && !strcmp(argv[argi], "--code-only")) {
if (!ccb.silent) {
fprintf(stderr, "NOTE: Will dump code section only.\n");
}
ccb.include_code = true;
ccb.include_data = false;
argi++;
}
#ifdef TOOL_CPP
else if (argc > argi && !strcmp(argv[argi], "-P")) { /* Invoke the built-in preprocessor instead of the backend. */
if (!ccb.silent) {
fprintf(stderr, "NOTE: Invoking the experimental preprocessor.\n");
}
// NOTE: This will only work for argv == 1, and because cppmain ignores the -P flag!
return preprocessormain(argc, argv);
}
#endif
#ifdef TOOL_MK
else if (argc > argi && !strcmp(argv[argi], "-M")) { /* Invoke the built-in make tool instead of the backend. */
if (!ccb.silent) {
fprintf(stderr, "NOTE: Invoking the experimental make tool.\n");
}
// NOTE: This will only work for argv == 1, and because cppmain ignores the -P flag!
void* tmp = NULL;
mk_main(argc, argv, &tmp);
return -1; // TODO: mk_main returns void but also has some exit calls, this behaviour needs to be checked to return error codes properly
}
#endif
#ifdef TOOL_SH
else if (argc > argi && !strcmp(argv[argi], "-S")) { /* Invoke the built-in make tool instead of the backend. */
if (!ccb.silent) {
fprintf(stderr, "NOTE: Invoking the experimental shell tool.\n");
}
// NOTE: This will only work for argv == 1, and because cppmain ignores the -P flag!
void* tmp = NULL;
return sh_main(argc, argv);
}
#endif
if (argc > argi && !strcmp(argv[argi], "--usage")) {
printf("USAGE?\n");
usage(argc, argv, -1);
return EXIT_SUCCESS;
}
if (argc > argi && !strcmp(argv[argi], "--101")) {
if (!ccb.silent) {
fprintf(stderr, "NOTE: Using calling convention 101 (__classic_call) by default.\n", argv[argi + 1]);
}
ccb.default_callconv = 101;
argi++;
}
if (argc > argi + 1 && !strcmp(argv[argi], "--mod")) {
if (!ccb.silent) {
fprintf(stderr, "NOTE: Using module name '%s'.\n", argv[argi + 1]);
}
ccb.mod_name = strdup(argv[argi + 1]);
argi += 2;
}
if (argc > argi + 1 && !strcmp(argv[argi], "--prefix")) {
if (!ccb.silent) {
fprintf(stderr, "NOTE: Using symbol prefix '%s'.\n", argv[argi + 1]);
}
ccb.sym_prefix = strdup(argv[argi + 1]);
argi += 2;
}
if (argc > argi + 1 && !strcmp(argv[argi], "--input")) {
if (!ccb.silent) {
fprintf(stderr, "NOTE: Using input from '%s'.\n", argv[argi + 1]);
}
ccb.input = fopen(argv[argi + 1], "r");
ccb.pos.ufile = strdup(argv[argi + 1]);
argi += 2;
}
if (argc > argi + 1 && !strcmp(argv[argi], "--output")) {
if (!ccb.silent) {
fprintf(stderr, "NOTE: Writing output to '%s'.\n", argv[argi + 1]);
}
ccb.output = fopen(argv[argi + 1], "w");
argi += 2;
}
else if (argc > argi + 1 && !strcmp(argv[argi], "--append")) {
if (!ccb.silent) {
fprintf(stderr, "NOTE: Appending output from '%s'.\n", argv[argi]);
}
ccb.output = fopen(argv[argi + 1], "a");
argi += 2;
}
if (ccb.input == NULL) {
fprintf(stderr, "ERROR: Failed to open input file.\n");
}
if (ccb.output == NULL) {
fprintf(stderr, "ERROR: Failed to open output file.\n");
}
if (argi != argc) {
usage(argc, argv, argi);
return EXIT_FAILURE;
}
int result = startcompile(&ccb);
if (ccb.input != stdin) {
fclose(ccb.input);
}
if (ccb.output != stdout) {
fclose(ccb.output);
}
if (result) {
return EXIT_SUCCESS;
}
else {
return EXIT_FAILURE;
}
}

6843
ccb.h Normal file

File diff suppressed because it is too large Load Diff

4256
ccbgeneric.h Normal file

File diff suppressed because it is too large Load Diff

5368
cpp.h Normal file

File diff suppressed because it is too large Load Diff

6
fakelibc/README Normal file
View File

@ -0,0 +1,6 @@
The fakelibc directory contains "fake" header files to allow the compiler to
self-host on Linux-compatible systems without needing complete C compatibility.
It can also be used for other small tools built with the compiler, but is not
a replacement for a full libc (just a more-portable wrapper over GNU libc or
compatible).

7
fakelibc/assert.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef _FAKELIBC_ASSERT_H
#define _FAKELIBC_ASSERT_H
#define assert(...) do {} while(0)
/* From ifndef at top of file: */
#endif

74
fakelibc/ctype.h Normal file
View File

@ -0,0 +1,74 @@
#ifndef _FAKELIBC_CTYPE_H
#define _FAKELIBC_CTYPE_H
/*
#define isspace fake_isspace
#define isdigit fake_isdigit
#define isxdigit fake_isxdigit
#define isalpha fake_isalpha
#define isalnum fake_isalnum
#define isprint fake_isprint
#define ispunct fake_ispunct
static int fake_isspace(int ch) {
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
}
static int fake_isdigit(int ch) {
return (ch >= '0') && (ch <= '9');
}
static int fake_isxdigit(int ch) {
return isdigit(ch) || ((ch >= 'a') && (ch <= 'f')) || ((ch >= 'A') && (ch <= 'F'));
}
static int fake_isalpha(int ch) {
return ((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'));
}
static int fake_isalnum(int ch) {
return isalpha(ch) || isdigit(ch);
}
static int fake_isprint(int ch) {
return isalnum(ch) || isspace(ch) || ispunct(ch);
}
static int fake_ispunct(int ch) {
switch (ch) {
case ',':
case '<':
case '.':
case '>':
case '/':
case '?':
case ';':
case ':':
case '\'':
case '\"':
case '[':
case ']':
case '{':
case '}':
case '`':
case '~':
case '@':
case '#':
case '$':
case '%':
case '^':
case '&':
case '*':
case '(':
case ')':
case '-':
case '_':
case '=':
case '+':
return 1;
default:
return 0;
}
}
*/
/* From ifndef at top of file: */
#endif

0
fakelibc/dirent.h Normal file
View File

20
fakelibc/errno.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef _FAKELIBC_ERRNO_H
#define _FAKELIBC_ERRNO_H
//extern int errno;
/* On modern Linux platforms the errno is simulated. This is presumably so that each
* thread can have it's own errno without the ABI becoming a huge mess.
*/
#ifdef __MAC
int errno;
#else
int* __errno_location();
#define errno __errno_location()[0]
#endif
#define ENOENT 2
/* From ifndef at top of file: */
#endif

0
fakelibc/fcntl.h Normal file
View File

7
fakelibc/float.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef _FAKELIBC_FLOAT_H
#define _FAKELIBC_FLOAT_H
#define DBL_MAX_EXP 1024
#define DBL_MANT_DIG 53
#endif

7
fakelibc/inttypes.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef _FAKELIBC_INTTYPES_H
#define _FAKELIBC_INTTYPES_H
#define PRIx32 "x"
/* From ifndef at top of file: */
#endif

8
fakelibc/limits.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef _FAKELIBC_LIMITS_H
#define _FAKELIBC_LIMITS_H
#define CHAR_BIT 8
#define UINT_MAX 0xFFFFFFFFU
/* From ifndef at top of file: */
#endif

0
fakelibc/math.h Normal file
View File

5
fakelibc/memory.h Normal file
View File

@ -0,0 +1,5 @@
#ifndef _FAKELIBC_MEMORY_H
#define _FAKELIBC_MEMORY_H
/* From ifndef at top of file: */
#endif

0
fakelibc/pwd.h Normal file
View File

18
fakelibc/setjmp.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef _FAKELIBC_SETJMP_H
#define _FAKELIBC_SETJMP_H
/*struct jmp_buf_struct {
int todo;
};
typedef struct jmp_buf_struct jmp_buf;*/
typedef int jmp_buf;
#define setjmp(x) \
0
// (printf("WARNING: Unimplemented: setjmp\n") && 0)
#define longjmp(x,y) \
printf("WARNING: Unimplemented: longjmp\n")
/* From ifndef at top of file: */
#endif

0
fakelibc/signal.h Normal file
View File

27
fakelibc/stdarg.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef _FAKELIBC_STDARG_H
#define _FAKELIBC_STDARG_H
// TODO: This will basically not work except for the simplest printf-like cases
typedef long long** va_list;
#define _VA_CHECK() \
if (__builtin_func_callconv != 101) {\
printf("ERROR: Unpacking varargs currently only works with __classic_call (#101). Function %s uses convention %d instead.\n", __func__, __builtin_func_callconv);\
}
#define va_start(list,lastarg) \
do {\
_VA_CHECK();\
list = &lastarg;\
list++;\
} while(0)
#define va_arg(list,T) \
(T)(list++)
#define va_end(list) \
do {list = (void*)0;} while(0)
/* From ifndef at top of file: */
#endif

10
fakelibc/stdbool.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef _FAKELIBC_STDBOOL_H
#define _FAKELIBC_STDBOOL_H
typedef int bool;
#define true 1
#define false 0
/* From ifndef at top of file: */
#endif

13
fakelibc/stddef.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef _FAKELIBC_STDDEF_H
#define _FAKELIBC_STDDEF_H
//#ifndef _FAKELIBC_STDLIB_H
typedef long size_t;
//#endif
#ifndef NULL
#define NULL ((void*) 0)
#endif
/* From ifndef at top of file: */
#endif

26
fakelibc/stdint.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef _FAKELIBC_STDINT_H
#define _FAKELIBC_STDINT_H
typedef char int8_t;
typedef short int16_t;
typedef int int32_t;
typedef long long int64_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef int64_t intptr_t;
typedef uint64_t uintptr_t;
typedef uint32_t uint_fast8_t;
typedef int32_t int_fast8_t;
typedef uint32_t uint_fast16_t;
typedef int32_t int_fast16_t;
typedef uint32_t uint_fast32_t;
typedef int32_t int_fast32_t;
typedef uint64_t uint_fast64_t;
typedef int64_t int_fast64_t;
/* From ifndef at top of file: */
#endif

58
fakelibc/stdio.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef _FAKELIBC_STDIO_H
#define _FAKELIBC_STDIO_H
struct FILE_internals {};
typedef struct FILE_internals FILE;
#define EXIT_FAILURE 1
#define EXIT_SUCCESS 0
#ifdef __MAC
extern FILE* __stderrp;
extern FILE* __stdinp;
extern FILE* __stdoutp;
#define stderr __stderrp
#define stdin __stdinp
#define stdout __stdoutp
#endif
extern FILE* stderr;
extern FILE* stdin;
extern FILE* stdout;
#define EOF ((int)-1)
int printf(const char* fmt, ...);
int sprintf(char *buf, const char* fmt, ...);
int fprintf(FILE* f, const char* fmt, ...);
FILE* fopen(const char* name, const char* mode);
int fclose(FILE* f);
int fflush(FILE* f);
long fread(void* buffer, long size, long count, FILE* f);
long fwrite(void* buffer, long size, long count, FILE* f);
char* fgets(char*, int, FILE*);
int fputs(const char*, FILE*);
int fputc(int, FILE*);
void perror(const char*);
int putc(int c, FILE* f);
int putchar(int c);
int fseek(FILE* f, long offset, int wh);
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
long ftell(FILE* f);
long getline(char** linevar, long *nvar, FILE* f);
/* From ifndef at top of file: */
#endif

26
fakelibc/stdlib.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef _FAKELIBC_STDLIB_H
#define _FAKELIBC_STDLIB_H
//typedef long size_t;
#include <stddef.h>
void* malloc(long sz);
void* calloc(long n, long sz);
void* realloc(void* mem, long sz);
void free(void* mem);
char* getenv(const char* name);
void exit(int x);
long strtol(const char* str, char**endvar, int base);
long long strtoll(const char* str, char**endvar, int base);
unsigned long strtoul(const char* str, char**endvar, int base);
unsigned long long strtoull(const char* str, char**endvar, int base);
float strtof(const char* str, char**endvar);
double strtod(const char* str, char**endvar);
double atof(const char* str);
int atoi(const char* str);
/* From ifndef at top of file: */
#endif

47
fakelibc/string.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef _FAKELIBC_STRING_H
#define _FAKELIBC_STRING_H
#include <stddef.h>
//size_t strlen(const char* foo);
#define strlen fake_strlen
static size_t fake_strlen(const char* foo) {
if (foo == NULL) {
return 0;
} else {
size_t i = 0;
while (foo[i] != 0) i++;
return i;
}
}
char *strchr(const char* str, int chr);
char *strrchr(const char* str, int chr);
char* strcat(char* str, const char* cat);
char* strcpy(char* buffer, const char* str);
char* strncpy(char* buffer, const char* str, size_t n);
const char* strpbrk(const char* str, const char* search);
int strcmp(const char* a, const char* b);
char* strdup(const char* str);
char* strndup(const char* str, size_t n);
//void* memcpy(void* dst, const void* src, size_t nbytes);
#define memcpy fake_memcpy
static void* fake_memcpy(void* dst, const void* src, size_t nbytes) {
// TODO: This was only required because calling libc's version triggered errors, this probably isn't an issue now that more bugs have been fixed.
//printf("fake_memcpy(%lx, %lx, %ld)\n", dst, src, nbytes);
char* cdst = (char*) dst;
const char* csrc = (char*) src;
size_t i = 0;
for (i = 0; i < nbytes; i++) {
//printf("fake_memcpy %ld\n", i);
cdst[i] = csrc[i];
}
//printf("fake_memcpy done!\n");
return dst;
}
void* memset(void* mem, int byt, size_t nbytes);
/* From ifndef at top of file: */
#endif

0
fakelibc/sys/resource.h Normal file
View File

50
fakelibc/sys/stat.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef _FAKELIBC_SYS_STAT_H
#define _FAKELIBC_SYS_STAT_H
#include <time.h>
/* This is currently designed to match x86-64 definitions for Linux. They probably don't
* differ a lot across architectures (besides differences between 32-/64-bit builds)
* but the standard definitions on Linux are unusually cryptic, some of the typedefs
* involve 3 or more nested macros spread out across all sorts of includes, so using
* them as a reference feels a bit ridiculous.
*/
#define st_mtime st_mtim.tv_sec
#define st_atime st_atim.tv_sec
#define st_ctime st_ctim.tv_sec
typedef unsigned int ino_t;
typedef unsigned int mode_t;
typedef unsigned int uid_t;
typedef unsigned int gid_t;
typedef unsigned long dev_t;
typedef unsigned long nlink_t;
struct stat {
dev_t st_dev;
ino_t st_ino;
nlink_t st_nlink;
mode_t st_mode;
uid_t st_uid;
gid_t st_gid;
int __padding_A;
dev_t st_rdev;
long st_size;
long st_blocksize;
long st_blocks;
struct timespec st_atim;
struct timespec st_mtim;
struct timespec st_ctim;
long __reserved_A;
long __reserved_B;
long __reserved_C;
};
/* From ifndef at top of file: */
#endif

0
fakelibc/sys/time.h Normal file
View File

0
fakelibc/sys/types.h Normal file
View File

29
fakelibc/time.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef _FAKELIBC_TIME_H
#define _FAKELIBC_TIME_H
typedef long time_t;
char* ctime(const time_t* timevar);
/* TODO: Check if modern systems add any more fields. */
struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
struct timespec {
int tv_sec;
long tv_nsec;
};
struct tm* localtime(const time_t* timep);
/* From ifndef at top of file: */
#endif

0
fakelibc/unistd.h Normal file
View File

0
fakelibc/utime.h Normal file
View File

913
frontend.c Normal file
View File

@ -0,0 +1,913 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef _WIN32
#include <process.h> // for _spawnvp on Windows
#endif
#define BRUTAL_MAX_FILES 1000
int backend_main(int argc, char** argv);
int system(const char* cmd);
char* strdup(const char* str);
void brutal_string_append(char** buffer, char* str) {
//fprintf(stderr, "appending '%s' to '%s'\n", str, buffer[0]);
int len1 = (buffer[0] == NULL) ? 0 : strlen(buffer[0]);
//fprintf(stderr, "len1=%d\n", len1);
int len2 = strlen(str);
//fprintf(stderr, "len2=%d\n", len2);
char* nbuf = calloc(len1 + len2 + 1, 1);
if (nbuf == NULL) {
fprintf(stderr, "ERROR: Out of memory!\n");
exit(1);
}
int i;
for (i = 0; i < len1+len2; i++) {
if (i < len1) {
nbuf[i] = buffer[0][i];
} else {
nbuf[i] = str[i-len1];
}
}
if (buffer[0] != NULL) {
free(buffer[0]);
}
buffer[0] = nbuf;
}
int brutal_args_count(char** argv) {
if (argv==NULL) {
return 0;
}
int i;
for (i = 0; argv[i] != NULL; i++) {
// Just count...
}
return i;
}
void brutal_args_append(char*** buffer, char* str) {
int len = brutal_args_count(buffer[0]);
char** nbuf = calloc(len + 2, sizeof(void*));
if (nbuf == NULL) {
fprintf(stderr, "ERROR: Out of memory!\n");
exit(1);
}
int i;
for (i = 0; i < len+1; i++) {
if (i < len) {
nbuf[i] = buffer[0][i];
} else {
nbuf[i] = str;
}
}
if (buffer[0] != NULL) {
free(buffer[0]);
}
buffer[0] = nbuf;
}
void brutal_args_append_all(char*** buffer, char** moreargs) {
int i;
for (i = 0; moreargs[i] != NULL; i++) {
brutal_args_append(buffer, moreargs[i]);
}
}
int brutal_needs_quotes(char* arg) {
int i;
for (i = 0; arg[i] != 0; i++) {
char c = arg[i];
if (c == ' ') {
return 1;
}
}
return 0;
}
int brutal_setenv(char* name, char* value, int replace, int echo, int fake) {
if (echo) {
fprintf(stderr, "SETENV: Using (%s) environment variable '%s' to '%s'\n", replace ? "replacing" : "if unset", name, value);
}
if (!fake) {
return setenv(name, value, replace);
}
return 0;
}
int brutal_exec(char** argv, int echo, int fake) {
char* cmd = NULL;
int i;
for (i = 0; argv[i] != NULL; i++) {
int q = brutal_needs_quotes(argv[i]);
if (q) {
brutal_string_append(&cmd, i == 0 ? "\"" : " \"");
} else if (i != 0) {
brutal_string_append(&cmd, " ");
}
brutal_string_append(&cmd, argv[i]);
if (q) brutal_string_append(&cmd, "\"");
}
if (echo) {
fprintf(stderr, "%s command: '%s'\n", fake ? "NOT executing" : "executing", cmd);
}
return fake ? 0 : system(cmd);
}
int brutal_startswith(const char* start, char* str) {
int i = 0;
while (start[i] != 0) {
if (str[i] != start[i]) {
return 0;
}
i++;
}
return 1;
}
int brutal_endswith(char* str, const char* end) {
size_t len1 = strlen(str);
size_t len2 = strlen(end);
if (len1 < len2) {
return 0;
}
int i = (len1-len2);
int j = 0;
while (str[i] != 0) {
if (str[i] != end[j]) {
return 0;
}
i++;
j++;
}
return 1;
}
typedef struct compilerjob compilerjob_t;
struct compilerjob {
char* selfcmd;
char** inputs;
char** incdirs;
char** defines;
int numinputs;
int numincdirs;
int numdefines;
int skipcompiler;
int skiplinker;
int skipassembler;
int echocmd;
int fakecmd;
int keeptmp;
int useyasm;
int usefasm;
int usenasm;
int usezasm;
int useas;
int nasmsyntax;
int gc;
int thread;
int mm;
int riscv;
int linkstatic;
int usemold;
int use101;
char* output;
char* tmpdir;
char** tmpfiles;
char* modinitfile; // If not null, should be added as an input
char* modinitfunc; // Name of function in modinit
char* sym_prefix;
};
char* brutal_path_end(compilerjob_t* job, char* path) {
char* lastpart = path;
int i;
for (i = 0; path[i] != 0; i++) {
if (path[i] == '/' || path[i] == '\\') {
lastpart = path+i+1;
}
}
return lastpart;
}
char* brutal_path_sanitise(compilerjob_t* job, char* path) {
path = strdup(brutal_path_end(job, path));
int i = 0;
while (path[i] != 0) {
char c = path[i];
if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9'))) {
// Keep it.
} else {
path[i] = '_';
}
i++;
}
return path;
}
/* I guess the most portable way of checking if a file "exists" is trying to open it for reading
* (If it exists but we can't open it for reading, then it's unlikely that we'll be able to accidentally overwrite it either!)
*/
int brutal_exists(char* fname) {
FILE* f = fopen(fname, "rb");
if (f == NULL) {
return 0;
} else {
fclose(f);
return 1;
}
}
char* brutal_tmp(compilerjob_t* job, char* namehint1, char* namehint2);
char* brutal_tmp(compilerjob_t* job, char* namehint1, char* namehint2) {
char* result = NULL;
namehint1 = brutal_path_end(job, namehint1);
namehint2 = brutal_path_end(job, namehint2);
brutal_string_append(&result, job->tmpdir);
brutal_string_append(&result, "/");
if (!brutal_startswith("brutal-", namehint1)) {
brutal_string_append(&result, "brutal-");
}
brutal_string_append(&result, namehint1);
brutal_string_append(&result, ".tmp");
brutal_string_append(&result, namehint2);
// If the file already exists, we'll print a notice that you've probably got temporary files hanging around and try a new name
if (brutal_exists(result)) {
char* newhint1 = NULL;
fprintf(stderr, "WARNING: File '%s' already exists, you probably have temporary files laying around. I'll try a new name just in case it's a user-modified file!.\n", result);
brutal_string_append(&newhint1, namehint1);
brutal_string_append(&newhint1, "2");
return brutal_tmp(job, newhint1, namehint2);
}
// Slight workaround while compiler bugs are being fixed, create a temporary copy of the tmpfiles pointer...
char** tmpfiles = job->tmpfiles;
// Add the result to tmpfiles
brutal_args_append(&tmpfiles, result);
// Other end of workaround...
job->tmpfiles = tmpfiles;
// And we're done!
return result;
}
int brutal_exec_cp(compilerjob_t* job, char* input, char* output) {
char** args = NULL;
brutal_args_append(&args, "cp");
brutal_args_append(&args, input);
brutal_args_append(&args, output);
return brutal_exec(args, job->echocmd, job->fakecmd);
}
int brutal_exec_rm(compilerjob_t* job, char* fname) {
char** args = NULL;
brutal_args_append(&args, "rm");
brutal_args_append(&args, fname);
return brutal_exec(args, job->echocmd, job->fakecmd);
}
int brutal_exec_preprocessor(compilerjob_t* job, char* input, char* output, int isasm) {
char** args = NULL;
brutal_args_append(&args, job->selfcmd);
brutal_args_append(&args, "-P");
int i;
for (i = 0; i < job->numincdirs; i++) {
//char* tmp = NULL;
//brutal_string_append(&tmp, "-I");
//brutal_string_append(&tmp, job->incdirs[i]);
brutal_args_append(&args, "-I");
brutal_args_append(&args, job->incdirs[i]);//tmp);
}
for (i = 0; i < job->numdefines; i++) {
char* tmp = NULL;
brutal_string_append(&tmp, "-D");
brutal_string_append(&tmp, job->defines[i]);
brutal_args_append(&args, tmp);
}
if (isasm) {
char* tmp = NULL;
brutal_string_append(&tmp, "-D");
brutal_string_append(&tmp, "__ASSEMBLER__");
brutal_args_append(&args, tmp);
}
brutal_args_append(&args, input);
brutal_args_append(&args, output);
return brutal_exec(args, job->echocmd, job->fakecmd);
}
int brutal_exec_compiler(compilerjob_t* job, char* input, char* output, char* modname) {
char** args = NULL;
brutal_args_append(&args, job->selfcmd);
brutal_args_append(&args, "-B");
if (job->use101) {
brutal_args_append(&args, "--101");
}
if (modname != NULL) {
brutal_args_append(&args, "--mod");
brutal_args_append(&args, modname);
}
if (job->sym_prefix != NULL) {
brutal_args_append(&args, "--prefix");
brutal_args_append(&args, job->sym_prefix);
}
brutal_args_append(&args, "--input");
brutal_args_append(&args, input);
brutal_args_append(&args, "--output");
brutal_args_append(&args, output);
return brutal_exec(args, job->echocmd, job->fakecmd);
}
int brutal_exec_assembler(compilerjob_t* job, char* input, char* output) {
char** args = NULL;
if (job->useyasm) {
brutal_args_append(&args, "yasm");
if (!job->nasmsyntax) {
brutal_args_append(&args, "-p");
brutal_args_append(&args, "gnu");
}
brutal_args_append(&args, "-f");
brutal_args_append(&args, "elf64");
} else if (job->usenasm) {
brutal_args_append(&args, "nasm");
brutal_args_append(&args, "-f");
brutal_args_append(&args, "elf64");
} else if (job->usefasm) {
brutal_args_append(&args, "fasm");
brutal_args_append(&args, "-m");
brutal_args_append(&args, "500000"); // Set a memory limit a bit under 512mb
} else if (job->usezasm) {
brutal_args_append(&args, "zasm");
brutal_args_append(&args, "--mode");
brutal_args_append(&args, "rv64");
} else if (job->useas) {
if (job->riscv) {
#ifdef __riscv
brutal_args_append(&args, "as");
#else
brutal_args_append(&args, "riscv64-linux-gnu-as");
#endif
brutal_args_append(&args, "--traditional-format");
brutal_args_append(&args, "-fno-pic");
brutal_args_append(&args, "-march=rv64imafd");
//brutal_args_append(&args, "-march=rv64ifd"); // NOTE: These settings need to be fine-tuned... floating-point ABI is not currently matching the default for Ubuntu cross-compilation
brutal_args_append(&args, "-mabi=lp64d");
//-march=rv64ifd -mabi=lp64d
} else {
brutal_args_append(&args, "as");
brutal_args_append(&args, "--64");
}
} else {
if (job->riscv) {
#ifdef __riscv
brutal_args_append(&args, "cc");
#else
brutal_args_append(&args, "riscv64-linux-gnu-gcc");
#endif
} else {
brutal_args_append(&args, "cc");
}
brutal_args_append(&args, "-c");
}
brutal_args_append(&args, input);
if (!job->usefasm) {
brutal_args_append(&args, "-o");
}
brutal_args_append(&args, output);
return brutal_exec(args, job->echocmd, job->fakecmd);
}
int brutal_exec_linker(compilerjob_t* job, char** inputs, char* output) {
char** args = NULL;
if (job->usemold) {
brutal_args_append(&args, "mold");
brutal_args_append(&args, "-L/usr/lib/x86_64-linux-gnu");
brutal_args_append(&args, "-L/usr/lib/gcc/x86_64-linux-gnu/11");
brutal_args_append(&args, "-lc");
brutal_args_append(&args, "-lgcc");
} else if (job->riscv) {
#ifdef __riscv
brutal_args_append(&args, "cc");
#else
brutal_args_append(&args, "riscv64-linux-gnu-gcc");
#endif
} else {
brutal_args_append(&args, "cc");
}
if (job->linkstatic) {
brutal_args_append(&args, "--static");
}
if (job->thread) {
brutal_args_append(&args, "-pthread");
}
brutal_args_append_all(&args, inputs);
if (job->gc) {
brutal_args_append(&args, "-lgc");
}
if (job->mm) {
// TODO: It probably makes more sense to dynamically link SOME of this stuff (or, at least, to have more options)
// But in any case, it seemed to make sense to have a shortcut to link a multimedia-enabled program
brutal_args_append(&args, "-lSDL2");
brutal_args_append(&args, "-lcairo");
}
brutal_args_append(&args, "-o");
brutal_args_append(&args, output);
return brutal_exec(args, job->echocmd, job->fakecmd);
}
int brutal_mkmodinit(compilerjob_t* job, char** modnames) {
if (job->fakecmd) {
return 0;
}
FILE* o = fopen(job->modinitfile, "w");
if (o == NULL) {
return 1;
}
int i;
for (i = 0; modnames[i] != NULL; i++) {
fprintf(o, "void __module__%s__init();\n", modnames[i]);
}
fprintf(o, "void __oop_init_begin();\n");
fprintf(o, "void __oop_init_end();\n");
fprintf(o, "\n");
fprintf(o, "void %s() {\n", job->modinitfunc);
fprintf(o, " __oop_init_begin();\n");
for (i = 0; modnames[i] != NULL; i++) {
fprintf(o, " __module__%s__init();\n", modnames[i]);
}
fprintf(o, " __oop_init_end();\n");
fprintf(o, "}\n");
fclose(o);
return 0;
}
int brutal_run(compilerjob_t* job) {
char** tolink = NULL;
char** toinit = NULL;
int result = 0;
char* last = NULL;
//fprintf(stderr, "Attempting to run...\n");
int i;
for (i = 0; i < job->numinputs; i++) {
char* input = job->inputs[i];
//fprintf(stderr, "Doing input '%s'\n", input);
char* modname = NULL;
int isasm = 0;
int shouldprep = 1;
if (brutal_endswith(input, ".m") || brutal_endswith(input, ".M")) {
modname = brutal_path_sanitise(job, input);
fprintf(stderr, "NOTE: Using module name '%s'\n", modname);
brutal_args_append(&toinit, modname);
} else if (brutal_endswith(input, ".s")) {
isasm = 1;
shouldprep = 0;
} else if (brutal_endswith(input, ".S")) {
isasm = 1;
}
/* If this is the modinit file, we need to create it before trying to compile it! */
if (job->modinitfile != NULL && !strcmp(input, job->modinitfile)) {
if (brutal_exists(job->modinitfile)) {
fprintf(stderr, "ERROR: Module init file already exists, will not overwrite existing file!\n");
exit(1);
}
if (brutal_mkmodinit(job, toinit) != 0) {
fprintf(stderr, "ERROR: Failed to create module init file!\n");
exit(1);
}
}
if (shouldprep) {
char* tmp = brutal_tmp(job, input, isasm ? ".preprocessed.s" : ".preprocessed.c");
result = brutal_exec_preprocessor(job, input, tmp, isasm);
if (result != 0) goto cleanup;
input = tmp;
}
if (!isasm && !job->skipcompiler) {
char* tmp = brutal_tmp(job, input, ".S");
result = brutal_exec_compiler(job, input, tmp, modname);
if (result != 0) goto cleanup;
input = tmp;
}
if (!job->skipassembler) {
char* tmp = brutal_tmp(job, input, ".o");
result = brutal_exec_assembler(job, input, tmp);
if (result != 0) goto cleanup;
input = tmp;
}
brutal_args_append(&tolink, input);
last = input;
}
//fprintf(stderr, "Finished first part...\n");
if (job->skiplinker) {
result = brutal_exec_cp(job, last, job->output);
if (result != 0) goto cleanup;
} else {
result = brutal_exec_linker(job, tolink, job->output);
if (result != 0) goto cleanup;
}
cleanup:
if (job->tmpfiles != NULL && !job->keeptmp) {
for (i = 0; job->tmpfiles[i] != NULL; i++) {
result = brutal_exec_rm(job, job->tmpfiles[i]);
if (result != 0) {
fprintf(stderr, "WARNING: Failed to remove temporary file '%s'\n", job->tmpfiles[i]);
}
}
}
return result;
}
int brutal_usage(int argc, char** argv, int argi, char* problem) {
FILE* o = (problem == NULL) ? stdout : stderr;
const char* n = argv[0];
fprintf(o, "USAGE:\n");
fprintf(o, " %s [options] input1.c [input2.c ...]\n", n);
fprintf(o, "\n");
fprintf(o, "COMMON OPTIONS:\n");
fprintf(o, " -I Add a preprocessor include directory (requires an argument)\n");
fprintf(o, " -D Add a preprocessor definition (requires an argument)\n");
fprintf(o, " -o Set the output filename (requires an argument)\n");
fprintf(o, " -c Skip linker (only produce assembled file, no executable)\n");
fprintf(o, " -S Skip assembler (only produce assembly code, no assembled file)\n");
fprintf(o, " -E Skip compiler (only produce preprocessed code, no compiled code)\n");
fprintf(o, "\n");
fprintf(o, "TARGET/TOOLCHAIN OPTIONS:\n");
fprintf(o, " --static Build with/for static linking\n");
fprintf(o, " --dynamic Build with/for dynamic linking\n");
fprintf(o, " --use-fasm Use FASM assembler (default will pass assembly files to the platform's 'cc')\n");
fprintf(o, " --use-nasm Use NASM assembler (default will pass assembly files to the platform's 'cc')\n");
fprintf(o, " --use-yasm Use YASM assembler (default will pass assembly files to the platform's 'cc')\n");
fprintf(o, " --use-zasm Use ZASM assembler (default will pass assembly files to the platform's 'cc')\n");
fprintf(o, " --use-as Use the 'as' assembler directly (this will usually be the same backend used in the default mode)\n");
fprintf(o, " --nasm-syntax Use NASM syntax in assembly output (this can be used with --use-yasm, otherwise GNU syntax will be default)\n");
fprintf(o, " --RV64 Use RISC-V target (this assumes a suitable GNU toolchain for assembly & linkage)\n");
fprintf(o, " --AMD64 Use AMD64/x86-64 target (this assumes a suitable GNU toolchain for assembly & linkage)\n");
fprintf(o, "\n");
fprintf(o, "SPECIAL CODE GENERATION OPTIONS:\n");
fprintf(o, " --prefix Add a prefix to standard C symbols (requires an argument)\n");
fprintf(o, " --101 Use __classic_call calling convention by default (only for 1337 h4x0r5)\n");
fprintf(o, "\n");
fprintf(o, "BACKEND INVOCATION OPTIONS:\n");
fprintf(o, " --echo Echo command invocations & environment settings to the shell\n");
fprintf(o, " --fake Fake command invocations (can be used in conjunction with --echo to just show which commands would be used to compile something)\n");
fprintf(o, " --keeptmp Keep temporary files\n");
fprintf(o, " --compiler Invoke the internal compiler directly (must be first option, passes all options to backend)\n");
fprintf(o, " --preprocessor Invoke the internal preprocessor directly (must be first option, passes all options to preprocessor)\n");
fprintf(o, "\n");
fprintf(o, "EXTENSION OPTIONS:\n");
fprintf(o, " --gc Enable garbage collection\n");
fprintf(o, " --thread Enable multithreading\n");
fprintf(o, " (aliases: -pthread, --pthread)\n");
fprintf(o, " --mm Enable multimedia (requires SDL2, cairo libraries)\n");
fprintf(o, "\n");
fprintf(o, "TOOLCHAIN INFORMATION:\n");
fprintf(o, " --usage Shows usage information\n");
fprintf(o, " (aliases: --help, -h, -u, -H, -U)\n");
fprintf(o, " --version Shows version information\n");
fprintf(o, "\n");
if (problem != NULL) {
fprintf(o, "ERROR:\n");
fprintf(o, " Around arg #%d: %s\n", argi, problem);
}
return (problem == NULL) ? 0 : 1;
}
void brutal_xdefine(compilerjob_t* job, const char* def) {
if (job->numdefines < BRUTAL_MAX_FILES) {
job->defines[job->numdefines] = def;
job->numdefines++;
} else {
fprintf(stderr, "ERROR: Too many defines\n");
exit(1);
}
}
int main(int argc, char** argv, char** envp) {
compilerjob_t* job;
/* Shortcuts to invoke the backend features directly. */
if (argc > 1) {
if (!strcmp(argv[1], "-B") || !strcmp(argv[1], "--compiler") || !strcmp(argv[1], "--backend")) {
argv[1] = "-B";
return backend_main(argc, argv);
} else if (!strcmp(argv[1], "-P") || !strcmp(argv[1], "--preprocessor")) {
argv[1] = "-P";
return backend_main(argc, argv);
#ifdef TOOL_MK
} else if (!strcmp(argv[1], "-M") || !strcmp(argv[1], "--make")) {
argv[1] = "-M";
mk_main(argc, argv, envp);
return 0;
#endif
}
}
fprintf(stderr, "SecureLang C Compiler Frontend\n");
job = calloc(1, sizeof(compilerjob_t));
if (job == NULL) {
fprintf(stderr, "ERROR: Out of memory!\n");
return 1;
}
job->inputs = calloc(BRUTAL_MAX_FILES, sizeof(void*));
job->incdirs = calloc(BRUTAL_MAX_FILES, sizeof(void*));
job->defines = calloc(BRUTAL_MAX_FILES, sizeof(void*));
if (job->inputs == NULL || job->incdirs == NULL || job->defines == NULL) {
fprintf(stderr, "ERROR: Out of memory!\n");
return 1;
}
job->selfcmd = strdup(argv[0]);
job->linkstatic = 0;
job->use101 = 0;
job->sym_prefix = NULL;
#ifdef __riscv
job->riscv = 1;
#else
job->riscv = 0;
#endif
int specifiedlinkage = 0;
int needsmodinit = 0;
int argi = 1;
while (argi < argc) {
char* a = argv[argi];
//fprintf(stderr, "Processing argument '%s'\n", a);
if (brutal_startswith("-o", a)) {
if (strlen(a) > 2) {
job->output = strdup(a+2);
} else if (argi+1 < argc) {
argi++;
job->output = strdup(argv[argi]);
} else {
return brutal_usage(argc, argv, argi, "Expected output filename following -o");
}
} else if (brutal_startswith("--prefix", a)) {
if (argi+1 < argc) {
argi++;
job->sym_prefix = strdup(argv[argi]);
} else {
return brutal_usage(argc, argv, argi, "Expected symbol prefix following --prefix");
}
} else if (brutal_startswith("-I", a)) {
char* dir = NULL;
if (strlen(a) > 2) {
dir = strdup(a+2);
} else if (argi+1 < argc) {
argi++;
dir = strdup(argv[argi]);
} else {
return brutal_usage(argc, argv, argi, "Expected directory name following -I");
}
//fprintf(stderr, "Got include directory '%s'\n", dir);
if (job->numincdirs < BRUTAL_MAX_FILES) {
job->incdirs[job->numincdirs] = dir;
job->numincdirs++;
} else {
return brutal_usage(argc, argv, argi, "Too many include directories");
}
} else if (brutal_startswith("-D", a)) {
char* def = NULL;
if (strlen(a) > 2) {
def = strdup(a+2);
} else if (argi+1 < argc) {
argi++;
def = strdup(argv[argi]);
} else {
return brutal_usage(argc, argv, argi, "Expected preprocessor definition following -D");
}
if (job->numdefines < BRUTAL_MAX_FILES) {
job->defines[job->numdefines] = def;
job->numdefines++;
} else {
return brutal_usage(argc, argv, argi, "Too many defines");
}
} else if (!strcmp("--static", a)) {
job->linkstatic = 1;
specifiedlinkage = 1;
} else if (!strcmp("--dynamic", a)) {
job->linkstatic = 0;
specifiedlinkage = 1;
} else if (brutal_startswith("--RV", a) || brutal_startswith("--rv", a)) {
fprintf(stderr, "WARNING: Current RISC-V backend is being tested for 64-bit with minimal floating-point support\n");
job->riscv = 1;
} else if (brutal_startswith("--AMD", a) || brutal_startswith("--amd", a)) {
job->riscv = 0;
} else if (!strcmp("--use-as", a)) {
job->useas = 1;
} else if (!strcmp("--use-yasm", a)) {
job->useyasm = 1;
} else if (!strcmp("--use-fasm", a)) {
job->usefasm = 1;
} else if (!strcmp("--use-nasm", a)) {
job->usenasm = 1;
job->nasmsyntax = 1;
} else if (!strcmp("--use-zasm", a)) {
job->usezasm = 1;
} else if (!strcmp("--nasm-syntax", a)) {
job->nasmsyntax = 1;
} else if (!strcmp("--use-mold", a)) {
job->usemold = 1;
} else if (!strcmp("--version", a)) {
fprintf(stderr, "Too early to tell.\n");
return 0;
} else if (!strcmp("--usage", a) || !strcmp("--help", a) || !strcmp("-h", a) || !strcmp("-u", a) || !strcmp("-H", a) || !strcmp("-U", a)) {
return brutal_usage(argc, argv, argi, NULL);
} else if (!strcmp("--version", a)) {
fprintf(stderr, "Too early to tell.\n");
return 0;
} else if (!strcmp("-E", a)) {
job->skipcompiler = 1;
job->skipassembler = 1;
job->skiplinker = 1;
} else if (!strcmp("-S", a)) {
job->skipassembler = 1;
job->skiplinker = 1;
} else if (!strcmp("-c", a)) {
job->skiplinker = 1;
} else if (!strcmp("--101", a)) {
job->use101 = 1;
} else if (!strcmp("--echo", a)) {
job->echocmd = 1;
} else if (!strcmp("--fake", a)) {
job->fakecmd = 1;
} else if (!strcmp("--keeptmp", a)) {
job->keeptmp = 1;
} else if (!strcmp("--gc", a)) {
job->gc = 1;
} else if (!strcmp("--thread", a) || !strcmp("-pthread", a) || !strcmp("--pthread", a)) {
job->thread = 1;
} else if (!strcmp("--mm", a)) {
job->mm = 1;
} else if (brutal_startswith("-", a)) {
return brutal_usage(argc, argv, argi, "Bad option");
} else {
if (job->numinputs < BRUTAL_MAX_FILES) {
char* cp = strdup(a);
if (brutal_endswith(cp, ".m") || brutal_endswith(cp, ".M")) {
needsmodinit = 1;
}
//fprintf(stderr, "Got strings '%s' '%s'\n", a, cp);
job->inputs[job->numinputs] = cp;
job->numinputs = job->numinputs + 1;
} else {
return brutal_usage(argc, argv, argi, "Too many input files");
}
}
argi++;
}
//fprintf(stderr, "Finished processing commands...\n");
if (needsmodinit && (job->skipassembler || job->skiplinker)) {
needsmodinit = false;
}
if (job->numinputs < 1) {
return brutal_usage(argc, argv, argi, "Nothing to do (expected input file)");
} else {
//fprintf(stderr, "Got %d inputs\n", job->numinputs);
}
if (job->output == NULL) {
if (job->skipcompiler) {
job->output = "./a.out.C";
} else if (job->skipassembler) {
job->output = "./a.out.S";
} else if (job->skiplinker) {
job->output = "./a.out.o";
} else {
job->output = "./a.out";
}
}
if (job->tmpdir == NULL) {
job->tmpdir = ".";
}
if (specifiedlinkage) {
if ((job->riscv || job->useyasm || job->usefasm || job->usezasm) && !job->linkstatic) {
fprintf(stderr, "WARNING: Dynamic linking using RISC-V or FASM/YASM targets is work-in-progress.\n");
}
} else {
if ((job->riscv || job->useyasm || job->usefasm)) {
fprintf(stderr, "NOTE: Using static linking as default on RISC-V or FASM/YASM targets.\n");
job->linkstatic = 1;
}
}
/* Set any environment variables for current backend.
* TODO: Evenutally move the more-stable options into regular command-line arguments.
*/
if (job->riscv) {
brutal_setenv("CCB_FAMILY", "risc-v", 1, job->echocmd, job->fakecmd);
}
if (job->usefasm) {
brutal_setenv("CCB_ASMFMT", "fasm", 1, job->echocmd, job->fakecmd);
}
if (job->nasmsyntax) {
brutal_setenv("CCB_ASMFMT", "nasm", 1, job->echocmd, job->fakecmd);
}
if (needsmodinit) {
if (job->modinitfile == NULL) {
job->modinitfile = brutal_tmp(job, "modinit", ".c");
}
if (job->modinitfunc == NULL) {
job->modinitfunc = "__modinit";
}
if (job->numinputs < BRUTAL_MAX_FILES) {
job->inputs[job->numinputs] = job->modinitfile;
job->numinputs = job->numinputs + 1;
} else {
return brutal_usage(argc, argv, argi, "Too many input files");
}
}
//fprintf(stderr, "Ready to run...\n");
brutal_xdefine(job, "__BRUTAL");
brutal_xdefine(job, "_ZCC");
if (job->gc) {
brutal_xdefine(job, "__BRUTAL_FEATURE_GC");
}
if (job->mm) {
brutal_xdefine(job, "__BRUTAL_FEATURE_MM");
}
if (job->thread) {
brutal_xdefine(job, "__BRUTAL_FEATURE_THREAD");
}
if (job->riscv) {
brutal_xdefine(job, "__BRUTAL_CPU_RV64");
brutal_xdefine(job, "__riscv");
} else {
brutal_xdefine(job, "__BRUTAL_CPU_X64");
brutal_xdefine(job, "__x86_64__");
}
brutal_xdefine(job, "__BRUTAL_OS_LINUX"); // TODO: Better/more target flags
return brutal_run(job);
}