diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 99d355242..5591c84b8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,8 +25,11 @@ jobs: with: msystem: MINGW64 update: true - install: git binutils mingw-w64-x86_64-mlir mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-llvm mingw-w64-x86_64-polly mingw-w64-x86_64-python mingw-w64-x86_64-lld - + install: git binutils mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-python + - shell: msys2 {0} + run: | + pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-13.0.1-2-any.pkg.tar.zst + pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-13.0.1-2-any.pkg.tar.zst - name: CMake run: | mkdir build && cd build diff --git a/CMakeLists.txt b/CMakeLists.txt index 3211051ad..fb878b4e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,7 @@ set(LLVM_LINK_COMPONENTS WindowsManifest ) -if (${LLVM_PACKAGE_VERSION} VERSION_GREATER 14) +if (${LLVM_PACKAGE_VERSION} VERSION_GREATER 14.1) set(LLVM_LINK_COMPONENTS ${LLVM_LINK_COMPONENTS} WindowsDriver) endif() diff --git a/lib/std/array.c3 b/lib/std/array.c3 index 3285de1d7..9ed41f25d 100644 --- a/lib/std/array.c3 +++ b/lib/std/array.c3 @@ -4,16 +4,22 @@ module std::array; import std::mem; -macro make($Type, usize elements) +/** + * @require elements > 0 + **/ +macro alloc($Type, usize elements) { - assert(elements > 0); - $Type* ptr = mem::alloc($sizeof($Type), elements); + assert($Type.max / elements < $Type.sizeof); + $Type* ptr = mem::alloc($Type.sizeof * elements, $alignof($Type)); return ptr[0..(elements - 1)]; } -macro make_zero($Type, usize elements) +/** + * @require elements > 0 + **/ +macro calloc($Type, usize elements) { - assert(elements > 0); - $Type* ptr = mem::calloc($sizeof($Type), elements); + assert($Type.max / elements < $Type.sizeof); + $Type* ptr = mem::calloc($sizeof($Type) * elements, $alignof($Type)); return ptr[0..(elements - 1)]; } diff --git a/lib/std/io.c3 b/lib/std/io.c3 index 44beb0e7c..c8ed6459b 100644 --- a/lib/std/io.c3 +++ b/lib/std/io.c3 @@ -11,15 +11,9 @@ struct File CFile file; } - -extern fn int _puts(char* message) @extname("puts"); -extern fn int printf(char* message, ...); -extern fn int _putchar(char c) @extname("putchar"); - - fn int putchar(char c) @inline { - return _putchar(c); + return libc::putchar(c); } /** @@ -43,7 +37,7 @@ fn int print(char* message) */ fn int println(char *message = "") @inline { - return _puts(message); + return libc::puts(message); } fn void! File.open(File* file, char[] filename, char[] mode) @@ -202,319 +196,3 @@ fn void File.error(File *file) @inline int err = ferror } */ -/* - -#define __SLBF 0x0001 /* line buffered */ -#define __SNBF 0x0002 /* unbuffered */ -#define __SRD 0x0004 /* OK to read */ -#define __SWR 0x0008 /* OK to write */ - /* RD and WR are never simultaneously asserted */ -#define __SRW 0x0010 /* open for reading & writing */ -#define __SEOF 0x0020 /* found EOF */ -#define __SERR 0x0040 /* found error */ -#define __SMBF 0x0080 /* _buf is from malloc */ -#define __SAPP 0x0100 /* fdopen()ed in append mode */ -#define __SSTR 0x0200 /* this is an sprintf/snprintf string */ -#define __SOPT 0x0400 /* do fseek() optimisation */ -#define __SNPT 0x0800 /* do not do fseek() optimisation */ -#define __SOFF 0x1000 /* set iff _offset is in fact correct */ -#define __SMOD 0x2000 /* true => fgetln modified _p text */ -#define __SALC 0x4000 /* allocate string space dynamically */ -#define __SIGN 0x8000 /* ignore this file in _fwalk */ - -/* - * The following three definitions are for ANSI C, which took them - * from System V, which brilliantly took internal interface macros and - * made them official arguments to setvbuf(), without renaming them. - * Hence, these ugly _IOxxx names are *supposed* to appear in user code. - * - * Although numbered as their counterparts above, the implementation - * does not rely on this. - */ -#define _IOFBF 0 /* setvbuf should set fully buffered */ -#define _IOLBF 1 /* setvbuf should set line buffered */ -#define _IONBF 2 /* setvbuf should set unbuffered */ - -#define BUFSIZ 1024 /* size of buffer used by setbuf */ -#define EOF (-1) - - /* must be == _POSIX_STREAM_MAX */ -#define FOPEN_MAX 20 /* must be <= OPEN_MAX */ -#define FILENAME_MAX 1024 /* must be <= PATH_MAX */ - -/* System V/ANSI C; this is the wrong way to do this, do *not* use these. */ -#ifndef _ANSI_SOURCE -#define P_tmpdir "/var/tmp/" -#endif -#define L_tmpnam 1024 /* XXX must be == PATH_MAX */ -#define TMP_MAX 308915776 - - -#define stdin __stdinp -#define stdout __stdoutp -#define stderr __stderrp - - -/* ANSI-C */ - -__BEGIN_DECLS -char *fgets(char * __restrict, int, FILE *); -#if defined(_DARWIN_UNLIMITED_STREAMS) || defined(_DARWIN_C_SOURCE) -#else /* !_DARWIN_UNLIMITED_STREAMS && !_DARWIN_C_SOURCE */ -FILE *fopen(const char * __restrict __filename, const char * __restrict __mode) __DARWIN_ALIAS_STARTING(__MAC_10_6, __IPHONE_2_0, __DARWIN_ALIAS(fopen)); -#endif /* (DARWIN_UNLIMITED_STREAMS || _DARWIN_C_SOURCE) */ -int fprintf(FILE * __restrict, const char * __restrict, ...) __printflike(2, 3); -int fputs(const char * __restrict, FILE * __restrict) __DARWIN_ALIAS(fputs); -size_t fread(void * __restrict __ptr, size_t __size, size_t __nitems, FILE * __restrict __stream); -FILE *freopen(const char * __restrict, const char * __restrict, - FILE * __restrict) __DARWIN_ALIAS(freopen); -int fscanf(FILE * __restrict, const char * __restrict, ...) __scanflike(2, 3); -int fsetpos(FILE *, const fpos_t *); -long ftell(FILE *); -size_t fwrite(const void * __restrict __ptr, size_t __size, size_t __nitems, FILE * __restrict __stream) __DARWIN_ALIAS(fwrite); -int getc(FILE *); -int getchar(void); -char *gets(char *); -void perror(const char *) __cold; -int printf(const char * __restrict, ...) __printflike(1, 2); -int putc(int, FILE *); -int putchar(int); -int puts(const char *); -int remove(const char *); -int rename (const char *__old, const char *__new); -void rewind(FILE *); -int scanf(const char * __restrict, ...) __scanflike(1, 2); -void setbuf(FILE * __restrict, char * __restrict); -int setvbuf(FILE * __restrict, char * __restrict, int, size_t); -int sprintf(char * __restrict, const char * __restrict, ...) __printflike(2, 3) __swift_unavailable("Use snprintf instead."); -int sscanf(const char * __restrict, const char * __restrict, ...) __scanflike(2, 3); -FILE *tmpfile(void); - -__swift_unavailable("Use mkstemp(3) instead.") -#if !defined(_POSIX_C_SOURCE) -__deprecated_msg("This function is provided for compatibility reasons only. Due to security concerns inherent in the design of tmpnam(3), it is highly recommended that you use mkstemp(3) instead.") -#endif -char *tmpnam(char *); -int ungetc(int, FILE *); -int vfprintf(FILE * __restrict, const char * __restrict, va_list) __printflike(2, 0); -int vprintf(const char * __restrict, va_list) __printflike(1, 0); -int vsprintf(char * __restrict, const char * __restrict, va_list) __printflike(2, 0) __swift_unavailable("Use vsnprintf instead."); -__END_DECLS - - - -/* Additional functionality provided by: - * POSIX.1-1988 - */ - -#if __DARWIN_C_LEVEL >= 198808L -#define L_ctermid 1024 /* size for ctermid(); PATH_MAX */ - -__BEGIN_DECLS -#include <_ctermid.h> - -#if defined(_DARWIN_UNLIMITED_STREAMS) || defined(_DARWIN_C_SOURCE) -FILE *fdopen(int, const char *) __DARWIN_ALIAS_STARTING(__MAC_10_6, __IPHONE_3_2, __DARWIN_EXTSN(fdopen)); -#else /* !_DARWIN_UNLIMITED_STREAMS && !_DARWIN_C_SOURCE */ -FILE *fdopen(int, const char *) __DARWIN_ALIAS_STARTING(__MAC_10_6, __IPHONE_2_0, __DARWIN_ALIAS(fdopen)); -#endif /* (DARWIN_UNLIMITED_STREAMS || _DARWIN_C_SOURCE) */ -int fileno(FILE *); -__END_DECLS -#endif /* __DARWIN_C_LEVEL >= 198808L */ - - -/* Additional functionality provided by: - * POSIX.2-1992 C Language Binding Option - */ -#if TARGET_OS_EMBEDDED -#define __swift_unavailable_on(osx_msg, ios_msg) __swift_unavailable(ios_msg) -#else -#define __swift_unavailable_on(osx_msg, ios_msg) __swift_unavailable(osx_msg) -#endif - -#if __DARWIN_C_LEVEL >= 199209L -__BEGIN_DECLS -int pclose(FILE *) __swift_unavailable_on("Use posix_spawn APIs or NSTask instead.", "Process spawning is unavailable."); -#if defined(_DARWIN_UNLIMITED_STREAMS) || defined(_DARWIN_C_SOURCE) -FILE *popen(const char *, const char *) __DARWIN_ALIAS_STARTING(__MAC_10_6, __IPHONE_3_2, __DARWIN_EXTSN(popen)) __swift_unavailable_on("Use posix_spawn APIs or NSTask instead.", "Process spawning is unavailable."); -#else /* !_DARWIN_UNLIMITED_STREAMS && !_DARWIN_C_SOURCE */ -FILE *popen(const char *, const char *) __DARWIN_ALIAS_STARTING(__MAC_10_6, __IPHONE_2_0, __DARWIN_ALIAS(popen)) __swift_unavailable_on("Use posix_spawn APIs or NSTask instead.", "Process spawning is unavailable."); -#endif /* (DARWIN_UNLIMITED_STREAMS || _DARWIN_C_SOURCE) */ -__END_DECLS -#endif /* __DARWIN_C_LEVEL >= 199209L */ - -#undef __swift_unavailable_on - -/* Additional functionality provided by: - * POSIX.1c-1995, - * POSIX.1i-1995, - * and the omnibus ISO/IEC 9945-1: 1996 - */ - -#if __DARWIN_C_LEVEL >= 199506L - -/* Functions internal to the implementation. */ -__BEGIN_DECLS -int __srget(FILE *); -int __svfscanf(FILE *, const char *, va_list) __scanflike(2, 0); -int __swbuf(int, FILE *); -__END_DECLS - -/* - * The __sfoo macros are here so that we can - * define function versions in the C library. - */ -#define __sgetc(p) (--(p)->_r < 0 ? __srget(p) : (int)(*(p)->_p++)) -#if defined(__GNUC__) && defined(__STDC__) -__header_always_inline int __sputc(int _c, FILE *_p) { - if (--_p->_w >= 0 || (_p->_w >= _p->_lbfsize && (char)_c != '\n')) - return (*_p->_p++ = _c); - else - return (__swbuf(_c, _p)); -} -#else -/* - * This has been tuned to generate reasonable code on the vax using pcc. - */ -#define __sputc(c, p) \ - (--(p)->_w < 0 ? \ - (p)->_w >= (p)->_lbfsize ? \ - (*(p)->_p = (c)), *(p)->_p != '\n' ? \ - (int)*(p)->_p++ : \ - __swbuf('\n', p) : \ - __swbuf((int)(c), p) : \ - (*(p)->_p = (c), (int)*(p)->_p++)) -#endif - -#define __sfeof(p) (((p)->_flags & __SEOF) != 0) -#define __sferror(p) (((p)->_flags & __SERR) != 0) -#define __sclearerr(p) ((void)((p)->_flags &= ~(__SERR|__SEOF))) -#define __sfileno(p) ((p)->_file) - -__BEGIN_DECLS -void flockfile(FILE *); -int ftrylockfile(FILE *); -void funlockfile(FILE *); -int getc_unlocked(FILE *); -int getchar_unlocked(void); -int putc_unlocked(int, FILE *); -int putchar_unlocked(int); - -/* Removed in Issue 6 */ -#if !defined(_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 200112L -int getw(FILE *); -int putw(int, FILE *); -#endif - -__swift_unavailable("Use mkstemp(3) instead.") -#if !defined(_POSIX_C_SOURCE) -__deprecated_msg("This function is provided for compatibility reasons only. Due to security concerns inherent in the design of tempnam(3), it is highly recommended that you use mkstemp(3) instead.") -#endif -char *tempnam(const char *__dir, const char *__prefix) __DARWIN_ALIAS(tempnam); -__END_DECLS - -#ifndef lint -#define getc_unlocked(fp) __sgetc(fp) -#define putc_unlocked(x, fp) __sputc(x, fp) -#endif /* lint */ - -#define getchar_unlocked() getc_unlocked(stdin) -#define putchar_unlocked(x) putc_unlocked(x, stdout) -#endif /* __DARWIN_C_LEVEL >= 199506L */ - - - -/* Additional functionality provided by: - * POSIX.1-2001 - * ISO C99 - */ - -#if __DARWIN_C_LEVEL >= 200112L -#include - -__BEGIN_DECLS -int fseeko(FILE * __stream, off_t __offset, int __whence); -off_t ftello(FILE * __stream); -__END_DECLS -#endif /* __DARWIN_C_LEVEL >= 200112L */ - -#if __DARWIN_C_LEVEL >= 200112L || defined(_C99_SOURCE) || defined(__cplusplus) -__BEGIN_DECLS -int snprintf(char * __restrict __str, size_t __size, const char * __restrict __format, ...) __printflike(3, 4); -int vfscanf(FILE * __restrict __stream, const char * __restrict __format, va_list) __scanflike(2, 0); -int vscanf(const char * __restrict __format, va_list) __scanflike(1, 0); -int vsnprintf(char * __restrict __str, size_t __size, const char * __restrict __format, va_list) __printflike(3, 0); -int vsscanf(const char * __restrict __str, const char * __restrict __format, va_list) __scanflike(2, 0); -__END_DECLS -#endif /* __DARWIN_C_LEVEL >= 200112L || defined(_C99_SOURCE) || defined(__cplusplus) */ - - - -/* Additional functionality provided by: - * POSIX.1-2008 - */ - -#if __DARWIN_C_LEVEL >= 200809L -#include - -__BEGIN_DECLS -int dprintf(int, const char * __restrict, ...) __printflike(2, 3) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); -int vdprintf(int, const char * __restrict, va_list) __printflike(2, 0) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); -ssize_t getdelim(char ** __restrict __linep, size_t * __restrict __linecapp, int __delimiter, FILE * __restrict __stream) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); -ssize_t getline(char ** __restrict __linep, size_t * __restrict __linecapp, FILE * __restrict __stream) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); -FILE *fmemopen(void * __restrict __buf, size_t __size, const char * __restrict __mode) __API_AVAILABLE(macos(10.13), ios(11.0), tvos(11.0), watchos(4.0)); -FILE *open_memstream(char **__bufp, size_t *__sizep) __API_AVAILABLE(macos(10.13), ios(11.0), tvos(11.0), watchos(4.0)); -__END_DECLS -#endif /* __DARWIN_C_LEVEL >= 200809L */ - - - -/* Darwin extensions */ - -#if __DARWIN_C_LEVEL >= __DARWIN_C_FULL -__BEGIN_DECLS -extern __const int sys_nerr; /* perror(3) external variables */ -extern __const char *__const sys_errlist[]; - -int asprintf(char ** __restrict, const char * __restrict, ...) __printflike(2, 3); -char *ctermid_r(char *); -char *fgetln(FILE *, size_t *); -__const char *fmtcheck(const char *, const char *); -int fpurge(FILE *); -void setbuffer(FILE *, char *, int); -int setlinebuf(FILE *); -int vasprintf(char ** __restrict, const char * __restrict, va_list) __printflike(2, 0); -FILE *zopen(const char *, const char *, int); - - -/* - * Stdio function-access interface. - */ -FILE *funopen(const void *, - int (* _Nullable)(void *, char *, int), - int (* _Nullable)(void *, const char *, int), - fpos_t (* _Nullable)(void *, fpos_t, int), - int (* _Nullable)(void *)); -__END_DECLS -#define fropen(cookie, fn) funopen(cookie, fn, 0, 0, 0) -#define fwopen(cookie, fn) funopen(cookie, 0, fn, 0, 0) - -#define feof_unlocked(p) __sfeof(p) -#define ferror_unlocked(p) __sferror(p) -#define clearerr_unlocked(p) __sclearerr(p) -#define fileno_unlocked(p) __sfileno(p) - -#endif /* __DARWIN_C_LEVEL >= __DARWIN_C_FULL */ - - -#ifdef _USE_EXTENDED_LOCALES_ -#include -#endif /* _USE_EXTENDED_LOCALES_ */ - -#if defined (__GNUC__) && _FORTIFY_SOURCE > 0 && !defined (__cplusplus) -/* Security checking functions. */ -#include -#endif - -#endif /* _STDIO_H_ */ -*/ \ No newline at end of file diff --git a/lib/std/mem.c3 b/lib/std/mem.c3 index e2ea38c28..2995b9b09 100644 --- a/lib/std/mem.c3 +++ b/lib/std/mem.c3 @@ -66,19 +66,21 @@ enum AllocationKind CALLOC, REALLOC, FREE, + RESET, } fault AllocationFailure { - OUT_OF_MEMORY + OUT_OF_MEMORY, + UNSUPPORTED_OPERATION, } -private tlocal Allocator thread_allocator = { SYSTEM_ALLOCATOR, null }; +private tlocal Allocator thread_allocator = { null, SYSTEM_ALLOCATOR }; struct Allocator { + void* data; AllocatorFunction function; - void *data; } macro malloc($Type) @@ -86,6 +88,11 @@ macro malloc($Type) return ($Type*)(mem::alloc($Type.sizeof)); } +fn char[] alloc_bytes(usize bytes) @inline +{ + return ((char*)thread_allocator.alloc(bytes, 1))[0..bytes - 1]!!; +} + /** * @require !alignment || @math::is_power_of_2(alignment) */ @@ -140,6 +147,9 @@ fn void free(void* ptr) return thread_allocator.free(ptr)!!; } +/** + * Run with a specific allocator inside of the macro body. + **/ macro void with_allocator(Allocator allocator; @body()) { Allocator old_allocator = thread_allocator; @@ -150,6 +160,50 @@ macro void with_allocator(Allocator allocator; @body()) fn void*! talloc(usize size) { - return default_allocator.alloc(size); + return temp_allocator.alloc(size); } +struct MemoryArena +{ + void* memory; + usize total; + usize used; +} + +/** + * @require alignment > 0 `alignment must be non zero` + * @require @math::is_power_of_2(alignment) + * @require size > 0 + * @require alignment <= MAX_MEMORY_ALIGNMENT `alignment too big` + * @require this != null + **/ +fn void*! MemoryArena.alloc(MemoryArena* this, usize size, usize alignment, usize prefixed_bytes = 0) +{ + void* start_mem = this.memory; + void* unaligned_pointer = start_mem + this.used + prefixed_bytes; + if ((uptr)unaligned_pointer < (uptr)start_mem) return AllocationFailure.OUT_OF_MEMORY!; + usize offset_start = aligned_offset((usize)(uptr)unaligned_pointer, alignment) - (usize)(uptr)start_mem; + usize end = offset_start + size; + if (end > this.total || end < offset_start) return AllocationFailure.OUT_OF_MEMORY!; + this.used = end; + return start_mem + offset_start; +} + +/** + * Initialize a memory arena for use using the provided bytes. + * + * @require this != null + **/ +fn void MemoryArena.init(MemoryArena* this, char[] data) +{ + this.memory = data.ptr; + this.total = data.len; + this.used = 0; +} +/** + * @require this != null + **/ +fn void MemoryArena.reset(MemoryArena* this) +{ + this.used = 0; +} \ No newline at end of file diff --git a/lib/std/mem_allocator.c3 b/lib/std/mem_allocator.c3 index faa02252d..89b865cd9 100644 --- a/lib/std/mem_allocator.c3 +++ b/lib/std/mem_allocator.c3 @@ -1,10 +1,13 @@ module std::mem; -define AllocatorFunction = fn void*!(void *data, usize new_size, usize alignment, void* old_pointer, AllocationKind kind); +define AllocatorFunction = fn void*!(void* alloc_data, usize new_size, usize alignment, void* old_pointer, AllocationKind kind); +const MAX_MEMORY_ALIGNMENT = 0x1000_0000; const DEFAULT_MEM_ALIGNMENT = $alignof(void*) * 2; +const DEFAULT_SIZE_PREFIX = usize.sizeof; +const DEFAULT_SIZE_PREFIX_ALIGNMENT = $alignof(usize); -Allocator main_allocator = { SYSTEM_ALLOCATOR, null }; +Allocator main_allocator = { null, SYSTEM_ALLOCATOR }; const AllocatorFunction NULL_ALLOCATOR = &null_allocator_fn; const AllocatorFunction SYSTEM_ALLOCATOR = &libc_allocator_fn; @@ -38,243 +41,315 @@ fn void! Allocator.free(Allocator *allocator, void* old_pointer) @inline allocator.function(allocator.data, 0, 0, old_pointer, FREE)?; } - -struct ArenaAllocator +fn void Allocator.reset(Allocator *allocator) { - void* memory; - void* last_ptr; - usize total; - usize used; + allocator.function(allocator.data, 0, 0, null, RESET)!!; } -macro void*! allocator_to_function($Type, void* data, usize new_size, usize alignment, void* old_pointer, AllocationKind kind) + +fn Allocator arena_allocator(MemoryArena *arena) { - $Type* allocator = data; + return { arena, &arena_allocator_function }; +} + +fn Allocator dynamic_arena_allocator(DynamicArenaAllocator* allocator) +{ + return { allocator, &dynamic_arena_allocator_function }; +} + + +private fn usize alignment_for_allocation(usize alignment) @inline +{ + if (alignment < DEFAULT_MEM_ALIGNMENT) + { + alignment = DEFAULT_SIZE_PREFIX_ALIGNMENT; + } + return alignment; +} + +/** + * @require !alignment || @math::is_power_of_2(alignment) + * @require data `unexpectedly missing the allocator` + */ +fn void*! arena_allocator_function(void* data, usize size, usize alignment, void* old_pointer, AllocationKind kind) +{ + MemoryArena* arena = data; switch (kind) { - case ALLOC: - return allocator.alloc(new_size, alignment) @inline; case CALLOC: - return allocator.calloc(new_size, alignment) @inline; + case ALLOC: + assert(!old_pointer, "Unexpected old pointer for alloc."); + if (!size) return null; + alignment = alignment_for_allocation(alignment); + void* mem = arena.alloc(size, alignment, DEFAULT_SIZE_PREFIX)?; + *(usize*)(mem - DEFAULT_SIZE_PREFIX) = size; + if (kind == AllocationKind.CALLOC) mem::set(mem, 0, size); + return mem; case REALLOC: - return allocator.realloc(old_pointer, new_size, alignment) @inline; + if (!size) nextcase FREE; + if (!old_pointer) nextcase ALLOC; + assert((uptr)old_pointer >= (uptr)arena.memory, "Pointer originates from a different allocator."); + if (size > arena.total) return AllocationFailure.OUT_OF_MEMORY!; + alignment = alignment_for_allocation(alignment); + usize* old_size_ptr = (usize*)(old_pointer - DEFAULT_SIZE_PREFIX); + usize old_size = *old_size_ptr; + // Do last allocation and alignment match? + if (arena.memory + arena.used == old_pointer + old_size && ptr_is_aligned(old_pointer, alignment)) + { + if (old_size >= size) + { + *old_size_ptr = size; + arena.used -= old_size - size; + return old_pointer; + } + usize new_used = arena.used + size - old_size; + if (new_used > arena.total) return AllocationFailure.OUT_OF_MEMORY!; + arena.used = new_used; + *old_size_ptr = size; + return old_pointer; + } + // Otherwise just allocate new memory. + void* mem = arena.alloc(size, alignment, DEFAULT_SIZE_PREFIX)?; + *(usize*)(mem - DEFAULT_SIZE_PREFIX) = size; + copy(mem, old_pointer, old_size); + return mem; case FREE: - allocator.free(old_pointer) @inline?; + if (!old_pointer) return null; + assert((uptr)old_pointer >= (uptr)arena.memory, "Pointer originates from a different allocator."); + usize old_size = *(usize*)(old_pointer - DEFAULT_SIZE_PREFIX); + if (old_pointer + old_size == arena.memory + arena.used) + { + arena.used -= old_size; + } + return null; + case RESET: + arena.used = 0; return null; } @unreachable(); } -fn void*! arena_allocator_function(void* allocator, usize new_size, usize alignment, void* old_pointer, AllocationKind kind) +struct DynamicArenaAllocator { - return @allocator_to_function(ArenaAllocator, allocator, new_size, alignment, old_pointer, kind); + Allocator backing_allocator; + DynamicArenaPage* page; + DynamicArenaPage* unused_page; + usize page_size; } -fn Allocator ArenaAllocator.to_allocator(ArenaAllocator* allocator) @inline -{ - return { &arena_allocator_function, allocator }; -} - - -/** - * @require !alignment || @math::is_power_of_2(alignment) - */ -fn void*! ArenaAllocator.alloc(ArenaAllocator* allocator, usize bytes, usize alignment = 0) -{ - if (!bytes) return null; - if (!alignment) alignment = DEFAULT_MEM_ALIGNMENT; - iptr next = aligned_offset((iptr)allocator.memory + allocator.used, alignment); - usize next_after = next - (iptr)allocator.memory + bytes; - if (next_after > allocator.total) return AllocationFailure.OUT_OF_MEMORY!; - allocator.used = next_after; - return allocator.last_ptr = (void*)next; -} - -fn void*! ArenaAllocator.calloc(ArenaAllocator* allocator, usize bytes, usize alignment = 0) -{ - char* bits = allocator.alloc(bytes) @inline?; - mem::set(bits, 0, bytes); - return bits; -} - -/** - * @require ptr != null - * @require allocator != null - **/ -fn void*! ArenaAllocator.realloc(ArenaAllocator* allocator, void *ptr, usize bytes, usize alignment = 0) -{ - if (!ptr) return allocator.alloc(bytes, alignment); - if (!alignment) alignment = DEFAULT_MEM_ALIGNMENT; - - // Is last allocation and alignment matches? - if (allocator.last_ptr == ptr && ptr_is_aligned(ptr, alignment)) - { - usize new_used = (usize)(ptr - allocator.memory) + bytes; - if (new_used > allocator.total) return AllocationFailure.OUT_OF_MEMORY!; - allocator.used = new_used; - return ptr; - } - - // Otherwise just allocate new memory. - void* new_mem = allocator.alloc(bytes, alignment)?; - // And copy too much probably! - copy(new_mem, ptr, (new_mem - ptr) > bytes ? bytes : (usize)(new_mem - ptr)); - return new_mem; -} - -fn void! ArenaAllocator.free(ArenaAllocator* allocator, void* ptr) -{ - if (!ptr) return; - if (ptr == allocator.last_ptr) - { - allocator.used = (usize)(ptr - allocator.memory); - allocator.last_ptr = null; - return; - } -} - -fn void! ArenaAllocator.init(ArenaAllocator* allocator, usize arena_size) -{ - allocator.memory = alloc_checked(arena_size)?; - allocator.total = arena_size; - allocator.used = 0; - allocator.last_ptr = null; -} - -fn void ArenaAllocator.reset(ArenaAllocator* allocator) -{ - allocator.used = 0; - allocator.last_ptr = null; -} - -fn void ArenaAllocator.destroy(ArenaAllocator* allocator) -{ - assert(allocator.memory); - free(allocator.memory); - allocator.total = allocator.used = 0; -} - - private struct DynamicArenaPage { void* memory; void* prev_arena; usize total; usize used; + void* last_ptr; } -struct DynamicArenaAllocator + +/** + * @require !alignment || @math::is_power_of_2(alignment) + * @require data `unexpectedly missing the allocator` + */ +fn void*! dynamic_arena_allocator_function(void* data, usize size, usize alignment, void* old_pointer, AllocationKind kind) { - DynamicArenaPage* page; - usize total; - usize used; - usize page_size; - Allocator allocator; + DynamicArenaAllocator* allocator = data; + switch (kind) + { + case CALLOC: + assert(!old_pointer, "Unexpected no old pointer for calloc."); + if (!size) return null; + void* mem = allocator.alloc(size, alignment)?; + set(mem, 0, size); + return mem; + case ALLOC: + assert(!old_pointer, "Unexpected no old pointer for alloc."); + if (!size) return null; + return allocator.alloc(size, alignment); + case REALLOC: + if (!size) + { + if (!old_pointer) return null; + allocator.free(old_pointer); + return null; + } + if (!old_pointer) return allocator.alloc(size, alignment); + void* mem = allocator.realloc(old_pointer, size, alignment)?; + return mem; + case FREE: + if (!old_pointer) return null; + allocator.free(old_pointer); + return null; + case RESET: + allocator.reset(); + return null; + } + @unreachable(); } -fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usize page_size, Allocator allocator = { null, null }) +/** + * @require page_size >= 128 + * @require this != null + **/ +fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usize page_size, Allocator* backing_allocator = null) { this.page = null; - this.used = this.total = 0; + this.unused_page = null; this.page_size = page_size; - this.allocator = allocator.function ? allocator : thread_allocator; + this.backing_allocator = backing_allocator ? *backing_allocator : main_allocator; } -fn void! DynamicArenaAllocator.reset(DynamicArenaAllocator* this) +/** + * @require this != null + **/ +fn void DynamicArenaAllocator.destroy(DynamicArenaAllocator* this) { DynamicArenaPage* page = this.page; - Allocator allocator = this.allocator; - while (page && page.prev_arena) + while (page) { DynamicArenaPage* next_page = page.prev_arena; - void* mem = page.memory; - allocator.free(page)?; - allocator.free(mem)?; + this.backing_allocator.free(page); + page = next_page; + } + page = this.unused_page; + while (page) + { + DynamicArenaPage* next_page = page.prev_arena; + this.backing_allocator.free(page); page = next_page; } - this.page = page; -} - -fn void*! dynamic_arena_allocator_function(void* allocator, usize new_size, usize alignment, void* old_pointer, AllocationKind kind) -{ - return @allocator_to_function(DynamicArenaAllocator, allocator, new_size, alignment, old_pointer, kind); -} - -fn Allocator DynamicArenaAllocator.to_allocator(DynamicArenaAllocator* this) -{ - return { &dynamic_arena_allocator_function, this }; -} - -fn void! DynamicArenaAllocator.destroy(DynamicArenaAllocator* this) -{ - this.reset(); - DynamicArenaPage* first_page = this.page; - if (!first_page) return; - void* mem = first_page.memory; - this.allocator.free(this.page)?; this.page = null; - this.allocator.free(mem)?; + this.unused_page = null; } -fn void! DynamicArenaAllocator.free(DynamicArenaAllocator* allocator, void* ptr) +/** + * @require ptr && this + * @require this.page `tried to free pointer on invalid allocator` + */ +private fn void DynamicArenaAllocator.free(DynamicArenaAllocator* this, void* ptr) { - // This can be made smarter. - return; + DynamicArenaPage* current_page = this.page; + if (ptr == current_page.last_ptr) + { + current_page.used = (usize)((ptr - DEFAULT_SIZE_PREFIX) - current_page.memory); + } + current_page.last_ptr = null; +} + +/** + * @require old_pointer && size > 0 + * @require this.page `tried to realloc pointer on invalid allocator` + */ +private fn void*! DynamicArenaAllocator.realloc(DynamicArenaAllocator* this, void* old_pointer, usize size, usize alignment) +{ + DynamicArenaPage* current_page = this.page; + alignment = alignment_for_allocation(alignment); + usize* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX; + usize old_size = *old_size_ptr; + // We have the old pointer and it's correctly aligned. + if (old_size >= size && ptr_is_aligned(old_pointer, alignment)) + { + *old_size_ptr = size; + if (current_page.last_ptr == old_pointer) + { + current_page.used = (usize)((old_pointer - DEFAULT_SIZE_PREFIX) - current_page.memory); + } + return old_pointer; + } + if REUSE: (current_page.last_ptr == old_pointer && ptr_is_aligned(old_pointer, alignment)) + { + assert(size > old_size); + usize add_size = size - old_size; + if (add_size + current_page.used > current_page.total) break REUSE; + *old_size_ptr = size; + current_page.used += add_size; + return old_pointer; + } + void* new_mem = this.alloc(size, alignment)?; + copy(new_mem, old_pointer, old_size); + return new_mem; +} + +private fn void DynamicArenaAllocator.reset(DynamicArenaAllocator* this) +{ + DynamicArenaPage* page = this.page; + DynamicArenaPage** unused_page_ptr = &this.unused_page; + while (page) + { + DynamicArenaPage* next_page = page.prev_arena; + page.used = 0; + DynamicArenaPage* prev_unused = *unused_page_ptr; + *unused_page_ptr = page; + page.prev_arena = prev_unused; + page = next_page; + } + this.page = page; } /** * @require @math::is_power_of_2(alignment) + * @require size > 0 */ private fn void*! DynamicArenaAllocator.alloc_new(DynamicArenaAllocator* this, usize size, usize alignment) { - usize page_size = @max(this.page_size, size); - void* mem = this.allocator.alloc(page_size, alignment)?; - DynamicArenaPage*! page = this.allocator.alloc(DynamicArenaPage.sizeof); + usize page_size = @max(this.page_size, size + DEFAULT_SIZE_PREFIX + alignment); + void* mem = this.backing_allocator.alloc(page_size)?; + DynamicArenaPage*! page = this.backing_allocator.alloc(DynamicArenaPage.sizeof); if (catch err = page) { - this.allocator.free(mem); + this.backing_allocator.free(mem); return err!; } page.memory = mem; + usize offset = aligned_offset((usize)mem + DEFAULT_SIZE_PREFIX, alignment) - (usize)mem; + usize* size_ptr = mem + offset - DEFAULT_SIZE_PREFIX; + *size_ptr = size; page.prev_arena = this.page; page.total = page_size; - page.used = size; + page.used = size + offset; this.page = page; - return page.memory; + return page.last_ptr = page.memory + offset; } /** * @require !alignment || @math::is_power_of_2(alignment) + * @require size > 0 + * @require this */ -fn void*! DynamicArenaAllocator.calloc(DynamicArenaAllocator* allocator, usize size, usize alignment = 0) +private fn void*! DynamicArenaAllocator.alloc(DynamicArenaAllocator* this, usize size, usize alignment) { - void* mem = allocator.alloc(size, alignment)?; - set(mem, 0, size); - return mem; + alignment = alignment_for_allocation(alignment); + DynamicArenaPage* page = this.page; + if (!page && this.unused_page) + { + this.page = page = this.unused_page; + this.unused_page = page.prev_arena; + page.prev_arena = null; + } + if (!page) return this.alloc_new(size, alignment); + usize start = aligned_offset((uptr)page.memory + page.used + DEFAULT_SIZE_PREFIX, alignment) - (usize)page.memory; + usize new_used = start + size; + if ALLOCATE_NEW: (new_used > page.total) + { + if ((page = this.unused_page)) + { + start = aligned_offset((uptr)page.memory + DEFAULT_SIZE_PREFIX, alignment) - (usize)page.memory; + new_used = start + size; + if (page.total >= new_used) + { + this.unused_page = page.prev_arena; + page.prev_arena = this.page; + this.page = page; + break ALLOCATE_NEW; + } + } + return this.alloc_new(size, alignment); + } + page.used = new_used; + void* mem = page.memory + start; + usize* size_offset = mem - DEFAULT_SIZE_PREFIX; + *size_offset = size; + return mem; } -/** - * @require !alignment || @math::is_power_of_2(alignment) - */ -fn void*! DynamicArenaAllocator.realloc(DynamicArenaAllocator* allocator, void* ptr, usize size, usize alignment = 0) -{ - void* mem = allocator.alloc(size, alignment)?; - copy(mem, ptr, size); - return mem; -} - -/** - * @require !alignment || @math::is_power_of_2(alignment) - */ -fn void*! DynamicArenaAllocator.alloc(DynamicArenaAllocator* this, usize size, usize alignment) -{ - DynamicArenaPage *page = this.page; - if (!alignment) alignment = DEFAULT_MEM_ALIGNMENT; - if (!page) return this.alloc_new(size, alignment); - usize start = aligned_offset((uptr)page.memory + page.used, alignment) - (usize)page.memory; - usize new_used = start + size; - if (new_used > page.total) return this.alloc_new(size, alignment); - page.used = new_used; - return page.memory + start; -} diff --git a/lib/std/mem_allocator_fn.c3 b/lib/std/mem_allocator_fn.c3 index 270a37000..3de78a2d3 100644 --- a/lib/std/mem_allocator_fn.c3 +++ b/lib/std/mem_allocator_fn.c3 @@ -54,6 +54,8 @@ fn void*! libc_allocator_fn(void *unused, usize bytes, usize alignment, void* ol } if (!data) return AllocationFailure.OUT_OF_MEMORY!; return data; + case RESET: + return AllocationFailure.UNSUPPORTED_OPERATION!; case FREE: libc::free(old_pointer); return null; diff --git a/lib/std/mem_temp_allocator.c3 b/lib/std/mem_temp_allocator.c3 index 9fc258b70..bf7101d7f 100644 --- a/lib/std/mem_temp_allocator.c3 +++ b/lib/std/mem_temp_allocator.c3 @@ -6,7 +6,7 @@ const TEMP_PAGES = 64; private char[TEMP_BLOCK_SIZE * TEMP_PAGES] allocator_static_storage; private void*[TEMP_PAGES] allocator_static_page_storage; -SlotAllocator default_allocator = { +SlotAllocator temp_allocator = { .pages = &allocator_static_storage, .page_size = TEMP_BLOCK_SIZE, .page_count = TEMP_PAGES, diff --git a/resources/examples/notworking/timeit.c3 b/resources/examples/notworking/timeit.c3 index 170cd9133..cd0e62ba1 100644 --- a/resources/examples/notworking/timeit.c3 +++ b/resources/examples/notworking/timeit.c3 @@ -1,13 +1,12 @@ module test; import std::time; -import std::io; public macro timeit(#call) { Time t = time::current(); typeof(#call) result = #call; TimeDiff diff = time::current() - t; - io::printf("'%s' took %f ms\n", $stringify(#call), diff * 1000); + libc::printf("'%s' took %f ms\n", $stringify(#call), diff * 1000); return result; } diff --git a/resources/testfragments/demo_err.c3 b/resources/testfragments/boolerr.c3 similarity index 94% rename from resources/testfragments/demo_err.c3 rename to resources/testfragments/boolerr.c3 index c7714740a..24f8b59d9 100644 --- a/resources/testfragments/demo_err.c3 +++ b/resources/testfragments/boolerr.c3 @@ -127,11 +127,12 @@ fn char* nameFromError(anyerr e) fn void main() { const char[][] URLS = { "good", "title-empty", "title-missing", "head-missing", "fail" }; - DynamicArenaAllocator allocator; - allocator.init(1024); + DynamicArenaAllocator dynamic_arena; + dynamic_arena.init(1024); + Allocator allocator = mem::dynamic_arena_allocator(&dynamic_arena); foreach (char[] url : URLS) { - @mem::with_allocator(allocator.to_allocator()) + @mem::with_allocator(allocator) { // Yes, it's pretty onerous to print strings for the moment in C3 libc::printf(`Checking "https://%.*s/":` "\n", (int)url.len, url.ptr); @@ -148,5 +149,5 @@ fn void main() }; allocator.reset(); } - allocator.destroy(); + dynamic_arena.destroy(); } diff --git a/resources/testfragments/demo_errors.c3 b/resources/testfragments/demo_errors.c3 deleted file mode 100644 index e69de29bb..000000000 diff --git a/resources/testfragments/tmem.c3 b/resources/testfragments/tmem.c3 index cbf440616..54388d0a5 100644 --- a/resources/testfragments/tmem.c3 +++ b/resources/testfragments/tmem.c3 @@ -82,5 +82,5 @@ fn void main() String w; w.init("Yo man!"); s.concat(&w); - io::printf("Message was: %s\n", s.zstr()); + libc::printf("Message was: %s\n", s.zstr()); } \ No newline at end of file diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 227d09145..8ea83f77b 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -2311,6 +2311,8 @@ static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_r LLVMValueRef parent_addr = parent_addr_x.value; LLVMValueRef parent_load_value = NULL; LLVMValueRef parent_base; + bool is_failable = type_is_failable(parent_type); + parent_type = type_no_fail(parent_type); switch (parent_type->type_kind) { case TYPE_POINTER: @@ -2433,8 +2435,8 @@ static void gencontext_emit_slice(GenContext *c, BEValue *be_value, Expr *expr) // Calculate the size LLVMValueRef size = LLVMBuildSub(c->builder, LLVMBuildAdd(c->builder, end.value, llvm_const_int(c, start.type, 1), ""), start.value, "size"); LLVMValueRef start_pointer; - - switch (parent.type->type_kind) + Type *type = type_lowering(parent.type); + switch (type->type_kind) { case TYPE_ARRAY: { @@ -2459,7 +2461,8 @@ static void gencontext_emit_slice(GenContext *c, BEValue *be_value, Expr *expr) } // Create a new subarray type - llvm_value_set(be_value, llvm_emit_aggregate_value(c, expr->type, start_pointer, size, NULL), expr->type); + Type *expr_type = type_lowering(expr->type); + llvm_value_set(be_value, llvm_emit_aggregate_value(c, expr_type, start_pointer, size, NULL), expr_type); } static void llvm_emit_slice_assign(GenContext *c, BEValue *be_value, Expr *expr) @@ -4540,7 +4543,7 @@ void llvm_emit_builtin_call(GenContext *c, BEValue *result_value, Expr *expr) llvm_emit_intrinsic_expr(c, llvm_get_intrinsic(func), result_value, expr); } -void llvm_add_call_attributes(GenContext *c, LLVMValueRef call_value, int start_index, int count, Type **types, ABIArgInfo **infos) +void llvm_add_abi_call_attributes(GenContext *c, LLVMValueRef call_value, int start_index, int count, Type **types, ABIArgInfo **infos) { for (unsigned i = 0; i < count; i++) { @@ -4817,10 +4820,15 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) } } - llvm_add_call_attributes(c, call_value, 1, non_variadic_params, params, abi_args); + llvm_add_abi_call_attributes(c, call_value, 1, non_variadic_params, params, abi_args); if (prototype->abi_varargs) { - llvm_add_call_attributes(c, call_value, 1 + non_variadic_params, vec_size(prototype->varargs), prototype->varargs, prototype->abi_varargs); + llvm_add_abi_call_attributes(c, + call_value, + 1 + non_variadic_params, + vec_size(prototype->varargs), + prototype->varargs, + prototype->abi_varargs); } // 11. Process the return value. @@ -4987,6 +4995,9 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 17f. Emit the "after" block. llvm_emit_block(c, after_block); + // Emit the current stack into the thread local or things will get messed up. + if (c->debug.last_ptr) llvm_store(c, c->debug.last_ptr, c->debug.stack_slot, type_alloca_alignment(type_voidptr)); + // 17g. If void, be_value contents should be skipped. if (!prototype->ret_by_ref) { @@ -4996,9 +5007,10 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 17h. Assign the return param to be_value. *result_value = synthetic_return_param; - if (c->debug.last_ptr) llvm_store(c, c->debug.last_ptr, c->debug.stack_slot, type_alloca_alignment(type_voidptr)); return; } + + // Emit the current stack into the thread local or things will get messed up. if (c->debug.last_ptr) llvm_store(c, c->debug.last_ptr, c->debug.stack_slot, type_alloca_alignment(type_voidptr)); // 17i. The simple case here is where there is a normal return. diff --git a/test/test_suite/compile_time/stringify.c3t b/test/test_suite/compile_time/stringify.c3t index 75d0d3fc4..1f5bfccf3 100644 --- a/test/test_suite/compile_time/stringify.c3t +++ b/test/test_suite/compile_time/stringify.c3t @@ -14,7 +14,7 @@ $else: $Type result = #call; $endif; long diff = (long)libc::clock() - t; - io::printf("'%s' took %lld us\n", $stringify(#call), diff); + libc::printf("'%s' took %lld us\n", $stringify(#call), diff); $if (!$is_void): return result; $endif; @@ -22,7 +22,7 @@ $endif; fn void test() { - for (int i = 0; i < 1000; i++) io::printf("%d\n", i); + for (int i = 0; i < 1000; i++) libc::printf("%d\n", i); } fn void main() @@ -30,7 +30,7 @@ fn void main() @timeit(test()); int a = 100; int x = @timeit(1 + 3 + a); - io::printf("Result was %d\n", x); + libc::printf("Result was %d\n", x); } /* #expect: test.ll diff --git a/test/test_suite/errors/general_error_regression.c3t b/test/test_suite/errors/general_error_regression.c3t index 0e8395633..a474bd8c3 100644 --- a/test/test_suite/errors/general_error_regression.c3t +++ b/test/test_suite/errors/general_error_regression.c3t @@ -47,22 +47,22 @@ fn void main() Foo ef = Foo.Y; anyerr x = f; ulong z = (ulong)(x); - io::printf("1: %p\n", z); + libc::printf("1: %p\n", z); x = ef; z = (ulong)(x); - io::printf("2: %p\n", z); + libc::printf("2: %p\n", z); x = Foo.W; z = (ulong)(x); - io::printf("21: %p\n", z); + libc::printf("21: %p\n", z); x = Foo.W1; z = (ulong)(x); - io::printf("22: %p\n", z); + libc::printf("22: %p\n", z); x = Foob.X1; z = (ulong)(x); - io::printf("3: %p\n", z); + libc::printf("3: %p\n", z); x = Foob.Y2; z = (ulong)(x); - io::printf("4: %p\n", z); + libc::printf("4: %p\n", z); Bar b; MyEnum a = MyEnum.A; f.hello(); diff --git a/test/test_suite/from_docs/examples_forswitch.c3t b/test/test_suite/from_docs/examples_forswitch.c3t index 775f547d6..bf9393649 100644 --- a/test/test_suite/from_docs/examples_forswitch.c3t +++ b/test/test_suite/from_docs/examples_forswitch.c3t @@ -8,7 +8,7 @@ fn void example_for() // the for-loop is the same as C99. for (int i = 0; i < 10; i++) { - io::printf("%d\n", i); + libc::printf("%d\n", i); } // also equal diff --git a/test/test_suite/from_docs/examples_if_catch.c3t b/test/test_suite/from_docs/examples_if_catch.c3t index e85d752ec..8b574e805 100644 --- a/test/test_suite/from_docs/examples_if_catch.c3t +++ b/test/test_suite/from_docs/examples_if_catch.c3t @@ -32,15 +32,15 @@ fn void main() if (catch err = ratio) { case MathError.DIVISION_BY_ZERO: - io::printf("Division by zero\n"); + libc::printf("Division by zero\n"); return; default: - io::printf("Unexpected error!"); + libc::printf("Unexpected error!"); return; } // Flow typing makes "ratio" // have the type double here. - io::printf("Ratio was %f\n", ratio); + libc::printf("Ratio was %f\n", ratio); } /* #expect: demo.ll diff --git a/test/test_suite/initializer_lists/statics.c3t b/test/test_suite/initializer_lists/statics.c3t index b0586a0de..8335f5429 100644 --- a/test/test_suite/initializer_lists/statics.c3t +++ b/test/test_suite/initializer_lists/statics.c3t @@ -17,7 +17,7 @@ fn void test() { Bar[] b = { { 1, 2 } }; static Bar[] c = { { 1, 2 } }; - io::printf("%d %d\n", b[0].y, c[0].y); + libc::printf("%d %d\n", b[0].y, c[0].y); b[0].y += 1; c[0].y += 1; diff --git a/test/test_suite/initializer_lists/subarrays.c3t b/test/test_suite/initializer_lists/subarrays.c3t index 59c107740..a96d4d092 100644 --- a/test/test_suite/initializer_lists/subarrays.c3t +++ b/test/test_suite/initializer_lists/subarrays.c3t @@ -21,13 +21,13 @@ int* fofeo = &&(int[2]{ 3, 4 }); fn int main() { Bar w = arrbar[1]; - io::printf("%d\n", arrbar[1].x); + libc::printf("%d\n", arrbar[1].x); int[] x = { 1, 2, 3 }; int* y = &&(int[3]{ 123, 234, 567 }); io::println("Start:"); - io::printf("X len: %d mid element %d\n", (int)(x.len), x[1]); - io::printf("Y mid element %d\n", y[1]); - io::printf("Fofeo second element %d\n", fofeo[1]); + libc::printf("X len: %d mid element %d\n", (int)(x.len), x[1]); + libc::printf("Y mid element %d\n", y[1]); + libc::printf("Fofeo second element %d\n", fofeo[1]); Baz ffe = { .x = 1 }; int[1] azz = {}; int[*] a = {}; diff --git a/test/test_suite/macros/macro_convert_literal.c3 b/test/test_suite/macros/macro_convert_literal.c3 index a6e41023a..d8d0ee7ab 100644 --- a/test/test_suite/macros/macro_convert_literal.c3 +++ b/test/test_suite/macros/macro_convert_literal.c3 @@ -8,5 +8,5 @@ macro foo(y) fn void main() { - io::printf("%d\n", @foo(10)); + libc::printf("%d\n", @foo(10)); } \ No newline at end of file diff --git a/test/test_suite/statements/switch_errors.c3 b/test/test_suite/statements/switch_errors.c3 index 9d4c2e0fa..f3bfdbb33 100644 --- a/test/test_suite/statements/switch_errors.c3 +++ b/test/test_suite/statements/switch_errors.c3 @@ -151,10 +151,10 @@ fn void main() switch (catch err = testMayError()) // #error: Catch unwrapping is only allowed { case MathError.DIVISION_BY_ZERO: - io::printf("Division by zero\n"); + libc::printf("Division by zero\n"); return; default: - io::printf("Unexpected error!"); + libc::printf("Unexpected error!"); return; } } \ No newline at end of file