diff --git a/code/demo.odin b/code/demo.odin index f6dfdefc5..991650ca0 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -4,12 +4,24 @@ Vec2 :: struct { x, y: i64 } + main :: proc() { - bar :: proc() -> i64 { - a := [3]i64{7, 4, 2} - v := Vec2{a[0], 2} - return v.x + foo :: proc() -> i64 { + bar :: proc() -> (i64, i64) { + a := [3]i64{7, 4, 2} + v := Vec2{a[0], 2} + return v.x, v.y + } + + x, y := bar() + + return x + y } - bar() + test :: proc(s: string) -> string { + return s + } + + foo() + x := test("Hello") } diff --git a/core/_preload.odin b/core/_preload.odin index 928c7d5ec..efa9f7974 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -292,11 +292,11 @@ __string_cmp :: proc(a, b : string) -> int { return mem.compare(a.data, b.data, min(a.count, b.count)) } -__string_ne :: proc(a, b : string) -> bool #inline { return !__string_eq(a, b) } -__string_lt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) < 0 } -__string_gt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) > 0 } -__string_le :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) <= 0 } -__string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) >= 0 } +__string_ne :: proc(a, b: string) -> bool #inline { return !__string_eq(a, b) } +__string_lt :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) < 0 } +__string_gt :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) > 0 } +__string_le :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) <= 0 } +__string_ge :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) >= 0 } __assert :: proc(file: string, line, column: int, msg: string) #inline { diff --git a/src/array.cpp b/src/array.cpp index 127e49f89..825fdc51e 100644 --- a/src/array.cpp +++ b/src/array.cpp @@ -19,6 +19,7 @@ struct Array { }; template void array_init (Array *array, gbAllocator a, isize init_capacity = ARRAY_GROW_FORMULA(0)); +template void array_init_count (Array *array, gbAllocator a, isize count); template Array array_make (T *data, isize count, isize capacity); template void array_free (Array *array); template void array_add (Array *array, T const &t); @@ -37,6 +38,15 @@ void array_init(Array *array, gbAllocator a, isize init_capacity) { array->capacity = init_capacity; } +template +void array_init_count(Array *array, gbAllocator a, isize count) { + array->allocator = a; + array->data = gb_alloc_array(a, T, count); + array->count = count; + array->capacity = count; +} + + template Array array_make(T *data, isize count, isize capacity) { Array a = {}; diff --git a/src/checker/types.cpp b/src/checker/types.cpp index 6cbd7b237..a44632ee5 100644 --- a/src/checker/types.cpp +++ b/src/checker/types.cpp @@ -150,6 +150,8 @@ struct Type { struct { Entity **variables; // Entity_Variable i32 variable_count; + b32 are_offsets_set; + i64 * offsets; } Tuple; struct { Scope *scope; @@ -1109,11 +1111,21 @@ i64 *type_set_offsets_of(BaseTypeSizes s, gbAllocator allocator, Entity **fields } b32 type_set_offsets(BaseTypeSizes s, gbAllocator allocator, Type *t) { - GB_ASSERT(is_type_struct(t)); - if (!t->Record.struct_are_offsets_set) { - t->Record.struct_offsets = type_set_offsets_of(s, allocator, t->Record.fields, t->Record.field_count, t->Record.struct_is_packed); - t->Record.struct_are_offsets_set = true; - return true; + t = base_type(t); + if (is_type_struct(t)) { + if (!t->Record.struct_are_offsets_set) { + t->Record.struct_offsets = type_set_offsets_of(s, allocator, t->Record.fields, t->Record.field_count, t->Record.struct_is_packed); + t->Record.struct_are_offsets_set = true; + return true; + } + } else if (is_type_tuple(t)) { + if (!t->Tuple.are_offsets_set) { + t->Tuple.offsets = type_set_offsets_of(s, allocator, t->Tuple.variables, t->Tuple.variable_count, false); + t->Tuple.are_offsets_set = true; + return true; + } + } else { + GB_PANIC("Invalid type for setting offsets"); } return false; } @@ -1239,7 +1251,12 @@ i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index) if (gb_is_between(index, 0, t->Record.field_count-1)) { return t->Record.struct_offsets[index]; } - } else if (t->kind == Type_Basic) { + } else if (t->kind == Type_Tuple) { + type_set_offsets(s, allocator, t); + if (gb_is_between(index, 0, t->Tuple.variable_count-1)) { + return t->Tuple.offsets[index]; + } + } else if (t->kind == Type_Basic) { if (t->Basic.kind == Basic_string) { switch (index) { case 0: return 0; diff --git a/src/dyncall/include/dyncall.h b/src/dyncall/include/dyncall.h new file mode 100644 index 000000000..28981472e --- /dev/null +++ b/src/dyncall/include/dyncall.h @@ -0,0 +1,143 @@ +/* + + Package: dyncall + Library: dyncall + File: dyncall/dyncall.h + Description: public header for library dyncall + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +/* + + dyncall C API + + REVISION + 2015/07/08 added SYS_PPC64 system call + 2015/01/16 added SYS_PPC32 system call + 2007/12/11 initial + +*/ + +#ifndef DYNCALL_H +#define DYNCALL_H + +#include "dyncall_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DCCallVM_ DCCallVM; +typedef struct DCstruct_ DCstruct; + +/* Supported Calling Convention Modes */ + +#define DC_CALL_C_DEFAULT 0 +#define DC_CALL_C_ELLIPSIS 100 +#define DC_CALL_C_ELLIPSIS_VARARGS 101 +#define DC_CALL_C_X86_CDECL 1 +#define DC_CALL_C_X86_WIN32_STD 2 +#define DC_CALL_C_X86_WIN32_FAST_MS 3 +#define DC_CALL_C_X86_WIN32_FAST_GNU 4 +#define DC_CALL_C_X86_WIN32_THIS_MS 5 +#define DC_CALL_C_X86_WIN32_THIS_GNU 6 +#define DC_CALL_C_X64_WIN64 7 +#define DC_CALL_C_X64_SYSV 8 +#define DC_CALL_C_PPC32_DARWIN 9 +#define DC_CALL_C_PPC32_OSX DC_CALL_C_PPC32_DARWIN /* alias */ +#define DC_CALL_C_ARM_ARM_EABI 10 +#define DC_CALL_C_ARM_THUMB_EABI 11 +#define DC_CALL_C_ARM_ARMHF 30 +#define DC_CALL_C_MIPS32_EABI 12 +#define DC_CALL_C_MIPS32_PSPSDK DC_CALL_C_MIPS32_EABI /* alias - deprecated. */ +#define DC_CALL_C_PPC32_SYSV 13 +#define DC_CALL_C_PPC32_LINUX DC_CALL_C_PPC32_SYSV /* alias */ +#define DC_CALL_C_ARM_ARM 14 +#define DC_CALL_C_ARM_THUMB 15 +#define DC_CALL_C_MIPS32_O32 16 +#define DC_CALL_C_MIPS64_N32 17 +#define DC_CALL_C_MIPS64_N64 18 +#define DC_CALL_C_X86_PLAN9 19 +#define DC_CALL_C_SPARC32 20 +#define DC_CALL_C_SPARC64 21 +#define DC_CALL_C_ARM64 22 +#define DC_CALL_C_PPC64 23 +#define DC_CALL_C_PPC64_LINUX DC_CALL_C_PPC64 /* alias */ +#define DC_CALL_SYS_DEFAULT 200 +#define DC_CALL_SYS_X86_INT80H_LINUX 201 +#define DC_CALL_SYS_X86_INT80H_BSD 202 +#define DC_CALL_SYS_PPC32 210 +#define DC_CALL_SYS_PPC64 211 + +/* Error codes. */ + +#define DC_ERROR_NONE 0 +#define DC_ERROR_UNSUPPORTED_MODE -1 + +DC_API DCCallVM* dcNewCallVM (DCsize size); +DC_API void dcFree (DCCallVM* vm); +DC_API void dcReset (DCCallVM* vm); + +DC_API void dcMode (DCCallVM* vm, DCint mode); + +DC_API void dcArgBool (DCCallVM* vm, DCbool value); +DC_API void dcArgChar (DCCallVM* vm, DCchar value); +DC_API void dcArgShort (DCCallVM* vm, DCshort value); +DC_API void dcArgInt (DCCallVM* vm, DCint value); +DC_API void dcArgLong (DCCallVM* vm, DClong value); +DC_API void dcArgLongLong (DCCallVM* vm, DClonglong value); +DC_API void dcArgFloat (DCCallVM* vm, DCfloat value); +DC_API void dcArgDouble (DCCallVM* vm, DCdouble value); +DC_API void dcArgPointer (DCCallVM* vm, DCpointer value); +DC_API void dcArgStruct (DCCallVM* vm, DCstruct* s, DCpointer value); + +DC_API void dcCallVoid (DCCallVM* vm, DCpointer funcptr); +DC_API DCbool dcCallBool (DCCallVM* vm, DCpointer funcptr); +DC_API DCchar dcCallChar (DCCallVM* vm, DCpointer funcptr); +DC_API DCshort dcCallShort (DCCallVM* vm, DCpointer funcptr); +DC_API DCint dcCallInt (DCCallVM* vm, DCpointer funcptr); +DC_API DClong dcCallLong (DCCallVM* vm, DCpointer funcptr); +DC_API DClonglong dcCallLongLong (DCCallVM* vm, DCpointer funcptr); +DC_API DCfloat dcCallFloat (DCCallVM* vm, DCpointer funcptr); +DC_API DCdouble dcCallDouble (DCCallVM* vm, DCpointer funcptr); +DC_API DCpointer dcCallPointer (DCCallVM* vm, DCpointer funcptr); +DC_API void dcCallStruct (DCCallVM* vm, DCpointer funcptr, DCstruct* s, DCpointer returnValue); + +DC_API DCint dcGetError (DCCallVM* vm); + +#define DEFAULT_ALIGNMENT 0 +DC_API DCstruct* dcNewStruct (DCsize fieldCount, DCint alignment); +DC_API void dcStructField (DCstruct* s, DCint type, DCint alignment, DCsize arrayLength); +DC_API void dcSubStruct (DCstruct* s, DCsize fieldCount, DCint alignment, DCsize arrayLength); +/* Each dcNewStruct or dcSubStruct call must be paired with a dcCloseStruct. */ +DC_API void dcCloseStruct (DCstruct* s); +DC_API DCsize dcStructSize (DCstruct* s); +DC_API DCsize dcStructAlignment(DCstruct* s); +DC_API void dcFreeStruct (DCstruct* s); + +DC_API DCstruct* dcDefineStruct (const char* signature); + + +#ifdef __cplusplus +} +#endif + +#endif /* DYNCALL_H */ + diff --git a/src/dyncall/include/dyncall_alloc_wx.h b/src/dyncall/include/dyncall_alloc_wx.h new file mode 100644 index 000000000..c948f0bd3 --- /dev/null +++ b/src/dyncall/include/dyncall_alloc_wx.h @@ -0,0 +1,47 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_alloc_wx.h + Description: Allocate write/executable memory - Interface + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALL_ALLOC_WX_HPP +#define DYNCALL_ALLOC_WX_HPP + +#include "dyncall_types.h" + +typedef int DCerror; + +#ifdef __cplusplus +extern "C" { +#endif + +DCerror dcAllocWX(DCsize size, void** p); +void dcFreeWX (void* p, DCsize size); + +#ifdef __cplusplus +} +#endif + + +#endif /* DYNCALL_ALLOC_WX_HPP */ + diff --git a/src/dyncall/include/dyncall_args.h b/src/dyncall/include/dyncall_args.h new file mode 100644 index 000000000..4379a53b1 --- /dev/null +++ b/src/dyncall/include/dyncall_args.h @@ -0,0 +1,66 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_args.h + Description: Callback's Arguments VM - Interface + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALL_ARGS_H +#define DYNCALL_ARGS_H + +/* + * dyncall args C API + * + * dyncall args provides serialized access to arguments of a function call. + * related concepts: callback + * + */ + +#include "dyncall.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DCArgs DCArgs; + +DC_API DCbool dcbArgBool (DCArgs*); +DC_API DCchar dcbArgChar (DCArgs*); +DC_API DCshort dcbArgShort (DCArgs*); +DC_API DCint dcbArgInt (DCArgs*); +DC_API DClong dcbArgLong (DCArgs*); +DC_API DClonglong dcbArgLongLong (DCArgs*); +DC_API DCuchar dcbArgUChar (DCArgs*); +DC_API DCushort dcbArgUShort (DCArgs*); +DC_API DCuint dcbArgUInt (DCArgs*); +DC_API DCulong dcbArgULong (DCArgs*); +DC_API DCulonglong dcbArgULongLong(DCArgs*); +DC_API DCfloat dcbArgFloat (DCArgs*); +DC_API DCdouble dcbArgDouble (DCArgs*); +DC_API DCpointer dcbArgPointer (DCArgs*); + +#ifdef __cplusplus +} +#endif + +#endif /* DYNCALL_ARGS_H */ diff --git a/src/dyncall/include/dyncall_args_arm32_arm.h b/src/dyncall/include/dyncall_args_arm32_arm.h new file mode 100644 index 000000000..92fbd9099 --- /dev/null +++ b/src/dyncall/include/dyncall_args_arm32_arm.h @@ -0,0 +1,46 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_args_arm32_arm.h + Description: Callback's Arguments VM - Header for ARM32 (ARM mode) + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALLBACK_ARGS_ARM32_ARM_H +#define DYNCALLBACK_ARGS_ARM32_ARM_H + +#include "dyncall_args.h" + +struct DCArgs +{ + /* Don't change order! */ + long reg_data[4]; + int reg_count; + long* stack_ptr; +#if defined(DC__ABI_ARM_HF) + DCfloat f[16]; + int freg_count; + int dreg_count; +#endif +}; + +#endif /* DYNCALLBACK_ARGS_ARM32_ARM_H */ + diff --git a/src/dyncall/include/dyncall_args_arm32_thumb.h b/src/dyncall/include/dyncall_args_arm32_thumb.h new file mode 100644 index 000000000..07b6d39ce --- /dev/null +++ b/src/dyncall/include/dyncall_args_arm32_thumb.h @@ -0,0 +1,33 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_args_arm32_thumb.h + Description: Callback's Arguments VM - Header for ARM32 (THUMB mode) + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALLBACK_ARGS_ARM32_THUMB_H +#define DYNCALLBACK_ARGS_ARM32_THUMB_H + +#include "dyncall_args_arm32_arm.h" /* Uses same code as ARM mode. */ + +#endif /* DYNCALLBACK_ARGS_ARM32_THUMB_H */ + diff --git a/src/dyncall/include/dyncall_args_mips.h b/src/dyncall/include/dyncall_args_mips.h new file mode 100644 index 000000000..4cb0220ea --- /dev/null +++ b/src/dyncall/include/dyncall_args_mips.h @@ -0,0 +1,42 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_args_mips.h + Description: Callback's Arguments VM - Header for MIPS + License: + + Copyright (c) 2013-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALLBACK_ARGS_MIPS_H +#define DYNCALLBACK_ARGS_MIPS_H + +#include "dyncall_args.h" + +struct DCArgs +{ + int ireg_data[8]; + float freg_data[8]; + int ireg_count; + int freg_count; + unsigned char* stackptr; +}; + +#endif /* DYNCALLBACK_ARGS_MIPS_H */ + diff --git a/src/dyncall/include/dyncall_args_ppc32.h b/src/dyncall/include/dyncall_args_ppc32.h new file mode 100644 index 000000000..46319ec28 --- /dev/null +++ b/src/dyncall/include/dyncall_args_ppc32.h @@ -0,0 +1,43 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_args_ppc32.h + Description: Callback's Arguments VM - Header for ppc32 + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +#ifndef DYNCALLBACK_ARGS_PPC32_H +#define DYNCALLBACK_ARGS_PPC32_H + +#include "dyncall_args.h" + +/* Common Args iterator for Apple and System V ABI. */ + +struct DCArgs +{ + int ireg_data[8]; /* offset: 0 size: 4*8 = 32 */ + double freg_data[13]; /* offset: 32 size: 8*13= 104 */ + unsigned char* stackptr; /* offset: 136 size: 4 */ + int ireg_count; /* offset: 140 size: 4 */ + int freg_count; /* offset: 144 size: 4 */ +}; /* total size: 148 */ + +#endif /* DYNCALLBACK_ARGS_PPC32_H */ + diff --git a/src/dyncall/include/dyncall_args_ppc64.h b/src/dyncall/include/dyncall_args_ppc64.h new file mode 100644 index 000000000..208e2c65d --- /dev/null +++ b/src/dyncall/include/dyncall_args_ppc64.h @@ -0,0 +1,40 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_args_ppc64.h + Description: Callback's Arguments VM - Header for ppc64 + License: + + Copyright (c) 2014-2015 Masanori Mitsugi + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +#ifndef DYNCALLBACK_ARGS_PPC64_H +#define DYNCALLBACK_ARGS_PPC64_H + +#include "dyncall_args.h" + +struct DCArgs +{ + long long ireg_data[8]; + double freg_data[13]; + unsigned char* stackptr; + int ireg_count; + int freg_count; +}; + +#endif /* DYNCALLBACK_ARGS_PPC64_H */ + diff --git a/src/dyncall/include/dyncall_args_sparc32.h b/src/dyncall/include/dyncall_args_sparc32.h new file mode 100644 index 000000000..d7e42581c --- /dev/null +++ b/src/dyncall/include/dyncall_args_sparc32.h @@ -0,0 +1,38 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_args_sparc32.h + Description: Callback's Arguments VM - Header for sparc32 + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALLBACK_ARGS_SPARC32_H +#define DYNCALLBACK_ARGS_SPARC32_H + +#include "dyncall_args.h" + +struct DCArgs +{ + int dummy; +}; + +#endif /* DYNCALLBACK_ARGS_SPARC32_H */ + diff --git a/src/dyncall/include/dyncall_args_sparc64.h b/src/dyncall/include/dyncall_args_sparc64.h new file mode 100644 index 000000000..d238d09cb --- /dev/null +++ b/src/dyncall/include/dyncall_args_sparc64.h @@ -0,0 +1,38 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_args_sparc64.h + Description: Callback's Arguments VM - Header for sparc32 - not yet + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALLBACK_ARGS_SPARC64_H +#define DYNCALLBACK_ARGS_SPARC64_H + +#include "dyncall_args.h" + +struct DCArgs +{ + int dummy; +}; + +#endif /* DYNCALLBACK_ARGS_SPARC64_H */ + diff --git a/src/dyncall/include/dyncall_args_x64.h b/src/dyncall/include/dyncall_args_x64.h new file mode 100644 index 000000000..cc10f6bed --- /dev/null +++ b/src/dyncall/include/dyncall_args_x64.h @@ -0,0 +1,45 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_args_x64.h + Description: Callback's Arguments VM - Header for x64 + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALLBACK_ARGS_X64_H +#define DYNCALLBACK_ARGS_X64_H + +#include "dyncall_args.h" +#include "dyncall_callvm_x64.h" /* reuse structures */ + + +struct DCArgs +{ + /* state */ + int64* stack_ptr; + DCRegCount_x64 reg_count; /* @@@ win64 version should maybe force alignment to 8 in order to be secure */ + + /* reg data */ + DCRegData_x64_s reg_data; +}; + +#endif /* DYNCALLBACK_ARGS_X64_H */ + diff --git a/src/dyncall/include/dyncall_args_x86.h b/src/dyncall/include/dyncall_args_x86.h new file mode 100644 index 000000000..d679dcee3 --- /dev/null +++ b/src/dyncall/include/dyncall_args_x86.h @@ -0,0 +1,59 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_args_x86.h + Description: Callback's Arguments VM - Header for x86 + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + + +#ifndef DYNCALL_ARGS_X86_H_ +#define DYNCALL_ARGS_X86_H_ + +#include "dyncall_args.h" + +typedef struct +{ + DCint (*i32)(DCArgs*); + DClonglong (*i64)(DCArgs*); + DCfloat (*f32)(DCArgs*); + DCdouble (*f64)(DCArgs*); +} DCArgsVT; + +extern DCArgsVT dcArgsVT_default; +extern DCArgsVT dcArgsVT_this_ms; +extern DCArgsVT dcArgsVT_fast_ms; +extern DCArgsVT dcArgsVT_fast_gnu; + +struct DCArgs +{ + /* callmode */ + DCArgsVT* vt; + + /* state */ + int* stack_ptr; + + /* fast data / 'this-ptr' info */ + int fast_data[2]; + int fast_count; +}; + +#endif /* DYNCALL_ARGS_X86_H_ */ diff --git a/src/dyncall/include/dyncall_callback.h b/src/dyncall/include/dyncall_callback.h new file mode 100644 index 000000000..6d599df61 --- /dev/null +++ b/src/dyncall/include/dyncall_callback.h @@ -0,0 +1,53 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_callback.h + Description: Callback - Interface + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +#ifndef DYNCALL_CALLBACK_H +#define DYNCALL_CALLBACK_H + +#include "dyncall_args.h" +#include "dyncall_signature.h" +#include "dyncall_value.h" + +typedef struct DCCallback DCCallback; + +// TODO: return value is the type encoded as a signature char (character of the set [vBcCsSiIjJlLfd]). + +typedef char (DCCallbackHandler)(DCCallback* pcb, DCArgs* args, DCValue* result, void* userdata); + +#ifdef __cplusplus +extern "C" { +#endif + +DCCallback* dcbNewCallback(const char* signature, DCCallbackHandler* funcptr, void* userdata); +void dcbInitCallback(DCCallback* pcb, const char* signature, DCCallbackHandler* handler, void* userdata); +void dcbFreeCallback(DCCallback* pcb); +void* dcbGetUserData (DCCallback* pcb); + + +#ifdef __cplusplus +} +#endif + +#endif /* DYNCALL_CALLBACK_H */ diff --git a/src/dyncall/include/dyncall_callback_arm32_arm.h b/src/dyncall/include/dyncall_callback_arm32_arm.h new file mode 100644 index 000000000..b463549e3 --- /dev/null +++ b/src/dyncall/include/dyncall_callback_arm32_arm.h @@ -0,0 +1,46 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_callback_arm32_arm.h + Description: Callback - Header for ARM32 (ARM mode) + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + + +#ifndef DYNCALL_CALLBACK_ARM32_ARM_H_ +#define DYNCALL_CALLBACK_ARM32_ARM_H_ + +#include "dyncall_callback.h" + +#include "dyncall_thunk.h" +#include "dyncall_args_arm32_arm.h" + + +struct DCCallback +{ + DCThunk thunk; // offset 0 + DCCallbackHandler* handler; // offset 12 + void* userdata; // offset 16 +}; + + +#endif /* DYNCALL_CALLBACK_ARM32_ARM_H_ */ + diff --git a/src/dyncall/include/dyncall_callback_arm32_thumb.h b/src/dyncall/include/dyncall_callback_arm32_thumb.h new file mode 100644 index 000000000..1e4651f37 --- /dev/null +++ b/src/dyncall/include/dyncall_callback_arm32_thumb.h @@ -0,0 +1,34 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_callback_arm32_thumb.h + Description: Callback - Header for ARM32 (THUMB mode) + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + + +#ifndef DYNCALL_CALLBACK_ARM32_THUMB_H_ +#define DYNCALL_CALLBACK_ARM32_THUMB_H_ + +#include "dyncall_callback_arm32_arm.h" /* Uses same code as ARM mode. */ + +#endif /* DYNCALL_CALLBACK_ARM32_THUMB_H_ */ + diff --git a/src/dyncall/include/dyncall_callback_mips.h b/src/dyncall/include/dyncall_callback_mips.h new file mode 100644 index 000000000..7814638f0 --- /dev/null +++ b/src/dyncall/include/dyncall_callback_mips.h @@ -0,0 +1,43 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_callback_mips.h + Description: Callback - Header for MIPS + License: + + Copyright (c) 2013-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALL_CALLBACK_MIPS_H +#define DYNCALL_CALLBACK_MIPS_H + +#include "dyncall_callback.h" + +#include "dyncall_thunk.h" +#include "dyncall_args_mips.h" + +struct DCCallback +{ + DCThunk thunk; /* offset 0, size 20 */ + DCCallbackHandler* handler; /* offset 20, size 4 */ + void* userdata; /* offset 24, size 4 */ +}; + +#endif /* DYNCALL_CALLBACK_MIPS_H */ + diff --git a/src/dyncall/include/dyncall_callback_ppc32.h b/src/dyncall/include/dyncall_callback_ppc32.h new file mode 100644 index 000000000..a252b1a12 --- /dev/null +++ b/src/dyncall/include/dyncall_callback_ppc32.h @@ -0,0 +1,43 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_callback_ppc32.h + Description: Callback - Header for ppc32 + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +#ifndef DYNCALL_CALLBACK_PPC32_H +#define DYNCALL_CALLBACK_PPC32_H + +#include "dyncall_callback.h" + +#include "dyncall_thunk.h" +#include "dyncall_args_ppc32.h" + +struct DCCallback +{ + DCThunk thunk; /* offset 0, size 24 */ + DCCallbackHandler* handler; /* offset 24, size 4 */ + size_t stack_cleanup; /* offset 28, size 4 */ + void* userdata; /* offset 32, size 4 */ +}; /* total size 36 */ + +#endif /* DYNCALL_CALLBACK_PPC32_H */ + diff --git a/src/dyncall/include/dyncall_callback_ppc64.h b/src/dyncall/include/dyncall_callback_ppc64.h new file mode 100644 index 000000000..81fdefa2f --- /dev/null +++ b/src/dyncall/include/dyncall_callback_ppc64.h @@ -0,0 +1,56 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_callback_ppc64.h + Description: Callback - Header for ppc64 + License: + + Copyright (c) 2014-2015 Masanori Mitsugi + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +#ifndef DYNCALL_CALLBACK_PPC64_H +#define DYNCALL_CALLBACK_PPC64_H + +#include "dyncall_callback.h" + +#include "dyncall_thunk.h" +#include "dyncall_args_ppc64.h" + +/* + ELF v2 + thunk : offset 0, size 48 + handler : offset 48, size 8 + stack_cleanup : offset 56, size 8 + userdata : offset 64, size 8 + + ELF v1 + thunk : offset 0, size 64 + handler : offset 64, size 8 + stack_cleanup : offset 72, size 8 + userdata : offset 80, size 8 +*/ + +struct DCCallback +{ + DCThunk thunk; + DCCallbackHandler* handler; + size_t stack_cleanup; + void* userdata; +}; + +#endif /* DYNCALL_CALLBACK_PPC64_H */ + diff --git a/src/dyncall/include/dyncall_callback_sparc32.h b/src/dyncall/include/dyncall_callback_sparc32.h new file mode 100644 index 000000000..a9cf762ef --- /dev/null +++ b/src/dyncall/include/dyncall_callback_sparc32.h @@ -0,0 +1,44 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_callback_sparc32.h + Description: Callback - Header for sparc32 + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALL_CALLBACK_SPARC32_H +#define DYNCALL_CALLBACK_SPARC32_H + +#include "dyncall_callback.h" + +#include "dyncall_thunk.h" +#include "dyncall_args_sparc32.h" + +struct DCCallback +{ + DCThunk thunk; /* offset 0, size ?? */ + DCCallbackHandler* handler; /* offset ??, size 4 */ + size_t stack_cleanup; /* offset ??, size 4 */ + void* userdata; /* offset ??, size 4 */ +}; + +#endif /* DYNCALL_CALLBACK_SPARC32_H */ + diff --git a/src/dyncall/include/dyncall_callback_x64.h b/src/dyncall/include/dyncall_callback_x64.h new file mode 100644 index 000000000..6517ad21e --- /dev/null +++ b/src/dyncall/include/dyncall_callback_x64.h @@ -0,0 +1,45 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_callback_x64.h + Description: Callback - Header for x64 + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + + +#ifndef DYNCALL_CALLBACK_X64_H_ +#define DYNCALL_CALLBACK_X64_H_ + +#include "dyncall_callback.h" + +#include "dyncall_thunk.h" +#include "dyncall_args_x64.h" + + +struct DCCallback +{ + DCThunk thunk; // offset 0, size 24 + DCCallbackHandler* handler; // offset 24 + void* userdata; // offset 32 +}; + +#endif /* DYNCALL_CALLBACK_X64_H_ */ + diff --git a/src/dyncall/include/dyncall_callback_x86.h b/src/dyncall/include/dyncall_callback_x86.h new file mode 100644 index 000000000..d2e6f7288 --- /dev/null +++ b/src/dyncall/include/dyncall_callback_x86.h @@ -0,0 +1,50 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_callback_x86.h + Description: Callback - Header for x86 + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + + +#ifndef DYNCALL_CALLBACK_X86_H_ +#define DYNCALL_CALLBACK_X86_H_ + +#include "dyncall_callback.h" + +#include "dyncall_thunk.h" +#include "dyncall_args_x86.h" + +struct DCCallback +{ + DCThunk thunk; /* offset 0, size 16 */ + DCCallbackHandler* handler; /* offset 16 */ + DCArgsVT* args_vt; /* offset 20 */ + size_t stack_cleanup; /* offset 24 */ + void* userdata; /* offset 28 */ +}; + +int dcCleanupSize_x86_cdecl (const char* args_signature); +int dcCleanupSize_x86_std (const char* args_signature); +int dcCleanupSize_x86_fast_ms (const char* args_signature); +int dcCleanupSize_x86_fast_gnu(const char* args_signature); + +#endif /* DYNCALL_CALLBACK_X86_H_ */ diff --git a/src/dyncall/include/dyncall_callf.h b/src/dyncall/include/dyncall_callf.h new file mode 100644 index 000000000..0a8108834 --- /dev/null +++ b/src/dyncall/include/dyncall_callf.h @@ -0,0 +1,56 @@ +/* + + Package: dyncall + Library: dyncall + File: dyncall/dyncall_callf.h + Description: formatted call interface to dyncall + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + + +/* + + dyncall formatted calls C API + + REVISION + 2007/12/11 initial + +*/ + + +#ifndef DYNCALL_CALLF_H +#define DYNCALL_CALLF_H + +/* dyncall formatted calls */ + +#include "dyncall.h" +#include "dyncall_signature.h" +#include "dyncall_value.h" + +#include + +void dcArgF (DCCallVM* vm, const DCsigchar* signature, ...); +void dcVArgF(DCCallVM* vm, const DCsigchar* signature, va_list args); + +void dcCallF (DCCallVM* vm, DCValue* result, DCpointer funcptr, const DCsigchar* signature, ...); +void dcVCallF(DCCallVM* vm, DCValue* result, DCpointer funcptr, const DCsigchar* signature, va_list args); + +#endif /* DYNCALL_CALLF_H */ + diff --git a/src/dyncall/include/dyncall_config.h b/src/dyncall/include/dyncall_config.h new file mode 100644 index 000000000..4f3588878 --- /dev/null +++ b/src/dyncall/include/dyncall_config.h @@ -0,0 +1,47 @@ +/* + + Package: dyncall + Library: dyncall + File: dyncall/dyncall_config.h + Description: Macro configuration file for non-standard C types + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + + +/* + + dyncall type configuration + + REVISION + 2007/12/11 initial + +*/ + +#ifndef DYNCALL_CONFIG_H +#define DYNCALL_CONFIG_H + +#include "dyncall_macros.h" + +#define DC_BOOL int +#define DC_LONG_LONG long long +#define DC_POINTER void* + +#endif /* DYNCALL_CONFIG_H */ + diff --git a/src/dyncall/include/dyncall_macros.h b/src/dyncall/include/dyncall_macros.h new file mode 100644 index 000000000..7731f50a8 --- /dev/null +++ b/src/dyncall/include/dyncall_macros.h @@ -0,0 +1,289 @@ +/* + + Package: dyncall + Library: dyncall + File: dyncall/dyncall_macros.h + Description: Platform detection macros + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + + +/* + + dyncall macros + + Platform detection, specific defines and configuration. + The purpose of this file is to provide coherent platform and compiler + specific defines. So instead of defines like WIN32, _OpenBSD_ or + __GNUC__, one should use DC__OS_Win32, DC__OS_OpenBSD or DC__C_GNU, + respectively. + + REVISION + 2007/12/11 initial + +*/ + + +#ifndef DYNCALL_MACROS_H +#define DYNCALL_MACROS_H + + +/* Platform specific defines. */ + +/* MS Windows XP x64/Vista64 or later. */ +#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) +#define DC__OS_Win64 + +/* MS Windows NT/95/98/ME/2000/XP/Vista32. */ +#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(__WINDOWS__) || defined(_WINDOWS) +#define DC__OS_Win32 + +/* All the OS' based on Darwin OS (MacOS X, OpenDarwin). Note that '__APPLE__' may be defined for classic MacOS, too. */ +/* __MACOSX__ is not defined in gcc assembler mode (switch: -S) */ +/* @@@ TODO: Check for Classic OS */ + +#elif defined(__APPLE__) || defined(__Darwin__) +# define DC__OS_Darwin +# if defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) +# define DC__OS_IPhone +# else /* defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) */ +# define DC__OS_MacOSX +# endif + +/* The most popular open source Unix-like OS - Linux. */ +#elif defined(__linux__) || defined(__linux) || defined(__gnu_linux__) +#define DC__OS_Linux + +/* The most powerful open source Unix-like OS - FreeBSD. */ +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#define DC__OS_FreeBSD + +/* The most secure open source Unix-like OS - OpenBSD. */ +#elif defined(__OpenBSD__) +#define DC__OS_OpenBSD + +/* The most portable open source Unix-like OS - NetBSD. */ +#elif defined(__NetBSD__) +#define DC__OS_NetBSD + +/* The FreeBSD fork having heavy clusterization in mind - DragonFlyBSD. */ +#elif defined(__DragonFly__) +#define DC__OS_DragonFlyBSD + +/* Sun's Unix-like OS - SunOS / Solaris. */ +#elif defined(__sun__) || defined(__sun) || defined(sun) +#define DC__OS_SunOS + +/* The "Linux-like environment for Windows" - Cygwin. */ +#elif defined(__CYGWIN__) +#define DC__OS_Cygwin + +/* The "Minimalist GNU for Windows" - MinGW. */ +#elif defined(__MINGW__)/*@@@*/ +#define DC__OS_MinGW + +/* The Nintendo DS (homebrew) using devkitpro. */ +#elif defined(__nds__) +#define DC__OS_NDS + +/* The PlayStation Portable (homebrew) SDK. */ +#elif defined(__psp__) || defined(PSP) +#define DC__OS_PSP + +/* Haiku (BeOS alike). */ +#elif defined(__HAIKU__) +#define DC__OS_BeOS + +/* The Unix successor - Plan9 from Bell Labs */ +#elif defined(Plan9) || defined(__Plan9__) +#define DC__OS_Plan9 + +/* Digital's Unix-like OS - VMS */ +#elif defined(__vms) +#define DC__OS_VMS + +#elif defined(__minix) +#define DC__OS_Minix + +#else + #error Unsupported OS. +#endif + + + +/* Compiler specific defines. Do not change the order, because */ +/* some of the compilers define flags for compatible ones, too. */ + +/* Intel's C/C++ compiler. */ +#if defined(__INTEL_COMPILER) +#define DC__C_Intel + +/* MS C/C++ compiler. */ +#elif defined(_MSC_VER) +#define DC__C_MSVC + +/* LLVM clang. */ +#elif defined(__clang__) +#define DC__C_CLANG + +/* The GNU Compiler Collection - GCC. */ +#elif defined(__GNUC__) +#define DC__C_GNU + +/* Watcom compiler. */ +#elif defined(__WATCOMC__) +#define DC__C_WATCOM + +/* Portable C Compiler. */ +#elif defined(__PCC__) +#define DC__C_PCC + +/* Sun Pro C. */ +#elif defined(__SUNPRO_C) +#define DC__C_SUNPRO + +/* Undetected C Compiler. */ +#else +#define DC__C_UNKNOWN +#endif + + + +/* Architecture. */ + +/* Check architecture. */ +#if defined(_M_X64_) || defined(_M_AMD64) || defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) +# define DC__Arch_AMD64 +#elif defined(_M_IX86) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(__386__) || defined(__i386) +# define DC__Arch_Intel_x86 +#elif defined(_M_IA64) || defined(__ia64__) +# define DC__Arch_Itanium +#elif defined(_M_PPC) || defined(__powerpc__) || defined(__powerpc) || defined(__POWERPC__) || defined(__ppc__) || defined(__power__) +# if defined(__ppc64__) || defined(_ARCH_PPC64) || defined(__power64__) || defined(__powerpc64__) +# define DC__Arch_PPC64 +# else +# define DC__Arch_PPC32 +# endif +#elif defined(__mips64__) || defined(__mips64) +# define DC__Arch_MIPS64 +#elif defined(_M_MRX000) || defined(__mips__) || defined(__mips) || defined(_mips) +# define DC__Arch_MIPS +#elif defined(__arm__) +# define DC__Arch_ARM +#elif defined(__aarch64__) +# define DC__Arch_ARM64 +#elif defined(__sh__) +# define DC__Arch_SuperH +#elif defined(__sparcv9) || defined(__sparc64__) || ( defined(__sparc) && defined(__arch64__) ) +/* this could be needed on Linux/GNU sparc64 in the future: || ( defined(__sparc) && defined(__arch64__) ) */ +# define DC__Arch_Sparcv9 +#elif defined(__sparc) +# define DC__Arch_Sparc +#endif + + + +/* Rough OS classification. */ + +#if defined(DC__OS_Win32) || defined(DC__OS_Win64) +# define DC_WINDOWS +#elif defined(DC__OS_Plan9) +# define DC_PLAN9 +#elif defined(DC__OS_NDS) || defined(DC__OS_PSP) +# define DC_OTHER +#else +# define DC_UNIX +#endif + + + +/* Misc machine-dependent modes, ABIs, etc.. */ + +#if defined(__arm__) && !defined(__thumb__) +# define DC__Arch_ARM_ARM +#elif defined(__arm__) && defined(__thumb__) +# define DC__Arch_ARM_THUMB +#endif + +#if defined(DC__Arch_ARM_ARM) || defined(DC__Arch_ARM_THUMB) +# if defined(__ARM_EABI__) || defined(DC__OS_NDS) +# if defined (__ARM_PCS_VFP) && (__ARM_PCS_VFP == 1) +# define DC__ABI_ARM_HF +# else +# define DC__ABI_ARM_EABI +# endif +# elif defined(__APCS_32__) +# define DC__ABI_ARM_OABI +# endif +#endif /* ARM */ + +#if defined(DC__Arch_MIPS) || defined(DC__Arch_MIPS64) +# if defined(_ABIO32) || defined(_MIPS_ARCH_MIPS1) || defined(_MIPS_ARCH_MIPS2) +# define DC__ABI_MIPS_O32 +# elif defined(_ABIN32) +# define DC__ABI_MIPS_N32 +# elif defined(_ABI64) +# define DC__ABI_MIPS_N64 +# else +# define DC__ABI_MIPS_EABI +# endif +#endif /* MIPS */ + +#if defined(DC__Arch_PPC64) +# if defined(_CALL_ELF) +# define DC__ABI_PPC64_ELF_V _CALL_ELF +# else +# define DC__ABI_PPC64_ELF_V 0 /* 0 means not explicitly set, otherwise this is 1 (big endian) and 2 (little endian) */ +# endif +#endif /* MIPS */ + + +/* Endian detection. */ +#if defined(DC__Arch_Intel_x86) || defined(DC__Arch_AMD64) /* always little */ +# define DC__Endian_LITTLE +#elif defined(DC__Arch_Sparc) /*always big until v9*/ +# define DC__Endian_BIG +#else /* all others are bi-endian */ +/* @@@check flags used on following bi-endianness archs: +DC__Arch_ARM +DC__Arch_ARM64 +DC__Arch_Itanium +DC__Arch_MIPS +DC__Arch_MIPS64 +DC__Arch_PPC32 +DC__Arch_PPC64 +DC__Arch_Sparcv9 +DC__Arch_SuperH +*/ +# if (defined(DC__Arch_PPC64) && (DC__ABI_PPC64_ELF_V == 1)) || defined(_BIG_ENDIAN) || defined(MIPSEB) +# define DC__Endian_BIG +# elif (defined(DC__Arch_PPC64) && (DC__ABI_PPC64_ELF_V == 2)) || defined(_LITTLE_ENDIAN) || defined(MIPSEL) +# define DC__Endian_LITTLE +# endif /* no else, leave unset if not sure */ +#endif + + +/* Internal macro/tag. */ +#if !defined(DC_API) +#define DC_API +#endif + +#endif /* DYNCALL_MACROS_H */ + diff --git a/src/dyncall/include/dyncall_signature.h b/src/dyncall/include/dyncall_signature.h new file mode 100644 index 000000000..385dec67a --- /dev/null +++ b/src/dyncall/include/dyncall_signature.h @@ -0,0 +1,72 @@ +/* + + Package: dyncall + Library: dyncall + File: dyncall/dyncall_signature.h + Description: Type and calling-convention signature character defines + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + + +/* + + dyncall signature characters + + REVISION + 2007/12/11 initial + +*/ + + +#ifndef DYNCALL_SIGNATURE_H +#define DYNCALL_SIGNATURE_H + +typedef char DCsigchar; + +#define DC_SIGCHAR_VOID 'v' +#define DC_SIGCHAR_BOOL 'B' +#define DC_SIGCHAR_CHAR 'c' +#define DC_SIGCHAR_UCHAR 'C' +#define DC_SIGCHAR_SHORT 's' +#define DC_SIGCHAR_USHORT 'S' +#define DC_SIGCHAR_INT 'i' +#define DC_SIGCHAR_UINT 'I' +#define DC_SIGCHAR_LONG 'j' +#define DC_SIGCHAR_ULONG 'J' +#define DC_SIGCHAR_LONGLONG 'l' +#define DC_SIGCHAR_ULONGLONG 'L' +#define DC_SIGCHAR_FLOAT 'f' +#define DC_SIGCHAR_DOUBLE 'd' +#define DC_SIGCHAR_POINTER 'p' +#define DC_SIGCHAR_STRING 'Z' +#define DC_SIGCHAR_STRUCT 'T' +#define DC_SIGCHAR_ENDARG ')' /* also works for end struct */ + +/* callback signatures */ + +#define DC_SIGCHAR_CC_PREFIX '_' +#define DC_SIGCHAR_CC_ELLIPSIS 'e' +#define DC_SIGCHAR_CC_STDCALL 's' +#define DC_SIGCHAR_CC_FASTCALL_GNU 'f' +#define DC_SIGCHAR_CC_FASTCALL_MS 'F' +#define DC_SIGCHAR_CC_THISCALL_MS '+' + +#endif /* DYNCALL_SIGNATURE_H */ + diff --git a/src/dyncall/include/dyncall_thunk.h b/src/dyncall/include/dyncall_thunk.h new file mode 100644 index 000000000..87b05d1d2 --- /dev/null +++ b/src/dyncall/include/dyncall_thunk.h @@ -0,0 +1,90 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_thunk.h + Description: Thunk - Interface + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALL_THUNK_H +#define DYNCALL_THUNK_H + +/** + ** dyncall thunks + ** + ** thunks are small-size hybrid code/data objects, created at run-time to + ** be used as function pointers with associated data and entry functions. + ** + ** The header contains code, that does load its address into a designated scratch + ** register and will jump to a thunk function. + ** + ** Thunk entry procedures are compiled functions, that are called as a result of + ** a thunk function. + ** There is one thunk entry currently for supporting callbacks. + ** + ** Thunk context register ( ::= an available scratch register in the calling convention): + ** + ** x86: eax + ** x64: rax + ** ppc: r2 + ** arm: r12 + ** arm64: x9 + ** + **/ + +#include "dyncall_macros.h" + +typedef struct DCThunk_ DCThunk; + +#ifdef __cplusplus +extern "C" { +#endif + +void dcbInitThunk(DCThunk* p, void (*entry)()); + +#if defined(DC__Arch_Intel_x86) +#include "dyncall_thunk_x86.h" +#elif defined (DC__Arch_AMD64) +#include "dyncall_thunk_x64.h" +#elif defined (DC__Arch_PPC32) +#include "dyncall_thunk_ppc32.h" +#elif defined (DC__Arch_PPC64) +#include "dyncall_thunk_ppc64.h" +#elif defined (DC__Arch_ARM_ARM) +#include "dyncall_thunk_arm32_arm.h" +#elif defined (DC__Arch_ARM_THUMB) +#include "dyncall_thunk_arm32_thumb.h" +#elif defined (DC__Arch_MIPS) +#include "dyncall_thunk_mips.h" +#elif defined (DC__Arch_Sparc) +#include "dyncall_thunk_sparc32.h" +#elif defined (DC__Arch_Sparcv9) +#include "dyncall_thunk_sparc64.h" +#elif defined (DC__Arch_ARM64) +#include "dyncall_thunk_arm64.h" +#endif + +#ifdef __cplusplus +} +#endif + + +#endif /* DYNCALL_THUNK_H */ diff --git a/src/dyncall/include/dyncall_thunk_arm32_arm.h b/src/dyncall/include/dyncall_thunk_arm32_arm.h new file mode 100644 index 000000000..12ffb3305 --- /dev/null +++ b/src/dyncall/include/dyncall_thunk_arm32_arm.h @@ -0,0 +1,41 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_thunk_arm32_arm.h + Description: Thunk - Header for ARM32 (ARM mode) + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALL_THUNK_ARM32_ARM_H +#define DYNCALL_THUNK_ARM32_ARM_H + + +struct DCThunk_ +{ + unsigned int code[2]; + void (*entry)(); +}; + +#define DCTHUNK_ARM32_ARM_SIZE 12 + + +#endif /* DYNCALL_THUNK_ARM32_ARM_H */ + diff --git a/src/dyncall/include/dyncall_thunk_arm32_thumb.h b/src/dyncall/include/dyncall_thunk_arm32_thumb.h new file mode 100644 index 000000000..832484c11 --- /dev/null +++ b/src/dyncall/include/dyncall_thunk_arm32_thumb.h @@ -0,0 +1,36 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_thunk_arm32_thumb.h + Description: Thunk - Header for ARM32 (THUMB mode) + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALL_THUNK_ARM32_THUMB_H +#define DYNCALL_THUNK_ARM32_THUMB_H + +#include "dyncall_thunk_arm32_arm.h" /* Uses same code as ARM mode. */ + +#define DCTHUNK_ARM32_THUMB_SIZE 12 + + +#endif /* DYNCALL_THUNK_ARM32_THUMB_H */ + diff --git a/src/dyncall/include/dyncall_thunk_arm64.h b/src/dyncall/include/dyncall_thunk_arm64.h new file mode 100644 index 000000000..baa5127cf --- /dev/null +++ b/src/dyncall/include/dyncall_thunk_arm64.h @@ -0,0 +1,42 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_thunk_arm64.h + Description: Thunk - Header for ARM64 / ARMv8 / AAPCS64 + License: + + Copyright (c) 2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +#ifndef DYNCALL_THUNK_ARM64_H +#define DYNCALL_THUNK_ARM64_H + +struct DCThunk_ +{ + // off size + //-----|---------- + unsigned int code[4]; // 0 16 + void (*entry)(); // 16 8 + void* reserved; // 24 8 + + // 32 total size + +}; + +#endif /* DYNCALL_THUNK_ARM64_H */ + diff --git a/src/dyncall/include/dyncall_thunk_mips.h b/src/dyncall/include/dyncall_thunk_mips.h new file mode 100644 index 000000000..b6acffd53 --- /dev/null +++ b/src/dyncall/include/dyncall_thunk_mips.h @@ -0,0 +1,38 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_thunk_mips.h + Description: Thunk - Header for MIPS + License: + + Copyright (c) 2013-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALL_THUNK_MIPS_H +#define DYNCALL_THUNK_MIPS_H + +struct DCThunk_ +{ + unsigned short data[6]; + unsigned int jump; + unsigned short bddt[2]; +}; + +#endif /* DYNCALL_THUNK_MIPS_H */ + diff --git a/src/dyncall/include/dyncall_thunk_ppc32.h b/src/dyncall/include/dyncall_thunk_ppc32.h new file mode 100644 index 000000000..42bce8091 --- /dev/null +++ b/src/dyncall/include/dyncall_thunk_ppc32.h @@ -0,0 +1,40 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_thunk_ppc32.h + Description: Thunk - Header for ppc32 (darwin/sysv) + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +#ifndef DYNCALL_THUNK_PPC32_H +#define DYNCALL_THUNK_PPC32_H + +struct DCThunk_ +{ + unsigned short code_load_hi, addr_self_hi; /* offset: 0 size: 4 */ + unsigned short code_load_lo, addr_self_lo; /* offset: 4 size: 4 */ + unsigned int code_jump[3]; /* offset: 8 size: 12 */ + void (*addr_entry)(); /* offset: 20 size: 4 */ +}; /* total size: 24 */ + +#define DCTHUNK_SIZE_PPC32 24 + +#endif /* DYNCALL_THUNK_PPC32_H */ + diff --git a/src/dyncall/include/dyncall_thunk_ppc64.h b/src/dyncall/include/dyncall_thunk_ppc64.h new file mode 100644 index 000000000..2a7e88ea4 --- /dev/null +++ b/src/dyncall/include/dyncall_thunk_ppc64.h @@ -0,0 +1,55 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_thunk_ppc64.h + Description: Thunk - Header for ppc64 + License: + + Copyright (c) 2014-2015 Masanori Mitsugi + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +#ifndef DYNCALL_THUNK_PPC64_H +#define DYNCALL_THUNK_PPC64_H + +#if DC__ABI_PPC64_ELF_V != 2 +struct DCThunk_ /* v1 */ +{ + void (*thunk_entry)(); /* offset: 0 */ + long toc_thunk; /* offset: 8 */ + unsigned short code_load_hi, addr_self_hi; /* offset: 16 */ + unsigned short code_load_lo, addr_self_lo; /* offset: 20 */ + unsigned int code_jump[6]; /* offset: 24 */ + void (*addr_entry)(); /* offset: 48 */ + long toc_entry; /* offset: 56 */ +}; +#define DCTHUNK_SIZE_PPC64 64 +#else +struct DCThunk_ /* v2 */ +{ + unsigned short addr_self_hist, code_load_hist; /* offset: 0 */ + unsigned short addr_self_hier, code_load_hier; /* offset: 4 */ + unsigned int code_rot; /* offset: 8 */ + unsigned short addr_self_hi, code_load_hi; /* offset: 12 */ + unsigned short addr_self_lo, code_load_lo; /* offset: 16 */ + unsigned int code_jump[5]; /* offset: 20 */ + void (*addr_entry)(); /* offset: 40 */ +}; +#define DCTHUNK_SIZE_PPC64 48 +#endif + +#endif /* DYNCALL_THUNK_PPC64_H */ + diff --git a/src/dyncall/include/dyncall_thunk_sparc32.h b/src/dyncall/include/dyncall_thunk_sparc32.h new file mode 100644 index 000000000..456e13e3d --- /dev/null +++ b/src/dyncall/include/dyncall_thunk_sparc32.h @@ -0,0 +1,37 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_thunk_sparc32.h + Description: Thunk - Header for sparc32 - not yet implemented + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALL_THUNK_SPARC32_H +#define DYNCALL_THUNK_SPARC32_H + +struct DCThunk_ +{ + int x[4]; /* dummy */ +}; + +#define DCTHUNK_SIZE_SPARC32 32 + +#endif /* DYNCALL_THUNK_SPARC32_H */ diff --git a/src/dyncall/include/dyncall_thunk_sparc64.h b/src/dyncall/include/dyncall_thunk_sparc64.h new file mode 100644 index 000000000..c8eb90d13 --- /dev/null +++ b/src/dyncall/include/dyncall_thunk_sparc64.h @@ -0,0 +1,37 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_thunk_sparc64.h + Description: Thunk - Header for sparc64 - not yet implemented + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALL_THUNK_SPARC64_H +#define DYNCALL_THUNK_SPARC64_H + +struct DCThunk_ +{ + int x[4]; /* dummy */ +}; + +#define DCTHUNK_SIZE_SPARC64 32 + +#endif /* DYNCALL_THUNK_SPARC32_H */ diff --git a/src/dyncall/include/dyncall_thunk_x64.h b/src/dyncall/include/dyncall_thunk_x64.h new file mode 100644 index 000000000..5e60d4f87 --- /dev/null +++ b/src/dyncall/include/dyncall_thunk_x64.h @@ -0,0 +1,40 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_thunk_x64.h + Description: Thunk - Header for x64 + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALL_THUNK_X64_H +#define DYNCALL_THUNK_X64_H + +struct DCThunk_ +{ + unsigned long long code[2]; + void (*entry)(); +}; + +#define DCTHUNK_X64_SIZE 24 + + +#endif /* DYNCALL_THUNK_X64_H */ + diff --git a/src/dyncall/include/dyncall_thunk_x86.h b/src/dyncall/include/dyncall_thunk_x86.h new file mode 100644 index 000000000..29b7ca81d --- /dev/null +++ b/src/dyncall/include/dyncall_thunk_x86.h @@ -0,0 +1,40 @@ +/* + + Package: dyncall + Library: dyncallback + File: dyncallback/dyncall_thunk_x86.h + Description: Thunk - Header for x86 + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +#ifndef DYNCALL_THUNK_X86_H +#define DYNCALL_THUNK_X86_H + +struct DCThunk_ +{ + unsigned int code_load; + void* addr_self; + unsigned int code_jump; + void (*addr_entry)(); +}; + +#define DCTHUNK_X86_SIZE 16 + +#endif /* DYNCALL_THUNK_X86_H */ diff --git a/src/dyncall/include/dyncall_types.h b/src/dyncall/include/dyncall_types.h new file mode 100644 index 000000000..1831eb744 --- /dev/null +++ b/src/dyncall/include/dyncall_types.h @@ -0,0 +1,75 @@ +/* + + Package: dyncall + Library: dyncall + File: dyncall/dyncall_types.h + Description: Typedefs + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + + +/* + + dyncall argument- and return-types + + REVISION + 2007/12/11 initial + +*/ + +#ifndef DYNCALL_TYPES_H +#define DYNCALL_TYPES_H + +#include + +#include "dyncall_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void DCvoid; +typedef DC_BOOL DCbool; +typedef char DCchar; +typedef unsigned char DCuchar; +typedef short DCshort; +typedef unsigned short DCushort; +typedef int DCint; +typedef unsigned int DCuint; +typedef long DClong; +typedef unsigned long DCulong; +typedef DC_LONG_LONG DClonglong; +typedef unsigned DC_LONG_LONG DCulonglong; +typedef float DCfloat; +typedef double DCdouble; +typedef DC_POINTER DCpointer; +typedef const char* DCstring; + +typedef size_t DCsize; + +#define DC_TRUE 1 +#define DC_FALSE 0 + +#ifdef __cplusplus +} +#endif + +#endif /* DYNCALL_TYPES_H */ + diff --git a/src/dyncall/include/dyncall_value.h b/src/dyncall/include/dyncall_value.h new file mode 100644 index 000000000..69068f0a2 --- /dev/null +++ b/src/dyncall/include/dyncall_value.h @@ -0,0 +1,91 @@ +/* + + Package: dyncall + Library: dyncall + File: dyncall/dyncall_value.h + Description: Value variant type + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + +/* + + dyncall value variant + + a value variant union-type that carries all supported dyncall types. + + REVISION + 2007/12/11 initial + +*/ + +#ifndef DYNCALL_VALUE_H +#define DYNCALL_VALUE_H + +#include "dyncall_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef union DCValue_ DCValue; + +union DCValue_ +{ +#if defined (DC__Arch_PPC32) && defined(DC__Endian_BIG) + DCbool B; + struct { DCchar c_pad[3]; DCchar c; }; + struct { DCuchar C_pad[3]; DCuchar C; }; + struct { DCshort s_pad; DCshort s; }; + struct { DCshort S_pad; DCshort S; }; + DCint i; + DCuint I; +#elif defined (DC__Arch_PPC64) && defined(DC__Endian_BIG) + struct { DCbool B_pad; DCbool B; }; + struct { DCchar c_pad[7]; DCchar c; }; + struct { DCuchar C_pad[7]; DCuchar C; }; + struct { DCshort s_pad[3]; DCshort s; }; + struct { DCshort S_pad[3]; DCshort S; }; + struct { DCint i_pad; DCint i; }; + struct { DCint I_pad; DCuint I; }; +#else + DCbool B; + DCchar c; + DCuchar C; + DCshort s; + DCushort S; + DCint i; + DCuint I; +#endif + DClong j; + DCulong J; + DClonglong l; + DCulonglong L; + DCfloat f; + DCdouble d; + DCpointer p; + DCstring Z; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* DYNCALL_VALUE_H */ + diff --git a/src/dyncall/include/dynload.h b/src/dyncall/include/dynload.h new file mode 100644 index 000000000..df9fe4141 --- /dev/null +++ b/src/dyncall/include/dynload.h @@ -0,0 +1,66 @@ +/* + + Package: dyncall + Library: dynload + File: dynload/dynload.h + Description: public header for library dynload + License: + + Copyright (c) 2007-2015 Daniel Adler , + Tassilo Philipp + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + + + +#ifndef DYNLOAD_H +#define DYNLOAD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef DL_API +#define DL_API +#endif + +/* --- public api ---------------------------------------------------------- */ + +/* shared library loading and explicit symbol resolving */ + +typedef struct DLLib_ DLLib; + +DL_API DLLib* dlLoadLibrary(const char* libpath); +DL_API void dlFreeLibrary(DLLib* pLib); +DL_API void* dlFindSymbol(DLLib* pLib, const char* pSymbolName); + +/* symbol table enumeration - only for symbol lookup, not resolve */ + +typedef struct DLSyms_ DLSyms; + +DL_API DLSyms* dlSymsInit (const char* libPath); +DL_API void dlSymsCleanup(DLSyms* pSyms); + +DL_API int dlSymsCount (DLSyms* pSyms); +DL_API const char* dlSymsName (DLSyms* pSyms, int index); +DL_API const char* dlSymsNameFromValue(DLSyms* pSyms, void* value); /* symbol must be loaded */ + + +#ifdef __cplusplus +} +#endif + +#endif /* DYNLOAD_H */ + diff --git a/src/dyncall/lib/libdyncall_s.lib b/src/dyncall/lib/libdyncall_s.lib new file mode 100644 index 000000000..e36ce7c23 Binary files /dev/null and b/src/dyncall/lib/libdyncall_s.lib differ diff --git a/src/dyncall/lib/libdyncallback_s.lib b/src/dyncall/lib/libdyncallback_s.lib new file mode 100644 index 000000000..dae008de5 Binary files /dev/null and b/src/dyncall/lib/libdyncallback_s.lib differ diff --git a/src/dyncall/lib/libdynload_s.lib b/src/dyncall/lib/libdynload_s.lib new file mode 100644 index 000000000..80fc28d06 Binary files /dev/null and b/src/dyncall/lib/libdynload_s.lib differ diff --git a/src/main.cpp b/src/main.cpp index ae0437f93..902c3db2c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,9 +7,9 @@ #include "parser.cpp" // #include "printer.cpp" #include "checker/checker.cpp" -#include "ssa/ssa.cpp" -#include "llvm/ssa_to_text.cpp" -#include "vm/vm.cpp" +#include "ssa.cpp" +#include "ssa_to_llvm.cpp" +#include "vm.cpp" // NOTE(bill): `name` is used in debugging and profiling modes i32 win32_exec_command_line_app(char *name, char *fmt, ...) { @@ -172,9 +172,8 @@ int main(int argc, char **argv) { vm_init(&vm, &ssa.module); defer (vm_destroy(&vm)); - ssaProcedure *start_proc = vm_lookup_procedure(&vm, make_string("main")); Array args = {}; // Empty - vm_call_procedure(&vm, start_proc, args); + vm_call_proc_by_name(&vm, make_string("main"), args); } #endif diff --git a/src/ssa.cpp b/src/ssa.cpp new file mode 100644 index 000000000..884eef685 --- /dev/null +++ b/src/ssa.cpp @@ -0,0 +1,5340 @@ +struct ssaProcedure; +struct ssaBlock; +struct ssaValue; +struct ssaDebugInfo; + +struct ssaModule { + CheckerInfo * info; + BaseTypeSizes sizes; + gbArena arena; + gbArena tmp_arena; + gbAllocator allocator; + gbAllocator tmp_allocator; + b32 generate_debug_info; + + u32 stmt_state_flags; + + // String source_filename; + String layout; + // String triple; + + + Map min_dep_map; // Key: Entity * + Map values; // Key: Entity * + Map members; // Key: String + Map type_names; // Key: Type * + Map debug_info; // Key: Unique pointer + i32 global_string_index; + i32 global_array_index; // For ConstantSlice + + Array procs; // NOTE(bill): Procedures to generate +}; + +// NOTE(bill): For more info, see https://en.wikipedia.org/wiki/Dominator_(graph_theory) +struct ssaDomNode { + ssaBlock * idom; // Parent (Immediate Dominator) + Array children; + i32 pre, post; // Ordering in tree +}; + + +struct ssaBlock { + i32 index; + String label; + ssaProcedure *parent; + AstNode * node; // Can be NULL + Scope * scope; + isize scope_index; + ssaDomNode dom; + i32 gaps; + + Array instrs; + Array locals; + + Array preds; + Array succs; +}; + +struct ssaTargetList { + ssaTargetList *prev; + ssaBlock * break_; + ssaBlock * continue_; + ssaBlock * fallthrough_; +}; + +enum ssaDeferExitKind { + ssaDeferExit_Default, + ssaDeferExit_Return, + ssaDeferExit_Branch, +}; +enum ssaDeferKind { + ssaDefer_Node, + ssaDefer_Instr, +}; + +struct ssaDefer { + ssaDeferKind kind; + isize scope_index; + ssaBlock * block; + union { + AstNode *stmt; + // NOTE(bill): `instr` will be copied every time to create a new one + ssaValue *instr; + }; +}; + +struct ssaProcedure { + ssaProcedure * parent; + Array children; + + Entity * entity; + ssaModule * module; + String name; + Type * type; + AstNode * type_expr; + AstNode * body; + u64 tags; + + Array params; + Array defer_stmts; + Array blocks; + i32 scope_index; + ssaBlock * decl_block; + ssaBlock * entry_block; + ssaBlock * curr_block; + ssaTargetList * target_list; + Array referrers; + + i32 local_count; + i32 instr_count; + i32 block_count; +}; + +#define SSA_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" +#define SSA_TYPE_INFO_DATA_NAME "__$type_info_data" +#define SSA_TYPE_INFO_DATA_MEMBER_NAME "__$type_info_data_member" + + +#define SSA_INSTR_KINDS \ + SSA_INSTR_KIND(Invalid), \ + SSA_INSTR_KIND(Comment), \ + SSA_INSTR_KIND(Local), \ + SSA_INSTR_KIND(ZeroInit), \ + SSA_INSTR_KIND(Store), \ + SSA_INSTR_KIND(Load), \ + SSA_INSTR_KIND(PtrOffset), \ + SSA_INSTR_KIND(ArrayElementPtr), \ + SSA_INSTR_KIND(StructElementPtr), \ + SSA_INSTR_KIND(ArrayExtractValue), \ + SSA_INSTR_KIND(StructExtractValue), \ + SSA_INSTR_KIND(Conv), \ + SSA_INSTR_KIND(Jump), \ + SSA_INSTR_KIND(If), \ + SSA_INSTR_KIND(Return), \ + SSA_INSTR_KIND(Select), \ + SSA_INSTR_KIND(Phi), \ + SSA_INSTR_KIND(Unreachable), \ + SSA_INSTR_KIND(BinaryOp), \ + SSA_INSTR_KIND(Call), \ + SSA_INSTR_KIND(VectorExtractElement), \ + SSA_INSTR_KIND(VectorInsertElement), \ + SSA_INSTR_KIND(VectorShuffle), \ + SSA_INSTR_KIND(StartupRuntime), \ + SSA_INSTR_KIND(BoundsCheck), \ + SSA_INSTR_KIND(SliceBoundsCheck), \ + +#define SSA_CONV_KINDS \ + SSA_CONV_KIND(Invalid), \ + SSA_CONV_KIND(trunc), \ + SSA_CONV_KIND(zext), \ + SSA_CONV_KIND(fptrunc), \ + SSA_CONV_KIND(fpext), \ + SSA_CONV_KIND(fptoui), \ + SSA_CONV_KIND(fptosi), \ + SSA_CONV_KIND(uitofp), \ + SSA_CONV_KIND(sitofp), \ + SSA_CONV_KIND(ptrtoint), \ + SSA_CONV_KIND(inttoptr), \ + SSA_CONV_KIND(bitcast), + +enum ssaInstrKind { +#define SSA_INSTR_KIND(x) GB_JOIN2(ssaInstr_, x) + SSA_INSTR_KINDS +#undef SSA_INSTR_KIND +}; + +String const ssa_instr_strings[] = { +#define SSA_INSTR_KIND(x) {cast(u8 *)#x, gb_size_of(#x)-1} + SSA_INSTR_KINDS +#undef SSA_INSTR_KIND +}; + +enum ssaConvKind { +#define SSA_CONV_KIND(x) GB_JOIN2(ssaConv_, x) + SSA_CONV_KINDS +#undef SSA_CONV_KIND +}; + +String const ssa_conv_strings[] = { +#define SSA_CONV_KIND(x) {cast(u8 *)#x, gb_size_of(#x)-1} + SSA_CONV_KINDS +#undef SSA_CONV_KIND +}; + +struct ssaInstr { + ssaInstrKind kind; + + ssaBlock *parent; + Type *type; + + union { + struct { + String text; + } Comment; + struct { + Entity * entity; + Type * type; + b32 zero_initialized; + Array referrers; + } Local; + struct { + ssaValue *address; + } ZeroInit; + struct { + ssaValue *address; + ssaValue *value; + } Store; + struct { + Type *type; + ssaValue *address; + } Load; + struct { + ssaValue *address; + Type * result_type; + ssaValue *elem_index; + } ArrayElementPtr; + struct { + ssaValue *address; + Type * result_type; + i32 elem_index; + } StructElementPtr; + struct { + ssaValue *address; + ssaValue *offset; + } PtrOffset; + struct { + ssaValue *address; + Type * result_type; + i32 index; + } ArrayExtractValue; + struct { + ssaValue *address; + Type * result_type; + i32 index; + } StructExtractValue; + struct { + ssaValue *value; + ssaValue *elem; + i32 index; + } InsertValue; + struct { + ssaConvKind kind; + ssaValue *value; + Type *from, *to; + } Conv; + struct { + ssaBlock *block; + } Jump; + struct { + ssaValue *cond; + ssaBlock *true_block; + ssaBlock *false_block; + } If; + struct { + ssaValue *value; + } Return; + struct {} Unreachable; + struct { + ssaValue *cond; + ssaValue *true_value; + ssaValue *false_value; + } Select; + struct { + Array edges; + Type *type; + } Phi; + struct { + Type *type; + TokenKind op; + ssaValue *left, *right; + } BinaryOp; + struct { + Type *type; // return type + ssaValue *value; + ssaValue **args; + isize arg_count; + } Call; + struct { + ssaValue *vector; + ssaValue *index; + } VectorExtractElement; + struct { + ssaValue *vector; + ssaValue *elem; + ssaValue *index; + } VectorInsertElement; + struct { + ssaValue *vector; + i32 *indices; + i32 index_count; + Type *type; + } VectorShuffle; + + struct {} StartupRuntime; + struct { + TokenPos pos; + ssaValue *index; + ssaValue *len; + } BoundsCheck; + struct { + TokenPos pos; + ssaValue *low; + ssaValue *high; + ssaValue *max; + b32 is_substring; + } SliceBoundsCheck; + }; +}; + + +enum ssaValueKind { + ssaValue_Invalid, + + ssaValue_Constant, + ssaValue_ConstantSlice, + ssaValue_Nil, + ssaValue_TypeName, + ssaValue_Global, + ssaValue_Param, + + ssaValue_Proc, + ssaValue_Block, + ssaValue_Instr, + + ssaValue_Count, +}; + +struct ssaValue { + ssaValueKind kind; + i32 index; + union { + struct { + Type * type; + ExactValue value; + } Constant; + struct { + Type * type; + ssaValue *backing_array; + i64 count; + } ConstantSlice; + struct { + Type *type; + } Nil; + struct { + Type * type; + String name; + } TypeName; + struct { + Entity * entity; + Type * type; + ssaValue * value; + Array referrers; + b8 is_constant; + b8 is_private; + b8 is_thread_local; + b8 is_unnamed_addr; + } Global; + struct { + ssaProcedure * parent; + Entity * entity; + Type * type; + Array referrers; + } Param; + ssaProcedure Proc; + ssaBlock Block; + ssaInstr Instr; + }; +}; + +gb_global ssaValue *v_zero = NULL; +gb_global ssaValue *v_one = NULL; +gb_global ssaValue *v_zero32 = NULL; +gb_global ssaValue *v_one32 = NULL; +gb_global ssaValue *v_two32 = NULL; +gb_global ssaValue *v_false = NULL; +gb_global ssaValue *v_true = NULL; + +enum ssaAddrKind { + ssaAddr_Default, + ssaAddr_Vector, +}; + +struct ssaAddr { + ssaValue * addr; + AstNode * expr; // NOTE(bill): Just for testing - probably remove later + ssaAddrKind kind; + union { + struct { ssaValue *index; } Vector; + }; +}; + +ssaAddr ssa_make_addr(ssaValue *addr, AstNode *expr) { + ssaAddr v = {addr, expr}; + return v; +} +ssaAddr ssa_make_addr_vector(ssaValue *addr, ssaValue *index, AstNode *expr) { + ssaAddr v = ssa_make_addr(addr, expr); + v.kind = ssaAddr_Vector; + v.Vector.index = index; + return v; +} + + + +enum ssaDebugEncoding { + ssaDebugBasicEncoding_Invalid = 0, + + ssaDebugBasicEncoding_address = 1, + ssaDebugBasicEncoding_boolean = 2, + ssaDebugBasicEncoding_float = 3, + ssaDebugBasicEncoding_signed = 4, + ssaDebugBasicEncoding_signed_char = 5, + ssaDebugBasicEncoding_unsigned = 6, + ssaDebugBasicEncoding_unsigned_char = 7, + + ssaDebugBasicEncoding_member = 13, + ssaDebugBasicEncoding_pointer_type = 15, + ssaDebugBasicEncoding_typedef = 22, + + ssaDebugBasicEncoding_array_type = 1, + ssaDebugBasicEncoding_enumeration_type = 4, + ssaDebugBasicEncoding_structure_type = 19, + ssaDebugBasicEncoding_union_type = 23, + +}; + +enum ssaDebugInfoKind { + ssaDebugInfo_Invalid, + + ssaDebugInfo_CompileUnit, + ssaDebugInfo_File, + ssaDebugInfo_Scope, + ssaDebugInfo_Proc, + ssaDebugInfo_AllProcs, + + ssaDebugInfo_BasicType, // basic types + ssaDebugInfo_ProcType, + ssaDebugInfo_DerivedType, // pointer, typedef + ssaDebugInfo_CompositeType, // array, struct, enum, (raw_)union + ssaDebugInfo_Enumerator, // For ssaDebugInfo_CompositeType if enum + ssaDebugInfo_GlobalVariable, + ssaDebugInfo_LocalVariable, + + + ssaDebugInfo_Count, +}; + +struct ssaDebugInfo { + ssaDebugInfoKind kind; + i32 id; + + union { + struct { + AstFile * file; + String producer; + ssaDebugInfo *all_procs; + } CompileUnit; + struct { + AstFile *file; + String filename; + String directory; + } File; + struct { + ssaDebugInfo *parent; + ssaDebugInfo *file; + TokenPos pos; + Scope * scope; // Actual scope + } Scope; + struct { + Entity * entity; + String name; + ssaDebugInfo *file; + TokenPos pos; + } Proc; + struct { + Array procs; + } AllProcs; + + + struct { + String name; + i32 size; + i32 align; + ssaDebugEncoding encoding; + } BasicType; + struct { + ssaDebugInfo * return_type; + Array param_types; + } ProcType; + struct { + ssaDebugInfo * base_type; + ssaDebugEncoding encoding; + } DerivedType; + struct { + ssaDebugEncoding encoding; + String name; + String identifier; + ssaDebugInfo * file; + TokenPos pos; + i32 size; + i32 align; + Array elements; + } CompositeType; + struct { + String name; + i64 value; + } Enumerator; + struct { + String name; + String linkage_name; + ssaDebugInfo *scope; + ssaDebugInfo *file; + TokenPos pos; + ssaValue *variable; + ssaDebugInfo *declaration; + } GlobalVariable; + struct { + String name; + ssaDebugInfo *scope; + ssaDebugInfo *file; + TokenPos pos; + i32 arg; // Non-zero if proc parameter + ssaDebugInfo *type; + } LocalVariable; + }; +}; + + + +struct ssaFileBuffer { + gbVirtualMemory vm; + isize offset; + gbFile *output; +}; + +void ssa_file_buffer_init(ssaFileBuffer *f, gbFile *output) { + isize size = 8*gb_virtual_memory_page_size(NULL); + f->vm = gb_vm_alloc(NULL, size); + f->offset = 0; + f->output = output; +} + +void ssa_file_buffer_destroy(ssaFileBuffer *f) { + if (f->offset > 0) { + // NOTE(bill): finish writing buffered data + gb_file_write(f->output, f->vm.data, f->offset); + } + + gb_vm_free(f->vm); +} + +void ssa_file_buffer_write(ssaFileBuffer *f, void *data, isize len) { + if (len > f->vm.size) { + gb_file_write(f->output, data, len); + return; + } + + if ((f->vm.size - f->offset) < len) { + gb_file_write(f->output, f->vm.data, f->offset); + f->offset = 0; + } + u8 *cursor = cast(u8 *)f->vm.data + f->offset; + gb_memmove(cursor, data, len); + f->offset += len; +} + + +void ssa_fprintf(ssaFileBuffer *f, char *fmt, ...) { + va_list va; + va_start(va, fmt); + char buf[4096] = {}; + isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va); + ssa_file_buffer_write(f, buf, len-1); + va_end(va); +} + + +void ssa_file_write(ssaFileBuffer *f, void *data, isize len) { + ssa_file_buffer_write(f, data, len); +} + +ssaValue *ssa_lookup_member(ssaModule *m, String name) { + ssaValue **v = map_get(&m->members, hash_string(name)); + if (v != NULL) { + return *v; + } + return NULL; +} + + +Type *ssa_type(ssaValue *value); +Type *ssa_instr_type(ssaInstr *instr) { + switch (instr->kind) { + case ssaInstr_Local: + return instr->Local.type; + case ssaInstr_Load: + return instr->Load.type; + case ssaInstr_StructElementPtr: + return instr->StructElementPtr.result_type; + case ssaInstr_ArrayElementPtr: + return instr->ArrayElementPtr.result_type; + case ssaInstr_PtrOffset: + return ssa_type(instr->PtrOffset.address); + case ssaInstr_Phi: + return instr->Phi.type; + case ssaInstr_ArrayExtractValue: + return instr->ArrayExtractValue.result_type; + case ssaInstr_StructExtractValue: + return instr->StructExtractValue.result_type; + case ssaInstr_BinaryOp: + return instr->BinaryOp.type; + case ssaInstr_Conv: + return instr->Conv.to; + case ssaInstr_Select: + return ssa_type(instr->Select.true_value); + case ssaInstr_Call: { + Type *pt = base_type(instr->Call.type); + if (pt != NULL) { + if (pt->kind == Type_Tuple && pt->Tuple.variable_count == 1) { + return pt->Tuple.variables[0]->type; + } + return pt; + } + return NULL; + } break; + case ssaInstr_VectorExtractElement: { + Type *vt = ssa_type(instr->VectorExtractElement.vector); + Type *bt = base_vector_type(vt); + GB_ASSERT(!is_type_vector(bt)); + return bt; + } break; + case ssaInstr_VectorInsertElement: + return ssa_type(instr->VectorInsertElement.vector); + case ssaInstr_VectorShuffle: + return instr->VectorShuffle.type; + } + return NULL; +} + +Type *ssa_type(ssaValue *value) { + switch (value->kind) { + case ssaValue_Constant: + return value->Constant.type; + case ssaValue_ConstantSlice: + return value->ConstantSlice.type; + case ssaValue_Nil: + return value->Nil.type; + case ssaValue_TypeName: + return value->TypeName.type; + case ssaValue_Global: + return value->Global.type; + case ssaValue_Param: + return value->Param.type; + case ssaValue_Proc: + return value->Proc.type; + case ssaValue_Instr: + return ssa_instr_type(&value->Instr); + } + return NULL; +} + +Type *ssa_addr_type(ssaAddr lval) { + if (lval.addr != NULL) { + Type *t = ssa_type(lval.addr); + GB_ASSERT(is_type_pointer(t)); + return type_deref(t); + } + return NULL; +} + + + +b32 ssa_is_blank_ident(AstNode *node) { + if (node->kind == AstNode_Ident) { + ast_node(i, Ident, node); + return is_blank_ident(i->string); + } + return false; +} + + +ssaInstr *ssa_get_last_instr(ssaBlock *block) { + if (block != NULL) { + isize len = block->instrs.count; + if (len > 0) { + ssaValue *v = block->instrs[len-1]; + GB_ASSERT(v->kind == ssaValue_Instr); + return &v->Instr; + } + } + return NULL; + +} + +b32 ssa_is_instr_terminating(ssaInstr *i) { + if (i != NULL) { + switch (i->kind) { + case ssaInstr_Return: + case ssaInstr_Unreachable: + return true; + } + } + + return false; +} + + +void ssa_add_edge(ssaBlock *from, ssaBlock *to) { + array_add(&from->succs, to); + array_add(&to->preds, from); +} + +void ssa_set_instr_parent(ssaValue *instr, ssaBlock *parent) { + if (instr->kind == ssaValue_Instr) { + instr->Instr.parent = parent; + } +} + +Array *ssa_value_referrers(ssaValue *v) { + switch (v->kind) { + case ssaValue_Global: + return &v->Global.referrers; + case ssaValue_Param: + return &v->Param.referrers; + case ssaValue_Proc: { + if (v->Proc.parent != NULL) { + return &v->Proc.referrers; + } + return NULL; + } + case ssaValue_Instr: { + ssaInstr *i = &v->Instr; + switch (i->kind) { + case ssaInstr_Local: + return &i->Local.referrers; + } + } break; + } + + return NULL; +} + + + +//////////////////////////////////////////////////////////////// +// +// @Make +// +//////////////////////////////////////////////////////////////// + +void ssa_module_add_value (ssaModule *m, Entity *e, ssaValue *v); +ssaValue *ssa_emit_zero_init (ssaProcedure *p, ssaValue *address); +ssaValue *ssa_emit_comment (ssaProcedure *p, String text); +ssaValue *ssa_emit_store (ssaProcedure *p, ssaValue *address, ssaValue *value); +ssaValue *ssa_emit_load (ssaProcedure *p, ssaValue *address); +void ssa_emit_jump (ssaProcedure *proc, ssaBlock *block); +ssaValue *ssa_emit_conv (ssaProcedure *proc, ssaValue *value, Type *t); +ssaValue *ssa_type_info (ssaProcedure *proc, Type *type); +ssaValue *ssa_build_expr (ssaProcedure *proc, AstNode *expr); +void ssa_build_stmt (ssaProcedure *proc, AstNode *node); +void ssa_build_cond (ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block); +void ssa_build_defer_stmt (ssaProcedure *proc, ssaDefer d); +ssaAddr ssa_build_addr (ssaProcedure *proc, AstNode *expr); +void ssa_build_proc (ssaValue *value, ssaProcedure *parent); +void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name); + + + + +ssaValue *ssa_alloc_value(gbAllocator a, ssaValueKind kind) { + ssaValue *v = gb_alloc_item(a, ssaValue); + v->kind = kind; + return v; +} +ssaValue *ssa_alloc_instr(ssaProcedure *proc, ssaInstrKind kind) { + ssaValue *v = ssa_alloc_value(proc->module->allocator, ssaValue_Instr); + v->Instr.kind = kind; + proc->instr_count++; + return v; +} +ssaDebugInfo *ssa_alloc_debug_info(gbAllocator a, ssaDebugInfoKind kind) { + ssaDebugInfo *di = gb_alloc_item(a, ssaDebugInfo); + di->kind = kind; + return di; +} + + + + +ssaValue *ssa_make_value_type_name(gbAllocator a, String name, Type *type) { + ssaValue *v = ssa_alloc_value(a, ssaValue_TypeName); + v->TypeName.name = name; + v->TypeName.type = type; + return v; +} + +ssaValue *ssa_make_value_global(gbAllocator a, Entity *e, ssaValue *value) { + ssaValue *v = ssa_alloc_value(a, ssaValue_Global); + v->Global.entity = e; + v->Global.type = make_type_pointer(a, e->type); + v->Global.value = value; + array_init(&v->Global.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here + return v; +} +ssaValue *ssa_make_value_param(gbAllocator a, ssaProcedure *parent, Entity *e) { + ssaValue *v = ssa_alloc_value(a, ssaValue_Param); + v->Param.parent = parent; + v->Param.entity = e; + v->Param.type = e->type; + array_init(&v->Param.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here + return v; +} +ssaValue *ssa_make_value_nil(gbAllocator a, Type *type) { + ssaValue *v = ssa_alloc_value(a, ssaValue_Nil); + v->Nil.type = type; + return v; +} + + + +ssaValue *ssa_make_instr_local(ssaProcedure *p, Entity *e, b32 zero_initialized) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Local); + ssaInstr *i = &v->Instr; + i->Local.entity = e; + i->Local.type = make_type_pointer(p->module->allocator, e->type); + i->Local.zero_initialized = zero_initialized; + array_init(&i->Local.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here + ssa_module_add_value(p->module, e, v); + return v; +} + + +ssaValue *ssa_make_instr_store(ssaProcedure *p, ssaValue *address, ssaValue *value) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Store); + ssaInstr *i = &v->Instr; + i->Store.address = address; + i->Store.value = value; + return v; +} + +ssaValue *ssa_make_instr_zero_init(ssaProcedure *p, ssaValue *address) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_ZeroInit); + ssaInstr *i = &v->Instr; + i->ZeroInit.address = address; + return v; +} + +ssaValue *ssa_make_instr_load(ssaProcedure *p, ssaValue *address) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Load); + ssaInstr *i = &v->Instr; + i->Load.address = address; + i->Load.type = type_deref(ssa_type(address)); + return v; +} + +ssaValue *ssa_make_instr_array_element_ptr(ssaProcedure *p, ssaValue *address, ssaValue *elem_index) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_ArrayElementPtr); + ssaInstr *i = &v->Instr; + Type *t = ssa_type(address); + GB_ASSERT(is_type_pointer(t)); + t = base_type(type_deref(t)); + GB_ASSERT(is_type_array(t)); + + Type *result_type = make_type_pointer(p->module->allocator, t->Array.elem); + + i->ArrayElementPtr.address = address; + i->ArrayElementPtr.elem_index = elem_index; + i->ArrayElementPtr.result_type = result_type; + + GB_ASSERT_MSG(is_type_pointer(ssa_type(address)), + "%s", type_to_string(ssa_type(address))); + return v; +} +ssaValue *ssa_make_instr_struct_element_ptr(ssaProcedure *p, ssaValue *address, i32 elem_index, Type *result_type) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_StructElementPtr); + ssaInstr *i = &v->Instr; + i->StructElementPtr.address = address; + i->StructElementPtr.elem_index = elem_index; + i->StructElementPtr.result_type = result_type; + + GB_ASSERT_MSG(is_type_pointer(ssa_type(address)), + "%s", type_to_string(ssa_type(address))); + return v; +} +ssaValue *ssa_make_instr_ptr_offset(ssaProcedure *p, ssaValue *address, ssaValue *offset) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_PtrOffset); + ssaInstr *i = &v->Instr; + i->PtrOffset.address = address; + i->PtrOffset.offset = offset; + + GB_ASSERT_MSG(is_type_pointer(ssa_type(address)), + "%s", type_to_string(ssa_type(address))); + GB_ASSERT_MSG(is_type_integer(ssa_type(offset)), + "%s", type_to_string(ssa_type(address))); + + return v; +} + + + +ssaValue *ssa_make_instr_array_extract_value(ssaProcedure *p, ssaValue *address, i32 index) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_ArrayExtractValue); + ssaInstr *i = &v->Instr; + i->ArrayExtractValue.address = address; + i->ArrayExtractValue.index = index; + Type *t = base_type(ssa_type(address)); + GB_ASSERT(is_type_array(t)); + i->ArrayExtractValue.result_type = t->Array.elem; + return v; +} + +ssaValue *ssa_make_instr_struct_extract_value(ssaProcedure *p, ssaValue *address, i32 index, Type *result_type) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_StructExtractValue); + ssaInstr *i = &v->Instr; + i->StructExtractValue.address = address; + i->StructExtractValue.index = index; + i->StructExtractValue.result_type = result_type; + return v; +} + +ssaValue *ssa_make_instr_binary_op(ssaProcedure *p, TokenKind op, ssaValue *left, ssaValue *right, Type *type) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_BinaryOp); + ssaInstr *i = &v->Instr; + i->BinaryOp.op = op; + i->BinaryOp.left = left; + i->BinaryOp.right = right; + i->BinaryOp.type = type; + return v; +} + +ssaValue *ssa_make_instr_jump(ssaProcedure *p, ssaBlock *block) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Jump); + ssaInstr *i = &v->Instr; + i->Jump.block = block; + return v; +} +ssaValue *ssa_make_instr_if(ssaProcedure *p, ssaValue *cond, ssaBlock *true_block, ssaBlock *false_block) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_If); + ssaInstr *i = &v->Instr; + i->If.cond = cond; + i->If.true_block = true_block; + i->If.false_block = false_block; + return v; +} + + +ssaValue *ssa_make_instr_phi(ssaProcedure *p, Array edges, Type *type) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Phi); + ssaInstr *i = &v->Instr; + i->Phi.edges = edges; + i->Phi.type = type; + return v; +} + +ssaValue *ssa_make_instr_unreachable(ssaProcedure *p) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Unreachable); + return v; +} + +ssaValue *ssa_make_instr_return(ssaProcedure *p, ssaValue *value) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Return); + v->Instr.Return.value = value; + return v; +} + +ssaValue *ssa_make_instr_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue *f) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Select); + v->Instr.Select.cond = cond; + v->Instr.Select.true_value = t; + v->Instr.Select.false_value = f; + return v; +} + +ssaValue *ssa_make_instr_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count, Type *result_type) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Call); + v->Instr.Call.value = value; + v->Instr.Call.args = args; + v->Instr.Call.arg_count = arg_count; + v->Instr.Call.type = result_type; + return v; +} + +ssaValue *ssa_make_instr_conv(ssaProcedure *p, ssaConvKind kind, ssaValue *value, Type *from, Type *to) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Conv); + v->Instr.Conv.kind = kind; + v->Instr.Conv.value = value; + v->Instr.Conv.from = from; + v->Instr.Conv.to = to; + return v; +} + +ssaValue *ssa_make_instr_extract_element(ssaProcedure *p, ssaValue *vector, ssaValue *index) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorExtractElement); + v->Instr.VectorExtractElement.vector = vector; + v->Instr.VectorExtractElement.index = index; + return v; +} + +ssaValue *ssa_make_instr_insert_element(ssaProcedure *p, ssaValue *vector, ssaValue *elem, ssaValue *index) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorInsertElement); + v->Instr.VectorInsertElement.vector = vector; + v->Instr.VectorInsertElement.elem = elem; + v->Instr.VectorInsertElement.index = index; + return v; +} + +ssaValue *ssa_make_instr_vector_shuffle(ssaProcedure *p, ssaValue *vector, i32 *indices, isize index_count) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorShuffle); + v->Instr.VectorShuffle.vector = vector; + v->Instr.VectorShuffle.indices = indices; + v->Instr.VectorShuffle.index_count = index_count; + + Type *vt = base_type(ssa_type(vector)); + v->Instr.VectorShuffle.type = make_type_vector(p->module->allocator, vt->Vector.elem, index_count); + + return v; +} + +ssaValue *ssa_make_instr_comment(ssaProcedure *p, String text) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_Comment); + v->Instr.Comment.text = text; + return v; +} + +ssaValue *ssa_make_instr_bounds_check(ssaProcedure *p, TokenPos pos, ssaValue *index, ssaValue *len) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_BoundsCheck); + v->Instr.BoundsCheck.pos = pos; + v->Instr.BoundsCheck.index = index; + v->Instr.BoundsCheck.len = len; + return v; +} +ssaValue *ssa_make_instr_slice_bounds_check(ssaProcedure *p, TokenPos pos, ssaValue *low, ssaValue *high, ssaValue *max, b32 is_substring) { + ssaValue *v = ssa_alloc_instr(p, ssaInstr_SliceBoundsCheck); + v->Instr.SliceBoundsCheck.pos = pos; + v->Instr.SliceBoundsCheck.low = low; + v->Instr.SliceBoundsCheck.high = high; + v->Instr.SliceBoundsCheck.max = max; + v->Instr.SliceBoundsCheck.is_substring = is_substring; + return v; +} + + + +ssaValue *ssa_make_value_constant(gbAllocator a, Type *type, ExactValue value) { + ssaValue *v = ssa_alloc_value(a, ssaValue_Constant); + v->Constant.type = type; + v->Constant.value = value; + return v; +} + + +ssaValue *ssa_make_value_constant_slice(gbAllocator a, Type *type, ssaValue *backing_array, i64 count) { + ssaValue *v = ssa_alloc_value(a, ssaValue_ConstantSlice); + v->ConstantSlice.type = type; + v->ConstantSlice.backing_array = backing_array; + v->ConstantSlice.count = count; + return v; +} + +ssaValue *ssa_make_const_int(gbAllocator a, i64 i) { + return ssa_make_value_constant(a, t_int, make_exact_value_integer(i)); +} +ssaValue *ssa_make_const_i32(gbAllocator a, i64 i) { + return ssa_make_value_constant(a, t_i32, make_exact_value_integer(i)); +} +ssaValue *ssa_make_const_i64(gbAllocator a, i64 i) { + return ssa_make_value_constant(a, t_i64, make_exact_value_integer(i)); +} +ssaValue *ssa_make_const_bool(gbAllocator a, b32 b) { + return ssa_make_value_constant(a, t_bool, make_exact_value_bool(b != 0)); +} +ssaValue *ssa_make_const_string(gbAllocator a, String s) { + return ssa_make_value_constant(a, t_string, make_exact_value_string(s)); +} + +ssaValue *ssa_make_value_procedure(gbAllocator a, ssaModule *m, Entity *entity, Type *type, AstNode *type_expr, AstNode *body, String name) { + ssaValue *v = ssa_alloc_value(a, ssaValue_Proc); + v->Proc.module = m; + v->Proc.entity = entity; + v->Proc.type = type; + v->Proc.type_expr = type_expr; + v->Proc.body = body; + v->Proc.name = name; + array_init(&v->Proc.referrers, heap_allocator(), 0); // TODO(bill): replace heap allocator + + Type *t = base_type(type); + GB_ASSERT(is_type_proc(t)); + array_init(&v->Proc.params, heap_allocator(), t->Proc.param_count); + + return v; +} + +ssaBlock *ssa_add_block(ssaProcedure *proc, AstNode *node, char *label) { + Scope *scope = NULL; + if (node != NULL) { + Scope **found = map_get(&proc->module->info->scopes, hash_pointer(node)); + if (found) { + scope = *found; + } else { + GB_PANIC("Block scope not found for %.*s", LIT(ast_node_strings[node->kind])); + } + } + + ssaValue *v = ssa_alloc_value(proc->module->allocator, ssaValue_Block); + v->Block.label = make_string(label); + v->Block.node = node; + v->Block.scope = scope; + v->Block.parent = proc; + + array_init(&v->Block.instrs, heap_allocator()); + array_init(&v->Block.locals, heap_allocator()); + + array_init(&v->Block.preds, heap_allocator()); + array_init(&v->Block.succs, heap_allocator()); + + ssaBlock *block = &v->Block; + + array_add(&proc->blocks, block); + proc->block_count++; + + return block; +} + + + + + +ssaDefer ssa_add_defer_node(ssaProcedure *proc, isize scope_index, AstNode *stmt) { + ssaDefer d = {ssaDefer_Node}; + d.scope_index = scope_index; + d.block = proc->curr_block; + d.stmt = stmt; + array_add(&proc->defer_stmts, d); + return d; +} + + +ssaDefer ssa_add_defer_instr(ssaProcedure *proc, isize scope_index, ssaValue *instr) { + ssaDefer d = {ssaDefer_Instr}; + d.scope_index = proc->scope_index; + d.block = proc->curr_block; + d.instr = instr; // NOTE(bill): It will make a copy everytime it is called + array_add(&proc->defer_stmts, d); + return d; +} + + + +ssaValue *ssa_add_module_constant(ssaModule *m, Type *type, ExactValue value) { + if (is_type_slice(type)) { + ast_node(cl, CompoundLit, value.value_compound); + gbAllocator a = m->allocator; + + isize count = cl->elems.count; + if (count == 0) { + return ssa_make_value_nil(a, type); + } + Type *elem = base_type(type)->Slice.elem; + Type *t = make_type_array(a, elem, count); + ssaValue *backing_array = ssa_add_module_constant(m, t, value); + + + isize max_len = 7+8+1; + u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len); + isize len = gb_snprintf(cast(char *)str, max_len, "__csba$%x", m->global_array_index); + m->global_array_index++; + + String name = make_string(str, len-1); + + Entity *e = make_entity_constant(a, NULL, make_token_ident(name), t, value); + ssaValue *g = ssa_make_value_global(a, e, backing_array); + ssa_module_add_value(m, e, g); + map_set(&m->members, hash_string(name), g); + + return ssa_make_value_constant_slice(a, type, g, count); + } + + return ssa_make_value_constant(m->allocator, type, value); +} + +ssaValue *ssa_add_global_string_array(ssaModule *m, String string) { + gbAllocator a = m->allocator; + + isize max_len = 6+8+1; + u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len); + isize len = gb_snprintf(cast(char *)str, max_len, "__str$%x", m->global_string_index); + m->global_string_index++; + + String name = make_string(str, len-1); + Token token = {Token_String}; + token.string = name; + Type *type = make_type_array(a, t_u8, string.len); + ExactValue ev = make_exact_value_string(string); + Entity *entity = make_entity_constant(a, NULL, token, type, ev); + ssaValue *g = ssa_make_value_global(a, entity, ssa_add_module_constant(m, type, ev)); + g->Global.is_private = true; + // g->Global.is_unnamed_addr = true; + // g->Global.is_constant = true; + + ssa_module_add_value(m, entity, g); + map_set(&m->members, hash_string(name), g); + + return g; +} + + + + +ssaValue *ssa_add_local(ssaProcedure *proc, Entity *e, b32 zero_initialized = true) { + ssaBlock *b = proc->decl_block; // all variables must be in the first block + ssaValue *instr = ssa_make_instr_local(proc, e, zero_initialized); + instr->Instr.parent = b; + array_add(&b->instrs, instr); + array_add(&b->locals, instr); + proc->local_count++; + + // if (zero_initialized) { + ssa_emit_zero_init(proc, instr); + // } + + return instr; +} + +ssaValue *ssa_add_local_for_identifier(ssaProcedure *proc, AstNode *name, b32 zero_initialized) { + Entity **found = map_get(&proc->module->info->definitions, hash_pointer(name)); + if (found) { + Entity *e = *found; + ssa_emit_comment(proc, e->token.string); + return ssa_add_local(proc, e, zero_initialized); + } + return NULL; +} + +ssaValue *ssa_add_local_generated(ssaProcedure *proc, Type *type) { + GB_ASSERT(type != NULL); + + Scope *scope = NULL; + if (proc->curr_block) { + scope = proc->curr_block->scope; + } + Entity *e = make_entity_variable(proc->module->allocator, + scope, + empty_token, + type); + return ssa_add_local(proc, e, true); +} + +ssaValue *ssa_add_param(ssaProcedure *proc, Entity *e) { + ssaValue *v = ssa_make_value_param(proc->module->allocator, proc, e); +#if 1 + ssaValue *l = ssa_add_local(proc, e); + ssa_emit_store(proc, l, v); +#else + ssa_module_add_value(proc->module, e, v); +#endif + return v; +} + + + +//////////////////////////////////////////////////////////////// +// +// @Debug +// +//////////////////////////////////////////////////////////////// + +ssaDebugInfo *ssa_add_debug_info_file(ssaProcedure *proc, AstFile *file) { + if (!proc->module->generate_debug_info) { + return NULL; + } + + GB_ASSERT(file != NULL); + ssaDebugInfo *di = ssa_alloc_debug_info(proc->module->allocator, ssaDebugInfo_File); + di->File.file = file; + + String filename = file->tokenizer.fullpath; + String directory = filename; + isize slash_index = 0; + for (isize i = filename.len-1; i >= 0; i--) { + if (filename.text[i] == '\\' || + filename.text[i] == '/') { + break; + } + slash_index = i; + } + directory.len = slash_index-1; + filename.text = filename.text + slash_index; + filename.len -= slash_index; + + + di->File.filename = filename; + di->File.directory = directory; + + map_set(&proc->module->debug_info, hash_pointer(file), di); + return di; +} + + +ssaDebugInfo *ssa_add_debug_info_proc(ssaProcedure *proc, Entity *entity, String name, ssaDebugInfo *file) { + if (!proc->module->generate_debug_info) { + return NULL; + } + + GB_ASSERT(entity != NULL); + ssaDebugInfo *di = ssa_alloc_debug_info(proc->module->allocator, ssaDebugInfo_Proc); + di->Proc.entity = entity; + di->Proc.name = name; + di->Proc.file = file; + di->Proc.pos = entity->token.pos; + + map_set(&proc->module->debug_info, hash_pointer(entity), di); + return di; +} + +//////////////////////////////////////////////////////////////// +// +// @Emit +// +//////////////////////////////////////////////////////////////// + + +ssaValue *ssa_emit(ssaProcedure *proc, ssaValue *instr) { + GB_ASSERT(instr->kind == ssaValue_Instr); + ssaBlock *b = proc->curr_block; + instr->Instr.parent = b; + if (b != NULL) { + ssaInstr *i = ssa_get_last_instr(b); + if (!ssa_is_instr_terminating(i)) { + array_add(&b->instrs, instr); + } + } + return instr; +} +ssaValue *ssa_emit_store(ssaProcedure *p, ssaValue *address, ssaValue *value) { + return ssa_emit(p, ssa_make_instr_store(p, address, value)); +} +ssaValue *ssa_emit_load(ssaProcedure *p, ssaValue *address) { + return ssa_emit(p, ssa_make_instr_load(p, address)); +} +ssaValue *ssa_emit_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue *f) { + return ssa_emit(p, ssa_make_instr_select(p, cond, t, f)); +} + +ssaValue *ssa_emit_zero_init(ssaProcedure *p, ssaValue *address) { + return ssa_emit(p, ssa_make_instr_zero_init(p, address)); +} + +ssaValue *ssa_emit_comment(ssaProcedure *p, String text) { + return ssa_emit(p, ssa_make_instr_comment(p, text)); +} + + +ssaValue *ssa_emit_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count) { + Type *pt = base_type(ssa_type(value)); + GB_ASSERT(pt->kind == Type_Proc); + Type *results = pt->Proc.results; + return ssa_emit(p, ssa_make_instr_call(p, value, args, arg_count, results)); +} + +ssaValue *ssa_emit_global_call(ssaProcedure *proc, char *name_, ssaValue **args, isize arg_count) { + String name = make_string(name_); + ssaValue **found = map_get(&proc->module->members, hash_string(name)); + GB_ASSERT_MSG(found != NULL, "%.*s", LIT(name)); + ssaValue *gp = *found; + return ssa_emit_call(proc, gp, args, arg_count); +} + + + +void ssa_emit_defer_stmts(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) { + isize count = proc->defer_stmts.count; + isize i = count; + while (i --> 0) { + ssaDefer d = proc->defer_stmts[i]; + if (kind == ssaDeferExit_Default) { + if (proc->scope_index == d.scope_index && + d.scope_index > 1) { + ssa_build_defer_stmt(proc, d); + array_pop(&proc->defer_stmts); + continue; + } else { + break; + } + } else if (kind == ssaDeferExit_Return) { + ssa_build_defer_stmt(proc, d); + } else if (kind == ssaDeferExit_Branch) { + GB_ASSERT(block != NULL); + isize lower_limit = block->scope_index+1; + if (lower_limit < d.scope_index) { + ssa_build_defer_stmt(proc, d); + } + } + } +} + + +void ssa_open_scope(ssaProcedure *proc) { + proc->scope_index++; +} + +void ssa_close_scope(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) { + ssa_emit_defer_stmts(proc, kind, block); + GB_ASSERT(proc->scope_index > 0); + proc->scope_index--; +} + + + +void ssa_emit_unreachable(ssaProcedure *proc) { + ssa_emit(proc, ssa_make_instr_unreachable(proc)); +} + +void ssa_emit_return(ssaProcedure *proc, ssaValue *v) { + ssa_emit_defer_stmts(proc, ssaDeferExit_Return, NULL); + ssa_emit(proc, ssa_make_instr_return(proc, v)); +} + +void ssa_emit_jump(ssaProcedure *proc, ssaBlock *target_block) { + ssaBlock *b = proc->curr_block; + if (b == NULL) { + return; + } + ssa_emit(proc, ssa_make_instr_jump(proc, target_block)); + ssa_add_edge(b, target_block); + proc->curr_block = NULL; +} + +void ssa_emit_if(ssaProcedure *proc, ssaValue *cond, ssaBlock *true_block, ssaBlock *false_block) { + ssaBlock *b = proc->curr_block; + if (b == NULL) { + return; + } + ssa_emit(proc, ssa_make_instr_if(proc, cond, true_block, false_block)); + ssa_add_edge(b, true_block); + ssa_add_edge(b, false_block); + proc->curr_block = NULL; +} + +void ssa_emit_startup_runtime(ssaProcedure *proc) { + GB_ASSERT(proc->parent == NULL && proc->name == "main"); + ssa_emit(proc, ssa_alloc_instr(proc, ssaInstr_StartupRuntime)); +} + + + + +ssaValue *ssa_addr_store(ssaProcedure *proc, ssaAddr addr, ssaValue *value) { + if (addr.addr == NULL) { + return NULL; + } + + if (addr.kind == ssaAddr_Vector) { + ssaValue *v = ssa_emit_load(proc, addr.addr); + Type *elem_type = base_type(ssa_type(v))->Vector.elem; + ssaValue *elem = ssa_emit_conv(proc, value, elem_type); + ssaValue *out = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, elem, addr.Vector.index)); + return ssa_emit_store(proc, addr.addr, out); + } else { + ssaValue *v = ssa_emit_conv(proc, value, ssa_addr_type(addr)); + return ssa_emit_store(proc, addr.addr, v); + } +} +ssaValue *ssa_addr_load(ssaProcedure *proc, ssaAddr addr) { + if (addr.addr == NULL) { + GB_PANIC("Illegal addr load"); + return NULL; + } + + if (addr.kind == ssaAddr_Vector) { + ssaValue *v = ssa_emit_load(proc, addr.addr); + return ssa_emit(proc, ssa_make_instr_extract_element(proc, v, addr.Vector.index)); + } + Type *t = base_type(ssa_type(addr.addr)); + if (t->kind == Type_Proc) { + // NOTE(bill): Imported procedures don't require a load as they are pointers + return addr.addr; + } + return ssa_emit_load(proc, addr.addr); +} + + + + +ssaValue *ssa_emit_ptr_offset(ssaProcedure *proc, ssaValue *ptr, ssaValue *offset) { + offset = ssa_emit_conv(proc, offset, t_int); + return ssa_emit(proc, ssa_make_instr_ptr_offset(proc, ptr, offset)); +} + +ssaValue *ssa_emit_arith(ssaProcedure *proc, TokenKind op, ssaValue *left, ssaValue *right, Type *type) { + Type *t_left = ssa_type(left); + Type *t_right = ssa_type(right); + + if (op == Token_Add) { + if (is_type_pointer(t_left)) { + ssaValue *ptr = ssa_emit_conv(proc, left, type); + ssaValue *offset = right; + return ssa_emit_ptr_offset(proc, ptr, offset); + } else if (is_type_pointer(ssa_type(right))) { + ssaValue *ptr = ssa_emit_conv(proc, right, type); + ssaValue *offset = left; + return ssa_emit_ptr_offset(proc, ptr, offset); + } + } else if (op == Token_Sub) { + if (is_type_pointer(t_left) && is_type_integer(t_right)) { + // ptr - int + ssaValue *ptr = ssa_emit_conv(proc, left, type); + ssaValue *offset = right; + return ssa_emit_ptr_offset(proc, ptr, offset); + } else if (is_type_pointer(t_left) && is_type_pointer(t_right)) { + GB_ASSERT(is_type_integer(type)); + Type *ptr_type = t_left; + ssaModule *m = proc->module; + ssaValue *x = ssa_emit_conv(proc, left, type); + ssaValue *y = ssa_emit_conv(proc, right, type); + ssaValue *diff = ssa_emit_arith(proc, op, x, y, type); + ssaValue *elem_size = ssa_make_const_int(m->allocator, type_size_of(m->sizes, m->allocator, ptr_type)); + return ssa_emit_arith(proc, Token_Quo, diff, elem_size, type); + } + } + + + switch (op) { + case Token_AndNot: { + // NOTE(bill): x &~ y == x & (~y) == x & (y ~ -1) + // NOTE(bill): "not" `x` == `x` "xor" `-1` + ssaValue *neg = ssa_add_module_constant(proc->module, type, make_exact_value_integer(-1)); + op = Token_Xor; + right = ssa_emit_arith(proc, op, right, neg, type); + GB_ASSERT(right->Instr.kind == ssaInstr_BinaryOp); + right->Instr.BinaryOp.type = type; + op = Token_And; + } /* fallthrough */ + case Token_Add: + case Token_Sub: + case Token_Mul: + case Token_Quo: + case Token_Mod: + case Token_And: + case Token_Or: + case Token_Xor: + left = ssa_emit_conv(proc, left, type); + right = ssa_emit_conv(proc, right, type); + break; + } + + return ssa_emit(proc, ssa_make_instr_binary_op(proc, op, left, right, type)); +} + +ssaValue *ssa_emit_comp(ssaProcedure *proc, TokenKind op_kind, ssaValue *left, ssaValue *right) { + Type *a = base_type(ssa_type(left)); + Type *b = base_type(ssa_type(right)); + + if (are_types_identical(a, b)) { + // NOTE(bill): No need for a conversion + } else if (left->kind == ssaValue_Constant || left->kind == ssaValue_Nil) { + left = ssa_emit_conv(proc, left, ssa_type(right)); + } else if (right->kind == ssaValue_Constant || right->kind == ssaValue_Nil) { + right = ssa_emit_conv(proc, right, ssa_type(left)); + } + + Type *result = t_bool; + if (is_type_vector(a)) { + result = make_type_vector(proc->module->allocator, t_bool, a->Vector.count); + } + return ssa_emit(proc, ssa_make_instr_binary_op(proc, op_kind, left, right, result)); +} + +ssaValue *ssa_emit_array_ep(ssaProcedure *proc, ssaValue *s, ssaValue *index) { + Type *st = base_type(type_deref(ssa_type(s))); + GB_ASSERT(is_type_array(st)); + + // NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32 + index = ssa_emit_conv(proc, index, t_i32); + return ssa_emit(proc, ssa_make_instr_array_element_ptr(proc, s, index)); +} + +ssaValue *ssa_emit_array_ep(ssaProcedure *proc, ssaValue *s, i32 index) { + return ssa_emit_array_ep(proc, s, ssa_make_const_i32(proc->module->allocator, index)); +} + + +ssaValue *ssa_emit_struct_ep(ssaProcedure *proc, ssaValue *s, i32 index) { + gbAllocator a = proc->module->allocator; + Type *t = base_type(type_deref(ssa_type(s))); + Type *result_type = NULL; + ssaValue *gep = NULL; + + if (is_type_struct(t)) { + GB_ASSERT(t->Record.field_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); + result_type = make_type_pointer(a, t->Record.fields[index]->type); + } else if (is_type_tuple(t)) { + GB_ASSERT(t->Tuple.variable_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1)); + result_type = make_type_pointer(a, t->Tuple.variables[index]->type); + } else if (is_type_slice(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->Slice.elem)); break; + case 1: result_type = make_type_pointer(a, t_int); break; + case 2: result_type = make_type_pointer(a, t_int); break; + } + } else if (is_type_string(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t_u8_ptr); break; + case 1: result_type = make_type_pointer(a, t_int); break; + } + } else if (is_type_any(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t_type_info_ptr); break; + case 1: result_type = make_type_pointer(a, t_rawptr); break; + } + } else if (is_type_maybe(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t->Maybe.elem); break; + case 1: result_type = make_type_pointer(a, t_bool); break; + } + } else if (is_type_union(t)) { + switch (index) { + case 1: result_type = make_type_pointer(a, t_int); break; + + case 0: + default: + GB_PANIC("TODO(bill): struct_gep 0 for unions"); + break; + } + } else { + GB_PANIC("TODO(bill): struct_gep type: %s, %d", type_to_string(ssa_type(s)), index); + } + + GB_ASSERT(result_type != NULL); + + gep = ssa_make_instr_struct_element_ptr(proc, s, index, result_type); + return ssa_emit(proc, gep); +} + + + +ssaValue *ssa_emit_array_ev(ssaProcedure *proc, ssaValue *s, i32 index) { + Type *st = base_type(ssa_type(s)); + GB_ASSERT(is_type_array(st)); + return ssa_emit(proc, ssa_make_instr_array_extract_value(proc, s, index)); +} + +ssaValue *ssa_emit_struct_ev(ssaProcedure *proc, ssaValue *s, i32 index) { + // NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32 + + gbAllocator a = proc->module->allocator; + Type *t = base_type(ssa_type(s)); + Type *result_type = NULL; + + if (is_type_struct(t)) { + GB_ASSERT(t->Record.field_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); + result_type = t->Record.fields[index]->type; + } else if (is_type_tuple(t)) { + GB_ASSERT(t->Tuple.variable_count > 0); + GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1)); + result_type = t->Tuple.variables[index]->type; + } else if (is_type_slice(t)) { + switch (index) { + case 0: result_type = make_type_pointer(a, t->Slice.elem); break; + case 1: result_type = t_int; break; + case 2: result_type = t_int; break; + } + } else if (is_type_string(t)) { + switch (index) { + case 0: result_type = t_u8_ptr; break; + case 1: result_type = t_int; break; + } + } else if (is_type_any(t)) { + switch (index) { + case 0: result_type = t_type_info_ptr; break; + case 1: result_type = t_rawptr; break; + } + } else if (is_type_maybe(t)) { + switch (index) { + case 0: result_type = t->Maybe.elem; break; + case 1: result_type = t_bool; break; + } + } else if (is_type_union(t)) { + switch (index) { + case 1: result_type = t_int; break; + + case 0: + default: + GB_PANIC("TODO(bill): struct_gep 0 for unions"); + break; + } + } else { + GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(ssa_type(s)), index); + } + + GB_ASSERT(result_type != NULL); + + return ssa_emit(proc, ssa_make_instr_struct_extract_value(proc, s, index, result_type)); +} + + +ssaValue *ssa_emit_deep_field_gep(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) { + GB_ASSERT(sel.index.count > 0); + + for_array(i, sel.index) { + i32 index = cast(i32)sel.index[i]; + if (is_type_pointer(type)) { + type = type_deref(type); + e = ssa_emit_load(proc, e); + e = ssa_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies? + } + type = base_type(type); + + + if (is_type_raw_union(type)) { + type = type->Record.fields[index]->type; + e = ssa_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type)); + } else if (type->kind == Type_Record) { + type = type->Record.fields[index]->type; + e = ssa_emit_struct_ep(proc, e, index); + } else if (type->kind == Type_Basic) { + switch (type->Basic.kind) { + case Basic_any: { + if (index == 0) { + type = t_type_info_ptr; + } else if (index == 1) { + type = t_rawptr; + } + e = ssa_emit_struct_ep(proc, e, index); + } break; + + case Basic_string: + e = ssa_emit_struct_ep(proc, e, index); + break; + + default: + GB_PANIC("un-gep-able type"); + break; + } + } else if (type->kind == Type_Slice) { + e = ssa_emit_struct_ep(proc, e, index); + } else { + GB_PANIC("un-gep-able type"); + } + } + + return e; +} + + +ssaValue *ssa_emit_deep_field_ev(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) { + GB_ASSERT(sel.index.count > 0); + + for_array(i, sel.index) { + isize index = sel.index[i]; + if (is_type_pointer(type)) { + type = type_deref(type); + e = ssa_emit_load(proc, e); + e = ssa_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies? + } + type = base_type(type); + + + if (is_type_raw_union(type)) { + type = type->Record.fields[index]->type; + e = ssa_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type)); + } else { + e = ssa_emit_struct_ev(proc, e, index); + } + } + + return e; +} + + + + +ssaValue *ssa_array_elem(ssaProcedure *proc, ssaValue *array) { + return ssa_emit_array_ep(proc, array, v_zero32); +} +ssaValue *ssa_array_len(ssaProcedure *proc, ssaValue *array) { + Type *t = ssa_type(array); + GB_ASSERT(t->kind == Type_Array); + return ssa_make_const_int(proc->module->allocator, t->Array.count); +} +ssaValue *ssa_array_cap(ssaProcedure *proc, ssaValue *array) { + return ssa_array_len(proc, array); +} + +ssaValue *ssa_slice_elem(ssaProcedure *proc, ssaValue *slice) { + Type *t = ssa_type(slice); + GB_ASSERT(t->kind == Type_Slice); + return ssa_emit_struct_ev(proc, slice, 0); +} +ssaValue *ssa_slice_len(ssaProcedure *proc, ssaValue *slice) { + Type *t = ssa_type(slice); + GB_ASSERT(t->kind == Type_Slice); + return ssa_emit_struct_ev(proc, slice, 1); +} +ssaValue *ssa_slice_cap(ssaProcedure *proc, ssaValue *slice) { + Type *t = ssa_type(slice); + GB_ASSERT(t->kind == Type_Slice); + return ssa_emit_struct_ev(proc, slice, 2); +} + +ssaValue *ssa_string_elem(ssaProcedure *proc, ssaValue *string) { + Type *t = ssa_type(string); + GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string); + return ssa_emit_struct_ev(proc, string, 0); +} +ssaValue *ssa_string_len(ssaProcedure *proc, ssaValue *string) { + Type *t = ssa_type(string); + GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string); + return ssa_emit_struct_ev(proc, string, 1); +} + + + +ssaValue *ssa_add_local_slice(ssaProcedure *proc, Type *slice_type, ssaValue *base, ssaValue *low, ssaValue *high, ssaValue *max) { + // TODO(bill): array bounds checking for slice creation + // TODO(bill): check that low < high <= max + gbAllocator a = proc->module->allocator; + Type *bt = base_type(ssa_type(base)); + + if (low == NULL) { + low = v_zero; + } + if (high == NULL) { + switch (bt->kind) { + case Type_Array: high = ssa_array_len(proc, base); break; + case Type_Slice: high = ssa_slice_len(proc, base); break; + case Type_Pointer: high = v_one; break; + } + } + if (max == NULL) { + switch (bt->kind) { + case Type_Array: max = ssa_array_cap(proc, base); break; + case Type_Slice: max = ssa_slice_cap(proc, base); break; + case Type_Pointer: max = high; break; + } + } + GB_ASSERT(max != NULL); + + ssaValue *len = ssa_emit_arith(proc, Token_Sub, high, low, t_int); + ssaValue *cap = ssa_emit_arith(proc, Token_Sub, max, low, t_int); + + ssaValue *elem = NULL; + switch (bt->kind) { + case Type_Array: elem = ssa_array_elem(proc, base); break; + case Type_Slice: elem = ssa_slice_elem(proc, base); break; + case Type_Pointer: elem = ssa_emit_load(proc, base); break; + } + + elem = ssa_emit_ptr_offset(proc, elem, low); + + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + + ssaValue *gep = NULL; + gep = ssa_emit_struct_ep(proc, slice, 0); + ssa_emit_store(proc, gep, elem); + + gep = ssa_emit_struct_ep(proc, slice, 1); + ssa_emit_store(proc, gep, len); + + gep = ssa_emit_struct_ep(proc, slice, 2); + ssa_emit_store(proc, gep, cap); + + return slice; +} + +ssaValue *ssa_emit_string(ssaProcedure *proc, ssaValue *elem, ssaValue *len) { + ssaValue *str = ssa_add_local_generated(proc, t_string); + ssaValue *str_elem = ssa_emit_struct_ep(proc, str, 0); + ssaValue *str_len = ssa_emit_struct_ep(proc, str, 1); + ssa_emit_store(proc, str_elem, elem); + ssa_emit_store(proc, str_len, len); + return ssa_emit_load(proc, str); +} + + + + +String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) { + Type *prev_src = src; + // Type *prev_dst = dst; + src = base_type(type_deref(src)); + // dst = base_type(type_deref(dst)); + b32 src_is_ptr = src != prev_src; + // b32 dst_is_ptr = dst != prev_dst; + + GB_ASSERT(is_type_struct(src)); + for (isize i = 0; i < src->Record.field_count; i++) { + Entity *f = src->Record.fields[i]; + if (f->kind == Entity_Variable && f->flags & EntityFlag_Anonymous) { + if (are_types_identical(dst, f->type)) { + return f->token.string; + } + if (src_is_ptr && is_type_pointer(dst)) { + if (are_types_identical(type_deref(dst), f->type)) { + return f->token.string; + } + } + String name = lookup_polymorphic_field(info, dst, f->type); + if (name.len > 0) { + return name; + } + } + } + return make_string(""); +} + +ssaValue *ssa_emit_bitcast(ssaProcedure *proc, ssaValue *data, Type *type) { + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, data, ssa_type(data), type)); +} + + +ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t) { + Type *src_type = ssa_type(value); + if (are_types_identical(t, src_type)) { + return value; + } + + + Type *src = get_enum_base_type(base_type(src_type)); + Type *dst = get_enum_base_type(base_type(t)); + + if (value->kind == ssaValue_Constant) { + if (is_type_any(dst)) { + ssaValue *default_value = ssa_add_local_generated(proc, default_type(src_type)); + ssa_emit_store(proc, default_value, value); + return ssa_emit_conv(proc, ssa_emit_load(proc, default_value), t_any); + } else if (dst->kind == Type_Basic) { + ExactValue ev = value->Constant.value; + if (is_type_float(dst)) { + ev = exact_value_to_float(ev); + } else if (is_type_string(dst)) { + // Handled elsewhere + GB_ASSERT(ev.kind == ExactValue_String); + } else if (is_type_integer(dst)) { + ev = exact_value_to_integer(ev); + } else if (is_type_pointer(dst)) { + // IMPORTANT NOTE(bill): LLVM doesn't support pointer constants expect `null` + ssaValue *i = ssa_add_module_constant(proc->module, t_uint, ev); + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, i, t_uint, dst)); + } + return ssa_add_module_constant(proc->module, t, ev); + } + } + + if (are_types_identical(src, dst)) { + return value; + } + + if (is_type_maybe(dst)) { + ssaValue *maybe = ssa_add_local_generated(proc, dst); + ssaValue *val = ssa_emit_struct_ep(proc, maybe, 0); + ssaValue *set = ssa_emit_struct_ep(proc, maybe, 1); + ssa_emit_store(proc, val, value); + ssa_emit_store(proc, set, v_true); + return ssa_emit_load(proc, maybe); + } + + // integer -> integer + if (is_type_integer(src) && is_type_integer(dst)) { + GB_ASSERT(src->kind == Type_Basic && + dst->kind == Type_Basic); + i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src); + i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst); + if (sz == dz) { + // NOTE(bill): In LLVM, all integers are signed and rely upon 2's compliment + return value; + } + + ssaConvKind kind = ssaConv_trunc; + if (dz >= sz) { + kind = ssaConv_zext; + } + return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); + } + + // boolean -> integer + if (is_type_boolean(src) && is_type_integer(dst)) { + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_zext, value, src, dst)); + } + + // integer -> boolean + if (is_type_integer(src) && is_type_boolean(dst)) { + return ssa_emit_comp(proc, Token_NotEq, value, v_zero); + } + + + // float -> float + if (is_type_float(src) && is_type_float(dst)) { + i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src); + i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst); + ssaConvKind kind = ssaConv_fptrunc; + if (dz >= sz) { + kind = ssaConv_fpext; + } + return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); + } + + // float <-> integer + if (is_type_float(src) && is_type_integer(dst)) { + ssaConvKind kind = ssaConv_fptosi; + if (is_type_unsigned(dst)) { + kind = ssaConv_fptoui; + } + return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); + } + if (is_type_integer(src) && is_type_float(dst)) { + ssaConvKind kind = ssaConv_sitofp; + if (is_type_unsigned(src)) { + kind = ssaConv_uitofp; + } + return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); + } + + // Pointer <-> int + if (is_type_pointer(src) && is_type_int_or_uint(dst)) { + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_ptrtoint, value, src, dst)); + } + if (is_type_int_or_uint(src) && is_type_pointer(dst)) { + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, value, src, dst)); + } + + if (is_type_union(dst)) { + for (isize i = 0; i < dst->Record.field_count; i++) { + Entity *f = dst->Record.fields[i]; + if (are_types_identical(f->type, src_type)) { + ssa_emit_comment(proc, make_string("union - child to parent")); + gbAllocator allocator = proc->module->allocator; + ssaValue *parent = ssa_add_local_generated(proc, t); + ssaValue *tag = ssa_make_const_int(allocator, i); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, parent, 1), tag); + + ssaValue *data = ssa_emit_conv(proc, parent, t_rawptr); + + Type *tag_type = src_type; + Type *tag_type_ptr = make_type_pointer(allocator, tag_type); + ssaValue *underlying = ssa_emit_bitcast(proc, data, tag_type_ptr); + ssa_emit_store(proc, underlying, value); + + return ssa_emit_load(proc, parent); + } + } + } + + // NOTE(bill): This has to be done beofre `Pointer <-> Pointer` as it's + // subtype polymorphism casting + { + Type *sb = base_type(type_deref(src)); + b32 src_is_ptr = src != sb; + if (is_type_struct(sb)) { + String field_name = lookup_polymorphic_field(proc->module->info, t, src); + // gb_printf("field_name: %.*s\n", LIT(field_name)); + if (field_name.len > 0) { + // NOTE(bill): It can be casted + Selection sel = lookup_field(proc->module->allocator, sb, field_name, false); + if (sel.entity != NULL) { + ssa_emit_comment(proc, make_string("cast - polymorphism")); + if (src_is_ptr) { + value = ssa_emit_load(proc, value); + } + return ssa_emit_deep_field_ev(proc, sb, value, sel); + } + } + } + } + + + + // Pointer <-> Pointer + if (is_type_pointer(src) && is_type_pointer(dst)) { + return ssa_emit_bitcast(proc, value, dst); + } + + + + // proc <-> proc + if (is_type_proc(src) && is_type_proc(dst)) { + return ssa_emit_bitcast(proc, value, dst); + } + + // pointer -> proc + if (is_type_pointer(src) && is_type_proc(dst)) { + return ssa_emit_bitcast(proc, value, dst); + } + // proc -> pointer + if (is_type_proc(src) && is_type_pointer(dst)) { + return ssa_emit_bitcast(proc, value, dst); + } + + + + // []byte/[]u8 <-> string + if (is_type_u8_slice(src) && is_type_string(dst)) { + ssaValue *elem = ssa_slice_elem(proc, value); + ssaValue *len = ssa_slice_len(proc, value); + return ssa_emit_string(proc, elem, len); + } + if (is_type_string(src) && is_type_u8_slice(dst)) { + ssaValue *elem = ssa_string_elem(proc, value); + ssaValue *elem_ptr = ssa_add_local_generated(proc, ssa_type(elem)); + ssa_emit_store(proc, elem_ptr, elem); + + ssaValue *len = ssa_string_len(proc, value); + ssaValue *slice = ssa_add_local_slice(proc, dst, elem_ptr, v_zero, len, len); + return ssa_emit_load(proc, slice); + } + + if (is_type_vector(dst)) { + Type *dst_elem = dst->Vector.elem; + value = ssa_emit_conv(proc, value, dst_elem); + ssaValue *v = ssa_add_local_generated(proc, t); + v = ssa_emit_load(proc, v); + v = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, value, v_zero32)); + // NOTE(bill): Broadcast lowest value to all values + isize index_count = dst->Vector.count; + i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count); + for (isize i = 0; i < index_count; i++) { + indices[i] = 0; + } + + v = ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, v, indices, index_count)); + return v; + } + + if (is_type_any(dst)) { + ssaValue *result = ssa_add_local_generated(proc, t_any); + + if (is_type_untyped_nil(src)) { + return ssa_emit_load(proc, result); + } + + ssaValue *data = NULL; + if (value->kind == ssaValue_Instr && + value->Instr.kind == ssaInstr_Load) { + // NOTE(bill): Addressable value + data = value->Instr.Load.address; + } else { + // NOTE(bill): Non-addressable value + data = ssa_add_local_generated(proc, src_type); + ssa_emit_store(proc, data, value); + } + GB_ASSERT(is_type_pointer(ssa_type(data))); + GB_ASSERT(is_type_typed(src_type)); + data = ssa_emit_conv(proc, data, t_rawptr); + + + ssaValue *ti = ssa_type_info(proc, src_type); + + ssaValue *gep0 = ssa_emit_struct_ep(proc, result, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, result, 1); + ssa_emit_store(proc, gep0, ti); + ssa_emit_store(proc, gep1, data); + + return ssa_emit_load(proc, result); + } + + if (is_type_untyped_nil(src) && type_has_nil(dst)) { + return ssa_make_value_nil(proc->module->allocator, t); + } + + + gb_printf_err("ssa_emit_conv: src -> dst\n"); + gb_printf_err("Not Identical %s != %s\n", type_to_string(src_type), type_to_string(t)); + gb_printf_err("Not Identical %s != %s\n", type_to_string(src), type_to_string(dst)); + + + GB_PANIC("Invalid type conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t)); + + return NULL; +} + + +ssaValue *ssa_emit_transmute(ssaProcedure *proc, ssaValue *value, Type *t) { + Type *src_type = ssa_type(value); + if (are_types_identical(t, src_type)) { + return value; + } + + Type *src = base_type(src_type); + Type *dst = base_type(t); + if (are_types_identical(t, src_type)) { + return value; + } + + i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src); + i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst); + + if (sz == dz) { + return ssa_emit_bitcast(proc, value, dst); + } + + + GB_PANIC("Invalid transmute conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t)); + + return NULL; +} + +ssaValue *ssa_emit_down_cast(ssaProcedure *proc, ssaValue *value, Type *t) { + GB_ASSERT(is_type_pointer(ssa_type(value))); + gbAllocator allocator = proc->module->allocator; + + String field_name = check_down_cast_name(t, type_deref(ssa_type(value))); + GB_ASSERT(field_name.len > 0); + Selection sel = lookup_field(proc->module->allocator, t, field_name, false); + Type *t_u8_ptr = make_type_pointer(allocator, t_u8); + ssaValue *bytes = ssa_emit_conv(proc, value, t_u8_ptr); + + i64 offset_ = type_offset_of_from_selection(proc->module->sizes, allocator, type_deref(t), sel); + ssaValue *offset = ssa_make_const_int(allocator, -offset_); + ssaValue *head = ssa_emit_ptr_offset(proc, bytes, offset); + return ssa_emit_conv(proc, head, t); +} + +ssaValue *ssa_emit_union_cast(ssaProcedure *proc, ssaValue *value, Type *tuple) { + GB_ASSERT(tuple->kind == Type_Tuple); + gbAllocator a = proc->module->allocator; + + Type *src_type = ssa_type(value); + b32 is_ptr = is_type_pointer(src_type); + + ssaValue *v = ssa_add_local_generated(proc, tuple); + + if (is_ptr) { + Type *src = base_type(type_deref(src_type)); + Type *src_ptr = src_type; + GB_ASSERT(is_type_union(src)); + Type *dst_ptr = tuple->Tuple.variables[0]->type; + Type *dst = type_deref(dst_ptr); + + ssaValue *tag = ssa_emit_load(proc, ssa_emit_struct_ep(proc, value, 1)); + ssaValue *dst_tag = NULL; + for (isize i = 1; i < src->Record.field_count; i++) { + Entity *f = src->Record.fields[i]; + if (are_types_identical(f->type, dst)) { + dst_tag = ssa_make_const_int(a, i); + break; + } + } + GB_ASSERT(dst_tag != NULL); + + ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok"); + ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end"); + ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag); + ssa_emit_if(proc, cond, ok_block, end_block); + proc->curr_block = ok_block; + + ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1); + + ssaValue *data = ssa_emit_conv(proc, value, dst_ptr); + ssa_emit_store(proc, gep0, data); + ssa_emit_store(proc, gep1, v_true); + + ssa_emit_jump(proc, end_block); + proc->curr_block = end_block; + + } else { + Type *src = base_type(src_type); + GB_ASSERT(is_type_union(src)); + Type *dst = tuple->Tuple.variables[0]->type; + Type *dst_ptr = make_type_pointer(a, dst); + + ssaValue *tag = ssa_emit_struct_ev(proc, value, 1); + ssaValue *dst_tag = NULL; + for (isize i = 1; i < src->Record.field_count; i++) { + Entity *f = src->Record.fields[i]; + if (are_types_identical(f->type, dst)) { + dst_tag = ssa_make_const_int(a, i); + break; + } + } + GB_ASSERT(dst_tag != NULL); + + // HACK(bill): This is probably not very efficient + ssaValue *union_copy = ssa_add_local_generated(proc, src_type); + ssa_emit_store(proc, union_copy, value); + + ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok"); + ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end"); + ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag); + ssa_emit_if(proc, cond, ok_block, end_block); + proc->curr_block = ok_block; + + ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1); + + ssaValue *data = ssa_emit_load(proc, ssa_emit_conv(proc, union_copy, dst_ptr)); + ssa_emit_store(proc, gep0, data); + ssa_emit_store(proc, gep1, v_true); + + ssa_emit_jump(proc, end_block); + proc->curr_block = end_block; + + } + return ssa_emit_load(proc, v); +} + + +isize ssa_type_info_index(CheckerInfo *info, Type *type) { + type = default_type(type); + + isize entry_index = -1; + HashKey key = hash_pointer(type); + auto *found_entry_index = map_get(&info->type_info_map, key); + if (found_entry_index) { + entry_index = *found_entry_index; + } + if (entry_index < 0) { + // NOTE(bill): Do manual search + // TODO(bill): This is O(n) and can be very slow + for_array(i, info->type_info_map.entries){ + auto *e = &info->type_info_map.entries[i]; + Type *prev_type = cast(Type *)e->key.ptr; + if (are_types_identical(prev_type, type)) { + entry_index = e->value; + // NOTE(bill): Add it to the search map + map_set(&info->type_info_map, key, entry_index); + break; + } + } + } + + if (entry_index < 0) { + compiler_error("Type_Info for `%s` could not be found", type_to_string(type)); + } + return entry_index; +} + +ssaValue *ssa_type_info(ssaProcedure *proc, Type *type) { + ssaValue **found = map_get(&proc->module->members, hash_string(make_string(SSA_TYPE_INFO_DATA_NAME))); + GB_ASSERT(found != NULL); + ssaValue *type_info_data = *found; + CheckerInfo *info = proc->module->info; + + type = default_type(type); + + i32 entry_index = ssa_type_info_index(info, type); + + // gb_printf_err("%d %s\n", entry_index, type_to_string(type)); + + return ssa_emit_array_ep(proc, type_info_data, ssa_make_const_i32(proc->module->allocator, entry_index)); +} + + + +ssaValue *ssa_emit_logical_binary_expr(ssaProcedure *proc, AstNode *expr) { + ast_node(be, BinaryExpr, expr); +#if 0 + ssaBlock *true_ = ssa_add_block(proc, NULL, "logical.cmp.true"); + ssaBlock *false_ = ssa_add_block(proc, NULL, "logical.cmp.false"); + ssaBlock *done = ssa_add_block(proc, NULL, "logical.cmp.done"); + + ssaValue *result = ssa_add_local_generated(proc, t_bool); + ssa_build_cond(proc, expr, true_, false_); + + proc->curr_block = true_; + ssa_emit_store(proc, result, v_true); + ssa_emit_jump(proc, done); + + proc->curr_block = false_; + ssa_emit_store(proc, result, v_false); + ssa_emit_jump(proc, done); + + proc->curr_block = done; + + return ssa_emit_load(proc, result); +#else + ssaBlock *rhs = ssa_add_block(proc, NULL, "logical.cmp.rhs"); + ssaBlock *done = ssa_add_block(proc, NULL, "logical.cmp.done"); + + Type *type = type_of_expr(proc->module->info, expr); + type = default_type(type); + + ssaValue *short_circuit = NULL; + if (be->op.kind == Token_CmpAnd) { + ssa_build_cond(proc, be->left, rhs, done); + short_circuit = v_false; + } else if (be->op.kind == Token_CmpOr) { + ssa_build_cond(proc, be->left, done, rhs); + short_circuit = v_true; + } + + if (rhs->preds.count == 0) { + proc->curr_block = done; + return short_circuit; + } + + if (done->preds.count == 0) { + proc->curr_block = rhs; + return ssa_build_expr(proc, be->right); + } + + Array edges = {}; + array_init(&edges, proc->module->allocator, done->preds.count+1); + for_array(i, done->preds) { + array_add(&edges, short_circuit); + } + + proc->curr_block = rhs; + array_add(&edges, ssa_build_expr(proc, be->right)); + ssa_emit_jump(proc, done); + proc->curr_block = done; + + return ssa_emit(proc, ssa_make_instr_phi(proc, edges, type)); +#endif +} + + +void ssa_emit_bounds_check(ssaProcedure *proc, Token token, ssaValue *index, ssaValue *len) { + if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) { + return; + } + + index = ssa_emit_conv(proc, index, t_int); + len = ssa_emit_conv(proc, len, t_int); + + ssa_emit(proc, ssa_make_instr_bounds_check(proc, token.pos, index, len)); + + // gbAllocator a = proc->module->allocator; + // ssaValue **args = gb_alloc_array(a, ssaValue *, 5); + // args[0] = ssa_emit_global_string(proc, token.pos.file); + // args[1] = ssa_make_const_int(a, token.pos.line); + // args[2] = ssa_make_const_int(a, token.pos.column); + // args[3] = ssa_emit_conv(proc, index, t_int); + // args[4] = ssa_emit_conv(proc, len, t_int); + + // ssa_emit_global_call(proc, "__bounds_check_error", args, 5); +} + +void ssa_emit_slice_bounds_check(ssaProcedure *proc, Token token, ssaValue *low, ssaValue *high, ssaValue *max, b32 is_substring) { + if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) { + return; + } + + + low = ssa_emit_conv(proc, low, t_int); + high = ssa_emit_conv(proc, high, t_int); + max = ssa_emit_conv(proc, max, t_int); + + ssa_emit(proc, ssa_make_instr_slice_bounds_check(proc, token.pos, low, high, max, is_substring)); + + // gbAllocator a = proc->module->allocator; + // ssaValue **args = gb_alloc_array(a, ssaValue *, 6); + // args[0] = ssa_emit_global_string(proc, token.pos.file); + // args[1] = ssa_make_const_int(a, token.pos.line); + // args[2] = ssa_make_const_int(a, token.pos.column); + // args[3] = ssa_emit_conv(proc, low, t_int); + // args[4] = ssa_emit_conv(proc, high, t_int); + // args[5] = ssa_emit_conv(proc, max, t_int); + + // if (!is_substring) { + // ssa_emit_global_call(proc, "__slice_expr_error", args, 6); + // } else { + // ssa_emit_global_call(proc, "__substring_expr_error", args, 5); + // } +} + + +//////////////////////////////////////////////////////////////// +// +// @Build +// +//////////////////////////////////////////////////////////////// + + +void ssa_push_target_list(ssaProcedure *proc, ssaBlock *break_, ssaBlock *continue_, ssaBlock *fallthrough_) { + ssaTargetList *tl = gb_alloc_item(proc->module->allocator, ssaTargetList); + tl->prev = proc->target_list; + tl->break_ = break_; + tl->continue_ = continue_; + tl->fallthrough_ = fallthrough_; + proc->target_list = tl; +} + +void ssa_pop_target_list(ssaProcedure *proc) { + proc->target_list = proc->target_list->prev; +} + + +void ssa_mangle_sub_type_name(ssaModule *m, Entity *field, String parent) { + if (field->kind != Entity_TypeName) { + return; + } + String cn = field->token.string; + + isize len = parent.len + 1 + cn.len; + String child = {NULL, len}; + child.text = gb_alloc_array(m->allocator, u8, len); + + isize i = 0; + gb_memmove(child.text+i, parent.text, parent.len); + i += parent.len; + child.text[i++] = '.'; + gb_memmove(child.text+i, cn.text, cn.len); + + map_set(&m->type_names, hash_pointer(field->type), child); + ssa_gen_global_type_name(m, field, child); +} + +void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) { + ssaValue *t = ssa_make_value_type_name(m->allocator, name, e->type); + ssa_module_add_value(m, e, t); + map_set(&m->members, hash_string(name), t); + + Type *bt = base_type(e->type); + if (bt->kind == Type_Record) { + auto *s = &bt->Record; + for (isize j = 0; j < s->other_field_count; j++) { + ssa_mangle_sub_type_name(m, s->other_fields[j], name); + } + } + + if (is_type_union(bt)) { + auto *s = &bt->Record; + // NOTE(bill): Zeroth entry is null (for `match type` stmts) + for (isize j = 1; j < s->field_count; j++) { + ssa_mangle_sub_type_name(m, s->fields[j], name); + } + } +} + + + + +void ssa_build_defer_stmt(ssaProcedure *proc, ssaDefer d) { + ssaBlock *b = ssa_add_block(proc, NULL, "defer"); + // NOTE(bill): The prev block may defer injection before it's terminator + ssaInstr *last_instr = ssa_get_last_instr(proc->curr_block); + if (last_instr == NULL || !ssa_is_instr_terminating(last_instr)) { + ssa_emit_jump(proc, b); + } + proc->curr_block = b; + ssa_emit_comment(proc, make_string("defer")); + if (d.kind == ssaDefer_Node) { + ssa_build_stmt(proc, d.stmt); + } else if (d.kind == ssaDefer_Instr) { + // NOTE(bill): Need to make a new copy + ssaValue *instr = cast(ssaValue *)gb_alloc_copy(proc->module->allocator, d.instr, gb_size_of(ssaValue)); + ssa_emit(proc, instr); + } +} + + + +ssaValue *ssa_find_global_variable(ssaProcedure *proc, String name) { + ssaValue **value = map_get(&proc->module->members, hash_string(name)); + GB_ASSERT_MSG(value != NULL, "Unable to find global variable `%.*s`", LIT(name)); + return *value; +} + +ssaValue *ssa_find_implicit_value_backing(ssaProcedure *proc, ImplicitValueId id) { + Entity *e = proc->module->info->implicit_values[id]; + GB_ASSERT(e->kind == Entity_ImplicitValue); + Entity *backing = e->ImplicitValue.backing; + ssaValue **value = map_get(&proc->module->values, hash_pointer(backing)); + GB_ASSERT_MSG(value != NULL, "Unable to find implicit value backing `%.*s`", LIT(backing->token.string)); + return *value; +} + + + +ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue *tv) { + expr = unparen_expr(expr); + switch (expr->kind) { + case_ast_node(bl, BasicLit, expr); + GB_PANIC("Non-constant basic literal"); + case_end; + + case_ast_node(i, Ident, expr); + Entity *e = *map_get(&proc->module->info->uses, hash_pointer(expr)); + if (e->kind == Entity_Builtin) { + Token token = ast_node_token(expr); + GB_PANIC("TODO(bill): ssa_build_single_expr Entity_Builtin `%.*s`\n" + "\t at %.*s(%td:%td)", LIT(builtin_procs[e->Builtin.id].name), + LIT(token.pos.file), token.pos.line, token.pos.column); + return NULL; + } else if (e->kind == Entity_Nil) { + return ssa_make_value_nil(proc->module->allocator, tv->type); + } else if (e->kind == Entity_ImplicitValue) { + return ssa_emit_load(proc, ssa_find_implicit_value_backing(proc, e->ImplicitValue.id)); + } + + auto *found = map_get(&proc->module->values, hash_pointer(e)); + if (found) { + ssaValue *v = *found; + if (v->kind == ssaValue_Proc) { + return v; + } + // if (e->kind == Entity_Variable && e->Variable.param) { + // return v; + // } + return ssa_emit_load(proc, v); + } + return NULL; + case_end; + + case_ast_node(re, RunExpr, expr); + // TODO(bill): Run Expression + return ssa_build_single_expr(proc, re->expr, tv); + case_end; + + case_ast_node(de, DerefExpr, expr); + return ssa_addr_load(proc, ssa_build_addr(proc, expr)); + case_end; + + case_ast_node(se, SelectorExpr, expr); + TypeAndValue *tav = map_get(&proc->module->info->types, hash_pointer(expr)); + GB_ASSERT(tav != NULL); + return ssa_addr_load(proc, ssa_build_addr(proc, expr)); + case_end; + + case_ast_node(ue, UnaryExpr, expr); + switch (ue->op.kind) { + case Token_Pointer: + return ssa_emit_ptr_offset(proc, ssa_build_addr(proc, ue->expr).addr, v_zero); // Make a copy of the pointer + + case Token_Maybe: + return ssa_emit_conv(proc, ssa_build_expr(proc, ue->expr), type_of_expr(proc->module->info, expr)); + + case Token_Add: + return ssa_build_expr(proc, ue->expr); + + case Token_Sub: // NOTE(bill): -`x` == 0 - `x` + return ssa_emit_arith(proc, ue->op.kind, v_zero, ssa_build_expr(proc, ue->expr), tv->type); + + case Token_Not: // Boolean not + case Token_Xor: { // Bitwise not + // NOTE(bill): "not" `x` == `x` "xor" `-1` + ssaValue *left = ssa_build_expr(proc, ue->expr); + ssaValue *right = ssa_add_module_constant(proc->module, tv->type, make_exact_value_integer(-1)); + return ssa_emit_arith(proc, ue->op.kind, + left, right, + tv->type); + } break; + } + case_end; + + case_ast_node(be, BinaryExpr, expr); + switch (be->op.kind) { + case Token_Add: + case Token_Sub: + case Token_Mul: + case Token_Quo: + case Token_Mod: + case Token_And: + case Token_Or: + case Token_Xor: + case Token_AndNot: + case Token_Shl: + case Token_Shr: + return ssa_emit_arith(proc, be->op.kind, + ssa_build_expr(proc, be->left), + ssa_build_expr(proc, be->right), + tv->type); + + + case Token_CmpEq: + case Token_NotEq: + case Token_Lt: + case Token_LtEq: + case Token_Gt: + case Token_GtEq: { + ssaValue *left = ssa_build_expr(proc, be->left); + ssaValue *right = ssa_build_expr(proc, be->right); + + ssaValue *cmp = ssa_emit_comp(proc, be->op.kind, left, right); + return ssa_emit_conv(proc, cmp, default_type(tv->type)); + } break; + + case Token_CmpAnd: + case Token_CmpOr: + return ssa_emit_logical_binary_expr(proc, expr); + + case Token_as: + ssa_emit_comment(proc, make_string("cast - as")); + return ssa_emit_conv(proc, ssa_build_expr(proc, be->left), tv->type); + + case Token_transmute: + ssa_emit_comment(proc, make_string("cast - transmute")); + return ssa_emit_transmute(proc, ssa_build_expr(proc, be->left), tv->type); + + case Token_down_cast: + ssa_emit_comment(proc, make_string("cast - down_cast")); + return ssa_emit_down_cast(proc, ssa_build_expr(proc, be->left), tv->type); + + case Token_union_cast: + ssa_emit_comment(proc, make_string("cast - union_cast")); + return ssa_emit_union_cast(proc, ssa_build_expr(proc, be->left), tv->type); + + default: + GB_PANIC("Invalid binary expression"); + break; + } + case_end; + + case_ast_node(pl, ProcLit, expr); + // NOTE(bill): Generate a new name + // parent$count + isize name_len = proc->name.len + 1 + 8 + 1; + u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); + name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s$%d", LIT(proc->name), cast(i32)proc->children.count); + String name = make_string(name_text, name_len-1); + + Type *type = type_of_expr(proc->module->info, expr); + ssaValue *value = ssa_make_value_procedure(proc->module->allocator, + proc->module, NULL, type, pl->type, pl->body, name); + + value->Proc.tags = pl->tags; + + array_add(&proc->children, &value->Proc); + ssa_build_proc(value, proc); + + return value; + case_end; + + + case_ast_node(cl, CompoundLit, expr); + ssa_emit_comment(proc, make_string("CompoundLit")); + Type *type = type_of_expr(proc->module->info, expr); + Type *bt = base_type(type); + ssaValue *v = ssa_add_local_generated(proc, type); + + Type *et = NULL; + switch (bt->kind) { + case Type_Vector: et = bt->Vector.elem; break; + case Type_Array: et = bt->Array.elem; break; + case Type_Slice: et = bt->Slice.elem; break; + } + + auto is_elem_const = [](ssaModule *m, AstNode *elem, Type *elem_type) -> b32 { + if (base_type(elem_type) == t_any) { + return false; + } + if (elem->kind == AstNode_FieldValue) { + elem = elem->FieldValue.value; + } + TypeAndValue *tav = type_and_value_of_expression(m->info, elem); + GB_ASSERT(tav != NULL); + return tav->value.kind != ExactValue_Invalid; + }; + + switch (bt->kind) { + default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break; + + case Type_Vector: { + ssaValue *result = ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr)); + for_array(index, cl->elems) { + AstNode *elem = cl->elems[index]; + if (is_elem_const(proc->module, elem, et)) { + continue; + } + ssaValue *field_elem = ssa_build_expr(proc, elem); + Type *t = ssa_type(field_elem); + GB_ASSERT(t->kind != Type_Tuple); + ssaValue *ev = ssa_emit_conv(proc, field_elem, et); + ssaValue *i = ssa_make_const_int(proc->module->allocator, index); + result = ssa_emit(proc, ssa_make_instr_insert_element(proc, result, ev, i)); + } + + if (cl->elems.count == 1 && bt->Vector.count > 1) { + isize index_count = bt->Vector.count; + i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count); + for (isize i = 0; i < index_count; i++) { + indices[i] = 0; + } + ssaValue *sv = ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, result, indices, index_count)); + ssa_emit_store(proc, v, sv); + return ssa_emit_load(proc, v); + } + return result; + } break; + + case Type_Record: { + GB_ASSERT(is_type_struct(bt)); + auto *st = &bt->Record; + if (cl->elems.count > 0) { + ssa_emit_store(proc, v, ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr))); + for_array(field_index, cl->elems) { + AstNode *elem = cl->elems[field_index]; + + ssaValue *field_expr = NULL; + Entity *field = NULL; + isize index = field_index; + + if (elem->kind == AstNode_FieldValue) { + ast_node(fv, FieldValue, elem); + Selection sel = lookup_field(proc->module->allocator, bt, fv->field->Ident.string, false); + index = sel.index[0]; + elem = fv->value; + } else { + TypeAndValue *tav = type_and_value_of_expression(proc->module->info, elem); + Selection sel = lookup_field(proc->module->allocator, bt, st->fields_in_src_order[field_index]->token.string, false); + index = sel.index[0]; + } + + field = st->fields[index]; + if (is_elem_const(proc->module, elem, field->type)) { + continue; + } + + field_expr = ssa_build_expr(proc, elem); + + GB_ASSERT(ssa_type(field_expr)->kind != Type_Tuple); + + + + Type *ft = field->type; + ssaValue *fv = ssa_emit_conv(proc, field_expr, ft); + ssaValue *gep = ssa_emit_struct_ep(proc, v, index); + ssa_emit_store(proc, gep, fv); + } + } + } break; + case Type_Array: { + if (cl->elems.count > 0) { + ssa_emit_store(proc, v, ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr))); + for_array(i, cl->elems) { + AstNode *elem = cl->elems[i]; + if (is_elem_const(proc->module, elem, et)) { + continue; + } + ssaValue *field_expr = ssa_build_expr(proc, elem); + Type *t = ssa_type(field_expr); + GB_ASSERT(t->kind != Type_Tuple); + ssaValue *ev = ssa_emit_conv(proc, field_expr, et); + ssaValue *gep = ssa_emit_array_ep(proc, v, i); + ssa_emit_store(proc, gep, ev); + } + } + } break; + case Type_Slice: { + if (cl->elems.count > 0) { + Type *elem_type = bt->Slice.elem; + Type *elem_ptr_type = make_type_pointer(proc->module->allocator, elem_type); + Type *elem_ptr_ptr_type = make_type_pointer(proc->module->allocator, elem_ptr_type); + Type *t_int_ptr = make_type_pointer(proc->module->allocator, t_int); + ssaValue *slice = ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr)); + GB_ASSERT(slice->kind == ssaValue_ConstantSlice); + + ssaValue *data = ssa_emit_array_ep(proc, slice->ConstantSlice.backing_array, v_zero32); + + for_array(i, cl->elems) { + AstNode *elem = cl->elems[i]; + if (is_elem_const(proc->module, elem, et)) { + continue; + } + + ssaValue *field_expr = ssa_build_expr(proc, elem); + Type *t = ssa_type(field_expr); + GB_ASSERT(t->kind != Type_Tuple); + ssaValue *ev = ssa_emit_conv(proc, field_expr, elem_type); + ssaValue *offset = ssa_emit_ptr_offset(proc, data, ssa_make_const_int(proc->module->allocator, i)); + ssa_emit_store(proc, offset, ev); + } + + ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1); + ssaValue *gep2 = ssa_emit_struct_ep(proc, v, 1); + + ssa_emit_store(proc, gep0, data); + ssa_emit_store(proc, gep1, ssa_make_const_int(proc->module->allocator, slice->ConstantSlice.count)); + ssa_emit_store(proc, gep2, ssa_make_const_int(proc->module->allocator, slice->ConstantSlice.count)); + } + } break; + } + + return ssa_emit_load(proc, v); + case_end; + + + case_ast_node(ce, CallExpr, expr); + AstNode *p = unparen_expr(ce->proc); + if (p->kind == AstNode_Ident) { + Entity **found = map_get(&proc->module->info->uses, hash_pointer(p)); + if (found && (*found)->kind == Entity_Builtin) { + Entity *e = *found; + switch (e->Builtin.id) { + case BuiltinProc_type_info: { + Type *t = default_type(type_of_expr(proc->module->info, ce->args[0])); + return ssa_type_info(proc, t); + } break; + case BuiltinProc_type_info_of_val: { + Type *t = default_type(type_of_expr(proc->module->info, ce->args[0])); + return ssa_type_info(proc, t); + } break; + + case BuiltinProc_new: { + ssa_emit_comment(proc, make_string("new")); + // new :: proc(Type) -> ^Type + gbAllocator allocator = proc->module->allocator; + + Type *type = type_of_expr(proc->module->info, ce->args[0]); + Type *ptr_type = make_type_pointer(allocator, type); + + i64 s = type_size_of(proc->module->sizes, allocator, type); + i64 a = type_align_of(proc->module->sizes, allocator, type); + + ssaValue **args = gb_alloc_array(allocator, ssaValue *, 2); + args[0] = ssa_make_const_int(allocator, s); + args[1] = ssa_make_const_int(allocator, a); + ssaValue *call = ssa_emit_global_call(proc, "alloc_align", args, 2); + ssaValue *v = ssa_emit_conv(proc, call, ptr_type); + return v; + } break; + + case BuiltinProc_new_slice: { + ssa_emit_comment(proc, make_string("new_slice")); + // new_slice :: proc(Type, len: int[, cap: int]) -> ^Type + gbAllocator allocator = proc->module->allocator; + + Type *type = type_of_expr(proc->module->info, ce->args[0]); + Type *ptr_type = make_type_pointer(allocator, type); + Type *slice_type = make_type_slice(allocator, type); + + i64 s = type_size_of(proc->module->sizes, allocator, type); + i64 a = type_align_of(proc->module->sizes, allocator, type); + + ssaValue *elem_size = ssa_make_const_int(allocator, s); + ssaValue *elem_align = ssa_make_const_int(allocator, a); + + ssaValue *len = ssa_emit_conv(proc, ssa_build_expr(proc, ce->args[1]), t_int); + ssaValue *cap = len; + if (ce->args.count == 3) { + cap = ssa_emit_conv(proc, ssa_build_expr(proc, ce->args[2]), t_int); + } + + ssa_emit_slice_bounds_check(proc, ast_node_token(ce->args[1]), v_zero, len, cap, false); + + ssaValue *slice_size = ssa_emit_arith(proc, Token_Mul, elem_size, cap, t_int); + + ssaValue **args = gb_alloc_array(allocator, ssaValue *, 2); + args[0] = slice_size; + args[1] = elem_align; + ssaValue *call = ssa_emit_global_call(proc, "alloc_align", args, 2); + + ssaValue *ptr = ssa_emit_conv(proc, call, ptr_type); + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + + ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2); + ssa_emit_store(proc, gep0, ptr); + ssa_emit_store(proc, gep1, len); + ssa_emit_store(proc, gep2, cap); + return ssa_emit_load(proc, slice); + } break; + + case BuiltinProc_assert: { + ssa_emit_comment(proc, make_string("assert")); + ssaValue *cond = ssa_build_expr(proc, ce->args[0]); + GB_ASSERT(is_type_boolean(ssa_type(cond))); + + cond = ssa_emit_comp(proc, Token_CmpEq, cond, v_false); + ssaBlock *err = ssa_add_block(proc, NULL, "builtin.assert.err"); + ssaBlock *done = ssa_add_block(proc, NULL, "builtin.assert.done"); + + ssa_emit_if(proc, cond, err, done); + proc->curr_block = err; + + // TODO(bill): Cleanup allocations here + Token token = ast_node_token(ce->args[0]); + TokenPos pos = token.pos; + gbString expr = expr_to_string(ce->args[0]); + defer (gb_string_free(expr)); + isize expr_len = gb_string_length(expr); + String expr_str = {}; + expr_str.text = cast(u8 *)gb_alloc_copy_align(proc->module->allocator, expr, expr_len, 1); + expr_str.len = expr_len; + + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 4); + args[0] = ssa_make_const_string(proc->module->allocator, pos.file); + args[1] = ssa_make_const_int(proc->module->allocator, pos.line); + args[2] = ssa_make_const_int(proc->module->allocator, pos.column); + args[3] = ssa_make_const_string(proc->module->allocator, expr_str); + ssa_emit_global_call(proc, "__assert", args, 4); + + ssa_emit_jump(proc, done); + proc->curr_block = done; + + return NULL; + } break; + + case BuiltinProc_panic: { + ssa_emit_comment(proc, make_string("panic")); + ssaValue *msg = ssa_build_expr(proc, ce->args[0]); + GB_ASSERT(is_type_string(ssa_type(msg))); + + Token token = ast_node_token(ce->args[0]); + TokenPos pos = token.pos; + + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 4); + args[0] = ssa_make_const_string(proc->module->allocator, pos.file); + args[1] = ssa_make_const_int(proc->module->allocator, pos.line); + args[2] = ssa_make_const_int(proc->module->allocator, pos.column); + args[3] = msg; + ssa_emit_global_call(proc, "__assert", args, 4); + + return NULL; + } break; + + + case BuiltinProc_copy: { + ssa_emit_comment(proc, make_string("copy")); + // copy :: proc(dst, src: []Type) -> int + AstNode *dst_node = ce->args[0]; + AstNode *src_node = ce->args[1]; + ssaValue *dst_slice = ssa_build_expr(proc, dst_node); + ssaValue *src_slice = ssa_build_expr(proc, src_node); + Type *slice_type = base_type(ssa_type(dst_slice)); + GB_ASSERT(slice_type->kind == Type_Slice); + Type *elem_type = slice_type->Slice.elem; + i64 size_of_elem = type_size_of(proc->module->sizes, proc->module->allocator, elem_type); + + + ssaValue *dst = ssa_emit_conv(proc, ssa_slice_elem(proc, dst_slice), t_rawptr); + ssaValue *src = ssa_emit_conv(proc, ssa_slice_elem(proc, src_slice), t_rawptr); + + ssaValue *len_dst = ssa_slice_len(proc, dst_slice); + ssaValue *len_src = ssa_slice_len(proc, src_slice); + + ssaValue *cond = ssa_emit_comp(proc, Token_Lt, len_dst, len_src); + ssaValue *len = ssa_emit_select(proc, cond, len_dst, len_src); + + ssaValue *elem_size = ssa_make_const_int(proc->module->allocator, size_of_elem); + ssaValue *byte_count = ssa_emit_arith(proc, Token_Mul, len, elem_size, t_int); + + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3); + args[0] = dst; + args[1] = src; + args[2] = byte_count; + + ssa_emit_global_call(proc, "__mem_copy", args, 3); + + return len; + } break; + case BuiltinProc_append: { + ssa_emit_comment(proc, make_string("append")); + // append :: proc(s: ^[]Type, item: Type) -> bool + AstNode *sptr_node = ce->args[0]; + AstNode *item_node = ce->args[1]; + ssaValue *slice_ptr = ssa_build_expr(proc, sptr_node); + ssaValue *slice = ssa_emit_load(proc, slice_ptr); + + ssaValue *elem = ssa_slice_elem(proc, slice); + ssaValue *len = ssa_slice_len(proc, slice); + ssaValue *cap = ssa_slice_cap(proc, slice); + + Type *elem_type = type_deref(ssa_type(elem)); + + ssaValue *item_value = ssa_build_expr(proc, item_node); + item_value = ssa_emit_conv(proc, item_value, elem_type); + + ssaValue *item = ssa_add_local_generated(proc, elem_type); + ssa_emit_store(proc, item, item_value); + + + // NOTE(bill): Check if can append is possible + ssaValue *cond = ssa_emit_comp(proc, Token_Lt, len, cap); + ssaBlock *able = ssa_add_block(proc, NULL, "builtin.append.able"); + ssaBlock *done = ssa_add_block(proc, NULL, "builtin.append.done"); + + ssa_emit_if(proc, cond, able, done); + proc->curr_block = able; + + // Add new slice item + i64 item_size = type_size_of(proc->module->sizes, proc->module->allocator, elem_type); + ssaValue *byte_count = ssa_make_const_int(proc->module->allocator, item_size); + + ssaValue *offset = ssa_emit_ptr_offset(proc, elem, len); + offset = ssa_emit_conv(proc, offset, t_rawptr); + + item = ssa_emit_ptr_offset(proc, item, v_zero); + item = ssa_emit_conv(proc, item, t_rawptr); + + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3); + args[0] = offset; + args[1] = item; + args[2] = byte_count; + + ssa_emit_global_call(proc, "__mem_copy", args, 3); + + // Increment slice length + ssaValue *new_len = ssa_emit_arith(proc, Token_Add, len, v_one, t_int); + ssaValue *gep = ssa_emit_struct_ep(proc, slice_ptr, 1); + ssa_emit_store(proc, gep, new_len); + + ssa_emit_jump(proc, done); + proc->curr_block = done; + + return ssa_emit_conv(proc, cond, t_bool); + } break; + + case BuiltinProc_swizzle: { + ssa_emit_comment(proc, make_string("swizzle")); + ssaValue *vector = ssa_build_expr(proc, ce->args[0]); + isize index_count = ce->args.count-1; + if (index_count == 0) { + return vector; + } + + i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count); + isize index = 0; + for_array(i, ce->args) { + if (i == 0) continue; + TypeAndValue *tv = type_and_value_of_expression(proc->module->info, ce->args[i]); + GB_ASSERT(is_type_integer(tv->type)); + GB_ASSERT(tv->value.kind == ExactValue_Integer); + indices[index++] = cast(i32)tv->value.value_integer; + } + + return ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, vector, indices, index_count)); + + } break; + +#if 0 + case BuiltinProc_ptr_offset: { + ssa_emit_comment(proc, make_string("ptr_offset")); + ssaValue *ptr = ssa_build_expr(proc, ce->args[0]); + ssaValue *offset = ssa_build_expr(proc, ce->args[1]); + return ssa_emit_ptr_offset(proc, ptr, offset); + } break; + + case BuiltinProc_ptr_sub: { + ssa_emit_comment(proc, make_string("ptr_sub")); + ssaValue *ptr_a = ssa_build_expr(proc, ce->args[0]); + ssaValue *ptr_b = ssa_build_expr(proc, ce->args[1]); + Type *ptr_type = base_type(ssa_type(ptr_a)); + GB_ASSERT(ptr_type->kind == Type_Pointer); + isize elem_size = type_size_of(proc->module->sizes, proc->module->allocator, ptr_type->Pointer.elem); + + ssaValue *v = ssa_emit_arith(proc, Token_Sub, ptr_a, ptr_b, t_int); + if (elem_size > 1) { + ssaValue *ez = ssa_make_const_int(proc->module->allocator, elem_size); + v = ssa_emit_arith(proc, Token_Quo, v, ez, t_int); + } + + return v; + } break; +#endif + + case BuiltinProc_slice_ptr: { + ssa_emit_comment(proc, make_string("slice_ptr")); + ssaValue *ptr = ssa_build_expr(proc, ce->args[0]); + ssaValue *len = ssa_build_expr(proc, ce->args[1]); + ssaValue *cap = len; + + len = ssa_emit_conv(proc, len, t_int); + + if (ce->args.count == 3) { + cap = ssa_build_expr(proc, ce->args[2]); + cap = ssa_emit_conv(proc, cap, t_int); + } + + + Type *slice_type = make_type_slice(proc->module->allocator, type_deref(ssa_type(ptr))); + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 0), ptr); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 1), len); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 2), cap); + return ssa_emit_load(proc, slice); + } break; + + case BuiltinProc_min: { + ssa_emit_comment(proc, make_string("min")); + ssaValue *x = ssa_build_expr(proc, ce->args[0]); + ssaValue *y = ssa_build_expr(proc, ce->args[1]); + Type *t = base_type(ssa_type(x)); + ssaValue *cond = ssa_emit_comp(proc, Token_Lt, x, y); + return ssa_emit_select(proc, cond, x, y); + } break; + + case BuiltinProc_max: { + ssa_emit_comment(proc, make_string("max")); + ssaValue *x = ssa_build_expr(proc, ce->args[0]); + ssaValue *y = ssa_build_expr(proc, ce->args[1]); + Type *t = base_type(ssa_type(x)); + ssaValue *cond = ssa_emit_comp(proc, Token_Gt, x, y); + return ssa_emit_select(proc, cond, x, y); + } break; + + case BuiltinProc_abs: { + ssa_emit_comment(proc, make_string("abs")); + + ssaValue *x = ssa_build_expr(proc, ce->args[0]); + Type *t = ssa_type(x); + + ssaValue *neg_x = ssa_emit_arith(proc, Token_Sub, v_zero, x, t); + ssaValue *cond = ssa_emit_comp(proc, Token_Lt, x, v_zero); + return ssa_emit_select(proc, cond, neg_x, x); + } break; + + case BuiltinProc_enum_to_string: { + ssa_emit_comment(proc, make_string("enum_to_string")); + ssaValue *x = ssa_build_expr(proc, ce->args[0]); + Type *t = ssa_type(x); + ssaValue *ti = ssa_type_info(proc, t); + + + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 2); + args[0] = ti; + args[1] = ssa_emit_conv(proc, x, t_i64); + return ssa_emit_global_call(proc, "__enum_to_string", args, 2); + } break; + } + } + } + + + // NOTE(bill): Regular call + ssaValue *value = ssa_build_expr(proc, ce->proc); + Type *proc_type_ = base_type(ssa_type(value)); + GB_ASSERT(proc_type_->kind == Type_Proc); + auto *type = &proc_type_->Proc; + + isize arg_index = 0; + + isize arg_count = 0; + for_array(i, ce->args) { + AstNode *a = ce->args[i]; + Type *at = base_type(type_of_expr(proc->module->info, a)); + if (at->kind == Type_Tuple) { + arg_count += at->Tuple.variable_count; + } else { + arg_count++; + } + } + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, arg_count); + b32 variadic = proc_type_->Proc.variadic; + b32 vari_expand = ce->ellipsis.pos.line != 0; + + for_array(i, ce->args) { + ssaValue *a = ssa_build_expr(proc, ce->args[i]); + Type *at = ssa_type(a); + if (at->kind == Type_Tuple) { + for (isize i = 0; i < at->Tuple.variable_count; i++) { + Entity *e = at->Tuple.variables[i]; + ssaValue *v = ssa_emit_struct_ev(proc, a, i); + args[arg_index++] = v; + } + } else { + args[arg_index++] = a; + } + } + + auto *pt = &type->params->Tuple; + + if (variadic) { + isize i = 0; + for (; i < type->param_count-1; i++) { + args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type); + } + if (!vari_expand) { + Type *variadic_type = pt->variables[i]->type; + GB_ASSERT(is_type_slice(variadic_type)); + variadic_type = base_type(variadic_type)->Slice.elem; + for (; i < arg_count; i++) { + args[i] = ssa_emit_conv(proc, args[i], variadic_type); + } + } + } else { + for (isize i = 0; i < arg_count; i++) { + args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type); + } + } + + if (variadic && !vari_expand) { + ssa_emit_comment(proc, make_string("variadic call argument generation")); + gbAllocator allocator = proc->module->allocator; + Type *slice_type = pt->variables[type->param_count-1]->type; + Type *elem_type = base_type(slice_type)->Slice.elem; + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + isize slice_len = arg_count+1 - type->param_count; + + if (slice_len > 0) { + ssaValue *base_array = ssa_add_local_generated(proc, make_type_array(allocator, elem_type, slice_len)); + + for (isize i = type->param_count-1, j = 0; i < arg_count; i++, j++) { + ssaValue *addr = ssa_emit_array_ep(proc, base_array, j); + ssa_emit_store(proc, addr, args[i]); + } + + ssaValue *base_elem = ssa_emit_array_ep(proc, base_array, 0); + ssaValue *slice_elem = ssa_emit_struct_ep(proc, slice, 0); + ssa_emit_store(proc, slice_elem, base_elem); + ssaValue *len = ssa_make_const_int(allocator, slice_len); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 1), len); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 2), len); + } + + if (args[0]->kind == ssaValue_Constant) { + auto *c = &args[0]->Constant; + gb_printf_err("%s %d\n", type_to_string(c->type), c->value.kind); + } + + arg_count = type->param_count; + args[arg_count-1] = ssa_emit_load(proc, slice); + } + + return ssa_emit_call(proc, value, args, arg_count); + case_end; + + case_ast_node(de, DemaybeExpr, expr); + return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr); + case_end; + + case_ast_node(se, SliceExpr, expr); + return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr); + case_end; + + case_ast_node(ie, IndexExpr, expr); + return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr); + case_end; + } + + GB_PANIC("Unexpected expression: %.*s", LIT(ast_node_strings[expr->kind])); + return NULL; +} + + +ssaValue *ssa_build_expr(ssaProcedure *proc, AstNode *expr) { + expr = unparen_expr(expr); + + TypeAndValue *tv = map_get(&proc->module->info->types, hash_pointer(expr)); + GB_ASSERT_NOT_NULL(tv); + + if (tv->value.kind != ExactValue_Invalid) { + return ssa_add_module_constant(proc->module, tv->type, tv->value); + } + + ssaValue *value = NULL; + if (tv->mode == Addressing_Variable) { + value = ssa_addr_load(proc, ssa_build_addr(proc, expr)); + } else { + value = ssa_build_single_expr(proc, expr, tv); + } + + return value; +} + +ssaValue *ssa_add_using_variable(ssaProcedure *proc, Entity *e) { + GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous); + String name = e->token.string; + Entity *parent = e->using_parent; + Selection sel = lookup_field(proc->module->allocator, parent->type, name, false); + GB_ASSERT(sel.entity != NULL); + ssaValue **pv = map_get(&proc->module->values, hash_pointer(parent)); + ssaValue *v = NULL; + if (pv != NULL) { + v = *pv; + } else { + v = ssa_build_addr(proc, e->using_expr).addr; + } + GB_ASSERT(v != NULL); + ssaValue *var = ssa_emit_deep_field_gep(proc, parent->type, v, sel); + map_set(&proc->module->values, hash_pointer(e), var); + return var; +} + +ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) { + switch (expr->kind) { + case_ast_node(i, Ident, expr); + if (ssa_is_blank_ident(expr)) { + ssaAddr val = {}; + return val; + } + + Entity *e = entity_of_ident(proc->module->info, expr); + TypeAndValue *tv = map_get(&proc->module->info->types, hash_pointer(expr)); + + GB_ASSERT(e->kind != Entity_Constant); + + ssaValue *v = NULL; + ssaValue **found = map_get(&proc->module->values, hash_pointer(e)); + if (found) { + v = *found; + } else if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) { + v = ssa_add_using_variable(proc, e); + } else if (e->kind == Entity_ImplicitValue) { + // TODO(bill): Should a copy be made? + v = ssa_find_implicit_value_backing(proc, e->ImplicitValue.id); + } + + if (v == NULL) { + GB_PANIC("Unknown value: %s, entity: %p %.*s\n", expr_to_string(expr), e, LIT(entity_strings[e->kind])); + } + + return ssa_make_addr(v, expr); + case_end; + + case_ast_node(pe, ParenExpr, expr); + return ssa_build_addr(proc, unparen_expr(expr)); + case_end; + + case_ast_node(se, SelectorExpr, expr); + ssa_emit_comment(proc, make_string("SelectorExpr")); + String selector = unparen_expr(se->selector)->Ident.string; + Type *type = base_type(type_of_expr(proc->module->info, se->expr)); + + if (type == t_invalid) { + // NOTE(bill): Imports + Entity *imp = entity_of_ident(proc->module->info, se->expr); + if (imp != NULL) { + GB_ASSERT(imp->kind == Entity_ImportName); + } + return ssa_build_addr(proc, unparen_expr(se->selector)); + } else { + Selection sel = lookup_field(proc->module->allocator, type, selector, false); + GB_ASSERT(sel.entity != NULL); + + ssaValue *a = ssa_build_addr(proc, se->expr).addr; + a = ssa_emit_deep_field_gep(proc, type, a, sel); + return ssa_make_addr(a, expr); + } + case_end; + + case_ast_node(ue, UnaryExpr, expr); + switch (ue->op.kind) { + case Token_Pointer: { + return ssa_build_addr(proc, ue->expr); + } + default: + GB_PANIC("Invalid unary expression for ssa_build_addr"); + } + case_end; + + case_ast_node(be, BinaryExpr, expr); + switch (be->op.kind) { + case Token_as: { + ssa_emit_comment(proc, make_string("Cast - as")); + // NOTE(bill): Needed for dereference of pointer conversion + Type *type = type_of_expr(proc->module->info, expr); + ssaValue *v = ssa_add_local_generated(proc, type); + ssa_emit_store(proc, v, ssa_emit_conv(proc, ssa_build_expr(proc, be->left), type)); + return ssa_make_addr(v, expr); + } + case Token_transmute: { + ssa_emit_comment(proc, make_string("Cast - transmute")); + // NOTE(bill): Needed for dereference of pointer conversion + Type *type = type_of_expr(proc->module->info, expr); + ssaValue *v = ssa_add_local_generated(proc, type); + ssa_emit_store(proc, v, ssa_emit_transmute(proc, ssa_build_expr(proc, be->left), type)); + return ssa_make_addr(v, expr); + } + default: + GB_PANIC("Invalid binary expression for ssa_build_addr: %.*s\n", LIT(be->op.string)); + break; + } + case_end; + + case_ast_node(ie, IndexExpr, expr); + ssa_emit_comment(proc, make_string("IndexExpr")); + Type *t = base_type(type_of_expr(proc->module->info, ie->expr)); + gbAllocator a = proc->module->allocator; + + + b32 deref = is_type_pointer(t); + t = type_deref(t); + + ssaValue *using_addr = NULL; + if (!is_type_indexable(t)) { + // Using index expression + Entity *using_field = find_using_index_expr(t); + if (using_field != NULL) { + Selection sel = lookup_field(a, t, using_field->token.string, false); + ssaValue *e = ssa_build_addr(proc, ie->expr).addr; + using_addr = ssa_emit_deep_field_gep(proc, t, e, sel); + + t = using_field->type; + } + } + + + switch (t->kind) { + case Type_Vector: { + ssaValue *vector = NULL; + if (using_addr != NULL) { + vector = using_addr; + } else { + vector = ssa_build_addr(proc, ie->expr).addr; + if (deref) { + vector = ssa_emit_load(proc, vector); + } + } + ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); + ssaValue *len = ssa_make_const_int(a, t->Vector.count); + ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + return ssa_make_addr_vector(vector, index, expr); + } break; + + case Type_Array: { + ssaValue *array = NULL; + if (using_addr != NULL) { + array = using_addr; + } else { + array = ssa_build_addr(proc, ie->expr).addr; + if (deref) { + array = ssa_emit_load(proc, array); + } + } + ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); + ssaValue *elem = ssa_emit_array_ep(proc, array, index); + ssaValue *len = ssa_make_const_int(a, t->Vector.count); + ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + return ssa_make_addr(elem, expr); + } break; + + case Type_Slice: { + ssaValue *slice = NULL; + if (using_addr != NULL) { + slice = ssa_emit_load(proc, using_addr); + } else { + slice = ssa_build_expr(proc, ie->expr); + if (deref) { + slice = ssa_emit_load(proc, slice); + } + } + ssaValue *elem = ssa_slice_elem(proc, slice); + ssaValue *len = ssa_slice_len(proc, slice); + ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); + ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + ssaValue *v = ssa_emit_ptr_offset(proc, elem, index); + return ssa_make_addr(v, expr); + + } break; + + case Type_Basic: { // Basic_string + TypeAndValue *tv = map_get(&proc->module->info->types, hash_pointer(ie->expr)); + ssaValue *str; + ssaValue *elem; + ssaValue *len; + ssaValue *index; + + if (using_addr != NULL) { + str = ssa_emit_load(proc, using_addr); + } else { + str = ssa_build_expr(proc, ie->expr); + if (deref) { + str = ssa_emit_load(proc, str); + } + } + elem = ssa_string_elem(proc, str); + len = ssa_string_len(proc, str); + + index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); + ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len); + + return ssa_make_addr(ssa_emit_ptr_offset(proc, elem, index), expr); + } break; + } + case_end; + + case_ast_node(se, SliceExpr, expr); + ssa_emit_comment(proc, make_string("SliceExpr")); + gbAllocator a = proc->module->allocator; + ssaValue *low = v_zero; + ssaValue *high = NULL; + ssaValue *max = NULL; + + if (se->low != NULL) low = ssa_build_expr(proc, se->low); + if (se->high != NULL) high = ssa_build_expr(proc, se->high); + if (se->triple_indexed) max = ssa_build_expr(proc, se->max); + ssaValue *addr = ssa_build_addr(proc, se->expr).addr; + ssaValue *base = ssa_emit_load(proc, addr); + Type *type = base_type(ssa_type(base)); + + if (is_type_pointer(type)) { + type = type_deref(type); + addr = base; + base = ssa_emit_load(proc, base); + } + + // TODO(bill): Cleanup like mad! + + switch (type->kind) { + case Type_Slice: { + Type *slice_type = type; + + if (high == NULL) high = ssa_slice_len(proc, base); + if (max == NULL) max = ssa_slice_cap(proc, base); + GB_ASSERT(max != NULL); + + ssa_emit_slice_bounds_check(proc, se->open, low, high, max, false); + + ssaValue *elem = ssa_slice_elem(proc, base); + ssaValue *len = ssa_emit_arith(proc, Token_Sub, high, low, t_int); + ssaValue *cap = ssa_emit_arith(proc, Token_Sub, max, low, t_int); + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + + ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2); + ssa_emit_store(proc, gep0, elem); + ssa_emit_store(proc, gep1, len); + ssa_emit_store(proc, gep2, cap); + + return ssa_make_addr(slice, expr); + } + + case Type_Array: { + Type *slice_type = make_type_slice(a, type->Array.elem); + + if (high == NULL) high = ssa_array_len(proc, base); + if (max == NULL) max = ssa_array_cap(proc, base); + GB_ASSERT(max != NULL); + + ssa_emit_slice_bounds_check(proc, se->open, low, high, max, false); + + ssaValue *elem = ssa_array_elem(proc, addr); + ssaValue *len = ssa_emit_arith(proc, Token_Sub, high, low, t_int); + ssaValue *cap = ssa_emit_arith(proc, Token_Sub, max, low, t_int); + ssaValue *slice = ssa_add_local_generated(proc, slice_type); + + ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2); + ssa_emit_store(proc, gep0, elem); + ssa_emit_store(proc, gep1, len); + ssa_emit_store(proc, gep2, cap); + + return ssa_make_addr(slice, expr); + } + + case Type_Basic: { + GB_ASSERT(type == t_string); + if (high == NULL) { + high = ssa_string_len(proc, base); + } + + ssa_emit_slice_bounds_check(proc, se->open, low, high, high, true); + + ssaValue *elem, *len; + len = ssa_emit_arith(proc, Token_Sub, high, low, t_int); + + elem = ssa_string_elem(proc, base); + elem = ssa_emit_ptr_offset(proc, elem, low); + + ssaValue *str = ssa_add_local_generated(proc, t_string); + ssaValue *gep0 = ssa_emit_struct_ep(proc, str, 0); + ssaValue *gep1 = ssa_emit_struct_ep(proc, str, 1); + ssa_emit_store(proc, gep0, elem); + ssa_emit_store(proc, gep1, len); + + return ssa_make_addr(str, expr); + } break; + } + + GB_PANIC("Unknown slicable type"); + case_end; + + case_ast_node(de, DerefExpr, expr); + // TODO(bill): Is a ptr copy needed? + ssaValue *addr = ssa_build_expr(proc, de->expr); + addr = ssa_emit_ptr_offset(proc, addr, v_zero); + return ssa_make_addr(addr, expr); + case_end; + + case_ast_node(de, DemaybeExpr, expr); + ssa_emit_comment(proc, make_string("DemaybeExpr")); + ssaValue *maybe = ssa_build_expr(proc, de->expr); + Type *t = default_type(type_of_expr(proc->module->info, expr)); + GB_ASSERT(is_type_tuple(t)); + + ssaValue *result = ssa_add_local_generated(proc, t); + ssa_emit_store(proc, result, maybe); + + return ssa_make_addr(result, expr); + case_end; + + case_ast_node(ce, CallExpr, expr); + ssaValue *e = ssa_build_expr(proc, expr); + ssaValue *v = ssa_add_local_generated(proc, ssa_type(e)); + ssa_emit_store(proc, v, e); + return ssa_make_addr(v, expr); + case_end; + } + + TokenPos token_pos = ast_node_token(expr).pos; + GB_PANIC("Unexpected address expression\n" + "\tAstNode: %.*s @ " + "%.*s(%td:%td)\n", + LIT(ast_node_strings[expr->kind]), + LIT(token_pos.file), token_pos.line, token_pos.column); + + + return ssa_make_addr(NULL, NULL); +} + +void ssa_build_assign_op(ssaProcedure *proc, ssaAddr lhs, ssaValue *value, TokenKind op) { + ssaValue *old_value = ssa_addr_load(proc, lhs); + Type *type = ssa_type(old_value); + + ssaValue *change = value; + if (is_type_pointer(type) && is_type_integer(ssa_type(value))) { + change = ssa_emit_conv(proc, value, default_type(ssa_type(value))); + } else { + change = ssa_emit_conv(proc, value, type); + } + ssaValue *new_value = ssa_emit_arith(proc, op, old_value, change, type); + ssa_addr_store(proc, lhs, new_value); +} + +void ssa_build_cond(ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block) { + switch (cond->kind) { + case_ast_node(pe, ParenExpr, cond); + ssa_build_cond(proc, pe->expr, true_block, false_block); + return; + case_end; + + case_ast_node(ue, UnaryExpr, cond); + if (ue->op.kind == Token_Not) { + ssa_build_cond(proc, ue->expr, false_block, true_block); + return; + } + case_end; + + case_ast_node(be, BinaryExpr, cond); + if (be->op.kind == Token_CmpAnd) { + ssaBlock *block = ssa_add_block(proc, NULL, "cmp.and"); + ssa_build_cond(proc, be->left, block, false_block); + proc->curr_block = block; + ssa_build_cond(proc, be->right, true_block, false_block); + return; + } else if (be->op.kind == Token_CmpOr) { + ssaBlock *block = ssa_add_block(proc, NULL, "cmp.or"); + ssa_build_cond(proc, be->left, true_block, block); + proc->curr_block = block; + ssa_build_cond(proc, be->right, true_block, false_block); + return; + } + case_end; + } + + ssaValue *expr = ssa_build_expr(proc, cond); + expr = ssa_emit_conv(proc, expr, t_bool); + ssa_emit_if(proc, expr, true_block, false_block); +} + + + + +void ssa_build_stmt_list(ssaProcedure *proc, AstNodeArray stmts) { + for_array(i, stmts) { + ssa_build_stmt(proc, stmts[i]); + } +} + +void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { + u32 prev_stmt_state_flags = proc->module->stmt_state_flags; + defer (proc->module->stmt_state_flags = prev_stmt_state_flags); + + if (node->stmt_state_flags != 0) { + u32 in = node->stmt_state_flags; + u32 out = proc->module->stmt_state_flags; + defer (proc->module->stmt_state_flags = out); + + if (in & StmtStateFlag_bounds_check) { + out |= StmtStateFlag_bounds_check; + out &= ~StmtStateFlag_no_bounds_check; + } else if (in & StmtStateFlag_no_bounds_check) { + out |= StmtStateFlag_no_bounds_check; + out &= ~StmtStateFlag_bounds_check; + } + } + + + switch (node->kind) { + case_ast_node(bs, EmptyStmt, node); + case_end; + + case_ast_node(us, UsingStmt, node); + AstNode *decl = unparen_expr(us->node); + if (decl->kind == AstNode_VarDecl) { + ssa_build_stmt(proc, decl); + } + case_end; + + case_ast_node(vd, VarDecl, node); + ssaModule *m = proc->module; + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena); + defer (gb_temp_arena_memory_end(tmp)); + + if (vd->values.count == 0) { // declared and zero-initialized + for_array(i, vd->names) { + AstNode *name = vd->names[i]; + if (!ssa_is_blank_ident(name)) { + ssa_add_local_for_identifier(proc, name, true); + } + } + } else { // Tuple(s) + Array lvals; + Array inits; + array_init(&lvals, m->tmp_allocator, vd->names.count); + array_init(&inits, m->tmp_allocator, vd->names.count); + + for_array(i, vd->names) { + AstNode *name = vd->names[i]; + ssaAddr lval = ssa_make_addr(NULL, NULL); + if (!ssa_is_blank_ident(name)) { + ssa_add_local_for_identifier(proc, name, false); + lval = ssa_build_addr(proc, name); + } + + array_add(&lvals, lval); + } + + for_array(i, vd->values) { + ssaValue *init = ssa_build_expr(proc, vd->values[i]); + Type *t = ssa_type(init); + if (t->kind == Type_Tuple) { + for (isize i = 0; i < t->Tuple.variable_count; i++) { + Entity *e = t->Tuple.variables[i]; + ssaValue *v = ssa_emit_struct_ev(proc, init, i); + array_add(&inits, v); + } + } else { + array_add(&inits, init); + } + } + + + for_array(i, inits) { + ssaValue *v = ssa_emit_conv(proc, inits[i], ssa_addr_type(lvals[i])); + ssa_addr_store(proc, lvals[i], v); + } + } + case_end; + + case_ast_node(pd, ProcDecl, node); + if (pd->body != NULL) { + auto *info = proc->module->info; + + Entity **found = map_get(&info->definitions, hash_pointer(pd->name)); + GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string)); + Entity *e = *found; + + + if (map_get(&proc->module->min_dep_map, hash_pointer(e)) == NULL) { + // NOTE(bill): Nothing depends upon it so doesn't need to be built + break; + } + + // NOTE(bill): Generate a new name + // parent.name-guid + String original_name = pd->name->Ident.string; + String pd_name = original_name; + if (pd->link_name.len > 0) { + pd_name = pd->link_name; + } + + isize name_len = proc->name.len + 1 + pd_name.len + 1 + 10 + 1; + u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); + i32 guid = cast(i32)proc->children.count; + name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(pd_name), guid); + String name = make_string(name_text, name_len-1); + + + ssaValue *value = ssa_make_value_procedure(proc->module->allocator, + proc->module, e, e->type, pd->type, pd->body, name); + + value->Proc.tags = pd->tags; + value->Proc.parent = proc; + + ssa_module_add_value(proc->module, e, value); + array_add(&proc->children, &value->Proc); + array_add(&proc->module->procs, value); + } else { + auto *info = proc->module->info; + + Entity **found = map_get(&info->definitions, hash_pointer(pd->name)); + GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string)); + Entity *e = *found; + + // FFI - Foreign function interace + String original_name = pd->name->Ident.string; + String name = original_name; + if (pd->foreign_name.len > 0) { + name = pd->foreign_name; + } + + ssaValue *value = ssa_make_value_procedure(proc->module->allocator, + proc->module, e, e->type, pd->type, pd->body, name); + + value->Proc.tags = pd->tags; + + ssa_module_add_value(proc->module, e, value); + ssa_build_proc(value, proc); + + if (value->Proc.tags & ProcTag_foreign) { + HashKey key = hash_string(name); + auto *prev_value = map_get(&proc->module->members, key); + if (prev_value == NULL) { + // NOTE(bill): Don't do mutliple declarations in the IR + map_set(&proc->module->members, key, value); + } + } else { + array_add(&proc->children, &value->Proc); + } + } + case_end; + + case_ast_node(td, TypeDecl, node); + + // NOTE(bill): Generate a new name + // parent_proc.name-guid + String td_name = td->name->Ident.string; + isize name_len = proc->name.len + 1 + td_name.len + 1 + 10 + 1; + u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); + i32 guid = cast(i32)proc->module->members.entries.count; + name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(td_name), guid); + String name = make_string(name_text, name_len-1); + + Entity **found = map_get(&proc->module->info->definitions, hash_pointer(td->name)); + GB_ASSERT(found != NULL); + Entity *e = *found; + ssaValue *value = ssa_make_value_type_name(proc->module->allocator, + name, e->type); + map_set(&proc->module->type_names, hash_pointer(e->type), name); + ssa_gen_global_type_name(proc->module, e, name); + case_end; + + case_ast_node(ids, IncDecStmt, node); + ssa_emit_comment(proc, make_string("IncDecStmt")); + TokenKind op = ids->op.kind; + if (op == Token_Increment) { + op = Token_Add; + } else if (op == Token_Decrement) { + op = Token_Sub; + } + ssaAddr lval = ssa_build_addr(proc, ids->expr); + ssaValue *one = ssa_emit_conv(proc, v_one, ssa_addr_type(lval)); + ssa_build_assign_op(proc, lval, one, op); + + case_end; + + case_ast_node(as, AssignStmt, node); + ssa_emit_comment(proc, make_string("AssignStmt")); + + ssaModule *m = proc->module; + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena); + defer (gb_temp_arena_memory_end(tmp)); + + switch (as->op.kind) { + case Token_Eq: { + Array lvals; + array_init(&lvals, m->tmp_allocator); + + for_array(i, as->lhs) { + AstNode *lhs = as->lhs[i]; + ssaAddr lval = {}; + if (!ssa_is_blank_ident(lhs)) { + lval = ssa_build_addr(proc, lhs); + } + array_add(&lvals, lval); + } + + if (as->lhs.count == as->rhs.count) { + if (as->lhs.count == 1) { + AstNode *rhs = as->rhs[0]; + ssaValue *init = ssa_build_expr(proc, rhs); + ssa_addr_store(proc, lvals[0], init); + } else { + Array inits; + array_init(&inits, m->tmp_allocator, lvals.count); + + for_array(i, as->rhs) { + ssaValue *init = ssa_build_expr(proc, as->rhs[i]); + array_add(&inits, init); + } + + for_array(i, inits) { + ssa_addr_store(proc, lvals[i], inits[i]); + } + } + } else { + Array inits; + array_init(&inits, m->tmp_allocator, lvals.count); + + for_array(i, as->rhs) { + ssaValue *init = ssa_build_expr(proc, as->rhs[i]); + Type *t = ssa_type(init); + // TODO(bill): refactor for code reuse as this is repeated a bit + if (t->kind == Type_Tuple) { + for (isize i = 0; i < t->Tuple.variable_count; i++) { + Entity *e = t->Tuple.variables[i]; + ssaValue *v = ssa_emit_struct_ev(proc, init, i); + array_add(&inits, v); + } + } else { + array_add(&inits, init); + } + } + + for_array(i, inits) { + ssa_addr_store(proc, lvals[i], inits[i]); + } + } + + } break; + + default: { + // NOTE(bill): Only 1 += 1 is allowed, no tuples + // +=, -=, etc + i32 op = cast(i32)as->op.kind; + op += Token_Add - Token_AddEq; // Convert += to + + ssaAddr lhs = ssa_build_addr(proc, as->lhs[0]); + ssaValue *value = ssa_build_expr(proc, as->rhs[0]); + ssa_build_assign_op(proc, lhs, value, cast(TokenKind)op); + } break; + } + case_end; + + case_ast_node(es, ExprStmt, node); + // NOTE(bill): No need to use return value + ssa_build_expr(proc, es->expr); + case_end; + + case_ast_node(bs, BlockStmt, node); + ssa_open_scope(proc); + ssa_build_stmt_list(proc, bs->stmts); + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + case_end; + + case_ast_node(ds, DeferStmt, node); + ssa_emit_comment(proc, make_string("DeferStmt")); + isize scope_index = proc->scope_index; + if (ds->stmt->kind == AstNode_BlockStmt) { + scope_index--; + } + ssa_add_defer_node(proc, scope_index, ds->stmt); + case_end; + + case_ast_node(rs, ReturnStmt, node); + ssa_emit_comment(proc, make_string("ReturnStmt")); + ssaValue *v = NULL; + auto *return_type_tuple = &proc->type->Proc.results->Tuple; + isize return_count = proc->type->Proc.result_count; + if (return_count == 0) { + // No return values + } else if (return_count == 1) { + Entity *e = return_type_tuple->variables[0]; + v = ssa_emit_conv(proc, ssa_build_expr(proc, rs->results[0]), e->type); + } else { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena); + defer (gb_temp_arena_memory_end(tmp)); + + Array results; + array_init(&results, proc->module->tmp_allocator, return_count); + + for_array(res_index, rs->results) { + ssaValue *res = ssa_build_expr(proc, rs->results[res_index]); + Type *t = ssa_type(res); + if (t->kind == Type_Tuple) { + for (isize i = 0; i < t->Tuple.variable_count; i++) { + Entity *e = t->Tuple.variables[i]; + ssaValue *v = ssa_emit_struct_ev(proc, res, i); + array_add(&results, v); + } + } else { + array_add(&results, res); + } + } + + Type *ret_type = proc->type->Proc.results; + v = ssa_add_local_generated(proc, ret_type); + for_array(i, results) { + Entity *e = return_type_tuple->variables[i]; + ssaValue *res = ssa_emit_conv(proc, results[i], e->type); + ssaValue *field = ssa_emit_struct_ep(proc, v, i); + ssa_emit_store(proc, field, res); + } + + v = ssa_emit_load(proc, v); + + } + ssa_emit_return(proc, v); + + case_end; + + case_ast_node(is, IfStmt, node); + ssa_emit_comment(proc, make_string("IfStmt")); + if (is->init != NULL) { + ssaBlock *init = ssa_add_block(proc, node, "if.init"); + ssa_emit_jump(proc, init); + proc->curr_block = init; + ssa_build_stmt(proc, is->init); + } + ssaBlock *then = ssa_add_block(proc, node, "if.then"); + ssaBlock *done = ssa_add_block(proc, node, "if.done"); // NOTE(bill): Append later + ssaBlock *else_ = done; + if (is->else_stmt != NULL) { + else_ = ssa_add_block(proc, is->else_stmt, "if.else"); + } + + ssa_build_cond(proc, is->cond, then, else_); + proc->curr_block = then; + + ssa_open_scope(proc); + ssa_build_stmt(proc, is->body); + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + + ssa_emit_jump(proc, done); + + if (is->else_stmt != NULL) { + proc->curr_block = else_; + + ssa_open_scope(proc); + ssa_build_stmt(proc, is->else_stmt); + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + + ssa_emit_jump(proc, done); + } + proc->curr_block = done; + case_end; + + case_ast_node(fs, ForStmt, node); + ssa_emit_comment(proc, make_string("ForStmt")); + if (fs->init != NULL) { + ssaBlock *init = ssa_add_block(proc, node, "for.init"); + ssa_emit_jump(proc, init); + proc->curr_block = init; + ssa_build_stmt(proc, fs->init); + } + ssaBlock *body = ssa_add_block(proc, node, "for.body"); + ssaBlock *done = ssa_add_block(proc, node, "for.done"); // NOTE(bill): Append later + + ssaBlock *loop = body; + + if (fs->cond != NULL) { + loop = ssa_add_block(proc, node, "for.loop"); + } + ssaBlock *cont = loop; + if (fs->post != NULL) { + cont = ssa_add_block(proc, node, "for.post"); + + } + ssa_emit_jump(proc, loop); + proc->curr_block = loop; + if (loop != body) { + ssa_build_cond(proc, fs->cond, body, done); + proc->curr_block = body; + } + + ssa_push_target_list(proc, done, cont, NULL); + + ssa_open_scope(proc); + ssa_build_stmt(proc, fs->body); + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + + ssa_pop_target_list(proc); + ssa_emit_jump(proc, cont); + + if (fs->post != NULL) { + proc->curr_block = cont; + ssa_build_stmt(proc, fs->post); + ssa_emit_jump(proc, loop); + } + + + proc->curr_block = done; + + case_end; + + case_ast_node(ms, MatchStmt, node); + ssa_emit_comment(proc, make_string("MatchStmt")); + if (ms->init != NULL) { + ssa_build_stmt(proc, ms->init); + } + ssaValue *tag = v_true; + if (ms->tag != NULL) { + tag = ssa_build_expr(proc, ms->tag); + } + ssaBlock *done = ssa_add_block(proc, node, "match.done"); // NOTE(bill): Append later + + ast_node(body, BlockStmt, ms->body); + + AstNodeArray default_stmts = {}; + ssaBlock *default_fall = NULL; + ssaBlock *default_block = NULL; + + ssaBlock *fall = NULL; + b32 append_fall = false; + + isize case_count = body->stmts.count; + for_array(i, body->stmts) { + AstNode *clause = body->stmts[i]; + ssaBlock *body = fall; + + ast_node(cc, CaseClause, clause); + + if (body == NULL) { + if (cc->list.count == 0) { + body = ssa_add_block(proc, clause, "match.dflt.body"); + } else { + body = ssa_add_block(proc, clause, "match.case.body"); + } + } + if (append_fall && body == fall) { + append_fall = false; + } + + fall = done; + if (i+1 < case_count) { + append_fall = true; + fall = ssa_add_block(proc, clause, "match.fall.body"); + } + + if (cc->list.count == 0) { + // default case + default_stmts = cc->stmts; + default_fall = fall; + default_block = body; + continue; + } + + ssaBlock *next_cond = NULL; + for_array(j, cc->list) { + AstNode *expr = cc->list[j]; + next_cond = ssa_add_block(proc, clause, "match.case.next"); + + ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, ssa_build_expr(proc, expr)); + ssa_emit_if(proc, cond, body, next_cond); + proc->curr_block = next_cond; + } + proc->curr_block = body; + + ssa_push_target_list(proc, done, NULL, fall); + ssa_open_scope(proc); + ssa_build_stmt_list(proc, cc->stmts); + ssa_close_scope(proc, ssaDeferExit_Default, body); + ssa_pop_target_list(proc); + + ssa_emit_jump(proc, done); + proc->curr_block = next_cond; + } + + if (default_block != NULL) { + ssa_emit_jump(proc, default_block); + proc->curr_block = default_block; + + ssa_push_target_list(proc, done, NULL, default_fall); + ssa_open_scope(proc); + ssa_build_stmt_list(proc, default_stmts); + ssa_close_scope(proc, ssaDeferExit_Default, default_block); + ssa_pop_target_list(proc); + } + + ssa_emit_jump(proc, done); + proc->curr_block = done; + case_end; + + + case_ast_node(ms, TypeMatchStmt, node); + ssa_emit_comment(proc, make_string("TypeMatchStmt")); + gbAllocator allocator = proc->module->allocator; + + ssaValue *parent = ssa_build_expr(proc, ms->tag); + Type *union_type = type_deref(ssa_type(parent)); + GB_ASSERT(is_type_union(union_type)); + + ssa_emit_comment(proc, make_string("get union's tag")); + ssaValue *tag_index = ssa_emit_struct_ep(proc, parent, 1); + tag_index = ssa_emit_load(proc, tag_index); + + ssaValue *data = ssa_emit_conv(proc, parent, t_rawptr); + + ssaBlock *start_block = ssa_add_block(proc, node, "type-match.case.first"); + ssa_emit_jump(proc, start_block); + proc->curr_block = start_block; + + ssaBlock *done = ssa_add_block(proc, node, "type-match.done"); // NOTE(bill): Append later + + ast_node(body, BlockStmt, ms->body); + + + String tag_var_name = ms->var->Ident.string; + + AstNodeArray default_stmts = {}; + ssaBlock *default_block = NULL; + + isize case_count = body->stmts.count; + for_array(i, body->stmts) { + AstNode *clause = body->stmts[i]; + ast_node(cc, CaseClause, clause); + + if (cc->list.count == 0) { + // default case + default_stmts = cc->stmts; + default_block = ssa_add_block(proc, clause, "type-match.dflt.body"); + continue; + } + + + ssaBlock *body = ssa_add_block(proc, clause, "type-match.case.body"); + + Scope *scope = *map_get(&proc->module->info->scopes, hash_pointer(clause)); + Entity *tag_var_entity = current_scope_lookup_entity(scope, tag_var_name); + GB_ASSERT_MSG(tag_var_entity != NULL, "%.*s", LIT(tag_var_name)); + ssaValue *tag_var = ssa_add_local(proc, tag_var_entity); + ssaValue *data_ptr = ssa_emit_conv(proc, data, tag_var_entity->type); + ssa_emit_store(proc, tag_var, data_ptr); + + + + Type *bt = type_deref(tag_var_entity->type); + ssaValue *index = NULL; + Type *ut = base_type(union_type); + GB_ASSERT(ut->Record.kind == TypeRecord_Union); + for (isize field_index = 1; field_index < ut->Record.field_count; field_index++) { + Entity *f = base_type(union_type)->Record.fields[field_index]; + if (are_types_identical(f->type, bt)) { + index = ssa_make_const_int(allocator, field_index); + break; + } + } + GB_ASSERT(index != NULL); + + ssaBlock *next_cond = ssa_add_block(proc, clause, "type-match.case.next"); + ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag_index, index); + ssa_emit_if(proc, cond, body, next_cond); + proc->curr_block = next_cond; + + proc->curr_block = body; + + ssa_push_target_list(proc, done, NULL, NULL); + ssa_open_scope(proc); + ssa_build_stmt_list(proc, cc->stmts); + ssa_close_scope(proc, ssaDeferExit_Default, body); + ssa_pop_target_list(proc); + + ssa_emit_jump(proc, done); + proc->curr_block = next_cond; + } + + if (default_block != NULL) { + ssa_emit_jump(proc, default_block); + proc->curr_block = default_block; + + ssa_push_target_list(proc, done, NULL, NULL); + ssa_open_scope(proc); + ssa_build_stmt_list(proc, default_stmts); + ssa_close_scope(proc, ssaDeferExit_Default, default_block); + ssa_pop_target_list(proc); + } + + ssa_emit_jump(proc, done); + proc->curr_block = done; + case_end; + + case_ast_node(bs, BranchStmt, node); + ssaBlock *block = NULL; + switch (bs->token.kind) { + case Token_break: + for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { + block = t->break_; + } + break; + case Token_continue: + for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { + block = t->continue_; + } + break; + case Token_fallthrough: + for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { + block = t->fallthrough_; + } + break; + } + if (block != NULL) { + ssa_emit_defer_stmts(proc, ssaDeferExit_Branch, block); + } + switch (bs->token.kind) { + case Token_break: ssa_emit_comment(proc, make_string("break")); break; + case Token_continue: ssa_emit_comment(proc, make_string("continue")); break; + case Token_fallthrough: ssa_emit_comment(proc, make_string("fallthrough")); break; + } + ssa_emit_jump(proc, block); + ssa_emit_unreachable(proc); + case_end; + + + + case_ast_node(pa, PushAllocator, node); + ssa_emit_comment(proc, make_string("PushAllocator")); + ssa_open_scope(proc); + defer (ssa_close_scope(proc, ssaDeferExit_Default, NULL)); + + ssaValue *context_ptr = ssa_find_implicit_value_backing(proc, ImplicitValue_context); + ssaValue *prev_context = ssa_add_local_generated(proc, t_context); + ssa_emit_store(proc, prev_context, ssa_emit_load(proc, context_ptr)); + + ssa_add_defer_instr(proc, proc->scope_index, ssa_make_instr_store(proc, context_ptr, ssa_emit_load(proc, prev_context))); + + ssaValue *gep = ssa_emit_struct_ep(proc, context_ptr, 1); + ssa_emit_store(proc, gep, ssa_build_expr(proc, pa->expr)); + + ssa_build_stmt(proc, pa->body); + + case_end; + + + case_ast_node(pa, PushContext, node); + ssa_emit_comment(proc, make_string("PushContext")); + ssa_open_scope(proc); + defer (ssa_close_scope(proc, ssaDeferExit_Default, NULL)); + + ssaValue *context_ptr = ssa_find_implicit_value_backing(proc, ImplicitValue_context); + ssaValue *prev_context = ssa_add_local_generated(proc, t_context); + ssa_emit_store(proc, prev_context, ssa_emit_load(proc, context_ptr)); + + ssa_add_defer_instr(proc, proc->scope_index, ssa_make_instr_store(proc, context_ptr, ssa_emit_load(proc, prev_context))); + + ssa_emit_store(proc, context_ptr, ssa_build_expr(proc, pa->expr)); + + ssa_build_stmt(proc, pa->body); + case_end; + + + } +} + + + + + + + + +//////////////////////////////////////////////////////////////// +// +// @Optimizations +// +//////////////////////////////////////////////////////////////// + +#include "ssa_opt.cpp" + + + +//////////////////////////////////////////////////////////////// +// +// @Procedure +// +//////////////////////////////////////////////////////////////// + +void ssa_begin_procedure_body(ssaProcedure *proc) { + array_init(&proc->blocks, heap_allocator()); + array_init(&proc->defer_stmts, heap_allocator()); + array_init(&proc->children, heap_allocator()); + + proc->decl_block = ssa_add_block(proc, proc->type_expr, "decls"); + proc->entry_block = ssa_add_block(proc, proc->type_expr, "entry"); + proc->curr_block = proc->entry_block; + + if (proc->type->Proc.params != NULL) { + auto *params = &proc->type->Proc.params->Tuple; + for (isize i = 0; i < params->variable_count; i++) { + Entity *e = params->variables[i]; + ssaValue *param = ssa_add_param(proc, e); + array_add(&proc->params, param); + } + } +} + + +void ssa_end_procedure_body(ssaProcedure *proc) { + if (proc->type->Proc.result_count == 0) { + ssa_emit_return(proc, NULL); + } + + if (proc->curr_block->instrs.count == 0) { + ssa_emit_unreachable(proc); + } + + proc->curr_block = proc->decl_block; + ssa_emit_jump(proc, proc->entry_block); + + ssa_opt_proc(proc); + +// Number registers + i32 reg_index = 0; + for_array(i, proc->blocks) { + ssaBlock *b = proc->blocks[i]; + b->index = i; + for_array(j, b->instrs) { + ssaValue *value = b->instrs[j]; + GB_ASSERT(value->kind == ssaValue_Instr); + ssaInstr *instr = &value->Instr; + if (ssa_instr_type(instr) == NULL) { // NOTE(bill): Ignore non-returning instructions + continue; + } + value->index = reg_index; + reg_index++; + } + } +} + + +void ssa_insert_code_before_proc(ssaProcedure* proc, ssaProcedure *parent) { + if (parent == NULL) { + if (proc->name == "main") { + ssa_emit_startup_runtime(proc); + } + } +} + +void ssa_build_proc(ssaValue *value, ssaProcedure *parent) { + ssaProcedure *proc = &value->Proc; + + proc->parent = parent; + + if (proc->entity != NULL) { + ssaModule *m = proc->module; + CheckerInfo *info = m->info; + Entity *e = proc->entity; + String filename = e->token.pos.file; + AstFile **found = map_get(&info->files, hash_string(filename)); + GB_ASSERT(found != NULL); + AstFile *f = *found; + ssaDebugInfo *di_file = NULL; + + ssaDebugInfo **di_file_found = map_get(&m->debug_info, hash_pointer(f)); + if (di_file_found) { + di_file = *di_file_found; + GB_ASSERT(di_file->kind == ssaDebugInfo_File); + } else { + di_file = ssa_add_debug_info_file(proc, f); + } + + ssa_add_debug_info_proc(proc, e, proc->name, di_file); + } + + if (proc->body != NULL) { + u32 prev_stmt_state_flags = proc->module->stmt_state_flags; + defer (proc->module->stmt_state_flags = prev_stmt_state_flags); + + if (proc->tags != 0) { + u32 in = proc->tags; + u32 out = proc->module->stmt_state_flags; + defer (proc->module->stmt_state_flags = out); + + if (in & ProcTag_bounds_check) { + out |= StmtStateFlag_bounds_check; + out &= ~StmtStateFlag_no_bounds_check; + } else if (in & ProcTag_no_bounds_check) { + out |= StmtStateFlag_no_bounds_check; + out &= ~StmtStateFlag_bounds_check; + } + } + + + ssa_begin_procedure_body(proc); + ssa_insert_code_before_proc(proc, parent); + ssa_build_stmt(proc, proc->body); + ssa_end_procedure_body(proc); + } +} + + + + + + + +//////////////////////////////////////////////////////////////// +// +// @Module +// +//////////////////////////////////////////////////////////////// + + + +void ssa_module_add_value(ssaModule *m, Entity *e, ssaValue *v) { + map_set(&m->values, hash_pointer(e), v); +} + +void ssa_init_module(ssaModule *m, Checker *c) { + // TODO(bill): Determine a decent size for the arena + isize token_count = c->parser->total_token_count; + isize arena_size = 4 * token_count * gb_size_of(ssaValue); + gb_arena_init_from_allocator(&m->arena, heap_allocator(), arena_size); + gb_arena_init_from_allocator(&m->tmp_arena, heap_allocator(), arena_size); + m->allocator = gb_arena_allocator(&m->arena); + m->tmp_allocator = gb_arena_allocator(&m->tmp_arena); + m->info = &c->info; + m->sizes = c->sizes; + + map_init(&m->values, heap_allocator()); + map_init(&m->members, heap_allocator()); + map_init(&m->debug_info, heap_allocator()); + map_init(&m->type_names, heap_allocator()); + array_init(&m->procs, heap_allocator()); + + // Default states + m->stmt_state_flags = 0; + m->stmt_state_flags |= StmtStateFlag_bounds_check; + + { + // Add type info data + { + String name = make_string(SSA_TYPE_INFO_DATA_NAME); + isize count = c->info.type_info_map.entries.count; + Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name), make_type_array(m->allocator, t_type_info, count)); + ssaValue *g = ssa_make_value_global(m->allocator, e, NULL); + g->Global.is_private = true; + ssa_module_add_value(m, e, g); + map_set(&m->members, hash_string(name), g); + } + + // Type info member buffer + { + // NOTE(bill): Removes need for heap allocation by making it global memory + isize count = 0; + + for_array(entry_index, m->info->type_info_map.entries) { + auto *entry = &m->info->type_info_map.entries[entry_index]; + Type *t = cast(Type *)cast(uintptr)entry->key.key; + + switch (t->kind) { + case Type_Record: + switch (t->Record.kind) { + case TypeRecord_Struct: + case TypeRecord_RawUnion: + count += t->Record.field_count; + } + break; + case Type_Tuple: + count += t->Tuple.variable_count; + break; + } + } + + String name = make_string(SSA_TYPE_INFO_DATA_MEMBER_NAME); + Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name), + make_type_array(m->allocator, t_type_info_member, count)); + ssaValue *g = ssa_make_value_global(m->allocator, e, NULL); + ssa_module_add_value(m, e, g); + map_set(&m->members, hash_string(name), g); + } + } + + { + ssaDebugInfo *di = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_CompileUnit); + di->CompileUnit.file = m->info->files.entries[0].value; // Zeroth is the init file + di->CompileUnit.producer = make_string("odin"); + + map_set(&m->debug_info, hash_pointer(m), di); + } +} + +void ssa_destroy_module(ssaModule *m) { + map_destroy(&m->values); + map_destroy(&m->members); + map_destroy(&m->type_names); + map_destroy(&m->debug_info); + array_free(&m->procs); + gb_arena_free(&m->arena); +} + + + +//////////////////////////////////////////////////////////////// +// +// @Code Generation +// +//////////////////////////////////////////////////////////////// + + + +struct ssaGen { + ssaModule module; + gbFile output_file; +}; + + +b32 ssa_gen_init(ssaGen *s, Checker *c) { + if (global_error_collector.count != 0) { + return false; + } + + isize tc = c->parser->total_token_count; + if (tc < 2) { + return false; + } + + ssa_init_module(&s->module, c); + s->module.generate_debug_info = false; + + // TODO(bill): generate appropriate output name + int pos = cast(int)string_extension_position(c->parser->init_fullpath); + gbFileError err = gb_file_create(&s->output_file, gb_bprintf("%.*s.ll", pos, c->parser->init_fullpath.text)); + if (err != gbFileError_None) { + return false; + } + + return true; +} + +void ssa_gen_destroy(ssaGen *s) { + ssa_destroy_module(&s->module); + gb_file_close(&s->output_file); +} + +String ssa_mangle_name(ssaGen *s, String path, String name) { + // NOTE(bill): prefix names not in the init scope + // TODO(bill): make robust and not just rely on the file's name + + ssaModule *m = &s->module; + CheckerInfo *info = m->info; + gbAllocator a = m->allocator; + AstFile *file = *map_get(&info->files, hash_string(path)); + + char *str = gb_alloc_array(a, char, path.len+1); + gb_memmove(str, path.text, path.len); + str[path.len] = 0; + for (isize i = 0; i < path.len; i++) { + if (str[i] == '\\') { + str[i] = '/'; + } + } + + char const *base = gb_path_base_name(str); + char const *ext = gb_path_extension(base); + isize base_len = ext-1-base; + + isize max_len = base_len + 1 + 10 + 1 + name.len; + u8 *new_name = gb_alloc_array(a, u8, max_len); + isize new_name_len = gb_snprintf( + cast(char *)new_name, max_len, + "%.*s-%u.%.*s", + cast(int)base_len, base, + file->id, + LIT(name)); + + return make_string(new_name, new_name_len-1); +} + + +void ssa_gen_tree(ssaGen *s) { + ssaModule *m = &s->module; + CheckerInfo *info = m->info; + gbAllocator a = m->allocator; + + if (v_zero == NULL) { + v_zero = ssa_make_const_int (m->allocator, 0); + v_one = ssa_make_const_int (m->allocator, 1); + v_zero32 = ssa_make_const_i32 (m->allocator, 0); + v_one32 = ssa_make_const_i32 (m->allocator, 1); + v_two32 = ssa_make_const_i32 (m->allocator, 2); + v_false = ssa_make_const_bool(m->allocator, false); + v_true = ssa_make_const_bool(m->allocator, true); + } + + isize global_variable_max_count = 0; + Entity *entry_point = NULL; + + for_array(i, info->entities.entries) { + auto *entry = &info->entities.entries[i]; + Entity *e = cast(Entity *)cast(uintptr)entry->key.key; + String name = e->token.string; + if (e->kind == Entity_Variable) { + global_variable_max_count++; + } else if (e->kind == Entity_Procedure) { + if (e->scope->is_init && name == "main") { + entry_point = e; + } + } + } + + struct ssaGlobalVariable { + ssaValue *var, *init; + DeclInfo *decl; + }; + Array global_variables; + array_init(&global_variables, m->tmp_allocator, global_variable_max_count); + + m->min_dep_map = generate_minimum_dependency_map(info, entry_point); + + for_array(i, info->entities.entries) { + auto *entry = &info->entities.entries[i]; + Entity *e = cast(Entity *)entry->key.ptr; + String name = e->token.string; + DeclInfo *decl = entry->value; + Scope *scope = e->scope; + + if (!scope->is_file) { + continue; + } + + if (map_get(&m->min_dep_map, hash_pointer(e)) == NULL) { + // NOTE(bill): Nothing depends upon it so doesn't need to be built + continue; + } + + if (!scope->is_global && !scope->is_init) { + name = ssa_mangle_name(s, e->token.pos.file, name); + } + + + switch (e->kind) { + case Entity_TypeName: + GB_ASSERT(e->type->kind == Type_Named); + map_set(&m->type_names, hash_pointer(e->type), name); + ssa_gen_global_type_name(m, e, name); + break; + + case Entity_Variable: { + ssaValue *g = ssa_make_value_global(a, e, NULL); + if (decl->var_decl_tags & VarDeclTag_thread_local) { + g->Global.is_thread_local = true; + } + ssaGlobalVariable var = {}; + var.var = g; + var.decl = decl; + + if (decl->init_expr != NULL) { + TypeAndValue *tav = map_get(&info->types, hash_pointer(decl->init_expr)); + if (tav != NULL) { + if (tav->value.kind != ExactValue_Invalid) { + ExactValue v = tav->value; + // if (v.kind != ExactValue_String) { + g->Global.value = ssa_add_module_constant(m, tav->type, v); + // } + } + } + } + + if (g->Global.value == NULL) { + array_add(&global_variables, var); + } + + map_set(&m->values, hash_pointer(e), g); + map_set(&m->members, hash_string(name), g); + } break; + + case Entity_Procedure: { + auto *pd = &decl->proc_decl->ProcDecl; + String original_name = name; + AstNode *body = pd->body; + if (pd->tags & ProcTag_foreign) { + name = pd->name->Ident.string; + } + if (pd->foreign_name.len > 0) { + name = pd->foreign_name; + } else if (pd->link_name.len > 0) { + name = pd->link_name; + } + + ssaValue *p = ssa_make_value_procedure(a, m, e, e->type, decl->type_expr, body, name); + p->Proc.tags = pd->tags; + + map_set(&m->values, hash_pointer(e), p); + HashKey hash_name = hash_string(name); + if (map_get(&m->members, hash_name) == NULL) { + map_set(&m->members, hash_name, p); + } + } break; + } + } + + for_array(i, m->members.entries) { + auto *entry = &m->members.entries[i]; + ssaValue *v = entry->value; + if (v->kind == ssaValue_Proc) + ssa_build_proc(v, NULL); + } + + ssaDebugInfo *compile_unit = m->debug_info.entries[0].value; + GB_ASSERT(compile_unit->kind == ssaDebugInfo_CompileUnit); + ssaDebugInfo *all_procs = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_AllProcs); + + isize all_proc_max_count = 0; + for_array(i, m->debug_info.entries) { + auto *entry = &m->debug_info.entries[i]; + ssaDebugInfo *di = entry->value; + di->id = i; + if (di->kind == ssaDebugInfo_Proc) { + all_proc_max_count++; + } + } + + array_init(&all_procs->AllProcs.procs, m->allocator, all_proc_max_count); + map_set(&m->debug_info, hash_pointer(all_procs), all_procs); // NOTE(bill): This doesn't need to be mapped + compile_unit->CompileUnit.all_procs = all_procs; + + + for_array(i, m->debug_info.entries) { + auto *entry = &m->debug_info.entries[i]; + ssaDebugInfo *di = entry->value; + di->id = i; + if (di->kind == ssaDebugInfo_Proc) { + array_add(&all_procs->AllProcs.procs, di); + } + } + + + { // Startup Runtime + // Cleanup(bill): probably better way of doing code insertion + String name = make_string(SSA_STARTUP_RUNTIME_PROC_NAME); + Type *proc_type = make_type_proc(a, gb_alloc_item(a, Scope), + NULL, 0, + NULL, 0, false); + AstNode *body = gb_alloc_item(a, AstNode); + ssaValue *p = ssa_make_value_procedure(a, m, NULL, proc_type, NULL, body, name); + Token token = {}; + token.string = name; + Entity *e = make_entity_procedure(a, NULL, token, proc_type); + + map_set(&m->values, hash_pointer(e), p); + map_set(&m->members, hash_string(name), p); + + ssaProcedure *proc = &p->Proc; + proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea? + + ssa_begin_procedure_body(proc); + + // TODO(bill): Should do a dependency graph do check which order to initialize them in? + for_array(i, global_variables) { + ssaGlobalVariable *var = &global_variables[i]; + if (var->decl->init_expr != NULL) { + var->init = ssa_build_expr(proc, var->decl->init_expr); + } + } + + // NOTE(bill): Initialize constants first + for_array(i, global_variables) { + ssaGlobalVariable *var = &global_variables[i]; + if (var->init != NULL) { + if (var->init->kind == ssaValue_Constant) { + ssa_emit_store(proc, var->var, var->init); + } + } + } + + for_array(i, global_variables) { + ssaGlobalVariable *var = &global_variables[i]; + if (var->init != NULL) { + if (var->init->kind != ssaValue_Constant) { + ssa_emit_store(proc, var->var, var->init); + } + } + } + + { // NOTE(bill): Setup type_info data + // TODO(bill): Try and make a lot of this constant aggregate literals in LLVM IR + ssaValue *type_info_data = NULL; + ssaValue *type_info_member_data = NULL; + + ssaValue **found = NULL; + found = map_get(&proc->module->members, hash_string(make_string(SSA_TYPE_INFO_DATA_NAME))); + GB_ASSERT(found != NULL); + type_info_data = *found; + + found = map_get(&proc->module->members, hash_string(make_string(SSA_TYPE_INFO_DATA_MEMBER_NAME))); + GB_ASSERT(found != NULL); + type_info_member_data = *found; + + CheckerInfo *info = proc->module->info; + + // Useful types + Type *t_i64_slice_ptr = make_type_pointer(a, make_type_slice(a, t_i64)); + Type *t_string_slice_ptr = make_type_pointer(a, make_type_slice(a, t_string)); + + auto get_type_info_ptr = [](ssaProcedure *proc, ssaValue *type_info_data, Type *type) -> ssaValue * { + i32 index = cast(i32)ssa_type_info_index(proc->module->info, type); + // gb_printf_err("%d %s\n", index, type_to_string(type)); + return ssa_emit_array_ep(proc, type_info_data, index); + }; + + i32 type_info_member_index = 0; + + auto type_info_member_offset = [](ssaProcedure *proc, ssaValue *data, isize count, i32 *index) -> ssaValue * { + ssaValue *offset = ssa_emit_array_ep(proc, data, *index); + *index += count; + return offset; + }; + + + + for_array(type_info_map_index, info->type_info_map.entries) { + auto *entry = &info->type_info_map.entries[type_info_map_index]; + Type *t = cast(Type *)cast(uintptr)entry->key.key; + t = default_type(t); + isize entry_index = entry->value; + + ssaValue *tag = NULL; + + switch (t->kind) { + case Type_Named: { + tag = ssa_add_local_generated(proc, t_type_info_named); + + // TODO(bill): Which is better? The mangled name or actual name? + ssaValue *name = ssa_make_const_string(a, t->Named.type_name->token.string); + ssaValue *gtip = get_type_info_ptr(proc, type_info_data, t->Named.base); + + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), name); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), gtip); + } break; + + case Type_Basic: + switch (t->Basic.kind) { + case Basic_bool: + tag = ssa_add_local_generated(proc, t_type_info_boolean); + break; + case Basic_i8: + case Basic_i16: + case Basic_i32: + case Basic_i64: + case Basic_u8: + case Basic_u16: + case Basic_u32: + case Basic_u64: + case Basic_int: + case Basic_uint: { + tag = ssa_add_local_generated(proc, t_type_info_integer); + b32 is_unsigned = (t->Basic.flags & BasicFlag_Unsigned) != 0; + ssaValue *bits = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); + ssaValue *is_signed = ssa_make_const_bool(a, !is_unsigned); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), bits); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), is_signed); + } break; + + case Basic_f32: + case Basic_f64: { + tag = ssa_add_local_generated(proc, t_type_info_float); + ssaValue *bits = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), bits); + } break; + + case Basic_rawptr: + tag = ssa_add_local_generated(proc, t_type_info_pointer); + break; + + case Basic_string: + tag = ssa_add_local_generated(proc, t_type_info_string); + break; + + case Basic_any: + tag = ssa_add_local_generated(proc, t_type_info_any); + break; + } + break; + + case Type_Pointer: { + tag = ssa_add_local_generated(proc, t_type_info_pointer); + ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Pointer.elem); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); + } break; + case Type_Maybe: { + tag = ssa_add_local_generated(proc, t_type_info_maybe); + ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Maybe.elem); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); + } break; + case Type_Array: { + tag = ssa_add_local_generated(proc, t_type_info_array); + ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Array.elem); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); + + isize ez = type_size_of(m->sizes, a, t->Array.elem); + ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1); + ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez)); + + ssaValue *count = ssa_emit_struct_ep(proc, tag, 2); + ssa_emit_store(proc, count, ssa_make_const_int(a, t->Array.count)); + + } break; + case Type_Slice: { + tag = ssa_add_local_generated(proc, t_type_info_slice); + ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Slice.elem); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); + + isize ez = type_size_of(m->sizes, a, t->Slice.elem); + ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1); + ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez)); + + } break; + case Type_Vector: { + tag = ssa_add_local_generated(proc, t_type_info_vector); + ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Vector.elem); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); + + isize ez = type_size_of(m->sizes, a, t->Vector.elem); + ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1); + ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez)); + + ssaValue *count = ssa_emit_struct_ep(proc, tag, 2); + ssa_emit_store(proc, count, ssa_make_const_int(a, t->Vector.count)); + + } break; + case Type_Record: { + switch (t->Record.kind) { + case TypeRecord_Struct: { + tag = ssa_add_local_generated(proc, t_type_info_struct); + + { + ssaValue *packed = ssa_make_const_bool(a, t->Record.struct_is_packed); + ssaValue *ordered = ssa_make_const_bool(a, t->Record.struct_is_ordered); + ssaValue *size = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); + ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), size); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 3), packed); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 4), ordered); + } + + ssaValue *memory = type_info_member_offset(proc, type_info_member_data, t->Record.field_count, &type_info_member_index); + + type_set_offsets(m->sizes, a, t); // NOTE(bill): Just incase the offsets have not been set yet + for (isize source_index = 0; source_index < t->Record.field_count; source_index++) { + // TODO(bill): Order fields in source order not layout order + Entity *f = t->Record.fields_in_src_order[source_index]; + ssaValue *tip = get_type_info_ptr(proc, type_info_data, f->type); + i64 foffset = t->Record.struct_offsets[f->Variable.field_index]; + GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field); + + ssaValue *field = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, source_index)); + ssaValue *name = ssa_emit_struct_ep(proc, field, 0); + ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1); + ssaValue *offset = ssa_emit_struct_ep(proc, field, 2); + + if (f->token.string.len > 0) { + ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string)); + } + ssa_emit_store(proc, type_info, tip); + ssa_emit_store(proc, offset, ssa_make_const_int(a, foffset)); + } + + Type *slice_type = make_type_slice(a, t_type_info_member); + Type *slice_type_ptr = make_type_pointer(a, slice_type); + ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0); + ssaValue *field_count = ssa_make_const_int(a, t->Record.field_count); + + ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *len = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *cap = ssa_emit_struct_ep(proc, slice, 2); + + ssa_emit_store(proc, elem, memory); + ssa_emit_store(proc, len, field_count); + ssa_emit_store(proc, cap, field_count); + } break; + case TypeRecord_Union: + tag = ssa_add_local_generated(proc, t_type_info_union); + { + ssaValue *size = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); + ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), size); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align); + } + break; + case TypeRecord_RawUnion: { + tag = ssa_add_local_generated(proc, t_type_info_raw_union); + { + ssaValue *size = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); + ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), size); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align); + } + + ssaValue *memory = type_info_member_offset(proc, type_info_member_data, t->Record.field_count, &type_info_member_index); + + for (isize i = 0; i < t->Record.field_count; i++) { + ssaValue *field = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, i)); + ssaValue *name = ssa_emit_struct_ep(proc, field, 0); + ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1); + ssaValue *offset = ssa_emit_struct_ep(proc, field, 2); + + Entity *f = t->Record.fields[i]; + ssaValue *tip = get_type_info_ptr(proc, type_info_data, f->type); + + if (f->token.string.len > 0) { + ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string)); + } + ssa_emit_store(proc, type_info, tip); + ssa_emit_store(proc, offset, ssa_make_const_int(a, 0)); + } + + Type *slice_type = make_type_slice(a, t_type_info_member); + Type *slice_type_ptr = make_type_pointer(a, slice_type); + ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0); + ssaValue *field_count = ssa_make_const_int(a, t->Record.field_count); + + ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *len = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *cap = ssa_emit_struct_ep(proc, slice, 2); + + ssa_emit_store(proc, elem, memory); + ssa_emit_store(proc, len, field_count); + ssa_emit_store(proc, cap, field_count); + } break; + case TypeRecord_Enum: { + tag = ssa_add_local_generated(proc, t_type_info_enum); + Type *enum_base = t->Record.enum_base; + if (enum_base == NULL) { + enum_base = t_int; + } + ssaValue *base = ssa_emit_struct_ep(proc, tag, 0); + ssa_emit_store(proc, base, get_type_info_ptr(proc, type_info_data, enum_base)); + + if (t->Record.other_field_count > 0) { + Entity **fields = t->Record.other_fields; + isize count = t->Record.other_field_count; + ssaValue *value_array = NULL; + ssaValue *name_array = NULL; + + + { + Token token = {Token_Identifier}; + i32 id = cast(i32)entry_index; + char name_base[] = "__$enum_values"; + isize name_len = gb_size_of(name_base) + 10; + token.string.text = gb_alloc_array(a, u8, name_len); + token.string.len = gb_snprintf(cast(char *)token.string.text, name_len, + "%s-%d", name_base, id)-1; + Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_i64, count)); + value_array = ssa_make_value_global(a, e, NULL); + value_array->Global.is_private = true; + ssa_module_add_value(m, e, value_array); + map_set(&m->members, hash_string(token.string), value_array); + } + { + Token token = {Token_Identifier}; + i32 id = cast(i32)entry_index; + char name_base[] = "__$enum_names"; + isize name_len = gb_size_of(name_base) + 10; + token.string.text = gb_alloc_array(a, u8, name_len); + token.string.len = gb_snprintf(cast(char *)token.string.text, name_len, + "%s-%d", name_base, id)-1; + Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_string, count)); + name_array = ssa_make_value_global(a, e, NULL); + name_array->Global.is_private = true; + ssa_module_add_value(m, e, name_array); + map_set(&m->members, hash_string(token.string), name_array); + } + + for (isize i = 0; i < count; i++) { + ssaValue *value_gep = ssa_emit_struct_ep(proc, value_array, i); + ssaValue *name_gep = ssa_emit_struct_ep(proc, name_array, i); + + ssa_emit_store(proc, value_gep, ssa_make_const_i64(a, fields[i]->Constant.value.value_integer)); + ssa_emit_store(proc, name_gep, ssa_make_const_string(a, fields[i]->token.string)); + } + + ssaValue *v_count = ssa_make_const_int(a, count); + + + ssaValue *values = ssa_emit_struct_ep(proc, tag, 1); + ssaValue *names = ssa_emit_struct_ep(proc, tag, 2); + ssaValue *value_slice = ssa_add_local_generated(proc, type_deref(t_i64_slice_ptr)); + ssaValue *name_slice = ssa_add_local_generated(proc, type_deref(t_string_slice_ptr)); + + ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 0), ssa_array_elem(proc, value_array)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 1), v_count); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 2), v_count); + + ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 0), ssa_array_elem(proc, name_array)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 1), v_count); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 2), v_count); + + ssa_emit_store(proc, values, ssa_emit_load(proc, value_slice)); + ssa_emit_store(proc, names, ssa_emit_load(proc, name_slice)); + } + } break; + } + } break; + + case Type_Tuple: { + tag = ssa_add_local_generated(proc, t_type_info_tuple); + + { + ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t)); + ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align); + } + + ssaValue *memory = type_info_member_offset(proc, type_info_member_data, t->Tuple.variable_count, &type_info_member_index); + + for (isize i = 0; i < t->Tuple.variable_count; i++) { + ssaValue *field = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, i)); + ssaValue *name = ssa_emit_struct_ep(proc, field, 0); + ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1); + // NOTE(bill): offset is not used for tuples + + Entity *f = t->Tuple.variables[i]; + ssaValue *tip = get_type_info_ptr(proc, type_info_data, f->type); + + if (f->token.string.len > 0) { + ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string)); + } + ssa_emit_store(proc, type_info, tip); + } + + Type *slice_type = make_type_slice(a, t_type_info_member); + Type *slice_type_ptr = make_type_pointer(a, slice_type); + ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0); + ssaValue *variable_count = ssa_make_const_int(a, t->Tuple.variable_count); + + ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0); + ssaValue *len = ssa_emit_struct_ep(proc, slice, 1); + ssaValue *cap = ssa_emit_struct_ep(proc, slice, 2); + + ssa_emit_store(proc, elem, memory); + ssa_emit_store(proc, len, variable_count); + ssa_emit_store(proc, cap, variable_count); + } break; + + case Type_Proc: { + tag = ssa_add_local_generated(proc, t_type_info_procedure); + + ssaValue *params = ssa_emit_struct_ep(proc, tag, 0); + ssaValue *results = ssa_emit_struct_ep(proc, tag, 1); + ssaValue *variadic = ssa_emit_struct_ep(proc, tag, 2); + + if (t->Proc.params) { + ssa_emit_store(proc, params, get_type_info_ptr(proc, type_info_data, t->Proc.params)); + } + if (t->Proc.results) { + ssa_emit_store(proc, results, get_type_info_ptr(proc, type_info_data, t->Proc.results)); + } + ssa_emit_store(proc, variadic, ssa_make_const_bool(a, t->Proc.variadic)); + + // TODO(bill): Type_Info for procedures + } break; + } + + if (tag != NULL) { + ssaValue *gep = ssa_emit_array_ep(proc, type_info_data, entry_index); + ssaValue *val = ssa_emit_conv(proc, ssa_emit_load(proc, tag), t_type_info); + ssa_emit_store(proc, gep, val); + } + } + } + + ssa_end_procedure_body(proc); + } + + for_array(i, m->procs) { + ssa_build_proc(m->procs[i], m->procs[i]->Proc.parent); + } + + + // m->layout = make_string("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"); +} + diff --git a/src/ssa/build.cpp b/src/ssa/build.cpp deleted file mode 100644 index 8540a39ad..000000000 --- a/src/ssa/build.cpp +++ /dev/null @@ -1,1965 +0,0 @@ -void ssa_emit_jump (ssaProcedure *proc, ssaBlock *block); -void ssa_build_stmt (ssaProcedure *proc, AstNode *s); -ssaAddr ssa_build_addr (ssaProcedure *proc, AstNode *expr); -void ssa_build_proc (ssaValue *value, ssaProcedure *parent); -void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name); - - - - -void ssa_push_target_list(ssaProcedure *proc, ssaBlock *break_, ssaBlock *continue_, ssaBlock *fallthrough_) { - ssaTargetList *tl = gb_alloc_item(proc->module->allocator, ssaTargetList); - tl->prev = proc->target_list; - tl->break_ = break_; - tl->continue_ = continue_; - tl->fallthrough_ = fallthrough_; - proc->target_list = tl; -} - -void ssa_pop_target_list(ssaProcedure *proc) { - proc->target_list = proc->target_list->prev; -} - - -void ssa_mangle_sub_type_name(ssaModule *m, Entity *field, String parent) { - if (field->kind != Entity_TypeName) { - return; - } - String cn = field->token.string; - - isize len = parent.len + 1 + cn.len; - String child = {NULL, len}; - child.text = gb_alloc_array(m->allocator, u8, len); - - isize i = 0; - gb_memmove(child.text+i, parent.text, parent.len); - i += parent.len; - child.text[i++] = '.'; - gb_memmove(child.text+i, cn.text, cn.len); - - map_set(&m->type_names, hash_pointer(field->type), child); - ssa_gen_global_type_name(m, field, child); -} - -void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) { - ssaValue *t = ssa_make_value_type_name(m->allocator, name, e->type); - ssa_module_add_value(m, e, t); - map_set(&m->members, hash_string(name), t); - - Type *bt = base_type(e->type); - if (bt->kind == Type_Record) { - auto *s = &bt->Record; - for (isize j = 0; j < s->other_field_count; j++) { - ssa_mangle_sub_type_name(m, s->other_fields[j], name); - } - } - - if (is_type_union(bt)) { - auto *s = &bt->Record; - // NOTE(bill): Zeroth entry is null (for `match type` stmts) - for (isize j = 1; j < s->field_count; j++) { - ssa_mangle_sub_type_name(m, s->fields[j], name); - } - } -} - - - - -void ssa_build_defer_stmt(ssaProcedure *proc, ssaDefer d) { - ssaBlock *b = ssa_add_block(proc, NULL, "defer"); - // NOTE(bill): The prev block may defer injection before it's terminator - ssaInstr *last_instr = ssa_get_last_instr(proc->curr_block); - if (last_instr == NULL || !ssa_is_instr_terminating(last_instr)) { - ssa_emit_jump(proc, b); - } - proc->curr_block = b; - ssa_emit_comment(proc, make_string("defer")); - if (d.kind == ssaDefer_Node) { - ssa_build_stmt(proc, d.stmt); - } else if (d.kind == ssaDefer_Instr) { - // NOTE(bill): Need to make a new copy - ssaValue *instr = cast(ssaValue *)gb_alloc_copy(proc->module->allocator, d.instr, gb_size_of(ssaValue)); - ssa_emit(proc, instr); - } -} - - - -ssaValue *ssa_find_global_variable(ssaProcedure *proc, String name) { - ssaValue **value = map_get(&proc->module->members, hash_string(name)); - GB_ASSERT_MSG(value != NULL, "Unable to find global variable `%.*s`", LIT(name)); - return *value; -} - -ssaValue *ssa_find_implicit_value_backing(ssaProcedure *proc, ImplicitValueId id) { - Entity *e = proc->module->info->implicit_values[id]; - GB_ASSERT(e->kind == Entity_ImplicitValue); - Entity *backing = e->ImplicitValue.backing; - ssaValue **value = map_get(&proc->module->values, hash_pointer(backing)); - GB_ASSERT_MSG(value != NULL, "Unable to find implicit value backing `%.*s`", LIT(backing->token.string)); - return *value; -} - - - -ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue *tv) { - expr = unparen_expr(expr); - switch (expr->kind) { - case_ast_node(bl, BasicLit, expr); - GB_PANIC("Non-constant basic literal"); - case_end; - - case_ast_node(i, Ident, expr); - Entity *e = *map_get(&proc->module->info->uses, hash_pointer(expr)); - if (e->kind == Entity_Builtin) { - Token token = ast_node_token(expr); - GB_PANIC("TODO(bill): ssa_build_single_expr Entity_Builtin `%.*s`\n" - "\t at %.*s(%td:%td)", LIT(builtin_procs[e->Builtin.id].name), - LIT(token.pos.file), token.pos.line, token.pos.column); - return NULL; - } else if (e->kind == Entity_Nil) { - return ssa_make_value_nil(proc->module->allocator, tv->type); - } else if (e->kind == Entity_ImplicitValue) { - return ssa_emit_load(proc, ssa_find_implicit_value_backing(proc, e->ImplicitValue.id)); - } - - auto *found = map_get(&proc->module->values, hash_pointer(e)); - if (found) { - ssaValue *v = *found; - if (v->kind == ssaValue_Proc) { - return v; - } - // if (e->kind == Entity_Variable && e->Variable.param) { - // return v; - // } - return ssa_emit_load(proc, v); - } - return NULL; - case_end; - - case_ast_node(re, RunExpr, expr); - // TODO(bill): Run Expression - return ssa_build_single_expr(proc, re->expr, tv); - case_end; - - case_ast_node(de, DerefExpr, expr); - return ssa_addr_load(proc, ssa_build_addr(proc, expr)); - case_end; - - case_ast_node(se, SelectorExpr, expr); - TypeAndValue *tav = map_get(&proc->module->info->types, hash_pointer(expr)); - GB_ASSERT(tav != NULL); - return ssa_addr_load(proc, ssa_build_addr(proc, expr)); - case_end; - - case_ast_node(ue, UnaryExpr, expr); - switch (ue->op.kind) { - case Token_Pointer: - return ssa_emit_ptr_offset(proc, ssa_build_addr(proc, ue->expr).addr, v_zero); // Make a copy of the pointer - - case Token_Maybe: - return ssa_emit_conv(proc, ssa_build_expr(proc, ue->expr), type_of_expr(proc->module->info, expr)); - - case Token_Add: - return ssa_build_expr(proc, ue->expr); - - case Token_Sub: // NOTE(bill): -`x` == 0 - `x` - return ssa_emit_arith(proc, ue->op.kind, v_zero, ssa_build_expr(proc, ue->expr), tv->type); - - case Token_Not: // Boolean not - case Token_Xor: { // Bitwise not - // NOTE(bill): "not" `x` == `x` "xor" `-1` - ssaValue *left = ssa_build_expr(proc, ue->expr); - ssaValue *right = ssa_add_module_constant(proc->module, tv->type, make_exact_value_integer(-1)); - return ssa_emit_arith(proc, ue->op.kind, - left, right, - tv->type); - } break; - } - case_end; - - case_ast_node(be, BinaryExpr, expr); - switch (be->op.kind) { - case Token_Add: - case Token_Sub: - case Token_Mul: - case Token_Quo: - case Token_Mod: - case Token_And: - case Token_Or: - case Token_Xor: - case Token_AndNot: - case Token_Shl: - case Token_Shr: - return ssa_emit_arith(proc, be->op.kind, - ssa_build_expr(proc, be->left), - ssa_build_expr(proc, be->right), - tv->type); - - - case Token_CmpEq: - case Token_NotEq: - case Token_Lt: - case Token_LtEq: - case Token_Gt: - case Token_GtEq: { - ssaValue *left = ssa_build_expr(proc, be->left); - ssaValue *right = ssa_build_expr(proc, be->right); - - ssaValue *cmp = ssa_emit_comp(proc, be->op.kind, left, right); - return ssa_emit_conv(proc, cmp, default_type(tv->type)); - } break; - - case Token_CmpAnd: - case Token_CmpOr: - return ssa_emit_logical_binary_expr(proc, expr); - - case Token_as: - ssa_emit_comment(proc, make_string("cast - as")); - return ssa_emit_conv(proc, ssa_build_expr(proc, be->left), tv->type); - - case Token_transmute: - ssa_emit_comment(proc, make_string("cast - transmute")); - return ssa_emit_transmute(proc, ssa_build_expr(proc, be->left), tv->type); - - case Token_down_cast: - ssa_emit_comment(proc, make_string("cast - down_cast")); - return ssa_emit_down_cast(proc, ssa_build_expr(proc, be->left), tv->type); - - case Token_union_cast: - ssa_emit_comment(proc, make_string("cast - union_cast")); - return ssa_emit_union_cast(proc, ssa_build_expr(proc, be->left), tv->type); - - default: - GB_PANIC("Invalid binary expression"); - break; - } - case_end; - - case_ast_node(pl, ProcLit, expr); - // NOTE(bill): Generate a new name - // parent$count - isize name_len = proc->name.len + 1 + 8 + 1; - u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); - name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s$%d", LIT(proc->name), cast(i32)proc->children.count); - String name = make_string(name_text, name_len-1); - - Type *type = type_of_expr(proc->module->info, expr); - ssaValue *value = ssa_make_value_procedure(proc->module->allocator, - proc->module, NULL, type, pl->type, pl->body, name); - - value->Proc.tags = pl->tags; - - array_add(&proc->children, &value->Proc); - ssa_build_proc(value, proc); - - return value; - case_end; - - - case_ast_node(cl, CompoundLit, expr); - ssa_emit_comment(proc, make_string("CompoundLit")); - Type *type = type_of_expr(proc->module->info, expr); - Type *bt = base_type(type); - ssaValue *v = ssa_add_local_generated(proc, type); - - Type *et = NULL; - switch (bt->kind) { - case Type_Vector: et = bt->Vector.elem; break; - case Type_Array: et = bt->Array.elem; break; - case Type_Slice: et = bt->Slice.elem; break; - } - - auto is_elem_const = [](ssaModule *m, AstNode *elem, Type *elem_type) -> b32 { - if (base_type(elem_type) == t_any) { - return false; - } - if (elem->kind == AstNode_FieldValue) { - elem = elem->FieldValue.value; - } - TypeAndValue *tav = type_and_value_of_expression(m->info, elem); - GB_ASSERT(tav != NULL); - return tav->value.kind != ExactValue_Invalid; - }; - - switch (bt->kind) { - default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break; - - case Type_Vector: { - ssaValue *result = ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr)); - for_array(index, cl->elems) { - AstNode *elem = cl->elems[index]; - if (is_elem_const(proc->module, elem, et)) { - continue; - } - ssaValue *field_elem = ssa_build_expr(proc, elem); - Type *t = ssa_type(field_elem); - GB_ASSERT(t->kind != Type_Tuple); - ssaValue *ev = ssa_emit_conv(proc, field_elem, et); - ssaValue *i = ssa_make_const_int(proc->module->allocator, index); - result = ssa_emit(proc, ssa_make_instr_insert_element(proc, result, ev, i)); - } - - if (cl->elems.count == 1 && bt->Vector.count > 1) { - isize index_count = bt->Vector.count; - i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count); - for (isize i = 0; i < index_count; i++) { - indices[i] = 0; - } - ssaValue *sv = ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, result, indices, index_count)); - ssa_emit_store(proc, v, sv); - return ssa_emit_load(proc, v); - } - return result; - } break; - - case Type_Record: { - GB_ASSERT(is_type_struct(bt)); - auto *st = &bt->Record; - if (cl->elems.count > 0) { - ssa_emit_store(proc, v, ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr))); - for_array(field_index, cl->elems) { - AstNode *elem = cl->elems[field_index]; - - ssaValue *field_expr = NULL; - Entity *field = NULL; - isize index = field_index; - - if (elem->kind == AstNode_FieldValue) { - ast_node(fv, FieldValue, elem); - Selection sel = lookup_field(proc->module->allocator, bt, fv->field->Ident.string, false); - index = sel.index[0]; - elem = fv->value; - } else { - TypeAndValue *tav = type_and_value_of_expression(proc->module->info, elem); - Selection sel = lookup_field(proc->module->allocator, bt, st->fields_in_src_order[field_index]->token.string, false); - index = sel.index[0]; - } - - field = st->fields[index]; - if (is_elem_const(proc->module, elem, field->type)) { - continue; - } - - field_expr = ssa_build_expr(proc, elem); - - GB_ASSERT(ssa_type(field_expr)->kind != Type_Tuple); - - - - Type *ft = field->type; - ssaValue *fv = ssa_emit_conv(proc, field_expr, ft); - ssaValue *gep = ssa_emit_struct_ep(proc, v, index); - ssa_emit_store(proc, gep, fv); - } - } - } break; - case Type_Array: { - if (cl->elems.count > 0) { - ssa_emit_store(proc, v, ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr))); - for_array(i, cl->elems) { - AstNode *elem = cl->elems[i]; - if (is_elem_const(proc->module, elem, et)) { - continue; - } - ssaValue *field_expr = ssa_build_expr(proc, elem); - Type *t = ssa_type(field_expr); - GB_ASSERT(t->kind != Type_Tuple); - ssaValue *ev = ssa_emit_conv(proc, field_expr, et); - ssaValue *gep = ssa_emit_array_ep(proc, v, i); - ssa_emit_store(proc, gep, ev); - } - } - } break; - case Type_Slice: { - if (cl->elems.count > 0) { - Type *elem_type = bt->Slice.elem; - Type *elem_ptr_type = make_type_pointer(proc->module->allocator, elem_type); - Type *elem_ptr_ptr_type = make_type_pointer(proc->module->allocator, elem_ptr_type); - Type *t_int_ptr = make_type_pointer(proc->module->allocator, t_int); - ssaValue *slice = ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr)); - GB_ASSERT(slice->kind == ssaValue_ConstantSlice); - - ssaValue *data = ssa_emit_array_ep(proc, slice->ConstantSlice.backing_array, v_zero32); - - for_array(i, cl->elems) { - AstNode *elem = cl->elems[i]; - if (is_elem_const(proc->module, elem, et)) { - continue; - } - - ssaValue *field_expr = ssa_build_expr(proc, elem); - Type *t = ssa_type(field_expr); - GB_ASSERT(t->kind != Type_Tuple); - ssaValue *ev = ssa_emit_conv(proc, field_expr, elem_type); - ssaValue *offset = ssa_emit_ptr_offset(proc, data, ssa_make_const_int(proc->module->allocator, i)); - ssa_emit_store(proc, offset, ev); - } - - ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0); - ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1); - ssaValue *gep2 = ssa_emit_struct_ep(proc, v, 1); - - ssa_emit_store(proc, gep0, data); - ssa_emit_store(proc, gep1, ssa_make_const_int(proc->module->allocator, slice->ConstantSlice.count)); - ssa_emit_store(proc, gep2, ssa_make_const_int(proc->module->allocator, slice->ConstantSlice.count)); - } - } break; - } - - return ssa_emit_load(proc, v); - case_end; - - - case_ast_node(ce, CallExpr, expr); - AstNode *p = unparen_expr(ce->proc); - if (p->kind == AstNode_Ident) { - Entity **found = map_get(&proc->module->info->uses, hash_pointer(p)); - if (found && (*found)->kind == Entity_Builtin) { - Entity *e = *found; - switch (e->Builtin.id) { - case BuiltinProc_type_info: { - Type *t = default_type(type_of_expr(proc->module->info, ce->args[0])); - return ssa_type_info(proc, t); - } break; - case BuiltinProc_type_info_of_val: { - Type *t = default_type(type_of_expr(proc->module->info, ce->args[0])); - return ssa_type_info(proc, t); - } break; - - case BuiltinProc_new: { - ssa_emit_comment(proc, make_string("new")); - // new :: proc(Type) -> ^Type - gbAllocator allocator = proc->module->allocator; - - Type *type = type_of_expr(proc->module->info, ce->args[0]); - Type *ptr_type = make_type_pointer(allocator, type); - - i64 s = type_size_of(proc->module->sizes, allocator, type); - i64 a = type_align_of(proc->module->sizes, allocator, type); - - ssaValue **args = gb_alloc_array(allocator, ssaValue *, 2); - args[0] = ssa_make_const_int(allocator, s); - args[1] = ssa_make_const_int(allocator, a); - ssaValue *call = ssa_emit_global_call(proc, "alloc_align", args, 2); - ssaValue *v = ssa_emit_conv(proc, call, ptr_type); - return v; - } break; - - case BuiltinProc_new_slice: { - ssa_emit_comment(proc, make_string("new_slice")); - // new_slice :: proc(Type, len: int[, cap: int]) -> ^Type - gbAllocator allocator = proc->module->allocator; - - Type *type = type_of_expr(proc->module->info, ce->args[0]); - Type *ptr_type = make_type_pointer(allocator, type); - Type *slice_type = make_type_slice(allocator, type); - - i64 s = type_size_of(proc->module->sizes, allocator, type); - i64 a = type_align_of(proc->module->sizes, allocator, type); - - ssaValue *elem_size = ssa_make_const_int(allocator, s); - ssaValue *elem_align = ssa_make_const_int(allocator, a); - - ssaValue *len = ssa_emit_conv(proc, ssa_build_expr(proc, ce->args[1]), t_int); - ssaValue *cap = len; - if (ce->args.count == 3) { - cap = ssa_emit_conv(proc, ssa_build_expr(proc, ce->args[2]), t_int); - } - - ssa_emit_slice_bounds_check(proc, ast_node_token(ce->args[1]), v_zero, len, cap, false); - - ssaValue *slice_size = ssa_emit_arith(proc, Token_Mul, elem_size, cap, t_int); - - ssaValue **args = gb_alloc_array(allocator, ssaValue *, 2); - args[0] = slice_size; - args[1] = elem_align; - ssaValue *call = ssa_emit_global_call(proc, "alloc_align", args, 2); - - ssaValue *ptr = ssa_emit_conv(proc, call, ptr_type, true); - ssaValue *slice = ssa_add_local_generated(proc, slice_type); - - ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0); - ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1); - ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2); - ssa_emit_store(proc, gep0, ptr); - ssa_emit_store(proc, gep1, len); - ssa_emit_store(proc, gep2, cap); - return ssa_emit_load(proc, slice); - } break; - - case BuiltinProc_assert: { - ssa_emit_comment(proc, make_string("assert")); - ssaValue *cond = ssa_build_expr(proc, ce->args[0]); - GB_ASSERT(is_type_boolean(ssa_type(cond))); - - cond = ssa_emit_comp(proc, Token_CmpEq, cond, v_false); - ssaBlock *err = ssa_add_block(proc, NULL, "builtin.assert.err"); - ssaBlock *done = ssa_add_block(proc, NULL, "builtin.assert.done"); - - ssa_emit_if(proc, cond, err, done); - proc->curr_block = err; - - // TODO(bill): Cleanup allocations here - Token token = ast_node_token(ce->args[0]); - TokenPos pos = token.pos; - gbString expr = expr_to_string(ce->args[0]); - defer (gb_string_free(expr)); - isize expr_len = gb_string_length(expr); - String expr_str = {}; - expr_str.text = cast(u8 *)gb_alloc_copy_align(proc->module->allocator, expr, expr_len, 1); - expr_str.len = expr_len; - - ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 4); - args[0] = ssa_make_const_string(proc->module->allocator, pos.file); - args[1] = ssa_make_const_int(proc->module->allocator, pos.line); - args[2] = ssa_make_const_int(proc->module->allocator, pos.column); - args[3] = ssa_make_const_string(proc->module->allocator, expr_str); - ssa_emit_global_call(proc, "__assert", args, 4); - - ssa_emit_jump(proc, done); - proc->curr_block = done; - - return NULL; - } break; - - case BuiltinProc_panic: { - ssa_emit_comment(proc, make_string("panic")); - ssaValue *msg = ssa_build_expr(proc, ce->args[0]); - GB_ASSERT(is_type_string(ssa_type(msg))); - - Token token = ast_node_token(ce->args[0]); - TokenPos pos = token.pos; - - ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 4); - args[0] = ssa_make_const_string(proc->module->allocator, pos.file); - args[1] = ssa_make_const_int(proc->module->allocator, pos.line); - args[2] = ssa_make_const_int(proc->module->allocator, pos.column); - args[3] = msg; - ssa_emit_global_call(proc, "__assert", args, 4); - - return NULL; - } break; - - - case BuiltinProc_copy: { - ssa_emit_comment(proc, make_string("copy")); - // copy :: proc(dst, src: []Type) -> int - AstNode *dst_node = ce->args[0]; - AstNode *src_node = ce->args[1]; - ssaValue *dst_slice = ssa_build_expr(proc, dst_node); - ssaValue *src_slice = ssa_build_expr(proc, src_node); - Type *slice_type = base_type(ssa_type(dst_slice)); - GB_ASSERT(slice_type->kind == Type_Slice); - Type *elem_type = slice_type->Slice.elem; - i64 size_of_elem = type_size_of(proc->module->sizes, proc->module->allocator, elem_type); - - - ssaValue *dst = ssa_emit_conv(proc, ssa_slice_elem(proc, dst_slice), t_rawptr, true); - ssaValue *src = ssa_emit_conv(proc, ssa_slice_elem(proc, src_slice), t_rawptr, true); - - ssaValue *len_dst = ssa_slice_len(proc, dst_slice); - ssaValue *len_src = ssa_slice_len(proc, src_slice); - - ssaValue *cond = ssa_emit_comp(proc, Token_Lt, len_dst, len_src); - ssaValue *len = ssa_emit_select(proc, cond, len_dst, len_src); - - ssaValue *elem_size = ssa_make_const_int(proc->module->allocator, size_of_elem); - ssaValue *byte_count = ssa_emit_arith(proc, Token_Mul, len, elem_size, t_int); - - ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3); - args[0] = dst; - args[1] = src; - args[2] = byte_count; - - ssa_emit_global_call(proc, "__mem_copy", args, 3); - - return len; - } break; - case BuiltinProc_append: { - ssa_emit_comment(proc, make_string("append")); - // append :: proc(s: ^[]Type, item: Type) -> bool - AstNode *sptr_node = ce->args[0]; - AstNode *item_node = ce->args[1]; - ssaValue *slice_ptr = ssa_build_expr(proc, sptr_node); - ssaValue *slice = ssa_emit_load(proc, slice_ptr); - - ssaValue *elem = ssa_slice_elem(proc, slice); - ssaValue *len = ssa_slice_len(proc, slice); - ssaValue *cap = ssa_slice_cap(proc, slice); - - Type *elem_type = type_deref(ssa_type(elem)); - - ssaValue *item_value = ssa_build_expr(proc, item_node); - item_value = ssa_emit_conv(proc, item_value, elem_type, true); - - ssaValue *item = ssa_add_local_generated(proc, elem_type); - ssa_emit_store(proc, item, item_value); - - - // NOTE(bill): Check if can append is possible - ssaValue *cond = ssa_emit_comp(proc, Token_Lt, len, cap); - ssaBlock *able = ssa_add_block(proc, NULL, "builtin.append.able"); - ssaBlock *done = ssa_add_block(proc, NULL, "builtin.append.done"); - - ssa_emit_if(proc, cond, able, done); - proc->curr_block = able; - - // Add new slice item - i64 item_size = type_size_of(proc->module->sizes, proc->module->allocator, elem_type); - ssaValue *byte_count = ssa_make_const_int(proc->module->allocator, item_size); - - ssaValue *offset = ssa_emit_ptr_offset(proc, elem, len); - offset = ssa_emit_conv(proc, offset, t_rawptr, true); - - item = ssa_emit_ptr_offset(proc, item, v_zero); - item = ssa_emit_conv(proc, item, t_rawptr, true); - - ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3); - args[0] = offset; - args[1] = item; - args[2] = byte_count; - - ssa_emit_global_call(proc, "__mem_copy", args, 3); - - // Increment slice length - ssaValue *new_len = ssa_emit_arith(proc, Token_Add, len, v_one, t_int); - ssaValue *gep = ssa_emit_struct_ep(proc, slice_ptr, 1); - ssa_emit_store(proc, gep, new_len); - - ssa_emit_jump(proc, done); - proc->curr_block = done; - - return ssa_emit_conv(proc, cond, t_bool, true); - } break; - - case BuiltinProc_swizzle: { - ssa_emit_comment(proc, make_string("swizzle")); - ssaValue *vector = ssa_build_expr(proc, ce->args[0]); - isize index_count = ce->args.count-1; - if (index_count == 0) { - return vector; - } - - i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count); - isize index = 0; - for_array(i, ce->args) { - if (i == 0) continue; - TypeAndValue *tv = type_and_value_of_expression(proc->module->info, ce->args[i]); - GB_ASSERT(is_type_integer(tv->type)); - GB_ASSERT(tv->value.kind == ExactValue_Integer); - indices[index++] = cast(i32)tv->value.value_integer; - } - - return ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, vector, indices, index_count)); - - } break; - -#if 0 - case BuiltinProc_ptr_offset: { - ssa_emit_comment(proc, make_string("ptr_offset")); - ssaValue *ptr = ssa_build_expr(proc, ce->args[0]); - ssaValue *offset = ssa_build_expr(proc, ce->args[1]); - return ssa_emit_ptr_offset(proc, ptr, offset); - } break; - - case BuiltinProc_ptr_sub: { - ssa_emit_comment(proc, make_string("ptr_sub")); - ssaValue *ptr_a = ssa_build_expr(proc, ce->args[0]); - ssaValue *ptr_b = ssa_build_expr(proc, ce->args[1]); - Type *ptr_type = base_type(ssa_type(ptr_a)); - GB_ASSERT(ptr_type->kind == Type_Pointer); - isize elem_size = type_size_of(proc->module->sizes, proc->module->allocator, ptr_type->Pointer.elem); - - ssaValue *v = ssa_emit_arith(proc, Token_Sub, ptr_a, ptr_b, t_int); - if (elem_size > 1) { - ssaValue *ez = ssa_make_const_int(proc->module->allocator, elem_size); - v = ssa_emit_arith(proc, Token_Quo, v, ez, t_int); - } - - return v; - } break; -#endif - - case BuiltinProc_slice_ptr: { - ssa_emit_comment(proc, make_string("slice_ptr")); - ssaValue *ptr = ssa_build_expr(proc, ce->args[0]); - ssaValue *len = ssa_build_expr(proc, ce->args[1]); - ssaValue *cap = len; - - len = ssa_emit_conv(proc, len, t_int, true); - - if (ce->args.count == 3) { - cap = ssa_build_expr(proc, ce->args[2]); - cap = ssa_emit_conv(proc, cap, t_int, true); - } - - - Type *slice_type = make_type_slice(proc->module->allocator, type_deref(ssa_type(ptr))); - ssaValue *slice = ssa_add_local_generated(proc, slice_type); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 0), ptr); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 1), len); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 2), cap); - return ssa_emit_load(proc, slice); - } break; - - case BuiltinProc_min: { - ssa_emit_comment(proc, make_string("min")); - ssaValue *x = ssa_build_expr(proc, ce->args[0]); - ssaValue *y = ssa_build_expr(proc, ce->args[1]); - Type *t = base_type(ssa_type(x)); - ssaValue *cond = ssa_emit_comp(proc, Token_Lt, x, y); - return ssa_emit_select(proc, cond, x, y); - } break; - - case BuiltinProc_max: { - ssa_emit_comment(proc, make_string("max")); - ssaValue *x = ssa_build_expr(proc, ce->args[0]); - ssaValue *y = ssa_build_expr(proc, ce->args[1]); - Type *t = base_type(ssa_type(x)); - ssaValue *cond = ssa_emit_comp(proc, Token_Gt, x, y); - return ssa_emit_select(proc, cond, x, y); - } break; - - case BuiltinProc_abs: { - ssa_emit_comment(proc, make_string("abs")); - - ssaValue *x = ssa_build_expr(proc, ce->args[0]); - Type *t = ssa_type(x); - - ssaValue *neg_x = ssa_emit_arith(proc, Token_Sub, v_zero, x, t); - ssaValue *cond = ssa_emit_comp(proc, Token_Lt, x, v_zero); - return ssa_emit_select(proc, cond, neg_x, x); - } break; - - case BuiltinProc_enum_to_string: { - ssa_emit_comment(proc, make_string("enum_to_string")); - ssaValue *x = ssa_build_expr(proc, ce->args[0]); - Type *t = ssa_type(x); - ssaValue *ti = ssa_type_info(proc, t); - - - ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 2); - args[0] = ti; - args[1] = ssa_emit_conv(proc, x, t_i64); - return ssa_emit_global_call(proc, "__enum_to_string", args, 2); - } break; - } - } - } - - - // NOTE(bill): Regular call - ssaValue *value = ssa_build_expr(proc, ce->proc); - Type *proc_type_ = base_type(ssa_type(value)); - GB_ASSERT(proc_type_->kind == Type_Proc); - auto *type = &proc_type_->Proc; - - isize arg_index = 0; - - isize arg_count = 0; - for_array(i, ce->args) { - AstNode *a = ce->args[i]; - Type *at = base_type(type_of_expr(proc->module->info, a)); - if (at->kind == Type_Tuple) { - arg_count += at->Tuple.variable_count; - } else { - arg_count++; - } - } - ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, arg_count); - b32 variadic = proc_type_->Proc.variadic; - b32 vari_expand = ce->ellipsis.pos.line != 0; - - for_array(i, ce->args) { - ssaValue *a = ssa_build_expr(proc, ce->args[i]); - Type *at = ssa_type(a); - if (at->kind == Type_Tuple) { - for (isize i = 0; i < at->Tuple.variable_count; i++) { - Entity *e = at->Tuple.variables[i]; - ssaValue *v = ssa_emit_struct_ev(proc, a, i); - args[arg_index++] = v; - } - } else { - args[arg_index++] = a; - } - } - - auto *pt = &type->params->Tuple; - - if (variadic) { - isize i = 0; - for (; i < type->param_count-1; i++) { - args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type, true); - } - if (!vari_expand) { - Type *variadic_type = pt->variables[i]->type; - GB_ASSERT(is_type_slice(variadic_type)); - variadic_type = base_type(variadic_type)->Slice.elem; - for (; i < arg_count; i++) { - args[i] = ssa_emit_conv(proc, args[i], variadic_type, true); - } - } - } else { - for (isize i = 0; i < arg_count; i++) { - args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type, true); - } - } - - if (variadic && !vari_expand) { - ssa_emit_comment(proc, make_string("variadic call argument generation")); - gbAllocator allocator = proc->module->allocator; - Type *slice_type = pt->variables[type->param_count-1]->type; - Type *elem_type = base_type(slice_type)->Slice.elem; - ssaValue *slice = ssa_add_local_generated(proc, slice_type); - isize slice_len = arg_count+1 - type->param_count; - - if (slice_len > 0) { - ssaValue *base_array = ssa_add_local_generated(proc, make_type_array(allocator, elem_type, slice_len)); - - for (isize i = type->param_count-1, j = 0; i < arg_count; i++, j++) { - ssaValue *addr = ssa_emit_array_ep(proc, base_array, j); - ssa_emit_store(proc, addr, args[i]); - } - - ssaValue *base_elem = ssa_emit_array_ep(proc, base_array, 0); - ssaValue *slice_elem = ssa_emit_struct_ep(proc, slice, 0); - ssa_emit_store(proc, slice_elem, base_elem); - ssaValue *len = ssa_make_const_int(allocator, slice_len); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 1), len); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 2), len); - } - - if (args[0]->kind == ssaValue_Constant) { - auto *c = &args[0]->Constant; - gb_printf_err("%s %d\n", type_to_string(c->type), c->value.kind); - } - - arg_count = type->param_count; - args[arg_count-1] = ssa_emit_load(proc, slice); - } - - return ssa_emit_call(proc, value, args, arg_count); - case_end; - - case_ast_node(de, DemaybeExpr, expr); - return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr); - case_end; - - case_ast_node(se, SliceExpr, expr); - return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr); - case_end; - - case_ast_node(ie, IndexExpr, expr); - return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr); - case_end; - } - - GB_PANIC("Unexpected expression: %.*s", LIT(ast_node_strings[expr->kind])); - return NULL; -} - - -ssaValue *ssa_build_expr(ssaProcedure *proc, AstNode *expr) { - expr = unparen_expr(expr); - - TypeAndValue *tv = map_get(&proc->module->info->types, hash_pointer(expr)); - GB_ASSERT_NOT_NULL(tv); - - if (tv->value.kind != ExactValue_Invalid) { - return ssa_add_module_constant(proc->module, tv->type, tv->value); - } - - ssaValue *value = NULL; - if (tv->mode == Addressing_Variable) { - value = ssa_addr_load(proc, ssa_build_addr(proc, expr)); - } else { - value = ssa_build_single_expr(proc, expr, tv); - } - - return value; -} - -ssaValue *ssa_add_using_variable(ssaProcedure *proc, Entity *e) { - GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous); - String name = e->token.string; - Entity *parent = e->using_parent; - Selection sel = lookup_field(proc->module->allocator, parent->type, name, false); - GB_ASSERT(sel.entity != NULL); - ssaValue **pv = map_get(&proc->module->values, hash_pointer(parent)); - ssaValue *v = NULL; - if (pv != NULL) { - v = *pv; - } else { - v = ssa_build_addr(proc, e->using_expr).addr; - } - GB_ASSERT(v != NULL); - ssaValue *var = ssa_emit_deep_field_gep(proc, parent->type, v, sel); - map_set(&proc->module->values, hash_pointer(e), var); - return var; -} - -ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) { - switch (expr->kind) { - case_ast_node(i, Ident, expr); - if (ssa_is_blank_ident(expr)) { - ssaAddr val = {}; - return val; - } - - Entity *e = entity_of_ident(proc->module->info, expr); - TypeAndValue *tv = map_get(&proc->module->info->types, hash_pointer(expr)); - - GB_ASSERT(e->kind != Entity_Constant); - - ssaValue *v = NULL; - ssaValue **found = map_get(&proc->module->values, hash_pointer(e)); - if (found) { - v = *found; - } else if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) { - v = ssa_add_using_variable(proc, e); - } else if (e->kind == Entity_ImplicitValue) { - // TODO(bill): Should a copy be made? - v = ssa_find_implicit_value_backing(proc, e->ImplicitValue.id); - } - - if (v == NULL) { - GB_PANIC("Unknown value: %s, entity: %p %.*s\n", expr_to_string(expr), e, LIT(entity_strings[e->kind])); - } - - return ssa_make_addr(v, expr); - case_end; - - case_ast_node(pe, ParenExpr, expr); - return ssa_build_addr(proc, unparen_expr(expr)); - case_end; - - case_ast_node(se, SelectorExpr, expr); - ssa_emit_comment(proc, make_string("SelectorExpr")); - String selector = unparen_expr(se->selector)->Ident.string; - Type *type = base_type(type_of_expr(proc->module->info, se->expr)); - - if (type == t_invalid) { - // NOTE(bill): Imports - Entity *imp = entity_of_ident(proc->module->info, se->expr); - if (imp != NULL) { - GB_ASSERT(imp->kind == Entity_ImportName); - } - return ssa_build_addr(proc, unparen_expr(se->selector)); - } else { - Selection sel = lookup_field(proc->module->allocator, type, selector, false); - GB_ASSERT(sel.entity != NULL); - - ssaValue *a = ssa_build_addr(proc, se->expr).addr; - a = ssa_emit_deep_field_gep(proc, type, a, sel); - return ssa_make_addr(a, expr); - } - case_end; - - case_ast_node(ue, UnaryExpr, expr); - switch (ue->op.kind) { - case Token_Pointer: { - return ssa_build_addr(proc, ue->expr); - } - default: - GB_PANIC("Invalid unary expression for ssa_build_addr"); - } - case_end; - - case_ast_node(be, BinaryExpr, expr); - switch (be->op.kind) { - case Token_as: { - ssa_emit_comment(proc, make_string("Cast - as")); - // NOTE(bill): Needed for dereference of pointer conversion - Type *type = type_of_expr(proc->module->info, expr); - ssaValue *v = ssa_add_local_generated(proc, type); - ssa_emit_store(proc, v, ssa_emit_conv(proc, ssa_build_expr(proc, be->left), type)); - return ssa_make_addr(v, expr); - } - case Token_transmute: { - ssa_emit_comment(proc, make_string("Cast - transmute")); - // NOTE(bill): Needed for dereference of pointer conversion - Type *type = type_of_expr(proc->module->info, expr); - ssaValue *v = ssa_add_local_generated(proc, type); - ssa_emit_store(proc, v, ssa_emit_transmute(proc, ssa_build_expr(proc, be->left), type)); - return ssa_make_addr(v, expr); - } - default: - GB_PANIC("Invalid binary expression for ssa_build_addr: %.*s\n", LIT(be->op.string)); - break; - } - case_end; - - case_ast_node(ie, IndexExpr, expr); - ssa_emit_comment(proc, make_string("IndexExpr")); - Type *t = base_type(type_of_expr(proc->module->info, ie->expr)); - gbAllocator a = proc->module->allocator; - - - b32 deref = is_type_pointer(t); - t = type_deref(t); - - ssaValue *using_addr = NULL; - if (!is_type_indexable(t)) { - // Using index expression - Entity *using_field = find_using_index_expr(t); - if (using_field != NULL) { - Selection sel = lookup_field(a, t, using_field->token.string, false); - ssaValue *e = ssa_build_addr(proc, ie->expr).addr; - using_addr = ssa_emit_deep_field_gep(proc, t, e, sel); - - t = using_field->type; - } - } - - - switch (t->kind) { - case Type_Vector: { - ssaValue *vector = NULL; - if (using_addr != NULL) { - vector = using_addr; - } else { - vector = ssa_build_addr(proc, ie->expr).addr; - if (deref) { - vector = ssa_emit_load(proc, vector); - } - } - ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); - ssaValue *len = ssa_make_const_int(a, t->Vector.count); - ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len); - return ssa_make_addr_vector(vector, index, expr); - } break; - - case Type_Array: { - ssaValue *array = NULL; - if (using_addr != NULL) { - array = using_addr; - } else { - array = ssa_build_addr(proc, ie->expr).addr; - if (deref) { - array = ssa_emit_load(proc, array); - } - } - ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); - ssaValue *elem = ssa_emit_array_ep(proc, array, index); - ssaValue *len = ssa_make_const_int(a, t->Vector.count); - ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len); - return ssa_make_addr(elem, expr); - } break; - - case Type_Slice: { - ssaValue *slice = NULL; - if (using_addr != NULL) { - slice = ssa_emit_load(proc, using_addr); - } else { - slice = ssa_build_expr(proc, ie->expr); - if (deref) { - slice = ssa_emit_load(proc, slice); - } - } - ssaValue *elem = ssa_slice_elem(proc, slice); - ssaValue *len = ssa_slice_len(proc, slice); - ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); - ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len); - ssaValue *v = ssa_emit_ptr_offset(proc, elem, index); - return ssa_make_addr(v, expr); - - } break; - - case Type_Basic: { // Basic_string - TypeAndValue *tv = map_get(&proc->module->info->types, hash_pointer(ie->expr)); - ssaValue *str; - ssaValue *elem; - ssaValue *len; - ssaValue *index; - - if (using_addr != NULL) { - str = ssa_emit_load(proc, using_addr); - } else { - str = ssa_build_expr(proc, ie->expr); - if (deref) { - str = ssa_emit_load(proc, str); - } - } - elem = ssa_string_elem(proc, str); - len = ssa_string_len(proc, str); - - index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int); - ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len); - - return ssa_make_addr(ssa_emit_ptr_offset(proc, elem, index), expr); - } break; - } - case_end; - - case_ast_node(se, SliceExpr, expr); - ssa_emit_comment(proc, make_string("SliceExpr")); - gbAllocator a = proc->module->allocator; - ssaValue *low = v_zero; - ssaValue *high = NULL; - ssaValue *max = NULL; - - if (se->low != NULL) low = ssa_build_expr(proc, se->low); - if (se->high != NULL) high = ssa_build_expr(proc, se->high); - if (se->triple_indexed) max = ssa_build_expr(proc, se->max); - ssaValue *addr = ssa_build_addr(proc, se->expr).addr; - ssaValue *base = ssa_emit_load(proc, addr); - Type *type = base_type(ssa_type(base)); - - if (is_type_pointer(type)) { - type = type_deref(type); - addr = base; - base = ssa_emit_load(proc, base); - } - - // TODO(bill): Cleanup like mad! - - switch (type->kind) { - case Type_Slice: { - Type *slice_type = type; - - if (high == NULL) high = ssa_slice_len(proc, base); - if (max == NULL) max = ssa_slice_cap(proc, base); - GB_ASSERT(max != NULL); - - ssa_emit_slice_bounds_check(proc, se->open, low, high, max, false); - - ssaValue *elem = ssa_slice_elem(proc, base); - ssaValue *len = ssa_emit_arith(proc, Token_Sub, high, low, t_int); - ssaValue *cap = ssa_emit_arith(proc, Token_Sub, max, low, t_int); - ssaValue *slice = ssa_add_local_generated(proc, slice_type); - - ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0); - ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1); - ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2); - ssa_emit_store(proc, gep0, elem); - ssa_emit_store(proc, gep1, len); - ssa_emit_store(proc, gep2, cap); - - return ssa_make_addr(slice, expr); - } - - case Type_Array: { - Type *slice_type = make_type_slice(a, type->Array.elem); - - if (high == NULL) high = ssa_array_len(proc, base); - if (max == NULL) max = ssa_array_cap(proc, base); - GB_ASSERT(max != NULL); - - ssa_emit_slice_bounds_check(proc, se->open, low, high, max, false); - - ssaValue *elem = ssa_array_elem(proc, addr); - ssaValue *len = ssa_emit_arith(proc, Token_Sub, high, low, t_int); - ssaValue *cap = ssa_emit_arith(proc, Token_Sub, max, low, t_int); - ssaValue *slice = ssa_add_local_generated(proc, slice_type); - - ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0); - ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1); - ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2); - ssa_emit_store(proc, gep0, elem); - ssa_emit_store(proc, gep1, len); - ssa_emit_store(proc, gep2, cap); - - return ssa_make_addr(slice, expr); - } - - case Type_Basic: { - GB_ASSERT(type == t_string); - if (high == NULL) { - high = ssa_string_len(proc, base); - } - - ssa_emit_slice_bounds_check(proc, se->open, low, high, high, true); - - ssaValue *elem, *len; - len = ssa_emit_arith(proc, Token_Sub, high, low, t_int); - - elem = ssa_string_elem(proc, base); - elem = ssa_emit_ptr_offset(proc, elem, low); - - ssaValue *str = ssa_add_local_generated(proc, t_string); - ssaValue *gep0 = ssa_emit_struct_ep(proc, str, 0); - ssaValue *gep1 = ssa_emit_struct_ep(proc, str, 1); - ssa_emit_store(proc, gep0, elem); - ssa_emit_store(proc, gep1, len); - - return ssa_make_addr(str, expr); - } break; - } - - GB_PANIC("Unknown slicable type"); - case_end; - - case_ast_node(de, DerefExpr, expr); - // TODO(bill): Is a ptr copy needed? - ssaValue *addr = ssa_build_expr(proc, de->expr); - addr = ssa_emit_ptr_offset(proc, addr, v_zero); - return ssa_make_addr(addr, expr); - case_end; - - case_ast_node(de, DemaybeExpr, expr); - ssa_emit_comment(proc, make_string("DemaybeExpr")); - ssaValue *maybe = ssa_build_expr(proc, de->expr); - Type *t = default_type(type_of_expr(proc->module->info, expr)); - GB_ASSERT(is_type_tuple(t)); - - ssaValue *result = ssa_add_local_generated(proc, t); - ssa_emit_store(proc, result, maybe); - - return ssa_make_addr(result, expr); - case_end; - - case_ast_node(ce, CallExpr, expr); - ssaValue *e = ssa_build_expr(proc, expr); - ssaValue *v = ssa_add_local_generated(proc, ssa_type(e)); - ssa_emit_store(proc, v, e); - return ssa_make_addr(v, expr); - case_end; - } - - TokenPos token_pos = ast_node_token(expr).pos; - GB_PANIC("Unexpected address expression\n" - "\tAstNode: %.*s @ " - "%.*s(%td:%td)\n", - LIT(ast_node_strings[expr->kind]), - LIT(token_pos.file), token_pos.line, token_pos.column); - - - return ssa_make_addr(NULL, NULL); -} - -void ssa_build_assign_op(ssaProcedure *proc, ssaAddr lhs, ssaValue *value, TokenKind op) { - ssaValue *old_value = ssa_addr_load(proc, lhs); - Type *type = ssa_type(old_value); - - ssaValue *change = value; - if (is_type_pointer(type) && is_type_integer(ssa_type(value))) { - change = ssa_emit_conv(proc, value, default_type(ssa_type(value))); - } else { - change = ssa_emit_conv(proc, value, type); - } - ssaValue *new_value = ssa_emit_arith(proc, op, old_value, change, type); - ssa_addr_store(proc, lhs, new_value); -} - -void ssa_build_cond(ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block) { - switch (cond->kind) { - case_ast_node(pe, ParenExpr, cond); - ssa_build_cond(proc, pe->expr, true_block, false_block); - return; - case_end; - - case_ast_node(ue, UnaryExpr, cond); - if (ue->op.kind == Token_Not) { - ssa_build_cond(proc, ue->expr, false_block, true_block); - return; - } - case_end; - - case_ast_node(be, BinaryExpr, cond); - if (be->op.kind == Token_CmpAnd) { - ssaBlock *block = ssa_add_block(proc, NULL, "cmp.and"); - ssa_build_cond(proc, be->left, block, false_block); - proc->curr_block = block; - ssa_build_cond(proc, be->right, true_block, false_block); - return; - } else if (be->op.kind == Token_CmpOr) { - ssaBlock *block = ssa_add_block(proc, NULL, "cmp.or"); - ssa_build_cond(proc, be->left, true_block, block); - proc->curr_block = block; - ssa_build_cond(proc, be->right, true_block, false_block); - return; - } - case_end; - } - - ssaValue *expr = ssa_build_expr(proc, cond); - expr = ssa_emit_conv(proc, expr, t_bool); - ssa_emit_if(proc, expr, true_block, false_block); -} - - - - -void ssa_build_stmt_list(ssaProcedure *proc, AstNodeArray stmts) { - for_array(i, stmts) { - ssa_build_stmt(proc, stmts[i]); - } -} - -void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { - u32 prev_stmt_state_flags = proc->module->stmt_state_flags; - defer (proc->module->stmt_state_flags = prev_stmt_state_flags); - - if (node->stmt_state_flags != 0) { - u32 in = node->stmt_state_flags; - u32 out = proc->module->stmt_state_flags; - defer (proc->module->stmt_state_flags = out); - - if (in & StmtStateFlag_bounds_check) { - out |= StmtStateFlag_bounds_check; - out &= ~StmtStateFlag_no_bounds_check; - } else if (in & StmtStateFlag_no_bounds_check) { - out |= StmtStateFlag_no_bounds_check; - out &= ~StmtStateFlag_bounds_check; - } - } - - - switch (node->kind) { - case_ast_node(bs, EmptyStmt, node); - case_end; - - case_ast_node(us, UsingStmt, node); - AstNode *decl = unparen_expr(us->node); - if (decl->kind == AstNode_VarDecl) { - ssa_build_stmt(proc, decl); - } - case_end; - - case_ast_node(vd, VarDecl, node); - ssaModule *m = proc->module; - gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena); - defer (gb_temp_arena_memory_end(tmp)); - - if (vd->values.count == 0) { // declared and zero-initialized - for_array(i, vd->names) { - AstNode *name = vd->names[i]; - if (!ssa_is_blank_ident(name)) { - ssa_add_local_for_identifier(proc, name, true); - } - } - } else { // Tuple(s) - Array lvals; - Array inits; - array_init(&lvals, m->tmp_allocator, vd->names.count); - array_init(&inits, m->tmp_allocator, vd->names.count); - - for_array(i, vd->names) { - AstNode *name = vd->names[i]; - ssaAddr lval = ssa_make_addr(NULL, NULL); - if (!ssa_is_blank_ident(name)) { - ssa_add_local_for_identifier(proc, name, false); - lval = ssa_build_addr(proc, name); - } - - array_add(&lvals, lval); - } - - for_array(i, vd->values) { - ssaValue *init = ssa_build_expr(proc, vd->values[i]); - Type *t = ssa_type(init); - if (t->kind == Type_Tuple) { - for (isize i = 0; i < t->Tuple.variable_count; i++) { - Entity *e = t->Tuple.variables[i]; - ssaValue *v = ssa_emit_struct_ev(proc, init, i); - array_add(&inits, v); - } - } else { - array_add(&inits, init); - } - } - - - for_array(i, inits) { - ssaValue *v = ssa_emit_conv(proc, inits[i], ssa_addr_type(lvals[i])); - ssa_addr_store(proc, lvals[i], v); - } - } - case_end; - - case_ast_node(pd, ProcDecl, node); - if (pd->body != NULL) { - auto *info = proc->module->info; - - Entity **found = map_get(&info->definitions, hash_pointer(pd->name)); - GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string)); - Entity *e = *found; - - - if (map_get(&proc->module->min_dep_map, hash_pointer(e)) == NULL) { - // NOTE(bill): Nothing depends upon it so doesn't need to be built - break; - } - - // NOTE(bill): Generate a new name - // parent.name-guid - String original_name = pd->name->Ident.string; - String pd_name = original_name; - if (pd->link_name.len > 0) { - pd_name = pd->link_name; - } - - isize name_len = proc->name.len + 1 + pd_name.len + 1 + 10 + 1; - u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); - i32 guid = cast(i32)proc->children.count; - name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(pd_name), guid); - String name = make_string(name_text, name_len-1); - - - ssaValue *value = ssa_make_value_procedure(proc->module->allocator, - proc->module, e, e->type, pd->type, pd->body, name); - - value->Proc.tags = pd->tags; - value->Proc.parent = proc; - - ssa_module_add_value(proc->module, e, value); - array_add(&proc->children, &value->Proc); - array_add(&proc->module->procs, value); - } else { - auto *info = proc->module->info; - - Entity **found = map_get(&info->definitions, hash_pointer(pd->name)); - GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string)); - Entity *e = *found; - - // FFI - Foreign function interace - String original_name = pd->name->Ident.string; - String name = original_name; - if (pd->foreign_name.len > 0) { - name = pd->foreign_name; - } - - ssaValue *value = ssa_make_value_procedure(proc->module->allocator, - proc->module, e, e->type, pd->type, pd->body, name); - - value->Proc.tags = pd->tags; - - ssa_module_add_value(proc->module, e, value); - ssa_build_proc(value, proc); - - if (value->Proc.tags & ProcTag_foreign) { - HashKey key = hash_string(name); - auto *prev_value = map_get(&proc->module->members, key); - if (prev_value == NULL) { - // NOTE(bill): Don't do mutliple declarations in the IR - map_set(&proc->module->members, key, value); - } - } else { - array_add(&proc->children, &value->Proc); - } - } - case_end; - - case_ast_node(td, TypeDecl, node); - - // NOTE(bill): Generate a new name - // parent_proc.name-guid - String td_name = td->name->Ident.string; - isize name_len = proc->name.len + 1 + td_name.len + 1 + 10 + 1; - u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); - i32 guid = cast(i32)proc->module->members.entries.count; - name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(td_name), guid); - String name = make_string(name_text, name_len-1); - - Entity **found = map_get(&proc->module->info->definitions, hash_pointer(td->name)); - GB_ASSERT(found != NULL); - Entity *e = *found; - ssaValue *value = ssa_make_value_type_name(proc->module->allocator, - name, e->type); - map_set(&proc->module->type_names, hash_pointer(e->type), name); - ssa_gen_global_type_name(proc->module, e, name); - case_end; - - case_ast_node(ids, IncDecStmt, node); - ssa_emit_comment(proc, make_string("IncDecStmt")); - TokenKind op = ids->op.kind; - if (op == Token_Increment) { - op = Token_Add; - } else if (op == Token_Decrement) { - op = Token_Sub; - } - ssaAddr lval = ssa_build_addr(proc, ids->expr); - ssaValue *one = ssa_emit_conv(proc, v_one, ssa_addr_type(lval)); - ssa_build_assign_op(proc, lval, one, op); - - case_end; - - case_ast_node(as, AssignStmt, node); - ssa_emit_comment(proc, make_string("AssignStmt")); - - ssaModule *m = proc->module; - gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena); - defer (gb_temp_arena_memory_end(tmp)); - - switch (as->op.kind) { - case Token_Eq: { - Array lvals; - array_init(&lvals, m->tmp_allocator); - - for_array(i, as->lhs) { - AstNode *lhs = as->lhs[i]; - ssaAddr lval = {}; - if (!ssa_is_blank_ident(lhs)) { - lval = ssa_build_addr(proc, lhs); - } - array_add(&lvals, lval); - } - - if (as->lhs.count == as->rhs.count) { - if (as->lhs.count == 1) { - AstNode *rhs = as->rhs[0]; - ssaValue *init = ssa_build_expr(proc, rhs); - ssa_addr_store(proc, lvals[0], init); - } else { - Array inits; - array_init(&inits, m->tmp_allocator, lvals.count); - - for_array(i, as->rhs) { - ssaValue *init = ssa_build_expr(proc, as->rhs[i]); - array_add(&inits, init); - } - - for_array(i, inits) { - ssa_addr_store(proc, lvals[i], inits[i]); - } - } - } else { - Array inits; - array_init(&inits, m->tmp_allocator, lvals.count); - - for_array(i, as->rhs) { - ssaValue *init = ssa_build_expr(proc, as->rhs[i]); - Type *t = ssa_type(init); - // TODO(bill): refactor for code reuse as this is repeated a bit - if (t->kind == Type_Tuple) { - for (isize i = 0; i < t->Tuple.variable_count; i++) { - Entity *e = t->Tuple.variables[i]; - ssaValue *v = ssa_emit_struct_ev(proc, init, i); - array_add(&inits, v); - } - } else { - array_add(&inits, init); - } - } - - for_array(i, inits) { - ssa_addr_store(proc, lvals[i], inits[i]); - } - } - - } break; - - default: { - // NOTE(bill): Only 1 += 1 is allowed, no tuples - // +=, -=, etc - i32 op = cast(i32)as->op.kind; - op += Token_Add - Token_AddEq; // Convert += to + - ssaAddr lhs = ssa_build_addr(proc, as->lhs[0]); - ssaValue *value = ssa_build_expr(proc, as->rhs[0]); - ssa_build_assign_op(proc, lhs, value, cast(TokenKind)op); - } break; - } - case_end; - - case_ast_node(es, ExprStmt, node); - // NOTE(bill): No need to use return value - ssa_build_expr(proc, es->expr); - case_end; - - case_ast_node(bs, BlockStmt, node); - ssa_open_scope(proc); - ssa_build_stmt_list(proc, bs->stmts); - ssa_close_scope(proc, ssaDeferExit_Default, NULL); - case_end; - - case_ast_node(ds, DeferStmt, node); - ssa_emit_comment(proc, make_string("DeferStmt")); - isize scope_index = proc->scope_index; - if (ds->stmt->kind == AstNode_BlockStmt) { - scope_index--; - } - ssa_add_defer_node(proc, scope_index, ds->stmt); - case_end; - - case_ast_node(rs, ReturnStmt, node); - ssa_emit_comment(proc, make_string("ReturnStmt")); - ssaValue *v = NULL; - auto *return_type_tuple = &proc->type->Proc.results->Tuple; - isize return_count = proc->type->Proc.result_count; - if (return_count == 0) { - // No return values - } else if (return_count == 1) { - Entity *e = return_type_tuple->variables[0]; - v = ssa_emit_conv(proc, ssa_build_expr(proc, rs->results[0]), e->type); - } else { - gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena); - defer (gb_temp_arena_memory_end(tmp)); - - Array results; - array_init(&results, proc->module->tmp_allocator, return_count); - - for_array(res_index, rs->results) { - ssaValue *res = ssa_build_expr(proc, rs->results[res_index]); - Type *t = ssa_type(res); - if (t->kind == Type_Tuple) { - for (isize i = 0; i < t->Tuple.variable_count; i++) { - Entity *e = t->Tuple.variables[i]; - ssaValue *v = ssa_emit_struct_ev(proc, res, i); - array_add(&results, v); - } - } else { - array_add(&results, res); - } - } - - Type *ret_type = proc->type->Proc.results; - v = ssa_add_local_generated(proc, ret_type); - for_array(i, results) { - Entity *e = return_type_tuple->variables[i]; - ssaValue *res = ssa_emit_conv(proc, results[i], e->type); - ssaValue *field = ssa_emit_struct_ep(proc, v, i); - ssa_emit_store(proc, field, res); - } - - v = ssa_emit_load(proc, v); - - } - ssa_emit_return(proc, v); - - case_end; - - case_ast_node(is, IfStmt, node); - ssa_emit_comment(proc, make_string("IfStmt")); - if (is->init != NULL) { - ssaBlock *init = ssa_add_block(proc, node, "if.init"); - ssa_emit_jump(proc, init); - proc->curr_block = init; - ssa_build_stmt(proc, is->init); - } - ssaBlock *then = ssa_add_block(proc, node, "if.then"); - ssaBlock *done = ssa_add_block(proc, node, "if.done"); // NOTE(bill): Append later - ssaBlock *else_ = done; - if (is->else_stmt != NULL) { - else_ = ssa_add_block(proc, is->else_stmt, "if.else"); - } - - ssa_build_cond(proc, is->cond, then, else_); - proc->curr_block = then; - - ssa_open_scope(proc); - ssa_build_stmt(proc, is->body); - ssa_close_scope(proc, ssaDeferExit_Default, NULL); - - ssa_emit_jump(proc, done); - - if (is->else_stmt != NULL) { - proc->curr_block = else_; - - ssa_open_scope(proc); - ssa_build_stmt(proc, is->else_stmt); - ssa_close_scope(proc, ssaDeferExit_Default, NULL); - - ssa_emit_jump(proc, done); - } - proc->curr_block = done; - case_end; - - case_ast_node(fs, ForStmt, node); - ssa_emit_comment(proc, make_string("ForStmt")); - if (fs->init != NULL) { - ssaBlock *init = ssa_add_block(proc, node, "for.init"); - ssa_emit_jump(proc, init); - proc->curr_block = init; - ssa_build_stmt(proc, fs->init); - } - ssaBlock *body = ssa_add_block(proc, node, "for.body"); - ssaBlock *done = ssa_add_block(proc, node, "for.done"); // NOTE(bill): Append later - - ssaBlock *loop = body; - - if (fs->cond != NULL) { - loop = ssa_add_block(proc, node, "for.loop"); - } - ssaBlock *cont = loop; - if (fs->post != NULL) { - cont = ssa_add_block(proc, node, "for.post"); - - } - ssa_emit_jump(proc, loop); - proc->curr_block = loop; - if (loop != body) { - ssa_build_cond(proc, fs->cond, body, done); - proc->curr_block = body; - } - - ssa_push_target_list(proc, done, cont, NULL); - - ssa_open_scope(proc); - ssa_build_stmt(proc, fs->body); - ssa_close_scope(proc, ssaDeferExit_Default, NULL); - - ssa_pop_target_list(proc); - ssa_emit_jump(proc, cont); - - if (fs->post != NULL) { - proc->curr_block = cont; - ssa_build_stmt(proc, fs->post); - ssa_emit_jump(proc, loop); - } - - - proc->curr_block = done; - - case_end; - - case_ast_node(ms, MatchStmt, node); - ssa_emit_comment(proc, make_string("MatchStmt")); - if (ms->init != NULL) { - ssa_build_stmt(proc, ms->init); - } - ssaValue *tag = v_true; - if (ms->tag != NULL) { - tag = ssa_build_expr(proc, ms->tag); - } - ssaBlock *done = ssa_add_block(proc, node, "match.done"); // NOTE(bill): Append later - - ast_node(body, BlockStmt, ms->body); - - AstNodeArray default_stmts = {}; - ssaBlock *default_fall = NULL; - ssaBlock *default_block = NULL; - - ssaBlock *fall = NULL; - b32 append_fall = false; - - isize case_count = body->stmts.count; - for_array(i, body->stmts) { - AstNode *clause = body->stmts[i]; - ssaBlock *body = fall; - - ast_node(cc, CaseClause, clause); - - if (body == NULL) { - if (cc->list.count == 0) { - body = ssa_add_block(proc, clause, "match.dflt.body"); - } else { - body = ssa_add_block(proc, clause, "match.case.body"); - } - } - if (append_fall && body == fall) { - append_fall = false; - } - - fall = done; - if (i+1 < case_count) { - append_fall = true; - fall = ssa_add_block(proc, clause, "match.fall.body"); - } - - if (cc->list.count == 0) { - // default case - default_stmts = cc->stmts; - default_fall = fall; - default_block = body; - continue; - } - - ssaBlock *next_cond = NULL; - for_array(j, cc->list) { - AstNode *expr = cc->list[j]; - next_cond = ssa_add_block(proc, clause, "match.case.next"); - - ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, ssa_build_expr(proc, expr)); - ssa_emit_if(proc, cond, body, next_cond); - proc->curr_block = next_cond; - } - proc->curr_block = body; - - ssa_push_target_list(proc, done, NULL, fall); - ssa_open_scope(proc); - ssa_build_stmt_list(proc, cc->stmts); - ssa_close_scope(proc, ssaDeferExit_Default, body); - ssa_pop_target_list(proc); - - ssa_emit_jump(proc, done); - proc->curr_block = next_cond; - } - - if (default_block != NULL) { - ssa_emit_jump(proc, default_block); - proc->curr_block = default_block; - - ssa_push_target_list(proc, done, NULL, default_fall); - ssa_open_scope(proc); - ssa_build_stmt_list(proc, default_stmts); - ssa_close_scope(proc, ssaDeferExit_Default, default_block); - ssa_pop_target_list(proc); - } - - ssa_emit_jump(proc, done); - proc->curr_block = done; - case_end; - - - case_ast_node(ms, TypeMatchStmt, node); - ssa_emit_comment(proc, make_string("TypeMatchStmt")); - gbAllocator allocator = proc->module->allocator; - - ssaValue *parent = ssa_build_expr(proc, ms->tag); - Type *union_type = type_deref(ssa_type(parent)); - GB_ASSERT(is_type_union(union_type)); - - ssa_emit_comment(proc, make_string("get union's tag")); - ssaValue *tag_index = ssa_emit_struct_ep(proc, parent, 1); - tag_index = ssa_emit_load(proc, tag_index); - - ssaValue *data = ssa_emit_conv(proc, parent, t_rawptr); - - ssaBlock *start_block = ssa_add_block(proc, node, "type-match.case.first"); - ssa_emit_jump(proc, start_block); - proc->curr_block = start_block; - - ssaBlock *done = ssa_add_block(proc, node, "type-match.done"); // NOTE(bill): Append later - - ast_node(body, BlockStmt, ms->body); - - - String tag_var_name = ms->var->Ident.string; - - AstNodeArray default_stmts = {}; - ssaBlock *default_block = NULL; - - isize case_count = body->stmts.count; - for_array(i, body->stmts) { - AstNode *clause = body->stmts[i]; - ast_node(cc, CaseClause, clause); - - if (cc->list.count == 0) { - // default case - default_stmts = cc->stmts; - default_block = ssa_add_block(proc, clause, "type-match.dflt.body"); - continue; - } - - - ssaBlock *body = ssa_add_block(proc, clause, "type-match.case.body"); - - Scope *scope = *map_get(&proc->module->info->scopes, hash_pointer(clause)); - Entity *tag_var_entity = current_scope_lookup_entity(scope, tag_var_name); - GB_ASSERT_MSG(tag_var_entity != NULL, "%.*s", LIT(tag_var_name)); - ssaValue *tag_var = ssa_add_local(proc, tag_var_entity); - ssaValue *data_ptr = ssa_emit_conv(proc, data, tag_var_entity->type); - ssa_emit_store(proc, tag_var, data_ptr); - - - - Type *bt = type_deref(tag_var_entity->type); - ssaValue *index = NULL; - Type *ut = base_type(union_type); - GB_ASSERT(ut->Record.kind == TypeRecord_Union); - for (isize field_index = 1; field_index < ut->Record.field_count; field_index++) { - Entity *f = base_type(union_type)->Record.fields[field_index]; - if (are_types_identical(f->type, bt)) { - index = ssa_make_const_int(allocator, field_index); - break; - } - } - GB_ASSERT(index != NULL); - - ssaBlock *next_cond = ssa_add_block(proc, clause, "type-match.case.next"); - ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag_index, index); - ssa_emit_if(proc, cond, body, next_cond); - proc->curr_block = next_cond; - - proc->curr_block = body; - - ssa_push_target_list(proc, done, NULL, NULL); - ssa_open_scope(proc); - ssa_build_stmt_list(proc, cc->stmts); - ssa_close_scope(proc, ssaDeferExit_Default, body); - ssa_pop_target_list(proc); - - ssa_emit_jump(proc, done); - proc->curr_block = next_cond; - } - - if (default_block != NULL) { - ssa_emit_jump(proc, default_block); - proc->curr_block = default_block; - - ssa_push_target_list(proc, done, NULL, NULL); - ssa_open_scope(proc); - ssa_build_stmt_list(proc, default_stmts); - ssa_close_scope(proc, ssaDeferExit_Default, default_block); - ssa_pop_target_list(proc); - } - - ssa_emit_jump(proc, done); - proc->curr_block = done; - case_end; - - case_ast_node(bs, BranchStmt, node); - ssaBlock *block = NULL; - switch (bs->token.kind) { - case Token_break: - for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { - block = t->break_; - } - break; - case Token_continue: - for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { - block = t->continue_; - } - break; - case Token_fallthrough: - for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { - block = t->fallthrough_; - } - break; - } - if (block != NULL) { - ssa_emit_defer_stmts(proc, ssaDeferExit_Branch, block); - } - switch (bs->token.kind) { - case Token_break: ssa_emit_comment(proc, make_string("break")); break; - case Token_continue: ssa_emit_comment(proc, make_string("continue")); break; - case Token_fallthrough: ssa_emit_comment(proc, make_string("fallthrough")); break; - } - ssa_emit_jump(proc, block); - ssa_emit_unreachable(proc); - case_end; - - - - case_ast_node(pa, PushAllocator, node); - ssa_emit_comment(proc, make_string("PushAllocator")); - ssa_open_scope(proc); - defer (ssa_close_scope(proc, ssaDeferExit_Default, NULL)); - - ssaValue *context_ptr = ssa_find_implicit_value_backing(proc, ImplicitValue_context); - ssaValue *prev_context = ssa_add_local_generated(proc, t_context); - ssa_emit_store(proc, prev_context, ssa_emit_load(proc, context_ptr)); - - ssa_add_defer_instr(proc, proc->scope_index, ssa_make_instr_store(proc, context_ptr, ssa_emit_load(proc, prev_context))); - - ssaValue *gep = ssa_emit_struct_ep(proc, context_ptr, 1); - ssa_emit_store(proc, gep, ssa_build_expr(proc, pa->expr)); - - ssa_build_stmt(proc, pa->body); - - case_end; - - - case_ast_node(pa, PushContext, node); - ssa_emit_comment(proc, make_string("PushContext")); - ssa_open_scope(proc); - defer (ssa_close_scope(proc, ssaDeferExit_Default, NULL)); - - ssaValue *context_ptr = ssa_find_implicit_value_backing(proc, ImplicitValue_context); - ssaValue *prev_context = ssa_add_local_generated(proc, t_context); - ssa_emit_store(proc, prev_context, ssa_emit_load(proc, context_ptr)); - - ssa_add_defer_instr(proc, proc->scope_index, ssa_make_instr_store(proc, context_ptr, ssa_emit_load(proc, prev_context))); - - ssa_emit_store(proc, context_ptr, ssa_build_expr(proc, pa->expr)); - - ssa_build_stmt(proc, pa->body); - case_end; - - - } -} - - diff --git a/src/ssa/codegen.cpp b/src/ssa/codegen.cpp deleted file mode 100644 index 423722d21..000000000 --- a/src/ssa/codegen.cpp +++ /dev/null @@ -1,671 +0,0 @@ -struct ssaGen { - ssaModule module; - gbFile output_file; -}; - -b32 ssa_gen_init(ssaGen *s, Checker *c) { - if (global_error_collector.count != 0) { - return false; - } - - isize tc = c->parser->total_token_count; - if (tc < 2) { - return false; - } - - ssa_init_module(&s->module, c); - s->module.generate_debug_info = false; - - // TODO(bill): generate appropriate output name - int pos = cast(int)string_extension_position(c->parser->init_fullpath); - gbFileError err = gb_file_create(&s->output_file, gb_bprintf("%.*s.ll", pos, c->parser->init_fullpath.text)); - if (err != gbFileError_None) { - return false; - } - - return true; -} - -void ssa_gen_destroy(ssaGen *s) { - ssa_destroy_module(&s->module); - gb_file_close(&s->output_file); -} - -String ssa_mangle_name(ssaGen *s, String path, String name) { - // NOTE(bill): prefix names not in the init scope - // TODO(bill): make robust and not just rely on the file's name - - ssaModule *m = &s->module; - CheckerInfo *info = m->info; - gbAllocator a = m->allocator; - AstFile *file = *map_get(&info->files, hash_string(path)); - - char *str = gb_alloc_array(a, char, path.len+1); - gb_memmove(str, path.text, path.len); - str[path.len] = 0; - for (isize i = 0; i < path.len; i++) { - if (str[i] == '\\') { - str[i] = '/'; - } - } - - char const *base = gb_path_base_name(str); - char const *ext = gb_path_extension(base); - isize base_len = ext-1-base; - - isize max_len = base_len + 1 + 10 + 1 + name.len; - u8 *new_name = gb_alloc_array(a, u8, max_len); - isize new_name_len = gb_snprintf( - cast(char *)new_name, max_len, - "%.*s-%u.%.*s", - cast(int)base_len, base, - file->id, - LIT(name)); - - return make_string(new_name, new_name_len-1); -} - - -void ssa_gen_tree(ssaGen *s) { - ssaModule *m = &s->module; - CheckerInfo *info = m->info; - gbAllocator a = m->allocator; - - if (v_zero == NULL) { - v_zero = ssa_make_const_int (m->allocator, 0); - v_one = ssa_make_const_int (m->allocator, 1); - v_zero32 = ssa_make_const_i32 (m->allocator, 0); - v_one32 = ssa_make_const_i32 (m->allocator, 1); - v_two32 = ssa_make_const_i32 (m->allocator, 2); - v_false = ssa_make_const_bool(m->allocator, false); - v_true = ssa_make_const_bool(m->allocator, true); - } - - isize global_variable_max_count = 0; - Entity *entry_point = NULL; - - for_array(i, info->entities.entries) { - auto *entry = &info->entities.entries[i]; - Entity *e = cast(Entity *)cast(uintptr)entry->key.key; - String name = e->token.string; - if (e->kind == Entity_Variable) { - global_variable_max_count++; - } else if (e->kind == Entity_Procedure) { - if (e->scope->is_init && name == "main") { - entry_point = e; - } - } - } - - struct ssaGlobalVariable { - ssaValue *var, *init; - DeclInfo *decl; - }; - Array global_variables; - array_init(&global_variables, m->tmp_allocator, global_variable_max_count); - - m->min_dep_map = generate_minimum_dependency_map(info, entry_point); - - for_array(i, info->entities.entries) { - auto *entry = &info->entities.entries[i]; - Entity *e = cast(Entity *)entry->key.ptr; - String name = e->token.string; - DeclInfo *decl = entry->value; - Scope *scope = e->scope; - - if (!scope->is_file) { - continue; - } - - if (map_get(&m->min_dep_map, hash_pointer(e)) == NULL) { - // NOTE(bill): Nothing depends upon it so doesn't need to be built - continue; - } - - if (!scope->is_global && !scope->is_init) { - name = ssa_mangle_name(s, e->token.pos.file, name); - } - - - switch (e->kind) { - case Entity_TypeName: - GB_ASSERT(e->type->kind == Type_Named); - map_set(&m->type_names, hash_pointer(e->type), name); - ssa_gen_global_type_name(m, e, name); - break; - - case Entity_Variable: { - ssaValue *g = ssa_make_value_global(a, e, NULL); - if (decl->var_decl_tags & VarDeclTag_thread_local) { - g->Global.is_thread_local = true; - } - ssaGlobalVariable var = {}; - var.var = g; - var.decl = decl; - - if (decl->init_expr != NULL) { - TypeAndValue *tav = map_get(&info->types, hash_pointer(decl->init_expr)); - if (tav != NULL) { - if (tav->value.kind != ExactValue_Invalid) { - ExactValue v = tav->value; - // if (v.kind != ExactValue_String) { - g->Global.value = ssa_add_module_constant(m, tav->type, v); - // } - } - } - } - - if (g->Global.value == NULL) { - array_add(&global_variables, var); - } - - map_set(&m->values, hash_pointer(e), g); - map_set(&m->members, hash_string(name), g); - } break; - - case Entity_Procedure: { - auto *pd = &decl->proc_decl->ProcDecl; - String original_name = name; - AstNode *body = pd->body; - if (pd->tags & ProcTag_foreign) { - name = pd->name->Ident.string; - } - if (pd->foreign_name.len > 0) { - name = pd->foreign_name; - } else if (pd->link_name.len > 0) { - name = pd->link_name; - } - - ssaValue *p = ssa_make_value_procedure(a, m, e, e->type, decl->type_expr, body, name); - p->Proc.tags = pd->tags; - - map_set(&m->values, hash_pointer(e), p); - HashKey hash_name = hash_string(name); - if (map_get(&m->members, hash_name) == NULL) { - map_set(&m->members, hash_name, p); - } - } break; - } - } - - for_array(i, m->members.entries) { - auto *entry = &m->members.entries[i]; - ssaValue *v = entry->value; - if (v->kind == ssaValue_Proc) - ssa_build_proc(v, NULL); - } - - ssaDebugInfo *compile_unit = m->debug_info.entries[0].value; - GB_ASSERT(compile_unit->kind == ssaDebugInfo_CompileUnit); - ssaDebugInfo *all_procs = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_AllProcs); - - isize all_proc_max_count = 0; - for_array(i, m->debug_info.entries) { - auto *entry = &m->debug_info.entries[i]; - ssaDebugInfo *di = entry->value; - di->id = i; - if (di->kind == ssaDebugInfo_Proc) { - all_proc_max_count++; - } - } - - array_init(&all_procs->AllProcs.procs, m->allocator, all_proc_max_count); - map_set(&m->debug_info, hash_pointer(all_procs), all_procs); // NOTE(bill): This doesn't need to be mapped - compile_unit->CompileUnit.all_procs = all_procs; - - - for_array(i, m->debug_info.entries) { - auto *entry = &m->debug_info.entries[i]; - ssaDebugInfo *di = entry->value; - di->id = i; - if (di->kind == ssaDebugInfo_Proc) { - array_add(&all_procs->AllProcs.procs, di); - } - } - - - { // Startup Runtime - // Cleanup(bill): probably better way of doing code insertion - String name = make_string(SSA_STARTUP_RUNTIME_PROC_NAME); - Type *proc_type = make_type_proc(a, gb_alloc_item(a, Scope), - NULL, 0, - NULL, 0, false); - AstNode *body = gb_alloc_item(a, AstNode); - ssaValue *p = ssa_make_value_procedure(a, m, NULL, proc_type, NULL, body, name); - Token token = {}; - token.string = name; - Entity *e = make_entity_procedure(a, NULL, token, proc_type); - - map_set(&m->values, hash_pointer(e), p); - map_set(&m->members, hash_string(name), p); - - ssaProcedure *proc = &p->Proc; - proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea? - - ssa_begin_procedure_body(proc); - - // TODO(bill): Should do a dependency graph do check which order to initialize them in? - for_array(i, global_variables) { - ssaGlobalVariable *var = &global_variables[i]; - if (var->decl->init_expr != NULL) { - var->init = ssa_build_expr(proc, var->decl->init_expr); - } - } - - // NOTE(bill): Initialize constants first - for_array(i, global_variables) { - ssaGlobalVariable *var = &global_variables[i]; - if (var->init != NULL) { - if (var->init->kind == ssaValue_Constant) { - ssa_emit_store(proc, var->var, var->init); - } - } - } - - for_array(i, global_variables) { - ssaGlobalVariable *var = &global_variables[i]; - if (var->init != NULL) { - if (var->init->kind != ssaValue_Constant) { - ssa_emit_store(proc, var->var, var->init); - } - } - } - - { // NOTE(bill): Setup type_info data - // TODO(bill): Try and make a lot of this constant aggregate literals in LLVM IR - ssaValue *type_info_data = NULL; - ssaValue *type_info_member_data = NULL; - - ssaValue **found = NULL; - found = map_get(&proc->module->members, hash_string(make_string(SSA_TYPE_INFO_DATA_NAME))); - GB_ASSERT(found != NULL); - type_info_data = *found; - - found = map_get(&proc->module->members, hash_string(make_string(SSA_TYPE_INFO_DATA_MEMBER_NAME))); - GB_ASSERT(found != NULL); - type_info_member_data = *found; - - CheckerInfo *info = proc->module->info; - - // Useful types - Type *t_i64_slice_ptr = make_type_pointer(a, make_type_slice(a, t_i64)); - Type *t_string_slice_ptr = make_type_pointer(a, make_type_slice(a, t_string)); - - auto get_type_info_ptr = [](ssaProcedure *proc, ssaValue *type_info_data, Type *type) -> ssaValue * { - i32 index = cast(i32)ssa_type_info_index(proc->module->info, type); - // gb_printf_err("%d %s\n", index, type_to_string(type)); - return ssa_emit_array_ep(proc, type_info_data, index); - }; - - i32 type_info_member_index = 0; - - auto type_info_member_offset = [](ssaProcedure *proc, ssaValue *data, isize count, i32 *index) -> ssaValue * { - ssaValue *offset = ssa_emit_array_ep(proc, data, *index); - *index += count; - return offset; - }; - - - - for_array(type_info_map_index, info->type_info_map.entries) { - auto *entry = &info->type_info_map.entries[type_info_map_index]; - Type *t = cast(Type *)cast(uintptr)entry->key.key; - t = default_type(t); - isize entry_index = entry->value; - - ssaValue *tag = NULL; - - switch (t->kind) { - case Type_Named: { - tag = ssa_add_local_generated(proc, t_type_info_named); - - // TODO(bill): Which is better? The mangled name or actual name? - ssaValue *name = ssa_make_const_string(a, t->Named.type_name->token.string); - ssaValue *gtip = get_type_info_ptr(proc, type_info_data, t->Named.base); - - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), name); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), gtip); - } break; - - case Type_Basic: - switch (t->Basic.kind) { - case Basic_bool: - tag = ssa_add_local_generated(proc, t_type_info_boolean); - break; - case Basic_i8: - case Basic_i16: - case Basic_i32: - case Basic_i64: - case Basic_u8: - case Basic_u16: - case Basic_u32: - case Basic_u64: - case Basic_int: - case Basic_uint: { - tag = ssa_add_local_generated(proc, t_type_info_integer); - b32 is_unsigned = (t->Basic.flags & BasicFlag_Unsigned) != 0; - ssaValue *bits = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); - ssaValue *is_signed = ssa_make_const_bool(a, !is_unsigned); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), bits); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), is_signed); - } break; - - case Basic_f32: - case Basic_f64: { - tag = ssa_add_local_generated(proc, t_type_info_float); - ssaValue *bits = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), bits); - } break; - - case Basic_rawptr: - tag = ssa_add_local_generated(proc, t_type_info_pointer); - break; - - case Basic_string: - tag = ssa_add_local_generated(proc, t_type_info_string); - break; - - case Basic_any: - tag = ssa_add_local_generated(proc, t_type_info_any); - break; - } - break; - - case Type_Pointer: { - tag = ssa_add_local_generated(proc, t_type_info_pointer); - ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Pointer.elem); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); - } break; - case Type_Maybe: { - tag = ssa_add_local_generated(proc, t_type_info_maybe); - ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Maybe.elem); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); - } break; - case Type_Array: { - tag = ssa_add_local_generated(proc, t_type_info_array); - ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Array.elem); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); - - isize ez = type_size_of(m->sizes, a, t->Array.elem); - ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1); - ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez)); - - ssaValue *count = ssa_emit_struct_ep(proc, tag, 2); - ssa_emit_store(proc, count, ssa_make_const_int(a, t->Array.count)); - - } break; - case Type_Slice: { - tag = ssa_add_local_generated(proc, t_type_info_slice); - ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Slice.elem); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); - - isize ez = type_size_of(m->sizes, a, t->Slice.elem); - ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1); - ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez)); - - } break; - case Type_Vector: { - tag = ssa_add_local_generated(proc, t_type_info_vector); - ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Vector.elem); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep); - - isize ez = type_size_of(m->sizes, a, t->Vector.elem); - ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1); - ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez)); - - ssaValue *count = ssa_emit_struct_ep(proc, tag, 2); - ssa_emit_store(proc, count, ssa_make_const_int(a, t->Vector.count)); - - } break; - case Type_Record: { - switch (t->Record.kind) { - case TypeRecord_Struct: { - tag = ssa_add_local_generated(proc, t_type_info_struct); - - { - ssaValue *packed = ssa_make_const_bool(a, t->Record.struct_is_packed); - ssaValue *ordered = ssa_make_const_bool(a, t->Record.struct_is_ordered); - ssaValue *size = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); - ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t)); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), size); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 3), packed); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 4), ordered); - } - - ssaValue *memory = type_info_member_offset(proc, type_info_member_data, t->Record.field_count, &type_info_member_index); - - type_set_offsets(m->sizes, a, t); // NOTE(bill): Just incase the offsets have not been set yet - for (isize source_index = 0; source_index < t->Record.field_count; source_index++) { - // TODO(bill): Order fields in source order not layout order - Entity *f = t->Record.fields_in_src_order[source_index]; - ssaValue *tip = get_type_info_ptr(proc, type_info_data, f->type); - i64 foffset = t->Record.struct_offsets[f->Variable.field_index]; - GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field); - - ssaValue *field = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, source_index)); - ssaValue *name = ssa_emit_struct_ep(proc, field, 0); - ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1); - ssaValue *offset = ssa_emit_struct_ep(proc, field, 2); - - if (f->token.string.len > 0) { - ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string)); - } - ssa_emit_store(proc, type_info, tip); - ssa_emit_store(proc, offset, ssa_make_const_int(a, foffset)); - } - - Type *slice_type = make_type_slice(a, t_type_info_member); - Type *slice_type_ptr = make_type_pointer(a, slice_type); - ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0); - ssaValue *field_count = ssa_make_const_int(a, t->Record.field_count); - - ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0); - ssaValue *len = ssa_emit_struct_ep(proc, slice, 1); - ssaValue *cap = ssa_emit_struct_ep(proc, slice, 2); - - ssa_emit_store(proc, elem, memory); - ssa_emit_store(proc, len, field_count); - ssa_emit_store(proc, cap, field_count); - } break; - case TypeRecord_Union: - tag = ssa_add_local_generated(proc, t_type_info_union); - { - ssaValue *size = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); - ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t)); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), size); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align); - } - break; - case TypeRecord_RawUnion: { - tag = ssa_add_local_generated(proc, t_type_info_raw_union); - { - ssaValue *size = ssa_make_const_int(a, type_size_of(m->sizes, a, t)); - ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t)); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), size); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align); - } - - ssaValue *memory = type_info_member_offset(proc, type_info_member_data, t->Record.field_count, &type_info_member_index); - - for (isize i = 0; i < t->Record.field_count; i++) { - ssaValue *field = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, i)); - ssaValue *name = ssa_emit_struct_ep(proc, field, 0); - ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1); - ssaValue *offset = ssa_emit_struct_ep(proc, field, 2); - - Entity *f = t->Record.fields[i]; - ssaValue *tip = get_type_info_ptr(proc, type_info_data, f->type); - - if (f->token.string.len > 0) { - ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string)); - } - ssa_emit_store(proc, type_info, tip); - ssa_emit_store(proc, offset, ssa_make_const_int(a, 0)); - } - - Type *slice_type = make_type_slice(a, t_type_info_member); - Type *slice_type_ptr = make_type_pointer(a, slice_type); - ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0); - ssaValue *field_count = ssa_make_const_int(a, t->Record.field_count); - - ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0); - ssaValue *len = ssa_emit_struct_ep(proc, slice, 1); - ssaValue *cap = ssa_emit_struct_ep(proc, slice, 2); - - ssa_emit_store(proc, elem, memory); - ssa_emit_store(proc, len, field_count); - ssa_emit_store(proc, cap, field_count); - } break; - case TypeRecord_Enum: { - tag = ssa_add_local_generated(proc, t_type_info_enum); - Type *enum_base = t->Record.enum_base; - if (enum_base == NULL) { - enum_base = t_int; - } - ssaValue *base = ssa_emit_struct_ep(proc, tag, 0); - ssa_emit_store(proc, base, get_type_info_ptr(proc, type_info_data, enum_base)); - - if (t->Record.other_field_count > 0) { - Entity **fields = t->Record.other_fields; - isize count = t->Record.other_field_count; - ssaValue *value_array = NULL; - ssaValue *name_array = NULL; - - - { - Token token = {Token_Identifier}; - i32 id = cast(i32)entry_index; - char name_base[] = "__$enum_values"; - isize name_len = gb_size_of(name_base) + 10; - token.string.text = gb_alloc_array(a, u8, name_len); - token.string.len = gb_snprintf(cast(char *)token.string.text, name_len, - "%s-%d", name_base, id)-1; - Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_i64, count)); - value_array = ssa_make_value_global(a, e, NULL); - value_array->Global.is_private = true; - ssa_module_add_value(m, e, value_array); - map_set(&m->members, hash_string(token.string), value_array); - } - { - Token token = {Token_Identifier}; - i32 id = cast(i32)entry_index; - char name_base[] = "__$enum_names"; - isize name_len = gb_size_of(name_base) + 10; - token.string.text = gb_alloc_array(a, u8, name_len); - token.string.len = gb_snprintf(cast(char *)token.string.text, name_len, - "%s-%d", name_base, id)-1; - Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_string, count)); - name_array = ssa_make_value_global(a, e, NULL); - name_array->Global.is_private = true; - ssa_module_add_value(m, e, name_array); - map_set(&m->members, hash_string(token.string), name_array); - } - - for (isize i = 0; i < count; i++) { - ssaValue *value_gep = ssa_emit_struct_ep(proc, value_array, i); - ssaValue *name_gep = ssa_emit_struct_ep(proc, name_array, i); - - ssa_emit_store(proc, value_gep, ssa_make_const_i64(a, fields[i]->Constant.value.value_integer)); - ssa_emit_store(proc, name_gep, ssa_make_const_string(a, fields[i]->token.string)); - } - - ssaValue *v_count = ssa_make_const_int(a, count); - - - ssaValue *values = ssa_emit_struct_ep(proc, tag, 1); - ssaValue *names = ssa_emit_struct_ep(proc, tag, 2); - ssaValue *value_slice = ssa_add_local_generated(proc, type_deref(t_i64_slice_ptr)); - ssaValue *name_slice = ssa_add_local_generated(proc, type_deref(t_string_slice_ptr)); - - ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 0), ssa_array_elem(proc, value_array)); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 1), v_count); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 2), v_count); - - ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 0), ssa_array_elem(proc, name_array)); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 1), v_count); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 2), v_count); - - ssa_emit_store(proc, values, ssa_emit_load(proc, value_slice)); - ssa_emit_store(proc, names, ssa_emit_load(proc, name_slice)); - } - } break; - } - } break; - - case Type_Tuple: { - tag = ssa_add_local_generated(proc, t_type_info_tuple); - - { - ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t)); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align); - } - - ssaValue *memory = type_info_member_offset(proc, type_info_member_data, t->Tuple.variable_count, &type_info_member_index); - - for (isize i = 0; i < t->Tuple.variable_count; i++) { - ssaValue *field = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, i)); - ssaValue *name = ssa_emit_struct_ep(proc, field, 0); - ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1); - // NOTE(bill): offset is not used for tuples - - Entity *f = t->Tuple.variables[i]; - ssaValue *tip = get_type_info_ptr(proc, type_info_data, f->type); - - if (f->token.string.len > 0) { - ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string)); - } - ssa_emit_store(proc, type_info, tip); - } - - Type *slice_type = make_type_slice(a, t_type_info_member); - Type *slice_type_ptr = make_type_pointer(a, slice_type); - ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0); - ssaValue *variable_count = ssa_make_const_int(a, t->Tuple.variable_count); - - ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0); - ssaValue *len = ssa_emit_struct_ep(proc, slice, 1); - ssaValue *cap = ssa_emit_struct_ep(proc, slice, 2); - - ssa_emit_store(proc, elem, memory); - ssa_emit_store(proc, len, variable_count); - ssa_emit_store(proc, cap, variable_count); - } break; - - case Type_Proc: { - tag = ssa_add_local_generated(proc, t_type_info_procedure); - - ssaValue *params = ssa_emit_struct_ep(proc, tag, 0); - ssaValue *results = ssa_emit_struct_ep(proc, tag, 1); - ssaValue *variadic = ssa_emit_struct_ep(proc, tag, 2); - - if (t->Proc.params) { - ssa_emit_store(proc, params, get_type_info_ptr(proc, type_info_data, t->Proc.params)); - } - if (t->Proc.results) { - ssa_emit_store(proc, results, get_type_info_ptr(proc, type_info_data, t->Proc.results)); - } - ssa_emit_store(proc, variadic, ssa_make_const_bool(a, t->Proc.variadic)); - - // TODO(bill): Type_Info for procedures - } break; - } - - if (tag != NULL) { - ssaValue *gep = ssa_emit_array_ep(proc, type_info_data, entry_index); - ssaValue *val = ssa_emit_conv(proc, ssa_emit_load(proc, tag), t_type_info); - ssa_emit_store(proc, gep, val); - } - } - } - - ssa_end_procedure_body(proc); - } - - for_array(i, m->procs) { - ssa_build_proc(m->procs[i], m->procs[i]->Proc.parent); - } - - - // m->layout = make_string("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"); -} diff --git a/src/ssa/debug.cpp b/src/ssa/debug.cpp deleted file mode 100644 index 003c0a7bd..000000000 --- a/src/ssa/debug.cpp +++ /dev/null @@ -1,48 +0,0 @@ -ssaDebugInfo *ssa_add_debug_info_file(ssaProcedure *proc, AstFile *file) { - if (!proc->module->generate_debug_info) { - return NULL; - } - - GB_ASSERT(file != NULL); - ssaDebugInfo *di = ssa_alloc_debug_info(proc->module->allocator, ssaDebugInfo_File); - di->File.file = file; - - String filename = file->tokenizer.fullpath; - String directory = filename; - isize slash_index = 0; - for (isize i = filename.len-1; i >= 0; i--) { - if (filename.text[i] == '\\' || - filename.text[i] == '/') { - break; - } - slash_index = i; - } - directory.len = slash_index-1; - filename.text = filename.text + slash_index; - filename.len -= slash_index; - - - di->File.filename = filename; - di->File.directory = directory; - - map_set(&proc->module->debug_info, hash_pointer(file), di); - return di; -} - - -ssaDebugInfo *ssa_add_debug_info_proc(ssaProcedure *proc, Entity *entity, String name, ssaDebugInfo *file) { - if (!proc->module->generate_debug_info) { - return NULL; - } - - GB_ASSERT(entity != NULL); - ssaDebugInfo *di = ssa_alloc_debug_info(proc->module->allocator, ssaDebugInfo_Proc); - di->Proc.entity = entity; - di->Proc.name = name; - di->Proc.file = file; - di->Proc.pos = entity->token.pos; - - map_set(&proc->module->debug_info, hash_pointer(entity), di); - return di; -} - diff --git a/src/ssa/emit.cpp b/src/ssa/emit.cpp deleted file mode 100644 index 86c1d7e65..000000000 --- a/src/ssa/emit.cpp +++ /dev/null @@ -1,1150 +0,0 @@ -ssaValue *ssa_type_info (ssaProcedure *proc, Type *type); -ssaValue *ssa_emit_conv (ssaProcedure *proc, ssaValue *value, Type *t, b32 is_argument = false); -ssaValue *ssa_build_expr (ssaProcedure *proc, AstNode *expr); -void ssa_build_stmt (ssaProcedure *proc, AstNode *node); -void ssa_build_cond (ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block); -void ssa_build_defer_stmt(ssaProcedure *proc, ssaDefer d); - - -ssaValue *ssa_emit(ssaProcedure *proc, ssaValue *instr) { - GB_ASSERT(instr->kind == ssaValue_Instr); - ssaBlock *b = proc->curr_block; - instr->Instr.parent = b; - if (b != NULL) { - ssaInstr *i = ssa_get_last_instr(b); - if (!ssa_is_instr_terminating(i)) { - array_add(&b->instrs, instr); - } - } - return instr; -} -ssaValue *ssa_emit_store(ssaProcedure *p, ssaValue *address, ssaValue *value) { - return ssa_emit(p, ssa_make_instr_store(p, address, value)); -} -ssaValue *ssa_emit_load(ssaProcedure *p, ssaValue *address) { - return ssa_emit(p, ssa_make_instr_load(p, address)); -} -ssaValue *ssa_emit_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue *f) { - return ssa_emit(p, ssa_make_instr_select(p, cond, t, f)); -} - -ssaValue *ssa_emit_zero_init(ssaProcedure *p, ssaValue *address) { - return ssa_emit(p, ssa_make_instr_zero_init(p, address)); -} - -ssaValue *ssa_emit_comment(ssaProcedure *p, String text) { - return ssa_emit(p, ssa_make_instr_comment(p, text)); -} - - -ssaValue *ssa_emit_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count) { - Type *pt = base_type(ssa_type(value)); - GB_ASSERT(pt->kind == Type_Proc); - Type *results = pt->Proc.results; - return ssa_emit(p, ssa_make_instr_call(p, value, args, arg_count, results)); -} - -ssaValue *ssa_emit_global_call(ssaProcedure *proc, char *name_, ssaValue **args, isize arg_count) { - String name = make_string(name_); - ssaValue **found = map_get(&proc->module->members, hash_string(name)); - GB_ASSERT_MSG(found != NULL, "%.*s", LIT(name)); - ssaValue *gp = *found; - return ssa_emit_call(proc, gp, args, arg_count); -} - - - -void ssa_emit_defer_stmts(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) { - isize count = proc->defer_stmts.count; - isize i = count; - while (i --> 0) { - ssaDefer d = proc->defer_stmts[i]; - if (kind == ssaDeferExit_Default) { - if (proc->scope_index == d.scope_index && - d.scope_index > 1) { - ssa_build_defer_stmt(proc, d); - array_pop(&proc->defer_stmts); - continue; - } else { - break; - } - } else if (kind == ssaDeferExit_Return) { - ssa_build_defer_stmt(proc, d); - } else if (kind == ssaDeferExit_Branch) { - GB_ASSERT(block != NULL); - isize lower_limit = block->scope_index+1; - if (lower_limit < d.scope_index) { - ssa_build_defer_stmt(proc, d); - } - } - } -} - - -void ssa_open_scope(ssaProcedure *proc) { - proc->scope_index++; -} - -void ssa_close_scope(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) { - ssa_emit_defer_stmts(proc, kind, block); - GB_ASSERT(proc->scope_index > 0); - proc->scope_index--; -} - - - -void ssa_emit_unreachable(ssaProcedure *proc) { - ssa_emit(proc, ssa_make_instr_unreachable(proc)); -} - -void ssa_emit_return(ssaProcedure *proc, ssaValue *v) { - ssa_emit_defer_stmts(proc, ssaDeferExit_Return, NULL); - ssa_emit(proc, ssa_make_instr_return(proc, v)); -} - -void ssa_emit_jump(ssaProcedure *proc, ssaBlock *target_block) { - ssaBlock *b = proc->curr_block; - if (b == NULL) { - return; - } - ssa_emit(proc, ssa_make_instr_jump(proc, target_block)); - ssa_add_edge(b, target_block); - proc->curr_block = NULL; -} - -void ssa_emit_if(ssaProcedure *proc, ssaValue *cond, ssaBlock *true_block, ssaBlock *false_block) { - ssaBlock *b = proc->curr_block; - if (b == NULL) { - return; - } - ssa_emit(proc, ssa_make_instr_if(proc, cond, true_block, false_block)); - ssa_add_edge(b, true_block); - ssa_add_edge(b, false_block); - proc->curr_block = NULL; -} - -void ssa_emit_startup_runtime(ssaProcedure *proc) { - GB_ASSERT(proc->parent == NULL && proc->name == "main"); - ssa_emit(proc, ssa_alloc_instr(proc, ssaInstr_StartupRuntime)); -} - - - - -ssaValue *ssa_addr_store(ssaProcedure *proc, ssaAddr addr, ssaValue *value) { - if (addr.addr == NULL) { - return NULL; - } - - if (addr.kind == ssaAddr_Vector) { - ssaValue *v = ssa_emit_load(proc, addr.addr); - Type *elem_type = base_type(ssa_type(v))->Vector.elem; - ssaValue *elem = ssa_emit_conv(proc, value, elem_type); - ssaValue *out = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, elem, addr.Vector.index)); - return ssa_emit_store(proc, addr.addr, out); - } else { - ssaValue *v = ssa_emit_conv(proc, value, ssa_addr_type(addr)); - return ssa_emit_store(proc, addr.addr, v); - } -} -ssaValue *ssa_addr_load(ssaProcedure *proc, ssaAddr addr) { - if (addr.addr == NULL) { - GB_PANIC("Illegal addr load"); - return NULL; - } - - if (addr.kind == ssaAddr_Vector) { - ssaValue *v = ssa_emit_load(proc, addr.addr); - return ssa_emit(proc, ssa_make_instr_extract_element(proc, v, addr.Vector.index)); - } - Type *t = base_type(ssa_type(addr.addr)); - if (t->kind == Type_Proc) { - // NOTE(bill): Imported procedures don't require a load as they are pointers - return addr.addr; - } - return ssa_emit_load(proc, addr.addr); -} - - - - -ssaValue *ssa_emit_ptr_offset(ssaProcedure *proc, ssaValue *ptr, ssaValue *offset) { - offset = ssa_emit_conv(proc, offset, t_int); - return ssa_emit(proc, ssa_make_instr_ptr_offset(proc, ptr, offset)); -} - -ssaValue *ssa_emit_arith(ssaProcedure *proc, TokenKind op, ssaValue *left, ssaValue *right, Type *type) { - Type *t_left = ssa_type(left); - Type *t_right = ssa_type(right); - - if (op == Token_Add) { - if (is_type_pointer(t_left)) { - ssaValue *ptr = ssa_emit_conv(proc, left, type); - ssaValue *offset = right; - return ssa_emit_ptr_offset(proc, ptr, offset); - } else if (is_type_pointer(ssa_type(right))) { - ssaValue *ptr = ssa_emit_conv(proc, right, type); - ssaValue *offset = left; - return ssa_emit_ptr_offset(proc, ptr, offset); - } - } else if (op == Token_Sub) { - if (is_type_pointer(t_left) && is_type_integer(t_right)) { - // ptr - int - ssaValue *ptr = ssa_emit_conv(proc, left, type); - ssaValue *offset = right; - return ssa_emit_ptr_offset(proc, ptr, offset); - } else if (is_type_pointer(t_left) && is_type_pointer(t_right)) { - GB_ASSERT(is_type_integer(type)); - Type *ptr_type = t_left; - ssaModule *m = proc->module; - ssaValue *x = ssa_emit_conv(proc, left, type); - ssaValue *y = ssa_emit_conv(proc, right, type); - ssaValue *diff = ssa_emit_arith(proc, op, x, y, type); - ssaValue *elem_size = ssa_make_const_int(m->allocator, type_size_of(m->sizes, m->allocator, ptr_type)); - return ssa_emit_arith(proc, Token_Quo, diff, elem_size, type); - } - } - - - switch (op) { - case Token_AndNot: { - // NOTE(bill): x &~ y == x & (~y) == x & (y ~ -1) - // NOTE(bill): "not" `x` == `x` "xor" `-1` - ssaValue *neg = ssa_add_module_constant(proc->module, type, make_exact_value_integer(-1)); - op = Token_Xor; - right = ssa_emit_arith(proc, op, right, neg, type); - GB_ASSERT(right->Instr.kind == ssaInstr_BinaryOp); - right->Instr.BinaryOp.type = type; - op = Token_And; - } /* fallthrough */ - case Token_Add: - case Token_Sub: - case Token_Mul: - case Token_Quo: - case Token_Mod: - case Token_And: - case Token_Or: - case Token_Xor: - left = ssa_emit_conv(proc, left, type); - right = ssa_emit_conv(proc, right, type); - break; - } - - return ssa_emit(proc, ssa_make_instr_binary_op(proc, op, left, right, type)); -} - -ssaValue *ssa_emit_comp(ssaProcedure *proc, TokenKind op_kind, ssaValue *left, ssaValue *right) { - Type *a = base_type(ssa_type(left)); - Type *b = base_type(ssa_type(right)); - - if (are_types_identical(a, b)) { - // NOTE(bill): No need for a conversion - } else if (left->kind == ssaValue_Constant || left->kind == ssaValue_Nil) { - left = ssa_emit_conv(proc, left, ssa_type(right)); - } else if (right->kind == ssaValue_Constant || right->kind == ssaValue_Nil) { - right = ssa_emit_conv(proc, right, ssa_type(left)); - } - - Type *result = t_bool; - if (is_type_vector(a)) { - result = make_type_vector(proc->module->allocator, t_bool, a->Vector.count); - } - return ssa_emit(proc, ssa_make_instr_binary_op(proc, op_kind, left, right, result)); -} - -ssaValue *ssa_emit_array_ep(ssaProcedure *proc, ssaValue *s, ssaValue *index) { - Type *st = base_type(type_deref(ssa_type(s))); - GB_ASSERT(is_type_array(st)); - - // NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32 - index = ssa_emit_conv(proc, index, t_i32); - return ssa_emit(proc, ssa_make_instr_array_element_ptr(proc, s, index)); -} - -ssaValue *ssa_emit_array_ep(ssaProcedure *proc, ssaValue *s, i32 index) { - return ssa_emit_array_ep(proc, s, ssa_make_const_i32(proc->module->allocator, index)); -} - - -ssaValue *ssa_emit_struct_ep(ssaProcedure *proc, ssaValue *s, i32 index) { - gbAllocator a = proc->module->allocator; - Type *t = base_type(type_deref(ssa_type(s))); - Type *result_type = NULL; - ssaValue *gep = NULL; - - if (is_type_struct(t)) { - GB_ASSERT(t->Record.field_count > 0); - GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); - result_type = make_type_pointer(a, t->Record.fields[index]->type); - } else if (is_type_tuple(t)) { - GB_ASSERT(t->Tuple.variable_count > 0); - GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1)); - result_type = make_type_pointer(a, t->Tuple.variables[index]->type); - } else if (is_type_slice(t)) { - switch (index) { - case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->Slice.elem)); break; - case 1: result_type = make_type_pointer(a, t_int); break; - case 2: result_type = make_type_pointer(a, t_int); break; - } - } else if (is_type_string(t)) { - switch (index) { - case 0: result_type = make_type_pointer(a, t_u8_ptr); break; - case 1: result_type = make_type_pointer(a, t_int); break; - } - } else if (is_type_any(t)) { - switch (index) { - case 0: result_type = make_type_pointer(a, t_type_info_ptr); break; - case 1: result_type = make_type_pointer(a, t_rawptr); break; - } - } else if (is_type_maybe(t)) { - switch (index) { - case 0: result_type = make_type_pointer(a, t->Maybe.elem); break; - case 1: result_type = make_type_pointer(a, t_bool); break; - } - } else if (is_type_union(t)) { - switch (index) { - case 1: result_type = make_type_pointer(a, t_int); break; - - case 0: - default: - GB_PANIC("TODO(bill): struct_gep 0 for unions"); - break; - } - } else { - GB_PANIC("TODO(bill): struct_gep type: %s, %d", type_to_string(ssa_type(s)), index); - } - - GB_ASSERT(result_type != NULL); - - gep = ssa_make_instr_struct_element_ptr(proc, s, index, result_type); - return ssa_emit(proc, gep); -} - - - -ssaValue *ssa_emit_array_ev(ssaProcedure *proc, ssaValue *s, i32 index) { - Type *st = base_type(ssa_type(s)); - GB_ASSERT(is_type_array(st)); - return ssa_emit(proc, ssa_make_instr_array_extract_value(proc, s, index)); -} - -ssaValue *ssa_emit_struct_ev(ssaProcedure *proc, ssaValue *s, i32 index) { - // NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32 - - gbAllocator a = proc->module->allocator; - Type *t = base_type(ssa_type(s)); - Type *result_type = NULL; - - if (is_type_struct(t)) { - GB_ASSERT(t->Record.field_count > 0); - GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1)); - result_type = t->Record.fields[index]->type; - } else if (is_type_tuple(t)) { - GB_ASSERT(t->Tuple.variable_count > 0); - GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1)); - result_type = t->Tuple.variables[index]->type; - } else if (is_type_slice(t)) { - switch (index) { - case 0: result_type = make_type_pointer(a, t->Slice.elem); break; - case 1: result_type = t_int; break; - case 2: result_type = t_int; break; - } - } else if (is_type_string(t)) { - switch (index) { - case 0: result_type = t_u8_ptr; break; - case 1: result_type = t_int; break; - } - } else if (is_type_any(t)) { - switch (index) { - case 0: result_type = t_type_info_ptr; break; - case 1: result_type = t_rawptr; break; - } - } else if (is_type_maybe(t)) { - switch (index) { - case 0: result_type = t->Maybe.elem; break; - case 1: result_type = t_bool; break; - } - } else if (is_type_union(t)) { - switch (index) { - case 1: result_type = t_int; break; - - case 0: - default: - GB_PANIC("TODO(bill): struct_gep 0 for unions"); - break; - } - } else { - GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(ssa_type(s)), index); - } - - GB_ASSERT(result_type != NULL); - - return ssa_emit(proc, ssa_make_instr_struct_extract_value(proc, s, index, result_type)); -} - - -ssaValue *ssa_emit_deep_field_gep(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) { - GB_ASSERT(sel.index.count > 0); - - for_array(i, sel.index) { - i32 index = cast(i32)sel.index[i]; - if (is_type_pointer(type)) { - type = type_deref(type); - e = ssa_emit_load(proc, e); - e = ssa_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies? - } - type = base_type(type); - - - if (is_type_raw_union(type)) { - type = type->Record.fields[index]->type; - e = ssa_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type)); - } else if (type->kind == Type_Record) { - type = type->Record.fields[index]->type; - e = ssa_emit_struct_ep(proc, e, index); - } else if (type->kind == Type_Basic) { - switch (type->Basic.kind) { - case Basic_any: { - if (index == 0) { - type = t_type_info_ptr; - } else if (index == 1) { - type = t_rawptr; - } - e = ssa_emit_struct_ep(proc, e, index); - } break; - - case Basic_string: - e = ssa_emit_struct_ep(proc, e, index); - break; - - default: - GB_PANIC("un-gep-able type"); - break; - } - } else if (type->kind == Type_Slice) { - e = ssa_emit_struct_ep(proc, e, index); - } else { - GB_PANIC("un-gep-able type"); - } - } - - return e; -} - - -ssaValue *ssa_emit_deep_field_ev(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) { - GB_ASSERT(sel.index.count > 0); - - for_array(i, sel.index) { - isize index = sel.index[i]; - if (is_type_pointer(type)) { - type = type_deref(type); - e = ssa_emit_load(proc, e); - e = ssa_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies? - } - type = base_type(type); - - - if (is_type_raw_union(type)) { - type = type->Record.fields[index]->type; - e = ssa_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type)); - } else { - e = ssa_emit_struct_ev(proc, e, index); - } - } - - return e; -} - - - - -ssaValue *ssa_array_elem(ssaProcedure *proc, ssaValue *array) { - return ssa_emit_array_ep(proc, array, v_zero32); -} -ssaValue *ssa_array_len(ssaProcedure *proc, ssaValue *array) { - Type *t = ssa_type(array); - GB_ASSERT(t->kind == Type_Array); - return ssa_make_const_int(proc->module->allocator, t->Array.count); -} -ssaValue *ssa_array_cap(ssaProcedure *proc, ssaValue *array) { - return ssa_array_len(proc, array); -} - -ssaValue *ssa_slice_elem(ssaProcedure *proc, ssaValue *slice) { - Type *t = ssa_type(slice); - GB_ASSERT(t->kind == Type_Slice); - return ssa_emit_struct_ev(proc, slice, 0); -} -ssaValue *ssa_slice_len(ssaProcedure *proc, ssaValue *slice) { - Type *t = ssa_type(slice); - GB_ASSERT(t->kind == Type_Slice); - return ssa_emit_struct_ev(proc, slice, 1); -} -ssaValue *ssa_slice_cap(ssaProcedure *proc, ssaValue *slice) { - Type *t = ssa_type(slice); - GB_ASSERT(t->kind == Type_Slice); - return ssa_emit_struct_ev(proc, slice, 2); -} - -ssaValue *ssa_string_elem(ssaProcedure *proc, ssaValue *string) { - Type *t = ssa_type(string); - GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string); - return ssa_emit_struct_ev(proc, string, 0); -} -ssaValue *ssa_string_len(ssaProcedure *proc, ssaValue *string) { - Type *t = ssa_type(string); - GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string); - return ssa_emit_struct_ev(proc, string, 1); -} - - - -ssaValue *ssa_add_local_slice(ssaProcedure *proc, Type *slice_type, ssaValue *base, ssaValue *low, ssaValue *high, ssaValue *max) { - // TODO(bill): array bounds checking for slice creation - // TODO(bill): check that low < high <= max - gbAllocator a = proc->module->allocator; - Type *bt = base_type(ssa_type(base)); - - if (low == NULL) { - low = v_zero; - } - if (high == NULL) { - switch (bt->kind) { - case Type_Array: high = ssa_array_len(proc, base); break; - case Type_Slice: high = ssa_slice_len(proc, base); break; - case Type_Pointer: high = v_one; break; - } - } - if (max == NULL) { - switch (bt->kind) { - case Type_Array: max = ssa_array_cap(proc, base); break; - case Type_Slice: max = ssa_slice_cap(proc, base); break; - case Type_Pointer: max = high; break; - } - } - GB_ASSERT(max != NULL); - - ssaValue *len = ssa_emit_arith(proc, Token_Sub, high, low, t_int); - ssaValue *cap = ssa_emit_arith(proc, Token_Sub, max, low, t_int); - - ssaValue *elem = NULL; - switch (bt->kind) { - case Type_Array: elem = ssa_array_elem(proc, base); break; - case Type_Slice: elem = ssa_slice_elem(proc, base); break; - case Type_Pointer: elem = ssa_emit_load(proc, base); break; - } - - elem = ssa_emit_ptr_offset(proc, elem, low); - - ssaValue *slice = ssa_add_local_generated(proc, slice_type); - - ssaValue *gep = NULL; - gep = ssa_emit_struct_ep(proc, slice, 0); - ssa_emit_store(proc, gep, elem); - - gep = ssa_emit_struct_ep(proc, slice, 1); - ssa_emit_store(proc, gep, len); - - gep = ssa_emit_struct_ep(proc, slice, 2); - ssa_emit_store(proc, gep, cap); - - return slice; -} - -ssaValue *ssa_emit_string(ssaProcedure *proc, ssaValue *elem, ssaValue *len) { - ssaValue *str = ssa_add_local_generated(proc, t_string); - ssaValue *str_elem = ssa_emit_struct_ep(proc, str, 0); - ssaValue *str_len = ssa_emit_struct_ep(proc, str, 1); - ssa_emit_store(proc, str_elem, elem); - ssa_emit_store(proc, str_len, len); - return ssa_emit_load(proc, str); -} - - - - -String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) { - Type *prev_src = src; - // Type *prev_dst = dst; - src = base_type(type_deref(src)); - // dst = base_type(type_deref(dst)); - b32 src_is_ptr = src != prev_src; - // b32 dst_is_ptr = dst != prev_dst; - - GB_ASSERT(is_type_struct(src)); - for (isize i = 0; i < src->Record.field_count; i++) { - Entity *f = src->Record.fields[i]; - if (f->kind == Entity_Variable && f->flags & EntityFlag_Anonymous) { - if (are_types_identical(dst, f->type)) { - return f->token.string; - } - if (src_is_ptr && is_type_pointer(dst)) { - if (are_types_identical(type_deref(dst), f->type)) { - return f->token.string; - } - } - String name = lookup_polymorphic_field(info, dst, f->type); - if (name.len > 0) { - return name; - } - } - } - return make_string(""); -} - -ssaValue *ssa_emit_bitcast(ssaProcedure *proc, ssaValue *data, Type *type) { - return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, data, ssa_type(data), type)); -} - - -ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_argument) { - Type *src_type = ssa_type(value); - if (are_types_identical(t, src_type)) { - return value; - } - - - Type *src = get_enum_base_type(base_type(src_type)); - Type *dst = get_enum_base_type(base_type(t)); - - if (value->kind == ssaValue_Constant) { - if (is_type_any(dst)) { - ssaValue *default_value = ssa_add_local_generated(proc, default_type(src_type)); - ssa_emit_store(proc, default_value, value); - return ssa_emit_conv(proc, ssa_emit_load(proc, default_value), t_any, is_argument); - } else if (dst->kind == Type_Basic) { - ExactValue ev = value->Constant.value; - if (is_type_float(dst)) { - ev = exact_value_to_float(ev); - } else if (is_type_string(dst)) { - // Handled elsewhere - GB_ASSERT(ev.kind == ExactValue_String); - } else if (is_type_integer(dst)) { - ev = exact_value_to_integer(ev); - } else if (is_type_pointer(dst)) { - // IMPORTANT NOTE(bill): LLVM doesn't support pointer constants expect `null` - ssaValue *i = ssa_add_module_constant(proc->module, t_uint, ev); - return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, i, t_uint, dst)); - } - return ssa_add_module_constant(proc->module, t, ev); - } - } - - if (are_types_identical(src, dst)) { - return value; - } - - if (is_type_maybe(dst)) { - ssaValue *maybe = ssa_add_local_generated(proc, dst); - ssaValue *val = ssa_emit_struct_ep(proc, maybe, 0); - ssaValue *set = ssa_emit_struct_ep(proc, maybe, 1); - ssa_emit_store(proc, val, value); - ssa_emit_store(proc, set, v_true); - return ssa_emit_load(proc, maybe); - } - - // integer -> integer - if (is_type_integer(src) && is_type_integer(dst)) { - GB_ASSERT(src->kind == Type_Basic && - dst->kind == Type_Basic); - i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src); - i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst); - if (sz == dz) { - // NOTE(bill): In LLVM, all integers are signed and rely upon 2's compliment - return value; - } - - ssaConvKind kind = ssaConv_trunc; - if (dz >= sz) { - kind = ssaConv_zext; - } - return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); - } - - // boolean -> integer - if (is_type_boolean(src) && is_type_integer(dst)) { - return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_zext, value, src, dst)); - } - - // integer -> boolean - if (is_type_integer(src) && is_type_boolean(dst)) { - return ssa_emit_comp(proc, Token_NotEq, value, v_zero); - } - - - // float -> float - if (is_type_float(src) && is_type_float(dst)) { - i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src); - i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst); - ssaConvKind kind = ssaConv_fptrunc; - if (dz >= sz) { - kind = ssaConv_fpext; - } - return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); - } - - // float <-> integer - if (is_type_float(src) && is_type_integer(dst)) { - ssaConvKind kind = ssaConv_fptosi; - if (is_type_unsigned(dst)) { - kind = ssaConv_fptoui; - } - return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); - } - if (is_type_integer(src) && is_type_float(dst)) { - ssaConvKind kind = ssaConv_sitofp; - if (is_type_unsigned(src)) { - kind = ssaConv_uitofp; - } - return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); - } - - // Pointer <-> int - if (is_type_pointer(src) && is_type_int_or_uint(dst)) { - return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_ptrtoint, value, src, dst)); - } - if (is_type_int_or_uint(src) && is_type_pointer(dst)) { - return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, value, src, dst)); - } - - if (is_type_union(dst)) { - for (isize i = 0; i < dst->Record.field_count; i++) { - Entity *f = dst->Record.fields[i]; - if (are_types_identical(f->type, src_type)) { - ssa_emit_comment(proc, make_string("union - child to parent")); - gbAllocator allocator = proc->module->allocator; - ssaValue *parent = ssa_add_local_generated(proc, t); - ssaValue *tag = ssa_make_const_int(allocator, i); - ssa_emit_store(proc, ssa_emit_struct_ep(proc, parent, 1), tag); - - ssaValue *data = ssa_emit_conv(proc, parent, t_rawptr); - - Type *tag_type = src_type; - Type *tag_type_ptr = make_type_pointer(allocator, tag_type); - ssaValue *underlying = ssa_emit_bitcast(proc, data, tag_type_ptr); - ssa_emit_store(proc, underlying, value); - - return ssa_emit_load(proc, parent); - } - } - } - - // NOTE(bill): This has to be done beofre `Pointer <-> Pointer` as it's - // subtype polymorphism casting - if (true || is_argument) { - Type *sb = base_type(type_deref(src)); - b32 src_is_ptr = src != sb; - if (is_type_struct(sb)) { - String field_name = lookup_polymorphic_field(proc->module->info, t, src); - // gb_printf("field_name: %.*s\n", LIT(field_name)); - if (field_name.len > 0) { - // NOTE(bill): It can be casted - Selection sel = lookup_field(proc->module->allocator, sb, field_name, false); - if (sel.entity != NULL) { - ssa_emit_comment(proc, make_string("cast - polymorphism")); - if (src_is_ptr) { - value = ssa_emit_load(proc, value); - } - return ssa_emit_deep_field_ev(proc, sb, value, sel); - } - } - } - } - - - - // Pointer <-> Pointer - if (is_type_pointer(src) && is_type_pointer(dst)) { - return ssa_emit_bitcast(proc, value, dst); - } - - - - // proc <-> proc - if (is_type_proc(src) && is_type_proc(dst)) { - return ssa_emit_bitcast(proc, value, dst); - } - - // pointer -> proc - if (is_type_pointer(src) && is_type_proc(dst)) { - return ssa_emit_bitcast(proc, value, dst); - } - // proc -> pointer - if (is_type_proc(src) && is_type_pointer(dst)) { - return ssa_emit_bitcast(proc, value, dst); - } - - - - // []byte/[]u8 <-> string - if (is_type_u8_slice(src) && is_type_string(dst)) { - ssaValue *elem = ssa_slice_elem(proc, value); - ssaValue *len = ssa_slice_len(proc, value); - return ssa_emit_string(proc, elem, len); - } - if (is_type_string(src) && is_type_u8_slice(dst)) { - ssaValue *elem = ssa_string_elem(proc, value); - ssaValue *elem_ptr = ssa_add_local_generated(proc, ssa_type(elem)); - ssa_emit_store(proc, elem_ptr, elem); - - ssaValue *len = ssa_string_len(proc, value); - ssaValue *slice = ssa_add_local_slice(proc, dst, elem_ptr, v_zero, len, len); - return ssa_emit_load(proc, slice); - } - - if (is_type_vector(dst)) { - Type *dst_elem = dst->Vector.elem; - value = ssa_emit_conv(proc, value, dst_elem); - ssaValue *v = ssa_add_local_generated(proc, t); - v = ssa_emit_load(proc, v); - v = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, value, v_zero32)); - // NOTE(bill): Broadcast lowest value to all values - isize index_count = dst->Vector.count; - i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count); - for (isize i = 0; i < index_count; i++) { - indices[i] = 0; - } - - v = ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, v, indices, index_count)); - return v; - } - - if (is_type_any(dst)) { - ssaValue *result = ssa_add_local_generated(proc, t_any); - - if (is_type_untyped_nil(src)) { - return ssa_emit_load(proc, result); - } - - ssaValue *data = NULL; - if (value->kind == ssaValue_Instr && - value->Instr.kind == ssaInstr_Load) { - // NOTE(bill): Addressable value - data = value->Instr.Load.address; - } else { - // NOTE(bill): Non-addressable value - data = ssa_add_local_generated(proc, src_type); - ssa_emit_store(proc, data, value); - } - GB_ASSERT(is_type_pointer(ssa_type(data))); - GB_ASSERT(is_type_typed(src_type)); - data = ssa_emit_conv(proc, data, t_rawptr); - - - ssaValue *ti = ssa_type_info(proc, src_type); - - ssaValue *gep0 = ssa_emit_struct_ep(proc, result, 0); - ssaValue *gep1 = ssa_emit_struct_ep(proc, result, 1); - ssa_emit_store(proc, gep0, ti); - ssa_emit_store(proc, gep1, data); - - return ssa_emit_load(proc, result); - } - - if (is_type_untyped_nil(src) && type_has_nil(dst)) { - return ssa_make_value_nil(proc->module->allocator, t); - } - - - gb_printf_err("ssa_emit_conv: src -> dst\n"); - gb_printf_err("Not Identical %s != %s\n", type_to_string(src_type), type_to_string(t)); - gb_printf_err("Not Identical %s != %s\n", type_to_string(src), type_to_string(dst)); - - - GB_PANIC("Invalid type conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t)); - - return NULL; -} - - -ssaValue *ssa_emit_transmute(ssaProcedure *proc, ssaValue *value, Type *t) { - Type *src_type = ssa_type(value); - if (are_types_identical(t, src_type)) { - return value; - } - - Type *src = base_type(src_type); - Type *dst = base_type(t); - if (are_types_identical(t, src_type)) { - return value; - } - - i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src); - i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst); - - if (sz == dz) { - return ssa_emit_bitcast(proc, value, dst); - } - - - GB_PANIC("Invalid transmute conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t)); - - return NULL; -} - -ssaValue *ssa_emit_down_cast(ssaProcedure *proc, ssaValue *value, Type *t) { - GB_ASSERT(is_type_pointer(ssa_type(value))); - gbAllocator allocator = proc->module->allocator; - - String field_name = check_down_cast_name(t, type_deref(ssa_type(value))); - GB_ASSERT(field_name.len > 0); - Selection sel = lookup_field(proc->module->allocator, t, field_name, false); - Type *t_u8_ptr = make_type_pointer(allocator, t_u8); - ssaValue *bytes = ssa_emit_conv(proc, value, t_u8_ptr); - - i64 offset_ = type_offset_of_from_selection(proc->module->sizes, allocator, type_deref(t), sel); - ssaValue *offset = ssa_make_const_int(allocator, -offset_); - ssaValue *head = ssa_emit_ptr_offset(proc, bytes, offset); - return ssa_emit_conv(proc, head, t); -} - -ssaValue *ssa_emit_union_cast(ssaProcedure *proc, ssaValue *value, Type *tuple) { - GB_ASSERT(tuple->kind == Type_Tuple); - gbAllocator a = proc->module->allocator; - - Type *src_type = ssa_type(value); - b32 is_ptr = is_type_pointer(src_type); - - ssaValue *v = ssa_add_local_generated(proc, tuple); - - if (is_ptr) { - Type *src = base_type(type_deref(src_type)); - Type *src_ptr = src_type; - GB_ASSERT(is_type_union(src)); - Type *dst_ptr = tuple->Tuple.variables[0]->type; - Type *dst = type_deref(dst_ptr); - - ssaValue *tag = ssa_emit_load(proc, ssa_emit_struct_ep(proc, value, 1)); - ssaValue *dst_tag = NULL; - for (isize i = 1; i < src->Record.field_count; i++) { - Entity *f = src->Record.fields[i]; - if (are_types_identical(f->type, dst)) { - dst_tag = ssa_make_const_int(a, i); - break; - } - } - GB_ASSERT(dst_tag != NULL); - - ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok"); - ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end"); - ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag); - ssa_emit_if(proc, cond, ok_block, end_block); - proc->curr_block = ok_block; - - ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0); - ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1); - - ssaValue *data = ssa_emit_conv(proc, value, dst_ptr); - ssa_emit_store(proc, gep0, data); - ssa_emit_store(proc, gep1, v_true); - - ssa_emit_jump(proc, end_block); - proc->curr_block = end_block; - - } else { - Type *src = base_type(src_type); - GB_ASSERT(is_type_union(src)); - Type *dst = tuple->Tuple.variables[0]->type; - Type *dst_ptr = make_type_pointer(a, dst); - - ssaValue *tag = ssa_emit_struct_ev(proc, value, 1); - ssaValue *dst_tag = NULL; - for (isize i = 1; i < src->Record.field_count; i++) { - Entity *f = src->Record.fields[i]; - if (are_types_identical(f->type, dst)) { - dst_tag = ssa_make_const_int(a, i); - break; - } - } - GB_ASSERT(dst_tag != NULL); - - // HACK(bill): This is probably not very efficient - ssaValue *union_copy = ssa_add_local_generated(proc, src_type); - ssa_emit_store(proc, union_copy, value); - - ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok"); - ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end"); - ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag); - ssa_emit_if(proc, cond, ok_block, end_block); - proc->curr_block = ok_block; - - ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0); - ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1); - - ssaValue *data = ssa_emit_load(proc, ssa_emit_conv(proc, union_copy, dst_ptr)); - ssa_emit_store(proc, gep0, data); - ssa_emit_store(proc, gep1, v_true); - - ssa_emit_jump(proc, end_block); - proc->curr_block = end_block; - - } - return ssa_emit_load(proc, v); -} - - -isize ssa_type_info_index(CheckerInfo *info, Type *type) { - type = default_type(type); - - isize entry_index = -1; - HashKey key = hash_pointer(type); - auto *found_entry_index = map_get(&info->type_info_map, key); - if (found_entry_index) { - entry_index = *found_entry_index; - } - if (entry_index < 0) { - // NOTE(bill): Do manual search - // TODO(bill): This is O(n) and can be very slow - for_array(i, info->type_info_map.entries){ - auto *e = &info->type_info_map.entries[i]; - Type *prev_type = cast(Type *)e->key.ptr; - if (are_types_identical(prev_type, type)) { - entry_index = e->value; - // NOTE(bill): Add it to the search map - map_set(&info->type_info_map, key, entry_index); - break; - } - } - } - - if (entry_index < 0) { - compiler_error("Type_Info for `%s` could not be found", type_to_string(type)); - } - return entry_index; -} - -ssaValue *ssa_type_info(ssaProcedure *proc, Type *type) { - ssaValue **found = map_get(&proc->module->members, hash_string(make_string(SSA_TYPE_INFO_DATA_NAME))); - GB_ASSERT(found != NULL); - ssaValue *type_info_data = *found; - CheckerInfo *info = proc->module->info; - - type = default_type(type); - - i32 entry_index = ssa_type_info_index(info, type); - - // gb_printf_err("%d %s\n", entry_index, type_to_string(type)); - - return ssa_emit_array_ep(proc, type_info_data, ssa_make_const_i32(proc->module->allocator, entry_index)); -} - - - -ssaValue *ssa_emit_logical_binary_expr(ssaProcedure *proc, AstNode *expr) { - ast_node(be, BinaryExpr, expr); -#if 0 - ssaBlock *true_ = ssa_add_block(proc, NULL, "logical.cmp.true"); - ssaBlock *false_ = ssa_add_block(proc, NULL, "logical.cmp.false"); - ssaBlock *done = ssa_add_block(proc, NULL, "logical.cmp.done"); - - ssaValue *result = ssa_add_local_generated(proc, t_bool); - ssa_build_cond(proc, expr, true_, false_); - - proc->curr_block = true_; - ssa_emit_store(proc, result, v_true); - ssa_emit_jump(proc, done); - - proc->curr_block = false_; - ssa_emit_store(proc, result, v_false); - ssa_emit_jump(proc, done); - - proc->curr_block = done; - - return ssa_emit_load(proc, result); -#else - ssaBlock *rhs = ssa_add_block(proc, NULL, "logical.cmp.rhs"); - ssaBlock *done = ssa_add_block(proc, NULL, "logical.cmp.done"); - - Type *type = type_of_expr(proc->module->info, expr); - type = default_type(type); - - ssaValue *short_circuit = NULL; - if (be->op.kind == Token_CmpAnd) { - ssa_build_cond(proc, be->left, rhs, done); - short_circuit = v_false; - } else if (be->op.kind == Token_CmpOr) { - ssa_build_cond(proc, be->left, done, rhs); - short_circuit = v_true; - } - - if (rhs->preds.count == 0) { - proc->curr_block = done; - return short_circuit; - } - - if (done->preds.count == 0) { - proc->curr_block = rhs; - return ssa_build_expr(proc, be->right); - } - - Array edges = {}; - array_init(&edges, proc->module->allocator, done->preds.count+1); - for_array(i, done->preds) { - array_add(&edges, short_circuit); - } - - proc->curr_block = rhs; - array_add(&edges, ssa_build_expr(proc, be->right)); - ssa_emit_jump(proc, done); - proc->curr_block = done; - - return ssa_emit(proc, ssa_make_instr_phi(proc, edges, type)); -#endif -} - - -void ssa_emit_bounds_check(ssaProcedure *proc, Token token, ssaValue *index, ssaValue *len) { - if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) { - return; - } - - index = ssa_emit_conv(proc, index, t_int); - len = ssa_emit_conv(proc, len, t_int); - - ssa_emit(proc, ssa_make_instr_bounds_check(proc, token.pos, index, len)); - - // gbAllocator a = proc->module->allocator; - // ssaValue **args = gb_alloc_array(a, ssaValue *, 5); - // args[0] = ssa_emit_global_string(proc, token.pos.file); - // args[1] = ssa_make_const_int(a, token.pos.line); - // args[2] = ssa_make_const_int(a, token.pos.column); - // args[3] = ssa_emit_conv(proc, index, t_int); - // args[4] = ssa_emit_conv(proc, len, t_int); - - // ssa_emit_global_call(proc, "__bounds_check_error", args, 5); -} - -void ssa_emit_slice_bounds_check(ssaProcedure *proc, Token token, ssaValue *low, ssaValue *high, ssaValue *max, b32 is_substring) { - if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) { - return; - } - - - low = ssa_emit_conv(proc, low, t_int); - high = ssa_emit_conv(proc, high, t_int); - max = ssa_emit_conv(proc, max, t_int); - - ssa_emit(proc, ssa_make_instr_slice_bounds_check(proc, token.pos, low, high, max, is_substring)); - - // gbAllocator a = proc->module->allocator; - // ssaValue **args = gb_alloc_array(a, ssaValue *, 6); - // args[0] = ssa_emit_global_string(proc, token.pos.file); - // args[1] = ssa_make_const_int(a, token.pos.line); - // args[2] = ssa_make_const_int(a, token.pos.column); - // args[3] = ssa_emit_conv(proc, low, t_int); - // args[4] = ssa_emit_conv(proc, high, t_int); - // args[5] = ssa_emit_conv(proc, max, t_int); - - // if (!is_substring) { - // ssa_emit_global_call(proc, "__slice_expr_error", args, 6); - // } else { - // ssa_emit_global_call(proc, "__substring_expr_error", args, 5); - // } -} - - - - - - diff --git a/src/ssa/make.cpp b/src/ssa/make.cpp deleted file mode 100644 index 47ae3a305..000000000 --- a/src/ssa/make.cpp +++ /dev/null @@ -1,495 +0,0 @@ -void ssa_module_add_value(ssaModule *m, Entity *e, ssaValue *v); -ssaValue *ssa_emit_zero_init(ssaProcedure *p, ssaValue *address); -ssaValue *ssa_emit_comment(ssaProcedure *p, String text); -ssaValue *ssa_emit_store(ssaProcedure *p, ssaValue *address, ssaValue *value); -ssaValue *ssa_emit_load(ssaProcedure *p, ssaValue *address); - - -ssaValue *ssa_alloc_value(gbAllocator a, ssaValueKind kind) { - ssaValue *v = gb_alloc_item(a, ssaValue); - v->kind = kind; - return v; -} -ssaValue *ssa_alloc_instr(ssaProcedure *proc, ssaInstrKind kind) { - ssaValue *v = ssa_alloc_value(proc->module->allocator, ssaValue_Instr); - v->Instr.kind = kind; - proc->instr_count++; - return v; -} -ssaDebugInfo *ssa_alloc_debug_info(gbAllocator a, ssaDebugInfoKind kind) { - ssaDebugInfo *di = gb_alloc_item(a, ssaDebugInfo); - di->kind = kind; - return di; -} - - - - -ssaValue *ssa_make_value_type_name(gbAllocator a, String name, Type *type) { - ssaValue *v = ssa_alloc_value(a, ssaValue_TypeName); - v->TypeName.name = name; - v->TypeName.type = type; - return v; -} - -ssaValue *ssa_make_value_global(gbAllocator a, Entity *e, ssaValue *value) { - ssaValue *v = ssa_alloc_value(a, ssaValue_Global); - v->Global.entity = e; - v->Global.type = make_type_pointer(a, e->type); - v->Global.value = value; - array_init(&v->Global.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here - return v; -} -ssaValue *ssa_make_value_param(gbAllocator a, ssaProcedure *parent, Entity *e) { - ssaValue *v = ssa_alloc_value(a, ssaValue_Param); - v->Param.parent = parent; - v->Param.entity = e; - v->Param.type = e->type; - array_init(&v->Param.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here - return v; -} -ssaValue *ssa_make_value_nil(gbAllocator a, Type *type) { - ssaValue *v = ssa_alloc_value(a, ssaValue_Nil); - v->Nil.type = type; - return v; -} - - - -ssaValue *ssa_make_instr_local(ssaProcedure *p, Entity *e, b32 zero_initialized) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_Local); - ssaInstr *i = &v->Instr; - i->Local.entity = e; - i->Local.type = make_type_pointer(p->module->allocator, e->type); - i->Local.zero_initialized = zero_initialized; - array_init(&i->Local.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here - ssa_module_add_value(p->module, e, v); - return v; -} - - -ssaValue *ssa_make_instr_store(ssaProcedure *p, ssaValue *address, ssaValue *value) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_Store); - ssaInstr *i = &v->Instr; - i->Store.address = address; - i->Store.value = value; - return v; -} - -ssaValue *ssa_make_instr_zero_init(ssaProcedure *p, ssaValue *address) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_ZeroInit); - ssaInstr *i = &v->Instr; - i->ZeroInit.address = address; - return v; -} - -ssaValue *ssa_make_instr_load(ssaProcedure *p, ssaValue *address) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_Load); - ssaInstr *i = &v->Instr; - i->Load.address = address; - i->Load.type = type_deref(ssa_type(address)); - return v; -} - -ssaValue *ssa_make_instr_array_element_ptr(ssaProcedure *p, ssaValue *address, ssaValue *elem_index) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_ArrayElementPtr); - ssaInstr *i = &v->Instr; - Type *t = ssa_type(address); - GB_ASSERT(is_type_pointer(t)); - t = base_type(type_deref(t)); - GB_ASSERT(is_type_array(t)); - - Type *result_type = make_type_pointer(p->module->allocator, t->Array.elem); - - i->ArrayElementPtr.address = address; - i->ArrayElementPtr.elem_index = elem_index; - i->ArrayElementPtr.result_type = result_type; - - GB_ASSERT_MSG(is_type_pointer(ssa_type(address)), - "%s", type_to_string(ssa_type(address))); - return v; -} -ssaValue *ssa_make_instr_struct_element_ptr(ssaProcedure *p, ssaValue *address, i32 elem_index, Type *result_type) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_StructElementPtr); - ssaInstr *i = &v->Instr; - i->StructElementPtr.address = address; - i->StructElementPtr.elem_index = elem_index; - i->StructElementPtr.result_type = result_type; - - GB_ASSERT_MSG(is_type_pointer(ssa_type(address)), - "%s", type_to_string(ssa_type(address))); - return v; -} -ssaValue *ssa_make_instr_ptr_offset(ssaProcedure *p, ssaValue *address, ssaValue *offset) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_PtrOffset); - ssaInstr *i = &v->Instr; - i->PtrOffset.address = address; - i->PtrOffset.offset = offset; - - GB_ASSERT_MSG(is_type_pointer(ssa_type(address)), - "%s", type_to_string(ssa_type(address))); - GB_ASSERT_MSG(is_type_integer(ssa_type(offset)), - "%s", type_to_string(ssa_type(address))); - - return v; -} - - - -ssaValue *ssa_make_instr_array_extract_value(ssaProcedure *p, ssaValue *address, i32 index) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_ArrayExtractValue); - ssaInstr *i = &v->Instr; - i->ArrayExtractValue.address = address; - i->ArrayExtractValue.index = index; - Type *t = base_type(ssa_type(address)); - GB_ASSERT(is_type_array(t)); - i->ArrayExtractValue.result_type = t->Array.elem; - return v; -} - -ssaValue *ssa_make_instr_struct_extract_value(ssaProcedure *p, ssaValue *address, i32 index, Type *result_type) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_StructExtractValue); - ssaInstr *i = &v->Instr; - i->StructExtractValue.address = address; - i->StructExtractValue.index = index; - i->StructExtractValue.result_type = result_type; - return v; -} - -ssaValue *ssa_make_instr_binary_op(ssaProcedure *p, TokenKind op, ssaValue *left, ssaValue *right, Type *type) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_BinaryOp); - ssaInstr *i = &v->Instr; - i->BinaryOp.op = op; - i->BinaryOp.left = left; - i->BinaryOp.right = right; - i->BinaryOp.type = type; - return v; -} - -ssaValue *ssa_make_instr_jump(ssaProcedure *p, ssaBlock *block) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_Jump); - ssaInstr *i = &v->Instr; - i->Jump.block = block; - return v; -} -ssaValue *ssa_make_instr_if(ssaProcedure *p, ssaValue *cond, ssaBlock *true_block, ssaBlock *false_block) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_If); - ssaInstr *i = &v->Instr; - i->If.cond = cond; - i->If.true_block = true_block; - i->If.false_block = false_block; - return v; -} - - -ssaValue *ssa_make_instr_phi(ssaProcedure *p, Array edges, Type *type) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_Phi); - ssaInstr *i = &v->Instr; - i->Phi.edges = edges; - i->Phi.type = type; - return v; -} - -ssaValue *ssa_make_instr_unreachable(ssaProcedure *p) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_Unreachable); - return v; -} - -ssaValue *ssa_make_instr_return(ssaProcedure *p, ssaValue *value) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_Return); - v->Instr.Return.value = value; - return v; -} - -ssaValue *ssa_make_instr_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue *f) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_Select); - v->Instr.Select.cond = cond; - v->Instr.Select.true_value = t; - v->Instr.Select.false_value = f; - return v; -} - -ssaValue *ssa_make_instr_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count, Type *result_type) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_Call); - v->Instr.Call.value = value; - v->Instr.Call.args = args; - v->Instr.Call.arg_count = arg_count; - v->Instr.Call.type = result_type; - return v; -} - -ssaValue *ssa_make_instr_conv(ssaProcedure *p, ssaConvKind kind, ssaValue *value, Type *from, Type *to) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_Conv); - v->Instr.Conv.kind = kind; - v->Instr.Conv.value = value; - v->Instr.Conv.from = from; - v->Instr.Conv.to = to; - return v; -} - -ssaValue *ssa_make_instr_extract_element(ssaProcedure *p, ssaValue *vector, ssaValue *index) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorExtractElement); - v->Instr.VectorExtractElement.vector = vector; - v->Instr.VectorExtractElement.index = index; - return v; -} - -ssaValue *ssa_make_instr_insert_element(ssaProcedure *p, ssaValue *vector, ssaValue *elem, ssaValue *index) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorInsertElement); - v->Instr.VectorInsertElement.vector = vector; - v->Instr.VectorInsertElement.elem = elem; - v->Instr.VectorInsertElement.index = index; - return v; -} - -ssaValue *ssa_make_instr_vector_shuffle(ssaProcedure *p, ssaValue *vector, i32 *indices, isize index_count) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorShuffle); - v->Instr.VectorShuffle.vector = vector; - v->Instr.VectorShuffle.indices = indices; - v->Instr.VectorShuffle.index_count = index_count; - - Type *vt = base_type(ssa_type(vector)); - v->Instr.VectorShuffle.type = make_type_vector(p->module->allocator, vt->Vector.elem, index_count); - - return v; -} - -ssaValue *ssa_make_instr_comment(ssaProcedure *p, String text) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_Comment); - v->Instr.Comment.text = text; - return v; -} - -ssaValue *ssa_make_instr_bounds_check(ssaProcedure *p, TokenPos pos, ssaValue *index, ssaValue *len) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_BoundsCheck); - v->Instr.BoundsCheck.pos = pos; - v->Instr.BoundsCheck.index = index; - v->Instr.BoundsCheck.len = len; - return v; -} -ssaValue *ssa_make_instr_slice_bounds_check(ssaProcedure *p, TokenPos pos, ssaValue *low, ssaValue *high, ssaValue *max, b32 is_substring) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_SliceBoundsCheck); - v->Instr.SliceBoundsCheck.pos = pos; - v->Instr.SliceBoundsCheck.low = low; - v->Instr.SliceBoundsCheck.high = high; - v->Instr.SliceBoundsCheck.max = max; - v->Instr.SliceBoundsCheck.is_substring = is_substring; - return v; -} - - - -ssaValue *ssa_make_value_constant(gbAllocator a, Type *type, ExactValue value) { - ssaValue *v = ssa_alloc_value(a, ssaValue_Constant); - v->Constant.type = type; - v->Constant.value = value; - return v; -} - - -ssaValue *ssa_make_value_constant_slice(gbAllocator a, Type *type, ssaValue *backing_array, i64 count) { - ssaValue *v = ssa_alloc_value(a, ssaValue_ConstantSlice); - v->ConstantSlice.type = type; - v->ConstantSlice.backing_array = backing_array; - v->ConstantSlice.count = count; - return v; -} - -ssaValue *ssa_make_const_int(gbAllocator a, i64 i) { - return ssa_make_value_constant(a, t_int, make_exact_value_integer(i)); -} -ssaValue *ssa_make_const_i32(gbAllocator a, i64 i) { - return ssa_make_value_constant(a, t_i32, make_exact_value_integer(i)); -} -ssaValue *ssa_make_const_i64(gbAllocator a, i64 i) { - return ssa_make_value_constant(a, t_i64, make_exact_value_integer(i)); -} -ssaValue *ssa_make_const_bool(gbAllocator a, b32 b) { - return ssa_make_value_constant(a, t_bool, make_exact_value_bool(b != 0)); -} -ssaValue *ssa_make_const_string(gbAllocator a, String s) { - return ssa_make_value_constant(a, t_string, make_exact_value_string(s)); -} - -ssaValue *ssa_make_value_procedure(gbAllocator a, ssaModule *m, Entity *entity, Type *type, AstNode *type_expr, AstNode *body, String name) { - ssaValue *v = ssa_alloc_value(a, ssaValue_Proc); - v->Proc.module = m; - v->Proc.entity = entity; - v->Proc.type = type; - v->Proc.type_expr = type_expr; - v->Proc.body = body; - v->Proc.name = name; - array_init(&v->Proc.referrers, heap_allocator(), 0); // TODO(bill): replace heap allocator - - Type *t = base_type(type); - GB_ASSERT(is_type_proc(t)); - array_init(&v->Proc.params, heap_allocator(), t->Proc.param_count); - - return v; -} - -ssaBlock *ssa_add_block(ssaProcedure *proc, AstNode *node, char *label) { - Scope *scope = NULL; - if (node != NULL) { - Scope **found = map_get(&proc->module->info->scopes, hash_pointer(node)); - if (found) { - scope = *found; - } else { - GB_PANIC("Block scope not found for %.*s", LIT(ast_node_strings[node->kind])); - } - } - - ssaValue *v = ssa_alloc_value(proc->module->allocator, ssaValue_Block); - v->Block.label = make_string(label); - v->Block.node = node; - v->Block.scope = scope; - v->Block.parent = proc; - - array_init(&v->Block.instrs, heap_allocator()); - array_init(&v->Block.locals, heap_allocator()); - - array_init(&v->Block.preds, heap_allocator()); - array_init(&v->Block.succs, heap_allocator()); - - ssaBlock *block = &v->Block; - - array_add(&proc->blocks, block); - proc->block_count++; - - return block; -} - - - - - -ssaDefer ssa_add_defer_node(ssaProcedure *proc, isize scope_index, AstNode *stmt) { - ssaDefer d = {ssaDefer_Node}; - d.scope_index = scope_index; - d.block = proc->curr_block; - d.stmt = stmt; - array_add(&proc->defer_stmts, d); - return d; -} - - -ssaDefer ssa_add_defer_instr(ssaProcedure *proc, isize scope_index, ssaValue *instr) { - ssaDefer d = {ssaDefer_Instr}; - d.scope_index = proc->scope_index; - d.block = proc->curr_block; - d.instr = instr; // NOTE(bill): It will make a copy everytime it is called - array_add(&proc->defer_stmts, d); - return d; -} - - - -ssaValue *ssa_add_module_constant(ssaModule *m, Type *type, ExactValue value) { - if (is_type_slice(type)) { - ast_node(cl, CompoundLit, value.value_compound); - gbAllocator a = m->allocator; - - isize count = cl->elems.count; - if (count == 0) { - return ssa_make_value_nil(a, type); - } - Type *elem = base_type(type)->Slice.elem; - Type *t = make_type_array(a, elem, count); - ssaValue *backing_array = ssa_add_module_constant(m, t, value); - - - isize max_len = 7+8+1; - u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len); - isize len = gb_snprintf(cast(char *)str, max_len, "__csba$%x", m->global_array_index); - m->global_array_index++; - - String name = make_string(str, len-1); - - Entity *e = make_entity_constant(a, NULL, make_token_ident(name), t, value); - ssaValue *g = ssa_make_value_global(a, e, backing_array); - ssa_module_add_value(m, e, g); - map_set(&m->members, hash_string(name), g); - - return ssa_make_value_constant_slice(a, type, g, count); - } - - return ssa_make_value_constant(m->allocator, type, value); -} - -ssaValue *ssa_add_global_string_array(ssaModule *m, String string) { - gbAllocator a = m->allocator; - - isize max_len = 6+8+1; - u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len); - isize len = gb_snprintf(cast(char *)str, max_len, "__str$%x", m->global_string_index); - m->global_string_index++; - - String name = make_string(str, len-1); - Token token = {Token_String}; - token.string = name; - Type *type = make_type_array(a, t_u8, string.len); - ExactValue ev = make_exact_value_string(string); - Entity *entity = make_entity_constant(a, NULL, token, type, ev); - ssaValue *g = ssa_make_value_global(a, entity, ssa_add_module_constant(m, type, ev)); - g->Global.is_private = true; - // g->Global.is_unnamed_addr = true; - // g->Global.is_constant = true; - - ssa_module_add_value(m, entity, g); - map_set(&m->members, hash_string(name), g); - - return g; -} - - - - -ssaValue *ssa_add_local(ssaProcedure *proc, Entity *e, b32 zero_initialized = true) { - ssaBlock *b = proc->decl_block; // all variables must be in the first block - ssaValue *instr = ssa_make_instr_local(proc, e, zero_initialized); - instr->Instr.parent = b; - array_add(&b->instrs, instr); - array_add(&b->locals, instr); - proc->local_count++; - - // if (zero_initialized) { - ssa_emit_zero_init(proc, instr); - // } - - return instr; -} - -ssaValue *ssa_add_local_for_identifier(ssaProcedure *proc, AstNode *name, b32 zero_initialized) { - Entity **found = map_get(&proc->module->info->definitions, hash_pointer(name)); - if (found) { - Entity *e = *found; - ssa_emit_comment(proc, e->token.string); - return ssa_add_local(proc, e, zero_initialized); - } - return NULL; -} - -ssaValue *ssa_add_local_generated(ssaProcedure *proc, Type *type) { - GB_ASSERT(type != NULL); - - Scope *scope = NULL; - if (proc->curr_block) { - scope = proc->curr_block->scope; - } - Entity *e = make_entity_variable(proc->module->allocator, - scope, - empty_token, - type); - return ssa_add_local(proc, e, true); -} - -ssaValue *ssa_add_param(ssaProcedure *proc, Entity *e) { - ssaValue *v = ssa_make_value_param(proc->module->allocator, proc, e); -#if 1 - ssaValue *l = ssa_add_local(proc, e); - ssa_emit_store(proc, l, v); -#else - ssa_module_add_value(proc->module, e, v); -#endif - return v; -} diff --git a/src/ssa/module.cpp b/src/ssa/module.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/ssa/proc.cpp b/src/ssa/proc.cpp deleted file mode 100644 index 4d2b03bdf..000000000 --- a/src/ssa/proc.cpp +++ /dev/null @@ -1,114 +0,0 @@ -void ssa_begin_procedure_body(ssaProcedure *proc) { - array_init(&proc->blocks, heap_allocator()); - array_init(&proc->defer_stmts, heap_allocator()); - array_init(&proc->children, heap_allocator()); - - proc->decl_block = ssa_add_block(proc, proc->type_expr, "decls"); - proc->entry_block = ssa_add_block(proc, proc->type_expr, "entry"); - proc->curr_block = proc->entry_block; - - if (proc->type->Proc.params != NULL) { - auto *params = &proc->type->Proc.params->Tuple; - for (isize i = 0; i < params->variable_count; i++) { - Entity *e = params->variables[i]; - ssaValue *param = ssa_add_param(proc, e); - array_add(&proc->params, param); - } - } -} - - -void ssa_end_procedure_body(ssaProcedure *proc) { - if (proc->type->Proc.result_count == 0) { - ssa_emit_return(proc, NULL); - } - - if (proc->curr_block->instrs.count == 0) { - ssa_emit_unreachable(proc); - } - - proc->curr_block = proc->decl_block; - ssa_emit_jump(proc, proc->entry_block); - - ssa_opt_proc(proc); - -// Number registers - i32 reg_index = 0; - for_array(i, proc->blocks) { - ssaBlock *b = proc->blocks[i]; - b->index = i; - for_array(j, b->instrs) { - ssaValue *value = b->instrs[j]; - GB_ASSERT(value->kind == ssaValue_Instr); - ssaInstr *instr = &value->Instr; - if (ssa_instr_type(instr) == NULL) { // NOTE(bill): Ignore non-returning instructions - continue; - } - value->index = reg_index; - reg_index++; - } - } -} - - -void ssa_insert_code_before_proc(ssaProcedure* proc, ssaProcedure *parent) { - if (parent == NULL) { - if (proc->name == "main") { - ssa_emit_startup_runtime(proc); - } - } -} - -void ssa_build_proc(ssaValue *value, ssaProcedure *parent) { - ssaProcedure *proc = &value->Proc; - - proc->parent = parent; - - if (proc->entity != NULL) { - ssaModule *m = proc->module; - CheckerInfo *info = m->info; - Entity *e = proc->entity; - String filename = e->token.pos.file; - AstFile **found = map_get(&info->files, hash_string(filename)); - GB_ASSERT(found != NULL); - AstFile *f = *found; - ssaDebugInfo *di_file = NULL; - - ssaDebugInfo **di_file_found = map_get(&m->debug_info, hash_pointer(f)); - if (di_file_found) { - di_file = *di_file_found; - GB_ASSERT(di_file->kind == ssaDebugInfo_File); - } else { - di_file = ssa_add_debug_info_file(proc, f); - } - - ssa_add_debug_info_proc(proc, e, proc->name, di_file); - } - - if (proc->body != NULL) { - u32 prev_stmt_state_flags = proc->module->stmt_state_flags; - defer (proc->module->stmt_state_flags = prev_stmt_state_flags); - - if (proc->tags != 0) { - u32 in = proc->tags; - u32 out = proc->module->stmt_state_flags; - defer (proc->module->stmt_state_flags = out); - - if (in & ProcTag_bounds_check) { - out |= StmtStateFlag_bounds_check; - out &= ~StmtStateFlag_no_bounds_check; - } else if (in & ProcTag_no_bounds_check) { - out |= StmtStateFlag_no_bounds_check; - out &= ~StmtStateFlag_bounds_check; - } - } - - - ssa_begin_procedure_body(proc); - ssa_insert_code_before_proc(proc, parent); - ssa_build_stmt(proc, proc->body); - ssa_end_procedure_body(proc); - } -} - - diff --git a/src/ssa/procedure.cpp b/src/ssa/procedure.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/ssa/ssa.cpp b/src/ssa/ssa.cpp deleted file mode 100644 index 797c8b48d..000000000 --- a/src/ssa/ssa.cpp +++ /dev/null @@ -1,846 +0,0 @@ -struct ssaProcedure; -struct ssaBlock; -struct ssaValue; -struct ssaDebugInfo; - -struct ssaModule { - CheckerInfo * info; - BaseTypeSizes sizes; - gbArena arena; - gbArena tmp_arena; - gbAllocator allocator; - gbAllocator tmp_allocator; - b32 generate_debug_info; - - u32 stmt_state_flags; - - // String source_filename; - String layout; - // String triple; - - - Map min_dep_map; // Key: Entity * - Map values; // Key: Entity * - Map members; // Key: String - Map type_names; // Key: Type * - Map debug_info; // Key: Unique pointer - i32 global_string_index; - i32 global_array_index; // For ConstantSlice - - Array procs; // NOTE(bill): Procedures to generate -}; - -// NOTE(bill): For more info, see https://en.wikipedia.org/wiki/Dominator_(graph_theory) -struct ssaDomNode { - ssaBlock * idom; // Parent (Immediate Dominator) - Array children; - i32 pre, post; // Ordering in tree -}; - - -struct ssaBlock { - i32 index; - String label; - ssaProcedure *parent; - AstNode * node; // Can be NULL - Scope * scope; - isize scope_index; - ssaDomNode dom; - i32 gaps; - - Array instrs; - Array locals; - - Array preds; - Array succs; -}; - -struct ssaTargetList { - ssaTargetList *prev; - ssaBlock * break_; - ssaBlock * continue_; - ssaBlock * fallthrough_; -}; - -enum ssaDeferExitKind { - ssaDeferExit_Default, - ssaDeferExit_Return, - ssaDeferExit_Branch, -}; -enum ssaDeferKind { - ssaDefer_Node, - ssaDefer_Instr, -}; - -struct ssaDefer { - ssaDeferKind kind; - isize scope_index; - ssaBlock * block; - union { - AstNode *stmt; - // NOTE(bill): `instr` will be copied every time to create a new one - ssaValue *instr; - }; -}; - -struct ssaProcedure { - ssaProcedure * parent; - Array children; - - Entity * entity; - ssaModule * module; - String name; - Type * type; - AstNode * type_expr; - AstNode * body; - u64 tags; - - Array params; - Array defer_stmts; - Array blocks; - i32 scope_index; - ssaBlock * decl_block; - ssaBlock * entry_block; - ssaBlock * curr_block; - ssaTargetList * target_list; - Array referrers; - - i32 local_count; - i32 instr_count; - i32 block_count; -}; - -#define SSA_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" -#define SSA_TYPE_INFO_DATA_NAME "__$type_info_data" -#define SSA_TYPE_INFO_DATA_MEMBER_NAME "__$type_info_data_member" - - -#define SSA_INSTR_KINDS \ - SSA_INSTR_KIND(Invalid), \ - SSA_INSTR_KIND(Comment), \ - SSA_INSTR_KIND(Local), \ - SSA_INSTR_KIND(ZeroInit), \ - SSA_INSTR_KIND(Store), \ - SSA_INSTR_KIND(Load), \ - SSA_INSTR_KIND(PtrOffset), \ - SSA_INSTR_KIND(ArrayElementPtr), \ - SSA_INSTR_KIND(StructElementPtr), \ - SSA_INSTR_KIND(ArrayExtractValue), \ - SSA_INSTR_KIND(StructExtractValue), \ - SSA_INSTR_KIND(Conv), \ - SSA_INSTR_KIND(Jump), \ - SSA_INSTR_KIND(If), \ - SSA_INSTR_KIND(Return), \ - SSA_INSTR_KIND(Select), \ - SSA_INSTR_KIND(Phi), \ - SSA_INSTR_KIND(Unreachable), \ - SSA_INSTR_KIND(BinaryOp), \ - SSA_INSTR_KIND(Call), \ - SSA_INSTR_KIND(VectorExtractElement), \ - SSA_INSTR_KIND(VectorInsertElement), \ - SSA_INSTR_KIND(VectorShuffle), \ - SSA_INSTR_KIND(StartupRuntime), \ - SSA_INSTR_KIND(BoundsCheck), \ - SSA_INSTR_KIND(SliceBoundsCheck), \ - -#define SSA_CONV_KINDS \ - SSA_CONV_KIND(Invalid), \ - SSA_CONV_KIND(trunc), \ - SSA_CONV_KIND(zext), \ - SSA_CONV_KIND(fptrunc), \ - SSA_CONV_KIND(fpext), \ - SSA_CONV_KIND(fptoui), \ - SSA_CONV_KIND(fptosi), \ - SSA_CONV_KIND(uitofp), \ - SSA_CONV_KIND(sitofp), \ - SSA_CONV_KIND(ptrtoint), \ - SSA_CONV_KIND(inttoptr), \ - SSA_CONV_KIND(bitcast), - -enum ssaInstrKind { -#define SSA_INSTR_KIND(x) GB_JOIN2(ssaInstr_, x) - SSA_INSTR_KINDS -#undef SSA_INSTR_KIND -}; - -String const ssa_instr_strings[] = { -#define SSA_INSTR_KIND(x) {cast(u8 *)#x, gb_size_of(#x)-1} - SSA_INSTR_KINDS -#undef SSA_INSTR_KIND -}; - -enum ssaConvKind { -#define SSA_CONV_KIND(x) GB_JOIN2(ssaConv_, x) - SSA_CONV_KINDS -#undef SSA_CONV_KIND -}; - -String const ssa_conv_strings[] = { -#define SSA_CONV_KIND(x) {cast(u8 *)#x, gb_size_of(#x)-1} - SSA_CONV_KINDS -#undef SSA_CONV_KIND -}; - -struct ssaInstr { - ssaInstrKind kind; - - ssaBlock *parent; - Type *type; - - union { - struct { - String text; - } Comment; - struct { - Entity * entity; - Type * type; - b32 zero_initialized; - Array referrers; - } Local; - struct { - ssaValue *address; - } ZeroInit; - struct { - ssaValue *address; - ssaValue *value; - } Store; - struct { - Type *type; - ssaValue *address; - } Load; - struct { - ssaValue *address; - Type * result_type; - ssaValue *elem_index; - } ArrayElementPtr; - struct { - ssaValue *address; - Type * result_type; - i32 elem_index; - } StructElementPtr; - struct { - ssaValue *address; - ssaValue *offset; - } PtrOffset; - struct { - ssaValue *address; - Type * result_type; - i32 index; - } ArrayExtractValue; - struct { - ssaValue *address; - Type * result_type; - i32 index; - } StructExtractValue; - struct { - ssaValue *value; - ssaValue *elem; - i32 index; - } InsertValue; - struct { - ssaConvKind kind; - ssaValue *value; - Type *from, *to; - } Conv; - struct { - ssaBlock *block; - } Jump; - struct { - ssaValue *cond; - ssaBlock *true_block; - ssaBlock *false_block; - } If; - struct { - ssaValue *value; - } Return; - struct {} Unreachable; - struct { - ssaValue *cond; - ssaValue *true_value; - ssaValue *false_value; - } Select; - struct { - Array edges; - Type *type; - } Phi; - struct { - Type *type; - TokenKind op; - ssaValue *left, *right; - } BinaryOp; - struct { - Type *type; // return type - ssaValue *value; - ssaValue **args; - isize arg_count; - } Call; - struct { - ssaValue *vector; - ssaValue *index; - } VectorExtractElement; - struct { - ssaValue *vector; - ssaValue *elem; - ssaValue *index; - } VectorInsertElement; - struct { - ssaValue *vector; - i32 *indices; - isize index_count; - Type *type; - } VectorShuffle; - - struct {} StartupRuntime; - struct { - TokenPos pos; - ssaValue *index; - ssaValue *len; - } BoundsCheck; - struct { - TokenPos pos; - ssaValue *low; - ssaValue *high; - ssaValue *max; - b32 is_substring; - } SliceBoundsCheck; - }; -}; - - -enum ssaValueKind { - ssaValue_Invalid, - - ssaValue_Constant, - ssaValue_ConstantSlice, - ssaValue_Nil, - ssaValue_TypeName, - ssaValue_Global, - ssaValue_Param, - - ssaValue_Proc, - ssaValue_Block, - ssaValue_Instr, - - ssaValue_Count, -}; - -struct ssaValue { - ssaValueKind kind; - i32 index; - union { - struct { - Type * type; - ExactValue value; - } Constant; - struct { - Type * type; - ssaValue *backing_array; - i64 count; - } ConstantSlice; - struct { - Type *type; - } Nil; - struct { - Type * type; - String name; - } TypeName; - struct { - Entity * entity; - Type * type; - ssaValue * value; - Array referrers; - b8 is_constant; - b8 is_private; - b8 is_thread_local; - b8 is_unnamed_addr; - } Global; - struct { - ssaProcedure * parent; - Entity * entity; - Type * type; - Array referrers; - } Param; - ssaProcedure Proc; - ssaBlock Block; - ssaInstr Instr; - }; -}; - -gb_global ssaValue *v_zero = NULL; -gb_global ssaValue *v_one = NULL; -gb_global ssaValue *v_zero32 = NULL; -gb_global ssaValue *v_one32 = NULL; -gb_global ssaValue *v_two32 = NULL; -gb_global ssaValue *v_false = NULL; -gb_global ssaValue *v_true = NULL; - -enum ssaAddrKind { - ssaAddr_Default, - ssaAddr_Vector, -}; - -struct ssaAddr { - ssaValue * addr; - AstNode * expr; // NOTE(bill): Just for testing - probably remove later - ssaAddrKind kind; - union { - struct { ssaValue *index; } Vector; - }; -}; - -ssaAddr ssa_make_addr(ssaValue *addr, AstNode *expr) { - ssaAddr v = {addr, expr}; - return v; -} -ssaAddr ssa_make_addr_vector(ssaValue *addr, ssaValue *index, AstNode *expr) { - ssaAddr v = ssa_make_addr(addr, expr); - v.kind = ssaAddr_Vector; - v.Vector.index = index; - return v; -} - - - -enum ssaDebugEncoding { - ssaDebugBasicEncoding_Invalid = 0, - - ssaDebugBasicEncoding_address = 1, - ssaDebugBasicEncoding_boolean = 2, - ssaDebugBasicEncoding_float = 3, - ssaDebugBasicEncoding_signed = 4, - ssaDebugBasicEncoding_signed_char = 5, - ssaDebugBasicEncoding_unsigned = 6, - ssaDebugBasicEncoding_unsigned_char = 7, - - ssaDebugBasicEncoding_member = 13, - ssaDebugBasicEncoding_pointer_type = 15, - ssaDebugBasicEncoding_typedef = 22, - - ssaDebugBasicEncoding_array_type = 1, - ssaDebugBasicEncoding_enumeration_type = 4, - ssaDebugBasicEncoding_structure_type = 19, - ssaDebugBasicEncoding_union_type = 23, - -}; - -enum ssaDebugInfoKind { - ssaDebugInfo_Invalid, - - ssaDebugInfo_CompileUnit, - ssaDebugInfo_File, - ssaDebugInfo_Scope, - ssaDebugInfo_Proc, - ssaDebugInfo_AllProcs, - - ssaDebugInfo_BasicType, // basic types - ssaDebugInfo_ProcType, - ssaDebugInfo_DerivedType, // pointer, typedef - ssaDebugInfo_CompositeType, // array, struct, enum, (raw_)union - ssaDebugInfo_Enumerator, // For ssaDebugInfo_CompositeType if enum - ssaDebugInfo_GlobalVariable, - ssaDebugInfo_LocalVariable, - - - ssaDebugInfo_Count, -}; - -struct ssaDebugInfo { - ssaDebugInfoKind kind; - i32 id; - - union { - struct { - AstFile * file; - String producer; - ssaDebugInfo *all_procs; - } CompileUnit; - struct { - AstFile *file; - String filename; - String directory; - } File; - struct { - ssaDebugInfo *parent; - ssaDebugInfo *file; - TokenPos pos; - Scope * scope; // Actual scope - } Scope; - struct { - Entity * entity; - String name; - ssaDebugInfo *file; - TokenPos pos; - } Proc; - struct { - Array procs; - } AllProcs; - - - struct { - String name; - i32 size; - i32 align; - ssaDebugEncoding encoding; - } BasicType; - struct { - ssaDebugInfo * return_type; - Array param_types; - } ProcType; - struct { - ssaDebugInfo * base_type; - ssaDebugEncoding encoding; - } DerivedType; - struct { - ssaDebugEncoding encoding; - String name; - String identifier; - ssaDebugInfo * file; - TokenPos pos; - i32 size; - i32 align; - Array elements; - } CompositeType; - struct { - String name; - i64 value; - } Enumerator; - struct { - String name; - String linkage_name; - ssaDebugInfo *scope; - ssaDebugInfo *file; - TokenPos pos; - ssaValue *variable; - ssaDebugInfo *declaration; - } GlobalVariable; - struct { - String name; - ssaDebugInfo *scope; - ssaDebugInfo *file; - TokenPos pos; - i32 arg; // Non-zero if proc parameter - ssaDebugInfo *type; - } LocalVariable; - }; -}; - - - -struct ssaFileBuffer { - gbVirtualMemory vm; - isize offset; - gbFile *output; -}; - -void ssa_file_buffer_init(ssaFileBuffer *f, gbFile *output) { - isize size = 8*gb_virtual_memory_page_size(NULL); - f->vm = gb_vm_alloc(NULL, size); - f->offset = 0; - f->output = output; -} - -void ssa_file_buffer_destroy(ssaFileBuffer *f) { - if (f->offset > 0) { - // NOTE(bill): finish writing buffered data - gb_file_write(f->output, f->vm.data, f->offset); - } - - gb_vm_free(f->vm); -} - -void ssa_file_buffer_write(ssaFileBuffer *f, void *data, isize len) { - if (len > f->vm.size) { - gb_file_write(f->output, data, len); - return; - } - - if ((f->vm.size - f->offset) < len) { - gb_file_write(f->output, f->vm.data, f->offset); - f->offset = 0; - } - u8 *cursor = cast(u8 *)f->vm.data + f->offset; - gb_memmove(cursor, data, len); - f->offset += len; -} - - -void ssa_fprintf(ssaFileBuffer *f, char *fmt, ...) { - va_list va; - va_start(va, fmt); - char buf[4096] = {}; - isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va); - ssa_file_buffer_write(f, buf, len-1); - va_end(va); -} - - -void ssa_file_write(ssaFileBuffer *f, void *data, isize len) { - ssa_file_buffer_write(f, data, len); -} - -ssaValue *ssa_lookup_member(ssaModule *m, String name) { - ssaValue **v = map_get(&m->members, hash_string(name)); - if (v != NULL) { - return *v; - } - return NULL; -} - - -Type *ssa_type(ssaValue *value); -Type *ssa_instr_type(ssaInstr *instr) { - switch (instr->kind) { - case ssaInstr_Local: - return instr->Local.type; - case ssaInstr_Load: - return instr->Load.type; - case ssaInstr_StructElementPtr: - return instr->StructElementPtr.result_type; - case ssaInstr_ArrayElementPtr: - return instr->ArrayElementPtr.result_type; - case ssaInstr_PtrOffset: - return ssa_type(instr->PtrOffset.address); - case ssaInstr_Phi: - return instr->Phi.type; - case ssaInstr_ArrayExtractValue: - return instr->ArrayExtractValue.result_type; - case ssaInstr_StructExtractValue: - return instr->StructExtractValue.result_type; - case ssaInstr_BinaryOp: - return instr->BinaryOp.type; - case ssaInstr_Conv: - return instr->Conv.to; - case ssaInstr_Select: - return ssa_type(instr->Select.true_value); - case ssaInstr_Call: { - Type *pt = base_type(instr->Call.type); - if (pt != NULL) { - if (pt->kind == Type_Tuple && pt->Tuple.variable_count == 1) { - return pt->Tuple.variables[0]->type; - } - return pt; - } - return NULL; - } break; - case ssaInstr_VectorExtractElement: { - Type *vt = ssa_type(instr->VectorExtractElement.vector); - Type *bt = base_vector_type(vt); - GB_ASSERT(!is_type_vector(bt)); - return bt; - } break; - case ssaInstr_VectorInsertElement: - return ssa_type(instr->VectorInsertElement.vector); - case ssaInstr_VectorShuffle: - return instr->VectorShuffle.type; - } - return NULL; -} - -Type *ssa_type(ssaValue *value) { - switch (value->kind) { - case ssaValue_Constant: - return value->Constant.type; - case ssaValue_ConstantSlice: - return value->ConstantSlice.type; - case ssaValue_Nil: - return value->Nil.type; - case ssaValue_TypeName: - return value->TypeName.type; - case ssaValue_Global: - return value->Global.type; - case ssaValue_Param: - return value->Param.type; - case ssaValue_Proc: - return value->Proc.type; - case ssaValue_Instr: - return ssa_instr_type(&value->Instr); - } - return NULL; -} - -Type *ssa_addr_type(ssaAddr lval) { - if (lval.addr != NULL) { - Type *t = ssa_type(lval.addr); - GB_ASSERT(is_type_pointer(t)); - return type_deref(t); - } - return NULL; -} - - - -b32 ssa_is_blank_ident(AstNode *node) { - if (node->kind == AstNode_Ident) { - ast_node(i, Ident, node); - return is_blank_ident(i->string); - } - return false; -} - - -ssaInstr *ssa_get_last_instr(ssaBlock *block) { - if (block != NULL) { - isize len = block->instrs.count; - if (len > 0) { - ssaValue *v = block->instrs[len-1]; - GB_ASSERT(v->kind == ssaValue_Instr); - return &v->Instr; - } - } - return NULL; - -} - -b32 ssa_is_instr_terminating(ssaInstr *i) { - if (i != NULL) { - switch (i->kind) { - case ssaInstr_Return: - case ssaInstr_Unreachable: - return true; - } - } - - return false; -} - - -void ssa_add_edge(ssaBlock *from, ssaBlock *to) { - array_add(&from->succs, to); - array_add(&to->preds, from); -} - -void ssa_set_instr_parent(ssaValue *instr, ssaBlock *parent) { - if (instr->kind == ssaValue_Instr) { - instr->Instr.parent = parent; - } -} - -Array *ssa_value_referrers(ssaValue *v) { - switch (v->kind) { - case ssaValue_Global: - return &v->Global.referrers; - case ssaValue_Param: - return &v->Param.referrers; - case ssaValue_Proc: { - if (v->Proc.parent != NULL) { - return &v->Proc.referrers; - } - return NULL; - } - case ssaValue_Instr: { - ssaInstr *i = &v->Instr; - switch (i->kind) { - case ssaInstr_Local: - return &i->Local.referrers; - } - } break; - } - - return NULL; -} - - - -#include "make.cpp" -#include "debug.cpp" -#include "emit.cpp" -#include "build.cpp" -#include "opt.cpp" -#include "proc.cpp" - - - - -void ssa_module_add_value(ssaModule *m, Entity *e, ssaValue *v) { - map_set(&m->values, hash_pointer(e), v); -} - -void ssa_init_module(ssaModule *m, Checker *c) { - // TODO(bill): Determine a decent size for the arena - isize token_count = c->parser->total_token_count; - isize arena_size = 4 * token_count * gb_size_of(ssaValue); - gb_arena_init_from_allocator(&m->arena, heap_allocator(), arena_size); - gb_arena_init_from_allocator(&m->tmp_arena, heap_allocator(), arena_size); - m->allocator = gb_arena_allocator(&m->arena); - m->tmp_allocator = gb_arena_allocator(&m->tmp_arena); - m->info = &c->info; - m->sizes = c->sizes; - - map_init(&m->values, heap_allocator()); - map_init(&m->members, heap_allocator()); - map_init(&m->debug_info, heap_allocator()); - map_init(&m->type_names, heap_allocator()); - array_init(&m->procs, heap_allocator()); - - // Default states - m->stmt_state_flags = 0; - m->stmt_state_flags |= StmtStateFlag_bounds_check; - - { - // Add type info data - { - String name = make_string(SSA_TYPE_INFO_DATA_NAME); - isize count = c->info.type_info_map.entries.count; - Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name), make_type_array(m->allocator, t_type_info, count)); - ssaValue *g = ssa_make_value_global(m->allocator, e, NULL); - g->Global.is_private = true; - ssa_module_add_value(m, e, g); - map_set(&m->members, hash_string(name), g); - } - - // Type info member buffer - { - // NOTE(bill): Removes need for heap allocation by making it global memory - isize count = 0; - - for_array(entry_index, m->info->type_info_map.entries) { - auto *entry = &m->info->type_info_map.entries[entry_index]; - Type *t = cast(Type *)cast(uintptr)entry->key.key; - - switch (t->kind) { - case Type_Record: - switch (t->Record.kind) { - case TypeRecord_Struct: - case TypeRecord_RawUnion: - count += t->Record.field_count; - } - break; - case Type_Tuple: - count += t->Tuple.variable_count; - break; - } - } - - String name = make_string(SSA_TYPE_INFO_DATA_MEMBER_NAME); - Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name), - make_type_array(m->allocator, t_type_info_member, count)); - ssaValue *g = ssa_make_value_global(m->allocator, e, NULL); - ssa_module_add_value(m, e, g); - map_set(&m->members, hash_string(name), g); - } - } - - { - ssaDebugInfo *di = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_CompileUnit); - di->CompileUnit.file = m->info->files.entries[0].value; // Zeroth is the init file - di->CompileUnit.producer = make_string("odin"); - - map_set(&m->debug_info, hash_pointer(m), di); - } -} - -void ssa_destroy_module(ssaModule *m) { - map_destroy(&m->values); - map_destroy(&m->members); - map_destroy(&m->type_names); - map_destroy(&m->debug_info); - array_free(&m->procs); - gb_arena_free(&m->arena); -} - - - - -#include "codegen.cpp" - - diff --git a/src/ssa/opt.cpp b/src/ssa_opt.cpp similarity index 97% rename from src/ssa/opt.cpp rename to src/ssa_opt.cpp index c1b1d0f76..48a92494b 100644 --- a/src/ssa/opt.cpp +++ b/src/ssa_opt.cpp @@ -79,6 +79,17 @@ void ssa_opt_add_operands(Array *ops, ssaInstr *i) { break; case ssaInstr_StartupRuntime: break; + case ssaInstr_BoundsCheck: + array_add(ops, i->BoundsCheck.index); + array_add(ops, i->BoundsCheck.len); + break; + case ssaInstr_SliceBoundsCheck: + array_add(ops, i->SliceBoundsCheck.low); + array_add(ops, i->SliceBoundsCheck.high); + array_add(ops, i->SliceBoundsCheck.max); + break; + + } } diff --git a/src/llvm/ssa_to_text.cpp b/src/ssa_to_llvm.cpp similarity index 100% rename from src/llvm/ssa_to_text.cpp rename to src/ssa_to_llvm.cpp diff --git a/src/vm/vm.cpp b/src/vm.cpp similarity index 69% rename from src/vm/vm.cpp rename to src/vm.cpp index 82801e1b0..f037cadaa 100644 --- a/src/vm/vm.cpp +++ b/src/vm.cpp @@ -1,3 +1,5 @@ +#include "dyncall/include/dyncall.h" + struct VirtualMachine; struct vmValueProc { @@ -37,8 +39,9 @@ struct vmFrame { VirtualMachine * vm; vmFrame * caller; ssaProcedure * curr_proc; + ssaBlock * prev_block; ssaBlock * curr_block; - isize instr_index; // For the current block + i32 instr_index; // For the current block Map values; // Key: ssaValue * gbTempArenaMemory temp_arena_memory; @@ -61,6 +64,14 @@ struct VirtualMachine { void vm_exec_instr (VirtualMachine *vm, ssaValue *value); vmValue vm_operand_value(VirtualMachine *vm, ssaValue *value); void vm_store (VirtualMachine *vm, void *dst, vmValue val, Type *type); +void vm_print_value (vmValue value, Type *type); + +void vm_jump_block(vmFrame *f, ssaBlock *target) { + f->prev_block = f->curr_block; + f->curr_block = target; + f->instr_index = 0; +} + vmFrame *vm_back_frame(VirtualMachine *vm) { if (vm->frame_stack.count > 0) { @@ -123,6 +134,7 @@ void vm_destroy(VirtualMachine *vm) { void vm_set_value(vmFrame *f, ssaValue *v, vmValue val) { if (v != NULL) { + GB_ASSERT(ssa_type(v) != NULL); map_set(&f->values, hash_pointer(v), val); } } @@ -134,6 +146,7 @@ vmFrame *vm_push_frame(VirtualMachine *vm, ssaProcedure *proc) { frame.vm = vm; frame.curr_proc = proc; + frame.prev_block = proc->blocks[0]; frame.curr_block = proc->blocks[0]; frame.instr_index = 0; frame.caller = vm_back_frame(vm); @@ -154,10 +167,10 @@ void vm_pop_frame(VirtualMachine *vm) { map_destroy(&f->values); array_pop(&vm->frame_stack); - } -vmValue vm_call_procedure(VirtualMachine *vm, ssaProcedure *proc, Array values) { + +vmValue vm_call_proc(VirtualMachine *vm, ssaProcedure *proc, Array values) { Type *type = base_type(proc->type); GB_ASSERT_MSG(type->Proc.param_count == values.count, "Incorrect number of arguments passed into procedure call!\n" @@ -169,7 +182,8 @@ vmValue vm_call_procedure(VirtualMachine *vm, ssaProcedure *proc, Array vmValue result = {}; if (proc->body == NULL) { - GB_PANIC("TODO(bill): external procedure"); + // GB_PANIC("TODO(bill): external procedure"); + gb_printf_err("TODO(bill): external procedure: %.*s\n", LIT(proc->name)); return result; } gb_printf("call: %.*s\n", LIT(proc->name)); @@ -179,6 +193,11 @@ vmValue vm_call_procedure(VirtualMachine *vm, ssaProcedure *proc, Array vm_set_value(f, proc->params[i], values[i]); } + if (proc->name == SSA_STARTUP_RUNTIME_PROC_NAME) { + ssaBlock *block = proc->curr_block; + + } + while (f->curr_block != NULL) { ssaValue *curr_instr = f->curr_block->instrs[f->instr_index++]; vm_exec_instr(vm, curr_instr); @@ -195,20 +214,28 @@ vmValue vm_call_procedure(VirtualMachine *vm, ssaProcedure *proc, Array rt = base_type(rt->Tuple.variables[0]->type); } - if (is_type_string(rt)) { - vmValue data = result.val_comp[0]; - vmValue count = result.val_comp[1]; - gb_printf("String: %.*s\n", cast(isize)count.val_int, cast(u8 *)data.val_ptr); - } else if (is_type_integer(rt)) { - gb_printf("Integer: %lld\n", cast(i64)result.val_int); - } - // gb_printf("%lld\n", cast(i64)result.val_int); + gb_printf("%.*s -> ", LIT(proc->name)); + vm_print_value(result, rt); + gb_printf("\n"); } vm_pop_frame(vm); return result; } + +ssaProcedure *vm_lookup_procedure(VirtualMachine *vm, String name) { + ssaValue *v = ssa_lookup_member(vm->module, name); + GB_ASSERT(v->kind == ssaValue_Proc); + ssaProcedure *proc = &v->Proc; + return proc; +} + +vmValue vm_call_proc_by_name(VirtualMachine *vm, String name, Array args) { + ssaProcedure *proc = vm_lookup_procedure(vm, name); + return vm_call_proc(vm, proc, args); +} + vmValue vm_exact_value(VirtualMachine *vm, ssaValue *ptr, ExactValue value, Type *t) { vmValue result = {}; Type *original_type = t; @@ -262,8 +289,7 @@ vmValue vm_exact_value(VirtualMachine *vm, ssaValue *ptr, ExactValue value, Type } Type *type = base_type(t); - array_init(&result.val_comp, vm->heap_allocator, type->Array.count); - array_resize(&result.val_comp, type->Array.count); + array_init_count(&result.val_comp, vm->heap_allocator, type->Array.count); for (isize i = 0; i < elem_count; i++) { TypeAndValue *tav = type_and_value_of_expression(vm->module->info, cl->elems[i]); vmValue elem = vm_exact_value(vm, NULL, tav->value, tav->type); @@ -283,8 +309,7 @@ vmValue vm_exact_value(VirtualMachine *vm, ssaValue *ptr, ExactValue value, Type } isize value_count = t->Record.field_count; - array_init(&result.val_comp, vm->heap_allocator, value_count); - array_resize(&result.val_comp, value_count); + array_init_count(&result.val_comp, vm->heap_allocator, value_count); if (cl->elems[0]->kind == AstNode_FieldValue) { isize elem_count = cl->elems.count; @@ -379,6 +404,7 @@ void vm_store_integer(VirtualMachine *vm, void *dst, vmValue val, i64 store_byte void vm_store(VirtualMachine *vm, void *dst, vmValue val, Type *type) { i64 size = vm_type_size_of(vm, type); + Type *original_type = type; type = base_type(get_enum_base_type(type)); // TODO(bill): I assume little endian here @@ -431,6 +457,10 @@ void vm_store(VirtualMachine *vm, void *dst, vmValue val, Type *type) { } break; + case Type_Pointer: + *cast(void **)dst = val.val_ptr; + break; + case Type_Record: { if (is_type_struct(type)) { u8 *mem = cast(u8 *)dst; @@ -444,13 +474,41 @@ void vm_store(VirtualMachine *vm, void *dst, vmValue val, Type *type) { for (isize i = 0; i < field_count; i++) { Entity *f = type->Record.fields[i]; i64 offset = vm_type_offset_of(vm, type, i); - void *ptr = mem+offset; - vmValue member = val.val_comp[i]; - vm_store(vm, ptr, member, f->type); + vm_store(vm, mem+offset, val.val_comp[i], f->type); } - } else { - gb_printf_err("TODO(bill): records for `vm_store` %s\n", type_to_string(type)); + // u8 *mem = cast(u8 *)dst; + // if (val.val_comp.count == 0) { + // gb_printf_err("%s\n", type_to_string(original_type)); + // // gb_zero_size(mem, vm_type_size_of(vm, type)); + // } else { + // GB_ASSERT(val.val_comp.count == 2); + // i64 word_size = vm_type_size_of(vm, t_int); + // i64 size_of_union = vm_type_size_of(vm, type) - word_size; + // for (isize i = 0; i < size_of_union; i++) { + // mem[i] = cast(u8)val.val_comp[0].val_comp[i].val_int; + // } + // vm_store_integer(vm, mem + size_of_union, val.val_comp[0], word_size); + // } + + // gb_printf_err("TODO(bill): records for `vm_store` %s\n", type_to_string(original_type)); + } + } break; + + case Type_Tuple: { + u8 *mem = cast(u8 *)dst; + + GB_ASSERT_MSG(type->Tuple.variable_count >= val.val_comp.count, + "%td vs %td", + type->Tuple.variable_count, val.val_comp.count); + + isize variable_count = gb_min(val.val_comp.count, type->Tuple.variable_count); + + for (isize i = 0; i < variable_count; i++) { + Entity *f = type->Tuple.variables[i]; + void *ptr = mem + vm_type_offset_of(vm, type, i); + vmValue member = val.val_comp[i]; + vm_store(vm, ptr, member, f->type); } } break; @@ -463,11 +521,17 @@ void vm_store(VirtualMachine *vm, void *dst, vmValue val, Type *type) { for (i64 i = 0; i < elem_count; i++) { void *ptr = mem + (elem_size*i); vmValue member = val.val_comp[i]; - *cast(i64 *)ptr = 123; vm_store(vm, ptr, member, elem_type); } - gb_printf_err("%lld\n", *cast(i64 *)mem); + } break; + case Type_Slice: { + i64 word_size = vm_type_size_of(vm, t_int); + + u8 *mem = cast(u8 *)dst; + vm_store(vm, mem+0*word_size, val.val_comp[0], t_rawptr); + vm_store(vm, mem+1*word_size, val.val_comp[1], t_int); + vm_store(vm, mem+2*word_size, val.val_comp[2], t_int); } break; default: @@ -518,8 +582,8 @@ vmValue vm_load(VirtualMachine *vm, void *ptr, Type *type) { case Basic_string: { i64 word_size = vm_type_size_of(vm, t_int); - u8 *mem = *cast(u8 **)ptr; - array_init(&result.val_comp, vm->heap_allocator, 2); + u8 *mem = cast(u8 *)ptr; + array_init_count(&result.val_comp, vm->heap_allocator, 2); i64 count = 0; u8 *data = mem + 0*word_size; @@ -530,8 +594,8 @@ vmValue vm_load(VirtualMachine *vm, void *ptr, Type *type) { default: GB_PANIC("Unknown int size"); break; } - array_add(&result.val_comp, vm_make_value_ptr(mem)); - array_add(&result.val_comp, vm_make_value_int(count)); + result.val_comp[0].val_ptr = mem; + result.val_comp[1].val_int = count; } break; @@ -541,12 +605,44 @@ vmValue vm_load(VirtualMachine *vm, void *ptr, Type *type) { } break; + case Type_Pointer: + result.val_ptr = *cast(void **)ptr; + break; + + case Type_Array: { + i64 count = type->Array.count; + Type *elem_type = type->Array.elem; + i64 elem_size = vm_type_size_of(vm, elem_type); + + array_init_count(&result.val_comp, vm->heap_allocator, count); + + u8 *mem = cast(u8 *)ptr; + for (isize i = 0; i < count; i++) { + i64 offset = elem_size*i; + vmValue val = vm_load(vm, mem+offset, elem_type); + result.val_comp[i] = val; + } + } break; + + case Type_Slice: { + Type *elem_type = type->Slice.elem; + i64 elem_size = vm_type_size_of(vm, elem_type); + i64 word_size = vm_type_size_of(vm, t_int); + + array_init_count(&result.val_comp, vm->heap_allocator, 3); + + u8 *mem = cast(u8 *)ptr; + result.val_comp[0] = vm_load(vm, mem+0*word_size, t_rawptr); // data + result.val_comp[1] = vm_load(vm, mem+1*word_size, t_int); // count + result.val_comp[2] = vm_load(vm, mem+2*word_size, t_int); // capacity + return result; + } break; + case Type_Record: { if (is_type_struct(type)) { isize field_count = type->Record.field_count; - array_init(&result.val_comp, vm->heap_allocator, field_count); - array_resize(&result.val_comp, field_count); + array_init_count(&result.val_comp, vm->heap_allocator, field_count); u8 *mem = cast(u8 *)ptr; for (isize i = 0; i < field_count; i++) { @@ -558,6 +654,20 @@ vmValue vm_load(VirtualMachine *vm, void *ptr, Type *type) { } } break; + case Type_Tuple: { + isize count = type->Tuple.variable_count; + + array_init_count(&result.val_comp, vm->heap_allocator, count); + + u8 *mem = cast(u8 *)ptr; + for (isize i = 0; i < count; i++) { + Entity *f = type->Tuple.variables[i]; + i64 offset = vm_type_offset_of(vm, type, i); + vmValue val = vm_load(vm, mem+offset, f->type); + result.val_comp[i] = val; + } + } break; + default: GB_PANIC("TODO(bill): other types for `vm_load` %s", type_to_string(type)); break; @@ -566,14 +676,8 @@ vmValue vm_load(VirtualMachine *vm, void *ptr, Type *type) { return result; } -ssaProcedure *vm_lookup_procedure(VirtualMachine *vm, String name) { - ssaValue *v = ssa_lookup_member(vm->module, name); - GB_ASSERT(v->kind == ssaValue_Proc); - ssaProcedure *proc = &v->Proc; - return proc; -} - void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { + GB_ASSERT(value != NULL); GB_ASSERT(value->kind == ssaValue_Instr); ssaInstr *instr = &value->Instr; vmFrame *f = vm_back_frame(vm); @@ -586,10 +690,10 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { switch (instr->kind) { case ssaInstr_StartupRuntime: { -#if 0 +#if 1 ssaProcedure *proc = vm_lookup_procedure(vm, make_string(SSA_STARTUP_RUNTIME_PROC_NAME)); Array args = {}; // Empty - vm_call_procedure(vm, proc, args); // NOTE(bill): No return value + vm_call_proc(vm, proc, args); // NOTE(bill): No return value #endif } break; @@ -659,7 +763,14 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { } break; case ssaInstr_Phi: { - GB_PANIC("TODO(bill): ssaInstr_Phi"); + for_array(i, f->curr_block->preds) { + ssaBlock *pred = f->curr_block->preds[i]; + if (f->prev_block == pred) { + vmValue edge = vm_operand_value(vm, instr->Phi.edges[i]); + vm_set_value(f, value, edge); + break; + } + } } break; case ssaInstr_ArrayExtractValue: { @@ -675,18 +786,16 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { } break; case ssaInstr_Jump: { - f->curr_block = instr->Jump.block; - f->instr_index = 0; + vm_jump_block(f, instr->Jump.block); } break; case ssaInstr_If: { vmValue cond = vm_operand_value(vm, instr->If.cond); if (cond.val_int != 0) { - f->curr_block = instr->If.true_block; + vm_jump_block(f, instr->If.true_block); } else { - f->curr_block = instr->If.false_block; + vm_jump_block(f, instr->If.false_block); } - f->instr_index = 0; } break; case ssaInstr_Return: { @@ -700,6 +809,7 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { f->result = result; f->curr_block = NULL; + f->instr_index = 0; return; } break; @@ -732,20 +842,20 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { Type *from = base_type(instr->Conv.from); if (from == t_f64) { u64 u = cast(u64)src.val_f64; - gb_memcopy(&dst, &u, to_size); + vm_store_integer(vm, &dst, vm_make_value_int(u), to_size); } else { u64 u = cast(u64)src.val_f32; - gb_memcopy(&dst, &u, to_size); + vm_store_integer(vm, &dst, vm_make_value_int(u), to_size); } } break; case ssaConv_fptosi: { Type *from = base_type(instr->Conv.from); if (from == t_f64) { i64 i = cast(i64)src.val_f64; - gb_memcopy(&dst, &i, to_size); + vm_store_integer(vm, &dst, vm_make_value_int(i), to_size); } else { i64 i = cast(i64)src.val_f32; - gb_memcopy(&dst, &i, to_size); + vm_store_integer(vm, &dst, vm_make_value_int(i), to_size); } } break; case ssaConv_uitofp: { @@ -792,16 +902,56 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { } if (gb_is_between(bo->op, Token__ComparisonBegin+1, Token__ComparisonEnd-1)) { - switch (bo->op) { - case Token_CmpEq: break; - case Token_NotEq: break; - case Token_Lt: break; - case Token_Gt: break; - case Token_LtEq: break; - case Token_GtEq: break; + vmValue v = {}; + vmValue l = vm_operand_value(vm, bo->left); + vmValue r = vm_operand_value(vm, bo->right); + + if (is_type_integer(t)) { + // TODO(bill): Do I need to take into account the size of the integer? + switch (bo->op) { + case Token_CmpEq: v.val_int = l.val_int == r.val_int; break; + case Token_NotEq: v.val_int = l.val_int != r.val_int; break; + case Token_Lt: v.val_int = l.val_int < r.val_int; break; + case Token_Gt: v.val_int = l.val_int > r.val_int; break; + case Token_LtEq: v.val_int = l.val_int <= r.val_int; break; + case Token_GtEq: v.val_int = l.val_int >= r.val_int; break; + } + } else if (t == t_f32) { + switch (bo->op) { + case Token_CmpEq: v.val_f32 = l.val_f32 == r.val_f32; break; + case Token_NotEq: v.val_f32 = l.val_f32 != r.val_f32; break; + case Token_Lt: v.val_f32 = l.val_f32 < r.val_f32; break; + case Token_Gt: v.val_f32 = l.val_f32 > r.val_f32; break; + case Token_LtEq: v.val_f32 = l.val_f32 <= r.val_f32; break; + case Token_GtEq: v.val_f32 = l.val_f32 >= r.val_f32; break; + } + } else if (t == t_f64) { + switch (bo->op) { + case Token_CmpEq: v.val_f64 = l.val_f64 == r.val_f64; break; + case Token_NotEq: v.val_f64 = l.val_f64 != r.val_f64; break; + case Token_Lt: v.val_f64 = l.val_f64 < r.val_f64; break; + case Token_Gt: v.val_f64 = l.val_f64 > r.val_f64; break; + case Token_LtEq: v.val_f64 = l.val_f64 <= r.val_f64; break; + case Token_GtEq: v.val_f64 = l.val_f64 >= r.val_f64; break; + } + } else if (is_type_string(t)) { + Array args = {}; + array_init(&args, vm->stack_allocator, 2); + array_add(&args, l); + array_add(&args, r); + switch (bo->op) { + case Token_CmpEq: v = vm_call_proc_by_name(vm, make_string("__string_eq"), args); break; + case Token_NotEq: v = vm_call_proc_by_name(vm, make_string("__string_ne"), args); break; + case Token_Lt: v = vm_call_proc_by_name(vm, make_string("__string_lt"), args); break; + case Token_Gt: v = vm_call_proc_by_name(vm, make_string("__string_gt"), args); break; + case Token_LtEq: v = vm_call_proc_by_name(vm, make_string("__string_le"), args); break; + case Token_GtEq: v = vm_call_proc_by_name(vm, make_string("__string_ge"), args); break; + } + } else { + GB_PANIC("TODO(bill): Vector BinaryOp"); } - gb_printf_err("TODO(bill): Comparison operations %.*s\n", LIT(token_strings[bo->op])); - vm_set_value(f, value, vm_make_value_int(1)); // HACK(bill): always true + + vm_set_value(f, value, v); } else { vmValue v = {}; vmValue l = vm_operand_value(vm, bo->left); @@ -862,7 +1012,7 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { } vmValue proc = vm_operand_value(vm, instr->Call.value); if (proc.val_proc.proc != NULL) { - vmValue result = vm_call_procedure(vm, proc.val_proc.proc, args); + vmValue result = vm_call_proc(vm, proc.val_proc.proc, args); vm_set_value(f, value, result); } else { GB_PANIC("TODO(bill): external procedure calls"); @@ -883,15 +1033,30 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { } break; case ssaInstr_VectorExtractElement: { - + vmValue vector = vm_operand_value(vm, instr->VectorExtractElement.vector); + vmValue index = vm_operand_value(vm, instr->VectorExtractElement.index); + vmValue v = vector.val_comp[index.val_int]; + vm_set_value(f, value, v); } break; case ssaInstr_VectorInsertElement: { - + vmValue vector = vm_operand_value(vm, instr->VectorInsertElement.vector); + vmValue elem = vm_operand_value(vm, instr->VectorInsertElement.elem); + vmValue index = vm_operand_value(vm, instr->VectorInsertElement.index); + vector.val_comp[index.val_int] = elem; } break; case ssaInstr_VectorShuffle: { + auto *vs = &instr->VectorShuffle; + vmValue old_vector = vm_operand_value(vm, instr->VectorShuffle.vector); + vmValue new_vector = {}; + array_init_count(&new_vector.val_comp, vm->stack_allocator, vs->index_count); + for (i32 i = 0; i < vs->index_count; i++) { + new_vector.val_comp[i] = old_vector.val_comp[vs->indices[i]]; + } + + vm_set_value(f, value, new_vector); } break; case ssaInstr_BoundsCheck: { @@ -904,14 +1069,12 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { array_add(&args, vm_operand_value(vm, bc->index)); array_add(&args, vm_operand_value(vm, bc->len)); - ssaProcedure *proc = vm_lookup_procedure(vm, make_string("__bounds_check_error")); - vm_call_procedure(vm, proc, args); + vm_call_proc_by_name(vm, make_string("__bounds_check_error"), args); } break; case ssaInstr_SliceBoundsCheck: { auto *bc = &instr->SliceBoundsCheck; Array args = {}; - ssaProcedure *proc; array_init(&args, vm->stack_allocator, 7); array_add(&args, vm_exact_value(vm, NULL, make_exact_value_string(bc->pos.file), t_string)); @@ -921,12 +1084,10 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { array_add(&args, vm_operand_value(vm, bc->high)); if (!bc->is_substring) { array_add(&args, vm_operand_value(vm, bc->max)); - proc = vm_lookup_procedure(vm, make_string("__slice_expr_error")); + vm_call_proc_by_name(vm, make_string("__slice_expr_error"), args); } else { - proc = vm_lookup_procedure(vm, make_string("__substring_expr_error")); + vm_call_proc_by_name(vm, make_string("__substring_expr_error"), args); } - - vm_call_procedure(vm, proc, args); } break; default: { @@ -934,3 +1095,88 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) { } break; } } + + + +void vm_print_value(vmValue value, Type *type) { + type = base_type(type); + if (is_type_string(type)) { + vmValue data = value.val_comp[0]; + vmValue count = value.val_comp[1]; + gb_printf("`%.*s`", cast(isize)count.val_int, cast(u8 *)data.val_ptr); + } else if (is_type_boolean(type)) { + if (value.val_int != 0) { + gb_printf("true"); + } else { + gb_printf("false"); + } + } else if (is_type_integer(type)) { + gb_printf("%lld", cast(i64)value.val_int); + } else if (type == t_f32) { + gb_printf("%f", value.val_f32); + } else if (type == t_f64) { + gb_printf("%f", value.val_f64); + } else if (is_type_pointer(type)) { + gb_printf("0x%08x", value.val_ptr); + } else if (is_type_array(type)) { + gb_printf("["); + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Array.elem); + } + gb_printf("]"); + } else if (is_type_vector(type)) { + gb_printf("<"); + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Vector.elem); + } + gb_printf(">"); + } else if (is_type_slice(type)) { + gb_printf("["); + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Slice.elem); + } + gb_printf("]"); + } else if (is_type_maybe(type)) { + if (value.val_comp[1].val_int != 0) { + gb_printf("?"); + vm_print_value(value.val_comp[0], type->Maybe.elem); + } else { + gb_printf("nil"); + } + } else if (is_type_struct(type)) { + if (value.val_comp.count == 0) { + gb_printf("nil"); + } else { + gb_printf("{"); + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Record.fields[i]->type); + } + gb_printf("}"); + } + } else if (is_type_tuple(type)) { + if (value.val_comp.count != 1) { + gb_printf("("); + } + for_array(i, value.val_comp) { + if (i > 0) { + gb_printf(", "); + } + vm_print_value(value.val_comp[i], type->Tuple.variables[i]->type); + } + if (value.val_comp.count != 1) { + gb_printf(")"); + } + } +}