diff --git a/src/tilde/tb.h b/src/tilde/tb.h new file mode 100644 index 000000000..2604da5c3 --- /dev/null +++ b/src/tilde/tb.h @@ -0,0 +1,1101 @@ +#ifndef TB_CORE_H +#define TB_CORE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +// https://semver.org/ +#define TB_VERSION_MAJOR 0 +#define TB_VERSION_MINOR 2 +#define TB_VERSION_PATCH 0 + +#ifdef __cplusplus +#define TB_EXTERN extern "C" +#else +#define TB_EXTERN +#endif + +#ifdef TB_DLL +# ifdef TB_IMPORT_DLL +# define TB_API TB_EXTERN __declspec(dllimport) +# else +# define TB_API TB_EXTERN __declspec(dllexport) +# endif +#else +# define TB_API TB_EXTERN +#endif + +// These are flags +typedef enum TB_ArithmeticBehavior { + TB_ARITHMATIC_NSW = 1, + TB_ARITHMATIC_NUW = 2, +} TB_ArithmeticBehavior; + +typedef enum TB_DebugFormat { + TB_DEBUGFMT_NONE, + + TB_DEBUGFMT_DWARF, + TB_DEBUGFMT_CODEVIEW, + + TB_DEBUGFMT_COLINPILLED +} TB_DebugFormat; + +typedef enum TB_Arch { + TB_ARCH_UNKNOWN, + + TB_ARCH_X86_64, + TB_ARCH_AARCH64, // unsupported but planned + TB_ARCH_WASM32, +} TB_Arch; + +typedef enum TB_System { + TB_SYSTEM_WINDOWS, + TB_SYSTEM_LINUX, + TB_SYSTEM_MACOS, + TB_SYSTEM_ANDROID, // Not supported yet + TB_SYSTEM_WEB, + + TB_SYSTEM_MAX, +} TB_System; + +typedef enum TB_WindowsSubsystem { + TB_WIN_SUBSYSTEM_UNKNOWN, + + TB_WIN_SUBSYSTEM_WINDOWS, + TB_WIN_SUBSYSTEM_CONSOLE, + TB_WIN_SUBSYSTEM_EFI_APP, +} TB_WindowsSubsystem; + +typedef enum TB_ABI { + // Used on 64bit Windows platforms + TB_ABI_WIN64, + + // Used on Mac, BSD and Linux platforms + TB_ABI_SYSTEMV, +} TB_ABI; + +typedef enum TB_OutputFlavor { + TB_FLAVOR_OBJECT, // .o .obj + TB_FLAVOR_ASSEMBLY, // .s .asm + TB_FLAVOR_SHARED, // .so .dll + TB_FLAVOR_STATIC, // .a .lib + TB_FLAVOR_EXECUTABLE, // .exe +} TB_OutputFlavor; + +typedef enum TB_CallingConv { + TB_CDECL, + TB_STDCALL +} TB_CallingConv; + +typedef enum TB_FeatureSet_X64 { + TB_FEATURE_X64_SSE3 = (1u << 0u), + TB_FEATURE_X64_SSE41 = (1u << 1u), + TB_FEATURE_X64_SSE42 = (1u << 2u), + + TB_FEATURE_X64_POPCNT = (1u << 3u), + TB_FEATURE_X64_LZCNT = (1u << 4u), + + TB_FEATURE_X64_CLMUL = (1u << 5u), + TB_FEATURE_X64_F16C = (1u << 6u), + + TB_FEATURE_X64_BMI1 = (1u << 7u), + TB_FEATURE_X64_BMI2 = (1u << 8u), + + TB_FEATURE_X64_AVX = (1u << 9u), + TB_FEATURE_X64_AVX2 = (1u << 10u), +} TB_FeatureSet_X64; + +typedef struct TB_FeatureSet { + TB_FeatureSet_X64 x64; +} TB_FeatureSet; + +typedef enum TB_BranchHint { + TB_BRANCH_HINT_NONE, + TB_BRANCH_HINT_LIKELY, + TB_BRANCH_HINT_UNLIKELY +} TB_BranchHint; + +typedef enum TB_Linkage { + TB_LINKAGE_PUBLIC, + TB_LINKAGE_PRIVATE +} TB_Linkage; + +typedef enum { + TB_COMDAT_NONE, + + TB_COMDAT_MATCH_ANY, +} TB_ComdatType; + +typedef enum TB_MemoryOrder { + TB_MEM_ORDER_RELAXED, + TB_MEM_ORDER_CONSUME, + TB_MEM_ORDER_ACQUIRE, + TB_MEM_ORDER_RELEASE, + TB_MEM_ORDER_ACQ_REL, + TB_MEM_ORDER_SEQ_CST, +} TB_MemoryOrder; + +typedef enum TB_ISelMode { + // FastISel + TB_ISEL_FAST, + TB_ISEL_COMPLEX +} TB_ISelMode; + +typedef enum TB_DataTypeEnum { + // Integers, note void is an i0 and bool is an i1 + // i(0-2047) + TB_INT, + // Floating point numbers + // f{32,64} + TB_FLOAT, + // Pointers + // ptr(0-2047) + TB_PTR, + // Tuples, these cannot be used in memory ops, just accessed via projections + TB_TUPLE, + // represents control flow as a kind of data + TB_CONTROL, +} TB_DataTypeEnum; + +typedef enum TB_FloatFormat { + // IEEE 754 floats + TB_FLT_32, TB_FLT_64 +} TB_FloatFormat; + +typedef union TB_DataType { + struct { + uint8_t type; + // 2^N where N is the width value. + // Only integers and floats can be wide. + uint8_t width; + // for integers it's the bitwidth + uint16_t data; + }; + uint32_t raw; +} TB_DataType; + +// classify data types +#define TB_IS_VOID_TYPE(x) ((x).type == TB_INT && (x).data == 0) +#define TB_IS_BOOL_TYPE(x) ((x).type == TB_INT && (x).data == 1) +#define TB_IS_INTEGER_TYPE(x) ((x).type == TB_INT) +#define TB_IS_FLOAT_TYPE(x) ((x).type == TB_FLOAT) +#define TB_IS_POINTER_TYPE(x) ((x).type == TB_PTR) + +// accessors +#define TB_GET_INT_BITWIDTH(x) ((x).data) +#define TB_GET_FLOAT_FORMAT(x) ((x).data) +#define TB_GET_PTR_ADDRSPACE(x) ((x).data) + +typedef enum TB_NodeTypeEnum { + TB_NULL = 0, + + TB_RETURN, // fn(r: region, x: data) + + // only one per function + TB_START, // fn() + + // regions represent the begining of BBs + TB_REGION, // fn(preds: []region) + + // projection + TB_PROJ, + + // metadata + TB_KEEPALIVE, + + TB_CALL, // normal call + TB_SCALL, // system call + + // Managed ops + TB_SAFEPOINT, + + // Memory operations + TB_STORE, // fn(r: control, addr: data, src: data) + TB_MEMCPY, + TB_MEMSET, + + // Atomics + TB_ATOMIC_TEST_AND_SET, + TB_ATOMIC_CLEAR, + + TB_ATOMIC_LOAD, + TB_ATOMIC_XCHG, + TB_ATOMIC_ADD, + TB_ATOMIC_SUB, + TB_ATOMIC_AND, + TB_ATOMIC_XOR, + TB_ATOMIC_OR, + + TB_ATOMIC_CMPXCHG, + TB_DEBUGBREAK, + + // Terminators + TB_BRANCH, + TB_RET, + TB_UNREACHABLE, + TB_TRAP, + + TB_POISON, + + // Load + TB_LOAD, + + // Pointers + TB_LOCAL, + + TB_GET_SYMBOL_ADDRESS, + + TB_MEMBER_ACCESS, + TB_ARRAY_ACCESS, + + // Immediates + TB_INTEGER_CONST, + TB_FLOAT32_CONST, + TB_FLOAT64_CONST, + + // Conversions + TB_TRUNCATE, + TB_FLOAT_EXT, + TB_SIGN_EXT, + TB_ZERO_EXT, + TB_INT2PTR, + TB_PTR2INT, + TB_UINT2FLOAT, + TB_FLOAT2UINT, + TB_INT2FLOAT, + TB_FLOAT2INT, + TB_BITCAST, + + // Select + TB_SELECT, + + // Bitmagic + TB_BSWAP, + TB_CLZ, + TB_CTZ, + TB_POPCNT, + + // Unary operations + TB_NOT, + TB_NEG, + + // Integer arithmatic + TB_AND, + TB_OR, + TB_XOR, + TB_ADD, + TB_SUB, + TB_MUL, + + TB_SHL, + TB_SHR, + TB_SAR, + TB_UDIV, + TB_SDIV, + TB_UMOD, + TB_SMOD, + + // Float arithmatic + TB_FADD, + TB_FSUB, + TB_FMUL, + TB_FDIV, + + // Comparisons + TB_CMP_EQ, + TB_CMP_NE, + TB_CMP_ULT, + TB_CMP_ULE, + TB_CMP_SLT, + TB_CMP_SLE, + TB_CMP_FLT, + TB_CMP_FLE, + + // Special ops + // does full multiplication (64x64=128 and so on) returning + // the low and high values in separate projections + TB_MULPAIR, + + // PHI + TB_PHI, // fn(r: region, x: []data) + + // NOTE(NeGate): only used internally, if you + // see one in normal IR things went wrong in + // an optimization pass + TB_PASS, + + // variadic + TB_VA_START, + + // x86 intrinsics + TB_X86INTRIN_RDTSC, + TB_X86INTRIN_LDMXCSR, + TB_X86INTRIN_STMXCSR, + TB_X86INTRIN_SQRT, + TB_X86INTRIN_RSQRT, +} TB_NodeTypeEnum; +typedef uint8_t TB_NodeType; + +#define TB_IS_NODE_SIDE_EFFECT(type) ((type) >= TB_LINE_INFO && (type) <= TB_DEBUGBREAK) +#define TB_IS_NODE_TERMINATOR(type) ((type) >= TB_BRANCH && (type) <= TB_TRAP) + +typedef int TB_Label; + +// just represents some region of bytes, usually in file parsing crap +typedef struct { + size_t length; + const uint8_t* data; +} TB_Slice; + +// represents byte counts +typedef uint32_t TB_CharUnits; + +typedef unsigned int TB_FileID; + +// SO refers to shared objects which mean either shared libraries (.so or .dll) +// or executables (.exe or ELF executables) +typedef enum { + // exports to the rest of the shared object + TB_EXTERNAL_SO_LOCAL, + + // exports outside of the shared object + TB_EXTERNAL_SO_EXPORT, +} TB_ExternalType; + +typedef struct TB_Global TB_Global; +typedef struct TB_External TB_External; +typedef struct TB_Function TB_Function; + +typedef struct TB_Module TB_Module; +typedef struct TB_Attrib TB_Attrib; +typedef struct TB_DebugType TB_DebugType; +typedef struct TB_ModuleSection TB_ModuleSection; +typedef struct TB_FunctionPrototype TB_FunctionPrototype; + +// Refers generically to objects within a module +// +// TB_Function, TB_Global, and TB_External are all subtypes of TB_Symbol +// and thus are safely allowed to cast into a symbol for operations. +typedef struct TB_Symbol { + enum TB_SymbolTag { + TB_SYMBOL_NONE, + + // symbol is dead now + TB_SYMBOL_TOMBSTONE, + + TB_SYMBOL_EXTERNAL, + TB_SYMBOL_GLOBAL, + TB_SYMBOL_FUNCTION, + + TB_SYMBOL_MAX, + } tag; + + // refers to the prev or next symbol with the same tag + struct TB_Symbol* prev; + struct TB_Symbol* next; + char* name; + + // It's kinda a weird circular reference but yea + TB_Module* module; + + // helpful for sorting and getting consistent builds + uint64_t ordinal; + + union { + // if we're JITing then this maps to the address of the symbol + void* address; + size_t symbol_id; + }; + + // after this point it's tag-specific storage +} TB_Symbol; + +typedef int TB_Reg; + +#define TB_NULL_REG NULL + +typedef struct TB_Node TB_Node; +struct TB_Node { + TB_NodeType type; + TB_DataType dt; + uint16_t input_count; // number of node inputs + uint16_t extra_count; // number of bytes for extra operand data + + TB_Attrib* first_attrib; + TB_Node** inputs; + + char extra[]; +}; + +#define TB_KILL_NODE(n) ((n)->type = TB_NULL) + +// These are the extra data in specific nodes +#define TB_NODE_GET_EXTRA(n) ((void*) n->extra) +#define TB_NODE_GET_EXTRA_T(n, T) ((T*) (n)->extra) +#define TB_NODE_SET_EXTRA(n, T, ...) (*((T*) (n)->extra) = (T){ __VA_ARGS__ }) + +// this represents switch (many targets), if (one target) and goto (only default) logic. +typedef struct { // TB_BRANCH + // avoid empty structs with flexible members + int64_t _; + int64_t keys[/* input_count - 1 */]; +} TB_NodeBranch; + +typedef struct { // TB_PROJ + int index; +} TB_NodeProj; + +typedef struct { // TB_INT + uint64_t num_words; + uint64_t words[]; +} TB_NodeInt; + +typedef struct { // any compare operator + TB_DataType cmp_dt; +} TB_NodeCompare; + +typedef struct { // any integer binary operator + TB_ArithmeticBehavior ab; +} TB_NodeBinopInt; + +typedef struct { // TB_MULPAIR + TB_Node *lo, *hi; +} TB_NodeMulPair; + +typedef struct { + TB_CharUnits align; + bool is_volatile; +} TB_NodeMemAccess; + +typedef struct { + TB_CharUnits size, align; +} TB_NodeLocal; + +typedef struct { + TB_FileID file; + int line; +} TB_NodeLine; + +typedef struct { + float value; +} TB_NodeFloat32; + +typedef struct { + double value; +} TB_NodeFloat64; + +typedef struct { + int64_t stride; +} TB_NodeArray; + +typedef struct { + int64_t offset; +} TB_NodeMember; + +typedef struct { + TB_Symbol* sym; +} TB_NodeSymbol; + +typedef struct { + TB_MemoryOrder order; + TB_MemoryOrder order2; +} TB_NodeAtomic; + +typedef struct { + TB_FunctionPrototype* proto; + TB_Node* projs[]; +} TB_NodeCall; + +typedef struct { + uint32_t id; +} TB_NodeSafepoint; + +typedef struct { + TB_Node* end; + + size_t succ_count; + TB_Node** succ; + + size_t proj_count; + TB_Node** projs; +} TB_NodeRegion; + +typedef struct TB_MultiOutput { + size_t count; + union { + // count = 1 + TB_Node* single; + // count > 1 + TB_Node** multiple; + }; +} TB_MultiOutput; +#define TB_MULTI_OUTPUT(o) ((o).count > 1 ? (o).multiple : &(o).single) + +typedef struct { + int64_t key; + TB_Node* value; +} TB_SwitchEntry; + +typedef struct TB_Loop { + // refers to another entry in TB_LoopInfo... unless it's -1 + ptrdiff_t parent_loop; + + TB_Node* header; + TB_Node* backedge; +} TB_Loop; + +typedef struct TB_LoopInfo { + size_t count; + TB_Loop* loops; +} TB_LoopInfo; + +typedef enum { + TB_EXECUTABLE_UNKNOWN, + TB_EXECUTABLE_PE, + TB_EXECUTABLE_ELF, +} TB_ExecutableType; + +typedef struct { + TB_Node* node; // type == TB_SAFEPOINT + void* userdata; + + uint32_t ip; // relative to the function body. + uint32_t count; // same as node->input_count + int32_t values[]; +} TB_Safepoint; + +// ******************************* +// Public macros +// ******************************* +#ifdef __cplusplus + +#define TB_TYPE_TUPLE TB_DataType{ { TB_TUPLE } } +#define TB_TYPE_CONTROL TB_DataType{ { TB_CONTROL } } +#define TB_TYPE_VOID TB_DataType{ { TB_INT, 0, 0 } } +#define TB_TYPE_I8 TB_DataType{ { TB_INT, 0, 8 } } +#define TB_TYPE_I16 TB_DataType{ { TB_INT, 0, 16 } } +#define TB_TYPE_I32 TB_DataType{ { TB_INT, 0, 32 } } +#define TB_TYPE_I64 TB_DataType{ { TB_INT, 0, 64 } } +#define TB_TYPE_F32 TB_DataType{ { TB_FLOAT, 0, TB_FLT_32 } } +#define TB_TYPE_F64 TB_DataType{ { TB_FLOAT, 0, TB_FLT_64 } } +#define TB_TYPE_BOOL TB_DataType{ { TB_INT, 0, 1 } } +#define TB_TYPE_PTR TB_DataType{ { TB_PTR, 0, 0 } } + +#define TB_TYPE_INTN(N) TB_DataType{ { TB_INT, 0, (N) } } +#define TB_TYPE_PTRN(N) TB_DataType{ { TB_PTR, 0, (N) } } + +#else + +#define TB_TYPE_TUPLE (TB_DataType){ { TB_TUPLE } } +#define TB_TYPE_CONTROL (TB_DataType){ { TB_CONTROL } } +#define TB_TYPE_VOID (TB_DataType){ { TB_INT, 0, 0 } } +#define TB_TYPE_I8 (TB_DataType){ { TB_INT, 0, 8 } } +#define TB_TYPE_I16 (TB_DataType){ { TB_INT, 0, 16 } } +#define TB_TYPE_I32 (TB_DataType){ { TB_INT, 0, 32 } } +#define TB_TYPE_I64 (TB_DataType){ { TB_INT, 0, 64 } } +#define TB_TYPE_F32 (TB_DataType){ { TB_FLOAT, 0, TB_FLT_32 } } +#define TB_TYPE_F64 (TB_DataType){ { TB_FLOAT, 0, TB_FLT_64 } } +#define TB_TYPE_BOOL (TB_DataType){ { TB_INT, 0, 1 } } +#define TB_TYPE_PTR (TB_DataType){ { TB_PTR, 0, 0 } } +#define TB_TYPE_INTN(N) (TB_DataType){ { TB_INT, 0, (N) } } +#define TB_TYPE_PTRN(N) (TB_DataType){ { TB_PTR, 0, (N) } } + +#endif + +typedef void (*TB_PrintCallback)(void* user_data, const char* fmt, ...); + +//////////////////////////////// +// Arena +//////////////////////////////// +// the goal is to move more things to transparent arenas, for now it's just function +// IR which is a big one if you're interested in freeing them in whatever organization +// you please. + +// allocations can make no guarentees about being sequential +// tho it would be greatly appreciated at least to some degree. +typedef struct TB_Arena TB_Arena; +struct TB_Arena { + // alignment never goes past max_align_t + void* (*alloc)(TB_Arena* arena, size_t size, size_t align); + + // clearing but we're not done with it yet, cheap + void (*clear)(TB_Arena* arena); + + // frees everything within the arena, potentially expensive + void (*free)(TB_Arena* arena); +}; + +// allocates in 16MiB chunks and does linear allocation in 'em +TB_Arena* tb_default_arena(void); + +//////////////////////////////// +// Module management +//////////////////////////////// +// Creates a module with the correct target and settings +TB_API TB_Module* tb_module_create(TB_Arch arch, TB_System sys, const TB_FeatureSet* features, bool is_jit); + +// Creates a module but defaults on the architecture and system based on the host machine +TB_API TB_Module* tb_module_create_for_host(const TB_FeatureSet* features, bool is_jit); + +TB_API size_t tb_module_get_function_count(TB_Module* m); + +// Frees all resources for the TB_Module and it's functions, globals and +// compiled code. +TB_API void tb_module_destroy(TB_Module* m); + +// When targetting windows & thread local storage, you'll need to bind a tls index +// which is usually just a global that the runtime support has initialized, if you +// dont and the tls_index is used, it'll crash +TB_API void tb_module_set_tls_index(TB_Module* m, const char* name); + +// You don't need to manually call this unless you want to resolve locations before +// exporting. +TB_API void tb_module_layout_sections(TB_Module* m); + +//////////////////////////////// +// Exporter +//////////////////////////////// + +// this is where the machine code and other relevant pieces go. +typedef struct TB_FunctionOutput TB_FunctionOutput; + +// returns NULL if it fails +TB_API TB_FunctionOutput* tb_module_compile_function(TB_Module* m, TB_Function* f, TB_ISelMode isel_mode); + +//////////////////////////////// +// Exporter +//////////////////////////////// +// Export buffers are generated in chunks because it's easier, usually the +// chunks are "massive" (representing some connected piece of the buffer) +// but they don't have to be. +typedef struct TB_ExportChunk TB_ExportChunk; +struct TB_ExportChunk { + TB_ExportChunk* next; + size_t pos, size; + uint8_t data[]; +}; + +typedef struct { + size_t total; + TB_ExportChunk *head, *tail; +} TB_ExportBuffer; + +TB_API TB_ExportBuffer tb_module_object_export(TB_Module* m, TB_DebugFormat debug_fmt); +TB_API bool tb_export_buffer_to_file(TB_ExportBuffer buffer, const char* path); +TB_API void tb_export_buffer_free(TB_ExportBuffer buffer); + +//////////////////////////////// +// Linker exporter +//////////////////////////////// +// This is used to export shared objects or executables +typedef struct TB_Linker TB_Linker; +typedef struct TB_LinkerSection TB_LinkerSection; +typedef struct TB_LinkerSectionPiece TB_LinkerSectionPiece; + +typedef struct { + enum { + TB_LINKER_MSG_NULL, + + // pragma comment(lib, "blah") + TB_LINKER_MSG_IMPORT, + } tag; + union { + // pragma lib request + TB_Slice import_path; + }; +} TB_LinkerMsg; + +TB_API TB_ExecutableType tb_system_executable_format(TB_System s); + +TB_API TB_Linker* tb_linker_create(TB_ExecutableType type, TB_Arch arch); +TB_API TB_ExportBuffer tb_linker_export(TB_Linker* l); +TB_API void tb_linker_destroy(TB_Linker* l); + +TB_API bool tb_linker_get_msg(TB_Linker* l, TB_LinkerMsg* msg); + +// windows only +TB_API void tb_linker_set_subsystem(TB_Linker* l, TB_WindowsSubsystem subsystem); + +TB_API void tb_linker_set_entrypoint(TB_Linker* l, const char* name); + +// Links compiled module into output +TB_API void tb_linker_append_module(TB_Linker* l, TB_Module* m); + +// Adds object file to output +TB_API void tb_linker_append_object(TB_Linker* l, TB_Slice obj_name, TB_Slice content); + +// Adds static library to output +// this can include imports (wrappers for DLL symbols) along with +// normal sections. +TB_API void tb_linker_append_library(TB_Linker* l, TB_Slice ar_name, TB_Slice content); + +//////////////////////////////// +// JIT compilation +//////////////////////////////// +typedef struct TB_JITContext TB_JITContext; + +// passing 0 to jit_heap_capacity will default to 4MiB +TB_API TB_JITContext* tb_module_begin_jit(TB_Module* m, size_t jit_heap_capacity); +TB_API void* tb_module_apply_function(TB_JITContext* jit, TB_Function* f); +TB_API void* tb_module_apply_global(TB_JITContext* jit, TB_Global* g); +// fixes page permissions, applies missing relocations +TB_API void tb_module_ready_jit(TB_JITContext* jit); +TB_API void tb_module_end_jit(TB_JITContext* jit); + +#define TB_FOR_FUNCTIONS(it, module) for (TB_Function* it = tb_first_function(module); it != NULL; it = tb_next_function(it)) +TB_API TB_Function* tb_first_function(TB_Module* m); +TB_API TB_Function* tb_next_function(TB_Function* f); + +#define TB_FOR_EXTERNALS(it, module) for (TB_External* it = tb_first_external(module); it != NULL; it = tb_next_external(it)) +TB_API TB_External* tb_first_external(TB_Module* m); +TB_API TB_External* tb_next_external(TB_External* e); + +// this is used JIT scenarios to tell the compiler what externals map to +TB_API TB_ExternalType tb_extern_get_type(TB_External* e); +TB_Global* tb_extern_transmute(TB_External* e, TB_DebugType* dbg_type, TB_Linkage linkage); + +TB_API TB_External* tb_extern_create(TB_Module* m, const char* name, TB_ExternalType type); +TB_API TB_FileID tb_file_create(TB_Module* m, const char* path); + +// Called once you're done with TB operations on a thread (or i guess when it's +// about to be killed :p), not calling it can only result in leaks on that thread +// and calling it too early will result in TB potentially reallocating it but there's +// should be no crashes from this, just potential slowdown or higher than expected memory +// usage. +TB_API void tb_free_thread_resources(void); + +//////////////////////////////// +// Function Prototypes +//////////////////////////////// +typedef struct TB_PrototypeParam { + TB_DataType dt; + TB_DebugType* debug_type; + + // does not apply for returns + const char* name; +} TB_PrototypeParam; + +struct TB_FunctionPrototype { + // header + TB_CallingConv call_conv; + uint16_t return_count, param_count; + bool has_varargs; + + // params are directly followed by returns + TB_PrototypeParam params[]; +}; +#define TB_PROTOTYPE_RETURNS(p) ((p)->params + (p)->param_count) + +// creates a function prototype used to define a function's parameters and returns. +// +// function prototypes do not get freed individually and last for the entire run +// of the backend, they can also be reused for multiple functions which have +// matching signatures. +TB_API TB_FunctionPrototype* tb_prototype_create(TB_Module* m, TB_CallingConv cc, size_t param_count, const TB_PrototypeParam* params, size_t return_count, const TB_PrototypeParam* returns, bool has_varargs); + +//////////////////////////////// +// Globals +//////////////////////////////// +TB_API TB_Global* tb_global_create(TB_Module* m, const char* name, TB_DebugType* dbg_type, TB_Linkage linkage); + +// allocate space for the global +TB_API void tb_global_set_storage(TB_Module* m, TB_ModuleSection* section, TB_Global* global, size_t size, size_t align, size_t max_objects); + +// returns a buffer which the user can fill to then have represented in the initializer +TB_API void* tb_global_add_region(TB_Module* m, TB_Global* global, size_t offset, size_t size); + +// places a relocation for a global at offset, the size of the relocation +// depends on the pointer size +TB_API void tb_global_add_symbol_reloc(TB_Module* m, TB_Global* global, size_t offset, const TB_Symbol* symbol); + +TB_API TB_ModuleSection* tb_module_get_text(TB_Module* m); +TB_API TB_ModuleSection* tb_module_get_rdata(TB_Module* m); +TB_API TB_ModuleSection* tb_module_get_data(TB_Module* m); +TB_API TB_ModuleSection* tb_module_get_tls(TB_Module* m); + +//////////////////////////////// +// Function Attributes +//////////////////////////////// +// These are parts of a function that describe metadata for instructions +TB_API void tb_function_attrib_variable(TB_Function* f, TB_Node* n, const char* name, TB_DebugType* type); + +//////////////////////////////// +// Debug info Generation +//////////////////////////////// +TB_API TB_DebugType* tb_debug_get_void(TB_Module* m); +TB_API TB_DebugType* tb_debug_get_bool(TB_Module* m); +TB_API TB_DebugType* tb_debug_get_integer(TB_Module* m, bool is_signed, int bits); +TB_API TB_DebugType* tb_debug_get_float(TB_Module* m, TB_FloatFormat fmt); +TB_API TB_DebugType* tb_debug_create_ptr(TB_Module* m, TB_DebugType* base); +TB_API TB_DebugType* tb_debug_create_array(TB_Module* m, TB_DebugType* base, size_t count); +TB_API TB_DebugType* tb_debug_create_struct(TB_Module* m, const char* tag); +TB_API TB_DebugType* tb_debug_create_union(TB_Module* m, const char* tag); +TB_API TB_DebugType* tb_debug_create_field(TB_Module* m, TB_DebugType* type, const char* name, TB_CharUnits offset); +TB_API void tb_debug_complete_record(TB_DebugType* type, TB_DebugType** members, size_t count, TB_CharUnits size, TB_CharUnits align); + +//////////////////////////////// +// IR access +//////////////////////////////// +// it is an index to the input +#define TB_FOR_INPUT_IN_NODE(it, parent) for (TB_Node **it = parent->inputs, **__end = it + (parent)->input_count; it != __end; it++) + +//////////////////////////////// +// Compiled code introspection +//////////////////////////////// +// this is relative to the start of the function (the start of the prologue) +TB_API TB_Safepoint* tb_safepoint_get(TB_Function* f, uint32_t relative_ip); + +//////////////////////////////// +// Symbols +//////////////////////////////// +TB_API bool tb_symbol_is_comdat(const TB_Symbol* s); + +// returns NULL if the tag doesn't match +TB_API TB_Function* tb_symbol_as_function(TB_Symbol* s); +TB_API TB_External* tb_symbol_as_external(TB_Symbol* s); +TB_API TB_Global* tb_symbol_as_global(TB_Symbol* s); + +//////////////////////////////// +// Function IR Generation +//////////////////////////////// +// the user_data is expected to be a valid FILE* +TB_API void tb_default_print_callback(void* user_data, const char* fmt, ...); + +TB_API void tb_inst_set_location(TB_Function* f, TB_FileID file, int line); + +// this only allows for power of two vector types +TB_API TB_DataType tb_vector_type(TB_DataTypeEnum type, int width); + +// if section is NULL, default to .text +TB_API TB_Function* tb_function_create(TB_Module* m, const char* name, TB_Linkage linkage, TB_ComdatType comdat); + +TB_API void* tb_function_get_jit_pos(TB_Function* f); + +TB_API void tb_symbol_bind_ptr(TB_Symbol* s, void* ptr); +TB_API void tb_symbol_set_name(TB_Symbol* s, const char* name); +TB_API const char* tb_symbol_get_name(TB_Symbol* s); + +// if arena is NULL, defaults to module arena which is freed on tb_free_thread_resources +TB_API void tb_function_set_prototype(TB_Function* f, TB_FunctionPrototype* p, TB_Arena* arena); +TB_API TB_FunctionPrototype* tb_function_get_prototype(TB_Function* f); + +TB_API void tb_function_print(TB_Function* f, TB_PrintCallback callback, void* user_data); + +TB_API void tb_inst_set_control(TB_Function* f, TB_Node* control); +TB_API TB_Node* tb_inst_get_control(TB_Function* f); + +TB_API TB_Node* tb_inst_region(TB_Function* f); + +TB_API void tb_inst_unreachable(TB_Function* f); +TB_API void tb_inst_debugbreak(TB_Function* f); +TB_API void tb_inst_trap(TB_Function* f); +TB_API void tb_inst_keep_alive(TB_Function* f, TB_Node* src); +TB_API TB_Node* tb_inst_poison(TB_Function* f); + +TB_API TB_Node* tb_inst_param(TB_Function* f, int param_id); +TB_API TB_Node* tb_inst_param_addr(TB_Function* f, int param_id); + +TB_API TB_Node* tb_inst_fpxt(TB_Function* f, TB_Node* src, TB_DataType dt); +TB_API TB_Node* tb_inst_sxt(TB_Function* f, TB_Node* src, TB_DataType dt); +TB_API TB_Node* tb_inst_zxt(TB_Function* f, TB_Node* src, TB_DataType dt); +TB_API TB_Node* tb_inst_trunc(TB_Function* f, TB_Node* src, TB_DataType dt); +TB_API TB_Node* tb_inst_int2ptr(TB_Function* f, TB_Node* src); +TB_API TB_Node* tb_inst_ptr2int(TB_Function* f, TB_Node* src, TB_DataType dt); +TB_API TB_Node* tb_inst_int2float(TB_Function* f, TB_Node* src, TB_DataType dt, bool is_signed); +TB_API TB_Node* tb_inst_float2int(TB_Function* f, TB_Node* src, TB_DataType dt, bool is_signed); +TB_API TB_Node* tb_inst_bitcast(TB_Function* f, TB_Node* src, TB_DataType dt); + +TB_API TB_Node* tb_inst_local(TB_Function* f, uint32_t size, TB_CharUnits align); +TB_API TB_Node* tb_inst_load(TB_Function* f, TB_DataType dt, TB_Node* addr, TB_CharUnits align, bool is_volatile); +TB_API void tb_inst_store(TB_Function* f, TB_DataType dt, TB_Node* addr, TB_Node* val, TB_CharUnits align, bool is_volatile); + +TB_API TB_Node* tb_inst_bool(TB_Function* f, bool imm); +TB_API TB_Node* tb_inst_ptr(TB_Function* f, uint64_t imm); +TB_API TB_Node* tb_inst_sint(TB_Function* f, TB_DataType dt, int64_t imm); +TB_API TB_Node* tb_inst_uint(TB_Function* f, TB_DataType dt, uint64_t imm); +TB_API TB_Node* tb_inst_float32(TB_Function* f, float imm); +TB_API TB_Node* tb_inst_float64(TB_Function* f, double imm); +TB_API TB_Node* tb_inst_cstring(TB_Function* f, const char* str); +TB_API TB_Node* tb_inst_string(TB_Function* f, size_t len, const char* str); + +// Broadcasts 'val' across 'count' elements starting 'dst' +TB_API void tb_inst_memset(TB_Function* f, TB_Node* dst, TB_Node* val, TB_Node* count, TB_CharUnits align, bool is_volatile); + +// performs a copy of 'count' elements from one memory location to another +// both locations cannot overlap. +TB_API void tb_inst_memcpy(TB_Function* f, TB_Node* dst, TB_Node* src, TB_Node* count, TB_CharUnits align, bool is_volatile); + +// result = base + (index * stride) +TB_API TB_Node* tb_inst_array_access(TB_Function* f, TB_Node* base, TB_Node* index, int64_t stride); + +// result = base + offset +// where base is a pointer +TB_API TB_Node* tb_inst_member_access(TB_Function* f, TB_Node* base, int64_t offset); + +TB_API TB_Node* tb_inst_get_symbol_address(TB_Function* f, TB_Symbol* target); + +// Performs a conditional select between two values, if the operation is +// performed wide then the cond is expected to be the same type as a and b where +// the condition is resolved as true if the MSB (per component) is 1. +// +// result = cond ? a : b +// a, b must match in type +TB_API TB_Node* tb_inst_select(TB_Function* f, TB_Node* cond, TB_Node* a, TB_Node* b); + +// Integer arithmatic +TB_API TB_Node* tb_inst_add(TB_Function* f, TB_Node* a, TB_Node* b, TB_ArithmeticBehavior arith_behavior); +TB_API TB_Node* tb_inst_sub(TB_Function* f, TB_Node* a, TB_Node* b, TB_ArithmeticBehavior arith_behavior); +TB_API TB_Node* tb_inst_mul(TB_Function* f, TB_Node* a, TB_Node* b, TB_ArithmeticBehavior arith_behavior); +TB_API TB_Node* tb_inst_div(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness); +TB_API TB_Node* tb_inst_mod(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness); + +// Bitmagic operations +TB_API TB_Node* tb_inst_bswap(TB_Function* f, TB_Node* n); +TB_API TB_Node* tb_inst_clz(TB_Function* f, TB_Node* n); +TB_API TB_Node* tb_inst_ctz(TB_Function* f, TB_Node* n); +TB_API TB_Node* tb_inst_popcount(TB_Function* f, TB_Node* n); + +// Bitwise operations +TB_API TB_Node* tb_inst_not(TB_Function* f, TB_Node* n); +TB_API TB_Node* tb_inst_neg(TB_Function* f, TB_Node* n); +TB_API TB_Node* tb_inst_and(TB_Function* f, TB_Node* a, TB_Node* b); +TB_API TB_Node* tb_inst_or(TB_Function* f, TB_Node* a, TB_Node* b); +TB_API TB_Node* tb_inst_xor(TB_Function* f, TB_Node* a, TB_Node* b); +TB_API TB_Node* tb_inst_sar(TB_Function* f, TB_Node* a, TB_Node* b); +TB_API TB_Node* tb_inst_shl(TB_Function* f, TB_Node* a, TB_Node* b, TB_ArithmeticBehavior arith_behavior); +TB_API TB_Node* tb_inst_shr(TB_Function* f, TB_Node* a, TB_Node* b); + +// Atomics +// By default you can use TB_MEM_ORDER_SEQ_CST for the memory order to get +// correct but possibly slower results on certain platforms (those with relaxed +// memory models). +TB_API TB_Node* tb_inst_atomic_test_and_set(TB_Function* f, TB_Node* addr, TB_MemoryOrder order); +TB_API TB_Node* tb_inst_atomic_clear(TB_Function* f, TB_Node* addr, TB_MemoryOrder order); + +// Must be aligned to the natural alignment of dt +TB_API TB_Node* tb_inst_atomic_load(TB_Function* f, TB_Node* addr, TB_DataType dt, TB_MemoryOrder order); + +// All atomic operations here return the old value and the operations are +// performed in the same data type as 'src' with alignment of 'addr' being +// the natural alignment of 'src' +TB_API TB_Node* tb_inst_atomic_xchg(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order); +TB_API TB_Node* tb_inst_atomic_add(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order); +TB_API TB_Node* tb_inst_atomic_sub(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order); +TB_API TB_Node* tb_inst_atomic_and(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order); +TB_API TB_Node* tb_inst_atomic_xor(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order); +TB_API TB_Node* tb_inst_atomic_or(TB_Function* f, TB_Node* addr, TB_Node* src, TB_MemoryOrder order); + +// returns old_value from *addr +TB_API TB_Node* tb_inst_atomic_cmpxchg(TB_Function* f, TB_Node* addr, TB_Node* expected, TB_Node* desired, TB_MemoryOrder succ, TB_MemoryOrder fail); + +// Float math +TB_API TB_Node* tb_inst_fadd(TB_Function* f, TB_Node* a, TB_Node* b); +TB_API TB_Node* tb_inst_fsub(TB_Function* f, TB_Node* a, TB_Node* b); +TB_API TB_Node* tb_inst_fmul(TB_Function* f, TB_Node* a, TB_Node* b); +TB_API TB_Node* tb_inst_fdiv(TB_Function* f, TB_Node* a, TB_Node* b); + +// Comparisons +TB_API TB_Node* tb_inst_cmp_eq(TB_Function* f, TB_Node* a, TB_Node* b); +TB_API TB_Node* tb_inst_cmp_ne(TB_Function* f, TB_Node* a, TB_Node* b); + +TB_API TB_Node* tb_inst_cmp_ilt(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness); +TB_API TB_Node* tb_inst_cmp_ile(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness); +TB_API TB_Node* tb_inst_cmp_igt(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness); +TB_API TB_Node* tb_inst_cmp_ige(TB_Function* f, TB_Node* a, TB_Node* b, bool signedness); + +TB_API TB_Node* tb_inst_cmp_flt(TB_Function* f, TB_Node* a, TB_Node* b); +TB_API TB_Node* tb_inst_cmp_fle(TB_Function* f, TB_Node* a, TB_Node* b); +TB_API TB_Node* tb_inst_cmp_fgt(TB_Function* f, TB_Node* a, TB_Node* b); +TB_API TB_Node* tb_inst_cmp_fge(TB_Function* f, TB_Node* a, TB_Node* b); + +// General intrinsics +TB_API TB_Node* tb_inst_va_start(TB_Function* f, TB_Node* a); + +// x86 Intrinsics +TB_API TB_Node* tb_inst_x86_rdtsc(TB_Function* f); +TB_API TB_Node* tb_inst_x86_ldmxcsr(TB_Function* f, TB_Node* a); +TB_API TB_Node* tb_inst_x86_stmxcsr(TB_Function* f); +TB_API TB_Node* tb_inst_x86_sqrt(TB_Function* f, TB_Node* a); +TB_API TB_Node* tb_inst_x86_rsqrt(TB_Function* f, TB_Node* a); + +// Control flow +TB_API TB_Node* tb_inst_syscall(TB_Function* f, TB_DataType dt, TB_Node* syscall_num, size_t param_count, TB_Node** params); +TB_API TB_MultiOutput tb_inst_call(TB_Function* f, TB_FunctionPrototype* proto, TB_Node* target, size_t param_count, TB_Node** params); + +// Managed +TB_API TB_Node* tb_inst_safepoint(TB_Function* f, size_t param_count, TB_Node** params); + +TB_API TB_Node* tb_inst_incomplete_phi(TB_Function* f, TB_DataType dt, TB_Node* region, size_t preds); +TB_API bool tb_inst_add_phi_operand(TB_Function* f, TB_Node* phi, TB_Node* region, TB_Node* val); +TB_API void tb_inst_set_phis_to_region(TB_Function* f, TB_Node* region, size_t phi_count, TB_Node** phis); + +TB_API TB_Node* tb_inst_phi2(TB_Function* f, TB_Node* region, TB_Node* a, TB_Node* b); +TB_API void tb_inst_goto(TB_Function* f, TB_Node* target); +TB_API void tb_inst_if(TB_Function* f, TB_Node* cond, TB_Node* true_case, TB_Node* false_case); +TB_API void tb_inst_branch(TB_Function* f, TB_DataType dt, TB_Node* key, TB_Node* default_case, size_t entry_count, const TB_SwitchEntry* keys); + +TB_API void tb_inst_ret(TB_Function* f, size_t count, TB_Node** values); + +//////////////////////////////// +// Transformation pass library +//////////////////////////////// +typedef struct TB_OptQueue TB_OptQueue; + +TB_API bool tb_optqueue_mark(TB_OptQueue* restrict queue, TB_Node* n, bool mark_kids); +TB_API void tb_optqueue_kill(TB_OptQueue* restrict queue, TB_Node* n); +TB_API void tb_optqueue_fill_all(TB_OptQueue* restrict queue, TB_Node* n); + +typedef struct TB_Pass { + // it's either a module-level pass or function-level + bool is_module; + const char* name; + + union { + bool(*func_run)(TB_Function* f, TB_OptQueue* queue); + bool(*mod_run)(TB_Module* m); + }; +} TB_Pass; + +typedef struct { + bool module_level; + uint32_t start, end; +} TB_Passes; + +typedef struct TB_PassManager { + size_t count; + const TB_Pass* passes; +} TB_PassManager; + +// each iteration the user can take the function sequence and apply +// it to the functions (or module if module_level is true) +#define TB_DO_PASSES(it, pm, mod) for (TB_Passes it = { 0 }; tb_passes_iter(pm, mod, &it);) +TB_API bool tb_passes_iter(TB_PassManager* manager, TB_Module* m, TB_Passes* passes); + +// Applies optimizations to the entire module +TB_API void tb_module_optimize(TB_Module* m, size_t pass_count, const TB_Pass* passes[]); + +// Applies a set of function level passes onto a function +TB_API void tb_function_apply_passes(TB_PassManager* manager, TB_Passes passes, TB_Function* f, TB_Arena* arena); +TB_API void tb_module_apply_passes(TB_PassManager* manager, TB_Passes passes, TB_Module* m, TB_Arena* arena); + +TB_API TB_Pass tb_opt_mem2reg(void); +TB_API TB_Pass tb_opt_identity(void); + +//////////////////////////////// +// IR access +//////////////////////////////// +TB_API const char* tb_node_get_name(TB_Node* n); + +TB_API TB_Node* tb_get_parent_region(TB_Node* n); +TB_API bool tb_has_effects(TB_Node* n); + +TB_API bool tb_node_is_constant_non_zero(TB_Node* n); +TB_API bool tb_node_is_constant_zero(TB_Node* n); + +#endif /* TB_CORE_H */ \ No newline at end of file diff --git a/src/tilde/tb.lib b/src/tilde/tb.lib new file mode 100644 index 000000000..bea6a58e3 Binary files /dev/null and b/src/tilde/tb.lib differ diff --git a/src/tilde/tb_coff.h b/src/tilde/tb_coff.h new file mode 100644 index 000000000..ae8f63863 --- /dev/null +++ b/src/tilde/tb_coff.h @@ -0,0 +1,330 @@ +// PE/COFF is the executable/object format used by Microsoft. +#ifndef TB_COFF_H +#define TB_COFF_H + +#include "tb_formats.h" + +#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 + +#define IMAGE_SYM_CLASS_EXTERNAL 0x0002 +#define IMAGE_SYM_CLASS_STATIC 0x0003 +#define IMAGE_SYM_CLASS_LABEL 0x0006 +#define IMAGE_SYM_CLASS_FILE 0x0067 +#define IMAGE_SYM_CLASS_SECTION 0x0068 +#define IMAGE_SYM_CLASS_WEAK_EXTERNAL 0x0069 + +#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 + +#define IMAGE_REL_AMD64_ADDR64 0x0001 +#define IMAGE_REL_AMD64_ADDR32 0x0002 +#define IMAGE_REL_AMD64_ADDR32NB 0x0003 +#define IMAGE_REL_AMD64_REL32 0x0004 +#define IMAGE_REL_AMD64_REL32_1 0x0005 +#define IMAGE_REL_AMD64_REL32_2 0x0006 +#define IMAGE_REL_AMD64_REL32_3 0x0007 +#define IMAGE_REL_AMD64_REL32_4 0x0008 +#define IMAGE_REL_AMD64_REL32_5 0x0009 +#define IMAGE_REL_AMD64_SECTION 0x000A +#define IMAGE_REL_AMD64_SECREL 0x000B + +#define IMAGE_SCN_LNK_REMOVE 0x00000800 +#define IMAGE_SCN_LNK_COMDAT 0x00001000 +#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 +#define IMAGE_SCN_MEM_READ 0x40000000 +#define IMAGE_SCN_MEM_WRITE 0x80000000 + +#define IMAGE_SCN_CNT_CODE 0x00000020 /* Section contains code. */ +#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* Section contains initialized data. */ +#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 /* Section contains uninitialized data. */ + +#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory +#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory +#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory +#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory +#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory +#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table +#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory +#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data +#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP +#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory +#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory +#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers +#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table +#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors +#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor + +#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 +#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 +#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 + +typedef enum { + TB_COFF_SECTION_NO_PAD = 0x00000008, + TB_COFF_SECTION_CODE = 0x00000020, + TB_COFF_SECTION_INIT = 0x00000040, + TB_COFF_SECTION_UNINIT = 0x00000080, + TB_COFF_SECTION_OTHER = 0x00000100, + TB_COFF_SECTION_INFO = 0x00000200, + TB_COFF_SECTION_REMOVE = 0x00000800, + TB_COFF_SECTION_COMDAT = 0x00001000, + + // this is actually a 4bit field + TB_COFF_SECTION_ALIGN = 0x00F00000, + + // if we have more than 65535 relocations we do this + TB_COFF_SECTION_RELOC_OVR = 0x00F00000, + + // memory flags + TB_COFF_SECTION_DISCARDABLE = 0x02000000, + TB_COFF_SECTION_NOT_CACHED = 0x04000000, + TB_COFF_SECTION_NOT_PAGED = 0x08000000, + TB_COFF_SECTION_SHARED = 0x10000000, + TB_COFF_SECTION_EXECUTE = 0x20000000, + TB_COFF_SECTION_READ = 0x40000000, + TB_COFF_SECTION_WRITE = 0x80000000, +} TB_COFF_SectionFlags; + +typedef struct TB_COFF_Parser { + // inputs + TB_Slice name, file; + + // results + size_t section_count; + size_t symbol_table, symbol_count; + + // private + TB_Slice string_table; +} TB_COFF_Parser; + +// fills the parser with results from the COFF header +bool tb_coff_parse_init(TB_COFF_Parser* restrict parser); +bool tb_coff_parse_section(TB_COFF_Parser* restrict parser, size_t i, TB_ObjectSection* out_sec); + +// how many symbols does this one symbol take up (basically 1 + aux symbols). +// returns 0 if error. +size_t tb_coff_parse_symbol(TB_COFF_Parser* restrict parser, size_t i, TB_ObjectSymbol* restrict out_sym); + +#endif // TB_COFF_H + +#ifdef TB_COFF_IMPL +#include + +#pragma pack(push, 2) +typedef struct COFF_SectionHeader { + char name[8]; + union { + uint32_t physical_address; + uint32_t virtual_size; + } misc; + uint32_t virtual_address; + uint32_t raw_data_size; + uint32_t raw_data_pos; + uint32_t pointer_to_reloc; + uint32_t pointer_to_lineno; + uint16_t num_reloc; + uint16_t num_lineno; + uint32_t characteristics; +} COFF_SectionHeader; + +typedef struct COFF_FileHeader { + uint16_t machine; + uint16_t section_count; + uint32_t timestamp; + uint32_t symbol_table; + uint32_t symbol_count; + uint16_t optional_header_size; + uint16_t flags; +} COFF_FileHeader; + +typedef struct COFF_Symbol { + union { + uint8_t short_name[8]; + uint32_t long_name[2]; + }; + uint32_t value; + int16_t section_number; + uint16_t type; + uint8_t storage_class; + uint8_t aux_symbols_count; +} COFF_Symbol; + +typedef struct COFF_ImageReloc { + union { + uint32_t VirtualAddress; + uint32_t RelocCount; + }; + uint32_t SymbolTableIndex; + uint16_t Type; +} COFF_ImageReloc; +#pragma pack(pop) + +// sanity checks +static_assert(sizeof(COFF_SectionHeader) == 40, "COFF Section header size != 40 bytes"); +static_assert(sizeof(COFF_ImageReloc) == 10, "COFF Image Relocation size != 10 bytes"); +static_assert(sizeof(COFF_FileHeader) == 20, "COFF File header size != 20 bytes"); +static_assert(sizeof(COFF_Symbol) == 18, "COFF Symbol size != 18 bytes"); + +bool tb_coff_parse_init(TB_COFF_Parser* restrict parser) { + TB_Slice file = parser->file; + + if (file.length < sizeof(COFF_FileHeader)) return false; + COFF_FileHeader* header = (COFF_FileHeader*) &parser->file.data[0]; + + // locate string table (it spans until the end of the file) + size_t string_table_pos = header->symbol_table + (header->symbol_count * sizeof(COFF_Symbol)); + if (file.length < string_table_pos) return false; + + parser->symbol_count = header->symbol_count; + parser->symbol_table = header->symbol_table; + parser->section_count = header->section_count; + parser->string_table = (TB_Slice){ + .length = file.length - string_table_pos, + .data = &file.data[string_table_pos] + }; + + return true; +} + +static long long tb__parse_decimal_int(size_t n, const char* str) { + const char* end = &str[n]; + + int result = 0; + while (str != end) { + if (*str < '0' || *str > '9') break; + + result *= 10; + result += *str - '0'; + str++; + } + + return result; +} + +bool tb_coff_parse_section(TB_COFF_Parser* restrict parser, size_t i, TB_ObjectSection* restrict out_sec) { + TB_Slice file = parser->file; + size_t section_offset = sizeof(COFF_FileHeader) + (i * sizeof(COFF_SectionHeader)); + + if (file.length < section_offset + sizeof(COFF_SectionHeader)) { + return false; + } + + COFF_SectionHeader* sec = (COFF_SectionHeader*) &file.data[section_offset]; + *out_sec = (TB_ObjectSection) { .flags = sec->characteristics }; + + // Parse string table name stuff + if (sec->name[0] == '/') { + // string table access + int offset = tb__parse_decimal_int(7, &sec->name[1]); + if (file.length > offset) { + return false; + } + + const uint8_t* data = &parser->string_table.data[offset]; + out_sec->name = (TB_Slice){ strlen((const char*) data), data }; + } else { + // normal inplace string + size_t len = strlen(sec->name); + out_sec->name = (TB_Slice){ len, (uint8_t*) sec->name }; + } + + // Parse relocations + if (sec->num_reloc > 0) { + out_sec->relocation_count = sec->num_reloc; + COFF_ImageReloc* src_relocs = (COFF_ImageReloc*) &file.data[sec->pointer_to_reloc]; + + TB_ObjectReloc* dst_relocs = tb_platform_heap_alloc(sec->num_reloc * sizeof(TB_ObjectReloc)); + FOREACH_N(j, 0, sec->num_reloc) { + dst_relocs[j] = (TB_ObjectReloc){ 0 }; + switch (src_relocs[j].Type) { + case IMAGE_REL_AMD64_ADDR32NB: dst_relocs[j].type = TB_OBJECT_RELOC_ADDR32NB; break; + case IMAGE_REL_AMD64_ADDR32: dst_relocs[j].type = TB_OBJECT_RELOC_ADDR32; break; + case IMAGE_REL_AMD64_ADDR64: dst_relocs[j].type = TB_OBJECT_RELOC_ADDR64; break; + case IMAGE_REL_AMD64_SECREL: dst_relocs[j].type = TB_OBJECT_RELOC_SECREL; break; + case IMAGE_REL_AMD64_SECTION: dst_relocs[j].type = TB_OBJECT_RELOC_SECTION; break; + + case IMAGE_REL_AMD64_REL32: + case IMAGE_REL_AMD64_REL32_1: + case IMAGE_REL_AMD64_REL32_2: + case IMAGE_REL_AMD64_REL32_3: + case IMAGE_REL_AMD64_REL32_4: + case IMAGE_REL_AMD64_REL32_5: + dst_relocs[j].type = TB_OBJECT_RELOC_REL32; + break; + + default: tb_todo(); + } + + if (src_relocs[j].Type >= IMAGE_REL_AMD64_REL32 && src_relocs[j].Type <= IMAGE_REL_AMD64_REL32_5) { + dst_relocs[j].addend = 4 + (src_relocs[j].Type - IMAGE_REL_AMD64_REL32); + } + + dst_relocs[j].symbol_index = src_relocs[j].SymbolTableIndex; + dst_relocs[j].virtual_address = src_relocs[j].VirtualAddress; + } + + out_sec->relocations = dst_relocs; + } + + // Parse virtual region + out_sec->virtual_address = sec->virtual_address; + out_sec->virtual_size = sec->misc.virtual_size; + + // Read raw data (if applies) + if (sec->raw_data_size) { + assert(sec->raw_data_pos + sec->raw_data_size < file.length); + out_sec->raw_data = (TB_Slice){ sec->raw_data_size, &file.data[sec->raw_data_pos] }; + } + + return true; +} + +TB_ObjectSymbolType classify_symbol_type(uint16_t st_class) { + switch (st_class) { + case 2: return TB_OBJECT_SYMBOL_EXTERN; + case 3: return TB_OBJECT_SYMBOL_STATIC; + case 6: return TB_OBJECT_SYMBOL_STATIC; + case 0x68: return TB_OBJECT_SYMBOL_SECTION; + case 0x69: return TB_OBJECT_SYMBOL_WEAK_EXTERN; + default: return TB_OBJECT_SYMBOL_UNKNOWN; + } +} + +size_t tb_coff_parse_symbol(TB_COFF_Parser* restrict parser, size_t i, TB_ObjectSymbol* restrict out_sym) { + TB_Slice file = parser->file; + size_t symbol_offset = parser->symbol_table + (i * sizeof(COFF_Symbol)); + + if (file.length < symbol_offset + sizeof(COFF_Symbol)) { + return 0; + } + + COFF_Symbol* sym = (COFF_Symbol*) &file.data[symbol_offset]; + *out_sym = (TB_ObjectSymbol) { + .ordinal = i, + .type = classify_symbol_type(sym->storage_class), + .section_num = sym->section_number, + .value = sym->value + }; + + // Parse string table name stuff + if (sym->long_name[0] == 0) { + // string table access (read a cstring) + // TODO(NeGate): bounds check this + const uint8_t* data = &parser->string_table.data[sym->long_name[1]]; + out_sym->name = (TB_Slice){ strlen((const char*) data), data }; + } else { + // normal inplace string + size_t len = strlen((const char*) sym->short_name); + out_sym->name = (TB_Slice){ len, sym->short_name }; + } + + // TODO(NeGate): Process aux symbols + if (sym->aux_symbols_count) { + out_sym->extra = &sym[1]; + + // FOREACH_N(j, 0, sym->aux_symbols_count) {} + } + + return sym->aux_symbols_count + 1; +} + +#endif // TB_COFF_IMPL diff --git a/src/tilde/tb_elf.h b/src/tilde/tb_elf.h new file mode 100644 index 000000000..5260d7372 --- /dev/null +++ b/src/tilde/tb_elf.h @@ -0,0 +1,170 @@ +#ifndef TB_ELF_H +#define TB_ELF_H + +#include + +#define TB_EI_MAG0 0 +#define TB_EI_MAG1 1 +#define TB_EI_MAG2 2 +#define TB_EI_MAG3 3 +#define TB_EI_CLASS 4 /* Class of machine. */ +#define TB_EI_DATA 5 /* Data format. */ +#define TB_EI_VERSION 6 /* ELF format version. */ +#define TB_EI_OSABI 7 /* Operating system / ABI identification */ +#define TB_EI_ABIVERSION 8 /* ABI version */ +#define TB_OLD_EI_BRAND 8 /* Start of architecture identification. */ +#define TB_EI_PAD 9 /* Start of padding (per SVR4 ABI). */ +#define TB_EI_NIDENT 16 /* Size of e_ident array. */ + +/* Values for e_type. */ +#define TB_ET_NONE 0 /* Unknown type. */ +#define TB_ET_REL 1 /* Relocatable. */ +#define TB_ET_EXEC 2 /* Executable. */ +#define TB_ET_DYN 3 /* Shared object. */ +#define TB_ET_CORE 4 /* Core file. */ +#define TB_ET_LOOS 0xfe00 /* First operating system specific. */ +#define TB_ET_HIOS 0xfeff /* Last operating system-specific. */ +#define TB_ET_LOPROC 0xff00 /* First processor-specific. */ +#define TB_ET_HIPROC 0xffff /* Last processor-specific. */ + +/* Values for e_machine. */ +#define TB_EM_NONE 0 /* Unknown machine. */ +#define TB_EM_X86_64 62 /* Advanced Micro Devices x86-64 */ +#define TB_EM_AARCH64 183 /* AArch64 (64-bit ARM) */ + +/* sh_type */ +#define TB_SHT_NULL 0 /* inactive */ +#define TB_SHT_PROGBITS 1 /* program defined information */ +#define TB_SHT_SYMTAB 2 /* symbol table section */ +#define TB_SHT_STRTAB 3 /* string table section */ +#define TB_SHT_RELA 4 /* relocation section with addends */ +#define TB_SHT_NOBITS 8 /* no space section */ + +/* Flags for sh_flags. */ +#define TB_SHF_WRITE 0x1 /* Section contains writable data. */ +#define TB_SHF_ALLOC 0x2 /* Section occupies memory. */ +#define TB_SHF_EXECINSTR 0x4 /* Section contains instructions. */ +#define TB_SHF_MERGE 0x10 /* Section may be merged. */ +#define TB_SHF_STRINGS 0x20 /* Section contains strings. */ +#define TB_SHF_INFO_LINK 0x40 /* sh_info holds section index. */ +#define TB_SHF_LINK_ORDER 0x80 /* Special ordering requirements. */ +#define TB_SHF_OS_NONCONFORMING 0x100 /* OS-specific processing required. */ +#define TB_SHF_GROUP 0x200 /* Member of section group. */ +#define TB_SHF_TLS 0x400 /* Section contains TLS data. */ +#define TB_SHF_MASKOS 0x0ff00000 /* OS-specific semantics. */ +#define TB_SHF_MASKPROC 0xf0000000 /* Processor-specific semantics. */ + +/* Values for p_flags. */ +#define TB_PF_X 0x1 /* Executable. */ +#define TB_PF_W 0x2 /* Writable. */ +#define TB_PF_R 0x4 /* Readable. */ +#define TB_PF_MASKOS 0x0ff00000 /* Operating system-specific. */ +#define TB_PF_MASKPROC 0xf0000000 /* Processor-specific. */ + +/* Values for p_type. */ +#define TB_PT_NULL 0 /* Unused entry. */ +#define TB_PT_LOAD 1 /* Loadable segment. */ +#define TB_PT_DYNAMIC 2 /* Dynamic linking information segment. */ +#define TB_PT_INTERP 3 /* Pathname of interpreter. */ +#define TB_PT_NOTE 4 /* Auxiliary information. */ +#define TB_PT_SHLIB 5 /* Reserved (not used). */ +#define TB_PT_PHDR 6 /* Location of program header itself. */ +#define TB_PT_TLS 7 /* Thread local storage segment */ + +/* Values for relocation */ +typedef enum { + TB_ELF_X86_64_NONE = 0, + TB_ELF_X86_64_64 = 1, + TB_ELF_X86_64_PC32 = 2, + TB_ELF_X86_64_GOT32 = 3, + TB_ELF_X86_64_PLT32 = 4, + TB_ELF_X86_64_GOTPCREL = 9, +} TB_ELF_RelocType; + +// ST_TYPE +#define TB_ELF64_STT_NOTYPE 0 +#define TB_ELF64_STT_OBJECT 1 +#define TB_ELF64_STT_FUNC 2 +#define TB_ELF64_STT_SECTION 3 + +// ST_INFO +#define TB_ELF64_STB_LOCAL 0 +#define TB_ELF64_STB_GLOBAL 1 +#define TB_ELF64_STB_WEAK 2 + +/* Macros for accessing the fields of st_info. */ +#define TB_ELF64_ST_BIND(info) ((info) >> 4) +#define TB_ELF64_ST_TYPE(info) ((info) & 0xf) + +#define TB_ELF64_ST_INFO(b, t) (((b) << 4) | ((t) & 0xF)) + +#define TB_ELF64_R_SYM(i) ((i) >> 32u) +#define TB_ELF64_R_TYPE(i) ((i)&0xffffffffULL) +#define TB_ELF64_R_INFO(s, t) (((uint64_t)(s) << 32ULL) + ((uint64_t)(t) & 0xffffffffULL)) + +// http://web.mit.edu/freebsd/head/sys/sys/elf64.h +// https://cirosantilli.com/elf-hello-world#minimal-elf-file +// https://en.wikipedia.org/wiki/Executable_and_Linkable_Format +typedef struct { + uint8_t ident[16]; + uint16_t type; + uint16_t machine; + uint32_t version; + uint64_t entry; + uint64_t phoff; + uint64_t shoff; + uint32_t flags; + uint16_t ehsize; + uint16_t phentsize; + uint16_t phnum; + uint16_t shentsize; + uint16_t shnum; + uint16_t shstrndx; +} TB_Elf64_Ehdr; + +typedef struct { + uint32_t name; + uint32_t type; + uint64_t flags; + uint64_t addr; + uint64_t offset; + uint64_t size; + uint32_t link; + uint32_t info; + uint64_t addralign; + uint64_t entsize; +} TB_Elf64_Shdr; + +// Segment header for ELF64. +typedef struct { + uint32_t type; // Type of segment + uint32_t flags; // Segment flags + uint64_t offset; // File offset where segment is located, in bytes + uint64_t vaddr; // Virtual address of beginning of segment + uint64_t paddr; // Physical addr of beginning of segment (OS-specific) + uint64_t filesz; // Num. of bytes in file image of segment (may be zero) + uint64_t memsz; // Num. of bytes in mem image of segment (may be zero) + uint64_t align; // Segment alignment constraint +} TB_Elf64_Phdr; + +typedef struct { + uint32_t name; + uint8_t info; + uint8_t other; + uint16_t shndx; + uint64_t value; + uint64_t size; +} TB_Elf64_Sym; + +typedef struct { + uint64_t offset; + uint64_t info; + int64_t addend; +} TB_Elf64_Rela; + +typedef struct { + uint64_t offset; + uint64_t info; +} TB_Elf64_Rel; + +#endif /* TB_ELF_H */ diff --git a/src/tilde/tb_formats.h b/src/tilde/tb_formats.h new file mode 100644 index 000000000..1975d5848 --- /dev/null +++ b/src/tilde/tb_formats.h @@ -0,0 +1,132 @@ +// This handles the generalized executable/object format parsing stuff +#ifndef TB_OBJECT_H +#define TB_OBJECT_H + +#include +#include + +typedef enum { + TB_OBJECT_RELOC_NONE, // how? + + // Target independent + TB_OBJECT_RELOC_ADDR32, + TB_OBJECT_RELOC_ADDR64, // unsupported on 32bit platforms + TB_OBJECT_RELOC_SECREL, + TB_OBJECT_RELOC_SECTION, + + // COFF only + TB_OBJECT_RELOC_ADDR32NB, // Relative virtual address + + // x64 only + TB_OBJECT_RELOC_REL32, // relative 32bit displacement + + // Aarch64 only + TB_OBJECT_RELOC_BRANCH26, // 26bit displacement for B and BL instructions + TB_OBJECT_RELOC_REL21, // for ADR instructions + + // TODO(NeGate): fill in the rest of this later +} TB_ObjectRelocType; + +typedef struct { + TB_ObjectRelocType type; + uint32_t symbol_index; + size_t virtual_address; + size_t addend; +} TB_ObjectReloc; + +typedef enum { + TB_OBJECT_SYMBOL_UNKNOWN, + TB_OBJECT_SYMBOL_EXTERN, // exported + TB_OBJECT_SYMBOL_WEAK_EXTERN, // weak + TB_OBJECT_SYMBOL_IMPORT, // forward decl + TB_OBJECT_SYMBOL_STATIC, // local + TB_OBJECT_SYMBOL_SECTION, // local +} TB_ObjectSymbolType; + +typedef struct { + TB_ObjectSymbolType type; + int section_num; + + uint32_t ordinal; + uint32_t value; + + TB_Slice name; + + // for COFF, this is the auxillary + void* extra; + + // this is zeroed out by the loader and left for the user to do crap with + void* user_data; +} TB_ObjectSymbol; + +typedef struct { + TB_Slice name; + uint32_t flags; + + size_t virtual_address; + size_t virtual_size; + + // You can have a virtual size without having a raw + // data size, that's how the BSS section works + TB_Slice raw_data; + + size_t relocation_count; + TB_ObjectReloc* relocations; + + // this is zeroed out by the loader and left for the user to do crap with + void* user_data; +} TB_ObjectSection; + +typedef enum { + TB_OBJECT_FILE_UNKNOWN, + + TB_OBJECT_FILE_COFF, + TB_OBJECT_FILE_ELF64 +} TB_ObjectFileType; + +typedef struct { + TB_ObjectFileType type; + TB_Arch arch; + + TB_Slice name; + TB_Slice ar_name; + + size_t symbol_count; + TB_ObjectSymbol* symbols; + + size_t section_count; + TB_ObjectSection sections[]; +} TB_ObjectFile; + +//////////////////////////////// +// Archive parser +//////////////////////////////// +typedef struct { + TB_Slice name; + + // if import_name is empty, we're dealing with an object file + TB_Slice import_name; + uint16_t ordinal; + + TB_Slice content; +} TB_ArchiveEntry; + +typedef struct { + TB_Slice file; + size_t pos; + + size_t member_count; + uint32_t* members; + + size_t symbol_count; + uint16_t* symbols; + + TB_Slice strtbl; +} TB_ArchiveFileParser; + +// We do this to parse the header +bool tb_archive_parse(TB_Slice file, TB_ArchiveFileParser* restrict out_parser); +// After that we can enumerate any symbol entries to resolve imports +size_t tb_archive_parse_entries(TB_ArchiveFileParser* restrict parser, size_t i, size_t count, TB_ArchiveEntry* out_entry); + +#endif // TB_OBJECT_H diff --git a/src/tilde/tb_x64.h b/src/tilde/tb_x64.h new file mode 100644 index 000000000..5f93f6bdb --- /dev/null +++ b/src/tilde/tb_x64.h @@ -0,0 +1,90 @@ +#ifndef TB_X64_H +#define TB_X64_H + +#include +#include + +typedef enum { + // uses xmm registers for the reg array + TB_X86_INSTR_XMMREG = (1u << 0u), + + // r/m is a memory operand + TB_X86_INSTR_USE_MEMOP = (1u << 1u), + + // r/m is a rip-relative address (TB_X86_INSTR_USE_MEMOP is always set when this is set) + TB_X86_INSTR_USE_RIPMEM = (1u << 2u), + + // LOCK prefix is present + TB_X86_INSTR_LOCK = (1u << 3u), + + // uses a signed immediate + TB_X86_INSTR_IMMEDIATE = (1u << 4u), + + // absolute means it's using the 64bit immediate (cannot be applied while a memory operand is active) + TB_X86_INSTR_ABSOLUTE = (1u << 5u), + + // set if the r/m can be found on the right hand side + TB_X86_INSTR_DIRECTION = (1u << 6u), + + // uses the second data type because the instruction is weird like MOVSX or MOVZX + TB_X86_INSTR_TWO_DATA_TYPES = (1u << 7u) +} TB_X86_InstFlags; + +typedef enum { + TB_X86_SEGMENT_DEFAULT = 0, + + TB_X86_SEGMENT_ES, TB_X86_SEGMENT_CS, + TB_X86_SEGMENT_SS, TB_X86_SEGMENT_DS, + TB_X86_SEGMENT_GS, TB_X86_SEGMENT_FS, +} TB_X86_Segment; + +typedef enum { + TB_X86_TYPE_NONE = 0, + + TB_X86_TYPE_BYTE, // 1 + TB_X86_TYPE_WORD, // 2 + TB_X86_TYPE_DWORD, // 4 + TB_X86_TYPE_QWORD, // 8 + + TB_X86_TYPE_PBYTE, // int8 x 16 = 16 + TB_X86_TYPE_PWORD, // int16 x 8 = 16 + TB_X86_TYPE_PDWORD, // int32 x 4 = 16 + TB_X86_TYPE_PQWORD, // int64 x 2 = 16 + + TB_X86_TYPE_SSE_SS, // float32 x 1 = 4 + TB_X86_TYPE_SSE_SD, // float64 x 1 = 8 + TB_X86_TYPE_SSE_PS, // float32 x 4 = 16 + TB_X86_TYPE_SSE_PD, // float64 x 2 = 16 + + TB_X86_TYPE_XMMWORD, // the generic idea of them +} TB_X86_DataType; + +typedef struct { + int16_t type; + + // registers (there's 4 max taking up 4bit slots each) + uint16_t regs; + uint8_t flags; + + // bitpacking amirite + TB_X86_DataType data_type : 4; + TB_X86_DataType data_type2 : 4; + TB_X86_Segment segment : 4; + uint8_t length : 4; + + // memory operand + // X86_INSTR_USE_MEMOP + int32_t disp; + + // immediate operand + // imm for INSTR_IMMEDIATE + // abs for INSTR_ABSOLUTE + union { + int32_t imm; + uint64_t abs; + }; +} TB_X86_Inst; + +TB_X86_Inst tb_x86_disasm(size_t length, const uint8_t data[length]); + +#endif /* TB_X64_H */