mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-19 13:00:28 +00:00
Update Tilde for the new TB_Passes approach
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
//+private
|
||||
//+build windows
|
||||
package runtime
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
foreign import kernel32 "system:Kernel32.lib"
|
||||
|
||||
@(private="file")
|
||||
@@ -102,12 +99,12 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
|
||||
|
||||
allocated_mem: rawptr
|
||||
if old_ptr != nil {
|
||||
original_old_ptr := intrinsics.ptr_offset((^rawptr)(old_ptr), -1)^
|
||||
original_old_ptr := ([^]rawptr)(old_ptr)[-1]
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
|
||||
} else {
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
|
||||
}
|
||||
aligned_mem := rawptr(intrinsics.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
|
||||
aligned_mem := ([^]u8)(allocated_mem)[size_of(rawptr):]
|
||||
|
||||
ptr := uintptr(aligned_mem)
|
||||
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
|
||||
@@ -116,10 +113,10 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
aligned_mem = rawptr(aligned_ptr)
|
||||
intrinsics.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem
|
||||
aligned_mem = ([^]byte)(aligned_ptr)
|
||||
([^]rawptr)(aligned_mem)[-1] = allocated_mem
|
||||
|
||||
return byte_slice(aligned_mem, size), nil
|
||||
return aligned_mem[:size], nil
|
||||
}
|
||||
|
||||
_windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory := true) -> ([]byte, Allocator_Error) {
|
||||
@@ -129,7 +126,7 @@ _windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory
|
||||
|
||||
_windows_default_free :: proc "contextless" (ptr: rawptr) {
|
||||
if ptr != nil {
|
||||
heap_free(intrinsics.ptr_offset((^rawptr)(ptr), -1)^)
|
||||
heap_free(([^]rawptr)(ptr)[-1])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,11 @@ _INTEGER_DIGITS_VAR := _INTEGER_DIGITS
|
||||
when !ODIN_NO_RTTI {
|
||||
print_any_single :: proc "contextless" (arg: any) {
|
||||
x := arg
|
||||
if x.data == nil {
|
||||
print_string("nil")
|
||||
return
|
||||
}
|
||||
|
||||
if loc, ok := x.(Source_Code_Location); ok {
|
||||
print_caller_location(loc)
|
||||
return
|
||||
@@ -48,6 +53,7 @@ when !ODIN_NO_RTTI {
|
||||
case int: print_int(v)
|
||||
case uint: print_uint(v)
|
||||
case uintptr: print_uintptr(v)
|
||||
case rawptr: print_uintptr(uintptr(v))
|
||||
|
||||
case bool: print_string("true" if v else "false")
|
||||
case b8: print_string("true" if v else "false")
|
||||
@@ -58,7 +64,7 @@ when !ODIN_NO_RTTI {
|
||||
case:
|
||||
ti := type_info_of(x.id)
|
||||
#partial switch v in ti.variant {
|
||||
case Type_Info_Pointer:
|
||||
case Type_Info_Pointer, Type_Info_Multi_Pointer:
|
||||
print_uintptr((^uintptr)(x.data)^)
|
||||
return
|
||||
}
|
||||
@@ -67,7 +73,9 @@ when !ODIN_NO_RTTI {
|
||||
}
|
||||
}
|
||||
println_any :: proc "contextless" (args: ..any) {
|
||||
context = default_context()
|
||||
loop: for arg, i in args {
|
||||
assert(arg.id != nil)
|
||||
if i != 0 {
|
||||
print_string(" ")
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
#include "tilde.hpp"
|
||||
|
||||
|
||||
gb_global Slice<TB_Arena *> global_tb_arenas;
|
||||
gb_global Slice<TB_Arena> global_tb_arenas;
|
||||
|
||||
gb_internal TB_Arena *cg_arena(void) {
|
||||
return global_tb_arenas[current_thread_index()];
|
||||
return &global_tb_arenas[current_thread_index()];
|
||||
}
|
||||
|
||||
gb_internal void cg_global_arena_init(void) {
|
||||
global_tb_arenas = slice_make<TB_Arena *>(permanent_allocator(), global_thread_pool.threads.count);
|
||||
global_tb_arenas = slice_make<TB_Arena>(permanent_allocator(), global_thread_pool.threads.count);
|
||||
for_array(i, global_tb_arenas) {
|
||||
global_tb_arenas[i] = tb_default_arena();
|
||||
tb_arena_create(&global_tb_arenas[i], 2ull<<20);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,14 +426,15 @@ gb_internal cgModule *cg_module_create(Checker *c) {
|
||||
|
||||
map_init(&m->values);
|
||||
map_init(&m->symbols);
|
||||
|
||||
map_init(&m->file_id_map);
|
||||
|
||||
map_init(&m->debug_type_map);
|
||||
map_init(&m->proc_debug_type_map);
|
||||
map_init(&m->proc_proto_map);
|
||||
|
||||
map_init(&m->anonymous_proc_lits_map);
|
||||
map_init(&m->equal_procs);
|
||||
map_init(&m->hasher_procs);
|
||||
map_init(&m->map_get_procs);
|
||||
map_init(&m->map_set_procs);
|
||||
|
||||
array_init(&m->single_threaded_procedure_queue, heap_allocator());
|
||||
|
||||
@@ -456,6 +457,10 @@ gb_internal void cg_module_destroy(cgModule *m) {
|
||||
map_destroy(&m->proc_debug_type_map);
|
||||
map_destroy(&m->proc_proto_map);
|
||||
map_destroy(&m->anonymous_proc_lits_map);
|
||||
map_destroy(&m->equal_procs);
|
||||
map_destroy(&m->hasher_procs);
|
||||
map_destroy(&m->map_get_procs);
|
||||
map_destroy(&m->map_set_procs);
|
||||
|
||||
array_free(&m->single_threaded_procedure_queue);
|
||||
|
||||
@@ -751,6 +756,19 @@ gb_internal bool cg_generate_code(Checker *c, LinkerData *linker_data) {
|
||||
array_add(&procedures_to_generate, p);
|
||||
}
|
||||
}
|
||||
for (cgProcedure *p : procedures_to_generate) {
|
||||
cg_add_procedure_to_queue(p);
|
||||
}
|
||||
|
||||
if (!m->do_threading) {
|
||||
for (isize i = 0; i < m->single_threaded_procedure_queue.count; i++) {
|
||||
cgProcedure *p = m->single_threaded_procedure_queue[i];
|
||||
cg_procedure_generate(p);
|
||||
}
|
||||
}
|
||||
|
||||
thread_pool_wait();
|
||||
|
||||
{
|
||||
cgProcedure *p = cg_startup_runtime_proc;
|
||||
cg_procedure_begin(p);
|
||||
@@ -765,18 +783,6 @@ gb_internal bool cg_generate_code(Checker *c, LinkerData *linker_data) {
|
||||
cg_procedure_end(p);
|
||||
}
|
||||
|
||||
for (cgProcedure *p : procedures_to_generate) {
|
||||
cg_add_procedure_to_queue(p);
|
||||
}
|
||||
|
||||
if (!m->do_threading) {
|
||||
for (isize i = 0; i < m->single_threaded_procedure_queue.count; i++) {
|
||||
cgProcedure *p = m->single_threaded_procedure_queue[i];
|
||||
cg_procedure_generate(p);
|
||||
}
|
||||
}
|
||||
|
||||
thread_pool_wait();
|
||||
|
||||
|
||||
TB_DebugFormat debug_format = TB_DEBUGFMT_NONE;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#endif
|
||||
|
||||
#include "tilde/tb.h"
|
||||
#include "tilde/tb_arena.h"
|
||||
|
||||
#define TB_TYPE_F16 TB_DataType{ { TB_INT, 0, 16 } }
|
||||
#define TB_TYPE_I128 TB_DataType{ { TB_INT, 0, 128 } }
|
||||
@@ -230,6 +231,12 @@ struct cgModule {
|
||||
BlockingMutex anonymous_proc_lits_mutex;
|
||||
PtrMap<Ast *, cgProcedure *> anonymous_proc_lits_map;
|
||||
|
||||
RecursiveMutex generated_procs_mutex;
|
||||
PtrMap<Type *, cgProcedure *> equal_procs;
|
||||
PtrMap<Type *, cgProcedure *> hasher_procs;
|
||||
PtrMap<Type *, cgProcedure *> map_get_procs;
|
||||
PtrMap<Type *, cgProcedure *> map_set_procs;
|
||||
|
||||
|
||||
// NOTE(bill): no need to protect this with a mutex
|
||||
PtrMap<uintptr, TB_FileID> file_id_map; // Key: AstFile.id (i32 cast to uintptr)
|
||||
@@ -319,6 +326,7 @@ gb_internal cgValue cg_build_call_expr(cgProcedure *p, Ast *expr);
|
||||
|
||||
gb_internal void cg_build_return_stmt(cgProcedure *p, Slice<Ast *> const &return_results);
|
||||
gb_internal void cg_build_return_stmt_internal(cgProcedure *p, Slice<cgValue> const &results);
|
||||
gb_internal void cg_build_return_stmt_internal_single(cgProcedure *p, cgValue result);
|
||||
gb_internal void cg_build_range_stmt(cgProcedure *p, Ast *node);
|
||||
|
||||
gb_internal cgValue cg_find_value_from_entity(cgModule *m, Entity *e);
|
||||
@@ -341,6 +349,10 @@ gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *t);
|
||||
gb_internal cgValue cg_emit_comp_against_nil(cgProcedure *p, TokenKind op_kind, cgValue x);
|
||||
gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left, cgValue right);
|
||||
gb_internal cgValue cg_emit_arith(cgProcedure *p, TokenKind op, cgValue lhs, cgValue rhs, Type *type);
|
||||
gb_internal cgValue cg_emit_unary_arith(cgProcedure *p, TokenKind op, cgValue x, Type *type);
|
||||
|
||||
gb_internal cgProcedure *cg_equal_proc_for_type(cgModule *m, Type *type);
|
||||
|
||||
|
||||
gb_internal cgValue cg_emit_call(cgProcedure * p, cgValue value, Slice<cgValue> const &args);
|
||||
gb_internal cgValue cg_emit_runtime_call(cgProcedure *p, char const *name, Slice<cgValue> const &args);
|
||||
|
||||
106
src/tilde/tb.h
106
src/tilde/tb.h
@@ -15,20 +15,21 @@
|
||||
#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)
|
||||
#ifndef TB_API
|
||||
# ifdef __cplusplus
|
||||
# define TB_EXTERN extern "C"
|
||||
# else
|
||||
# define TB_API TB_EXTERN __declspec(dllexport)
|
||||
# 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
|
||||
#else
|
||||
# define TB_API TB_EXTERN
|
||||
#endif
|
||||
|
||||
// These are flags
|
||||
@@ -171,7 +172,6 @@ typedef enum 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
|
||||
@@ -406,8 +406,6 @@ typedef struct TB_Symbol {
|
||||
|
||||
typedef int TB_Reg;
|
||||
|
||||
#define TB_NULL_REG NULL
|
||||
|
||||
typedef struct TB_Node TB_Node;
|
||||
struct TB_Node {
|
||||
TB_NodeType type;
|
||||
@@ -586,29 +584,13 @@ typedef struct {
|
||||
|
||||
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.
|
||||
// defined in common/arena.h
|
||||
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_API TB_Arena* tb_default_arena(void);
|
||||
// 0 for default
|
||||
TB_API void tb_arena_create(TB_Arena* restrict arena, size_t chunk_size);
|
||||
TB_API void tb_arena_destroy(TB_Arena* restrict arena);
|
||||
TB_API bool tb_arena_is_empty(TB_Arena* arena);
|
||||
|
||||
////////////////////////////////
|
||||
// Module management
|
||||
@@ -651,8 +633,7 @@ struct TB_Assembly {
|
||||
// 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, bool emit_asm);
|
||||
TB_API void tb_output_print_asm(TB_FunctionOutput* out, FILE* fp);
|
||||
|
||||
TB_API uint8_t* tb_output_get_code(TB_FunctionOutput* out, size_t* out_length);
|
||||
|
||||
@@ -865,6 +846,9 @@ TB_API TB_DebugType* tb_debug_create_func(TB_Module* m, TB_CallingConv cc, size_
|
||||
|
||||
TB_API TB_DebugType* tb_debug_field_type(TB_DebugType* type);
|
||||
|
||||
TB_API size_t tb_debug_func_return_count(TB_DebugType* type);
|
||||
TB_API size_t tb_debug_func_param_count(TB_DebugType* type);
|
||||
|
||||
// you'll need to fill these if you make a function
|
||||
TB_API TB_DebugType** tb_debug_func_params(TB_DebugType* type);
|
||||
TB_API TB_DebugType** tb_debug_func_returns(TB_DebugType* type);
|
||||
@@ -895,9 +879,6 @@ 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, ptrdiff_t len, const char* name, TB_Linkage linkage, TB_ComdatType comdat);
|
||||
|
||||
@@ -1073,25 +1054,40 @@ TB_API void tb_inst_branch(TB_Function* f, TB_DataType dt, TB_Node* key, TB_Node
|
||||
TB_API void tb_inst_ret(TB_Function* f, size_t count, TB_Node** values);
|
||||
|
||||
////////////////////////////////
|
||||
// Optimizer
|
||||
// Passes
|
||||
////////////////////////////////
|
||||
// Function analysis, optimizations, and codegen are all part of this
|
||||
typedef struct TB_FuncOpt TB_FuncOpt;
|
||||
typedef struct TB_Passes TB_Passes;
|
||||
|
||||
// the arena is used to allocate the nodes
|
||||
TB_API TB_FuncOpt* tb_funcopt_enter(TB_Function* f, TB_Arena* arena);
|
||||
TB_API void tb_funcopt_exit(TB_FuncOpt* opt);
|
||||
// the arena is used to allocate the nodes while passes are being done.
|
||||
TB_API TB_Passes* tb_pass_enter(TB_Function* f, TB_Arena* arena);
|
||||
TB_API void tb_pass_exit(TB_Passes* opt);
|
||||
|
||||
TB_API bool tb_funcopt_peephole(TB_FuncOpt* opt);
|
||||
TB_API bool tb_funcopt_mem2reg(TB_FuncOpt* opt);
|
||||
TB_API bool tb_funcopt_loop(TB_FuncOpt* opt);
|
||||
// transformation passes:
|
||||
// peephole: runs most simple reductions on the code,
|
||||
// should be run after any bigger passes (it's incremental
|
||||
// so it's not that bad)
|
||||
//
|
||||
// mem2reg: lowers TB_LOCALs into SSA values, this makes more
|
||||
// data flow analysis possible on the code and allows to codegen
|
||||
// to place variables into registers.
|
||||
//
|
||||
// loop: NOT READY
|
||||
//
|
||||
TB_API bool tb_pass_peephole(TB_Passes* opt);
|
||||
TB_API bool tb_pass_mem2reg(TB_Passes* opt);
|
||||
TB_API bool tb_pass_loop(TB_Passes* opt);
|
||||
|
||||
// isn't an optimization, just does the name flat form of IR printing
|
||||
TB_API bool tb_funcopt_print(TB_FuncOpt* opt);
|
||||
// analysis
|
||||
// print: prints IR in a flattened text form.
|
||||
TB_API bool tb_pass_print(TB_Passes* opt);
|
||||
|
||||
TB_API void tb_funcopt_kill(TB_FuncOpt* opt, TB_Node* n);
|
||||
TB_API bool tb_funcopt_mark(TB_FuncOpt* opt, TB_Node* n);
|
||||
TB_API void tb_funcopt_mark_users(TB_FuncOpt* opt, TB_Node* n);
|
||||
// codegen
|
||||
TB_API TB_FunctionOutput* tb_pass_codegen(TB_Passes* opt, bool emit_asm);
|
||||
|
||||
TB_API void tb_pass_kill_node(TB_Passes* opt, TB_Node* n);
|
||||
TB_API bool tb_pass_mark(TB_Passes* opt, TB_Node* n);
|
||||
TB_API void tb_pass_mark_users(TB_Passes* opt, TB_Node* n);
|
||||
|
||||
////////////////////////////////
|
||||
// IR access
|
||||
@@ -1099,8 +1095,6 @@ TB_API void tb_funcopt_mark_users(TB_FuncOpt* opt, TB_Node* n);
|
||||
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);
|
||||
|
||||
|
||||
BIN
src/tilde/tb.lib
BIN
src/tilde/tb.lib
Binary file not shown.
76
src/tilde/tb_arena.h
Normal file
76
src/tilde/tb_arena.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef TB_API
|
||||
# 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
|
||||
#endif
|
||||
|
||||
enum {
|
||||
TB_ARENA_SMALL_CHUNK_SIZE = 4 * 1024,
|
||||
TB_ARENA_MEDIUM_CHUNK_SIZE = 512 * 1024,
|
||||
TB_ARENA_LARGE_CHUNK_SIZE = 2 * 1024 * 1024,
|
||||
|
||||
TB_ARENA_ALIGNMENT = 16,
|
||||
};
|
||||
|
||||
typedef struct TB_ArenaChunk TB_ArenaChunk;
|
||||
struct TB_ArenaChunk {
|
||||
TB_ArenaChunk* next;
|
||||
size_t pad;
|
||||
char data[];
|
||||
};
|
||||
|
||||
typedef struct TB_Arena {
|
||||
size_t chunk_size;
|
||||
TB_ArenaChunk* base;
|
||||
TB_ArenaChunk* top;
|
||||
|
||||
// top of the allocation space
|
||||
char* watermark;
|
||||
char* high_point; // &top->data[chunk_size]
|
||||
} TB_Arena;
|
||||
|
||||
typedef struct TB_ArenaSavepoint {
|
||||
TB_ArenaChunk* top;
|
||||
char* watermark;
|
||||
} TB_ArenaSavepoint;
|
||||
|
||||
#define TB_ARENA_FOR(it, arena) for (TB_ArenaChunk* it = (arena)->base; it != NULL; it = it->next)
|
||||
|
||||
#define TB_ARENA_ALLOC(arena, T) tb_arena_alloc(arena, sizeof(T))
|
||||
#define TB_ARENA_ARR_ALLOC(arena, count, T) tb_arena_alloc(arena, (count) * sizeof(T))
|
||||
|
||||
TB_API void tb_arena_create(TB_Arena* restrict arena, size_t chunk_size);
|
||||
TB_API void tb_arena_destroy(TB_Arena* restrict arena);
|
||||
|
||||
TB_API void* tb_arena_unaligned_alloc(TB_Arena* restrict arena, size_t size);
|
||||
TB_API void* tb_arena_alloc(TB_Arena* restrict arena, size_t size);
|
||||
|
||||
// asserts if ptr+size != watermark
|
||||
TB_API void tb_arena_pop(TB_Arena* restrict arena, void* ptr, size_t size);
|
||||
|
||||
// in case you wanna mix unaligned and aligned arenas
|
||||
TB_API void tb_arena_realign(TB_Arena* restrict arena);
|
||||
|
||||
TB_API bool tb_arena_is_empty(TB_Arena* arena);
|
||||
|
||||
// savepoints
|
||||
TB_API TB_ArenaSavepoint tb_arena_save(TB_Arena* arena);
|
||||
TB_API void tb_arena_restore(TB_Arena* arena, TB_ArenaSavepoint sp);
|
||||
|
||||
// resets to only having one chunk
|
||||
TB_API void tb_arena_clear(TB_Arena* arena);
|
||||
@@ -304,6 +304,42 @@ gb_internal cgValue cg_emit_byte_swap(cgProcedure *p, cgValue value, Type *end_t
|
||||
return cg_emit_transmute(p, value, end_type);
|
||||
}
|
||||
|
||||
gb_internal cgValue cg_emit_comp_records(cgProcedure *p, TokenKind op_kind, cgValue left, cgValue right, Type *type) {
|
||||
GB_ASSERT((is_type_struct(type) || is_type_union(type)) && is_type_comparable(type));
|
||||
cgValue left_ptr = cg_address_from_load_or_generate_local(p, left);
|
||||
cgValue right_ptr = cg_address_from_load_or_generate_local(p, right);
|
||||
cgValue res = {};
|
||||
if (type_size_of(type) == 0) {
|
||||
switch (op_kind) {
|
||||
case Token_CmpEq:
|
||||
return cg_const_bool(p, t_bool, true);
|
||||
case Token_NotEq:
|
||||
return cg_const_bool(p, t_bool, false);
|
||||
}
|
||||
GB_PANIC("invalid operator");
|
||||
}
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
if (is_type_simple_compare(type)) {
|
||||
// TODO(bill): Test to see if this is actually faster!!!!
|
||||
auto args = slice_make<cgValue>(temporary_allocator(), 3);
|
||||
args[0] = cg_emit_conv(p, left_ptr, t_rawptr);
|
||||
args[1] = cg_emit_conv(p, right_ptr, t_rawptr);
|
||||
args[2] = cg_const_int(p, t_int, type_size_of(type));
|
||||
res = cg_emit_runtime_call(p, "memory_equal", args);
|
||||
} else {
|
||||
cgProcedure *equal_proc = cg_equal_proc_for_type(p->module, type);
|
||||
cgValue value = cg_value(tb_inst_get_symbol_address(p->func, equal_proc->symbol), equal_proc->type);
|
||||
auto args = slice_make<cgValue>(temporary_allocator(), 2);
|
||||
args[0] = cg_emit_conv(p, left_ptr, t_rawptr);
|
||||
args[1] = cg_emit_conv(p, right_ptr, t_rawptr);
|
||||
res = cg_emit_call(p, value, args);
|
||||
}
|
||||
if (op_kind == Token_NotEq) {
|
||||
res = cg_emit_unary_arith(p, Token_Not, res, res.type);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left, cgValue right) {
|
||||
GB_ASSERT(gb_is_between(op_kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1));
|
||||
|
||||
@@ -440,13 +476,11 @@ gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left
|
||||
}
|
||||
|
||||
if ((is_type_struct(a) || is_type_union(a)) && is_type_comparable(a)) {
|
||||
GB_PANIC("TODO(bill): cg_compare_records");
|
||||
// return cg_compare_records(p, op_kind, left, right, a);
|
||||
return cg_emit_comp_records(p, op_kind, left, right, a);
|
||||
}
|
||||
|
||||
if ((is_type_struct(b) || is_type_union(b)) && is_type_comparable(b)) {
|
||||
GB_PANIC("TODO(bill): cg_compare_records");
|
||||
// return cg_compare_records(p, op_kind, left, right, b);
|
||||
return cg_emit_comp_records(p, op_kind, left, right, b);
|
||||
}
|
||||
|
||||
if (is_type_string(a)) {
|
||||
|
||||
@@ -368,22 +368,17 @@ gb_internal void cg_procedure_begin(cgProcedure *p) {
|
||||
gb_internal WORKER_TASK_PROC(cg_procedure_compile_worker_proc) {
|
||||
cgProcedure *p = cast(cgProcedure *)data;
|
||||
|
||||
bool emit_asm = false;
|
||||
TB_Passes *opt = tb_pass_enter(p->func, cg_arena());
|
||||
defer (tb_pass_exit(opt));
|
||||
|
||||
if (false &&
|
||||
string_starts_with(p->name, str_lit("bug@main"))) {
|
||||
TB_Arena *arena = cg_arena();
|
||||
TB_FuncOpt *opt = tb_funcopt_enter(p->func, arena);
|
||||
defer (tb_funcopt_exit(opt));
|
||||
|
||||
tb_funcopt_peephole(opt);
|
||||
tb_funcopt_mem2reg(opt);
|
||||
tb_funcopt_peephole(opt);
|
||||
|
||||
emit_asm = true;
|
||||
// optimization passes
|
||||
if (false) {
|
||||
tb_pass_peephole(opt);
|
||||
tb_pass_mem2reg(opt);
|
||||
tb_pass_peephole(opt);
|
||||
}
|
||||
|
||||
|
||||
bool emit_asm = false;
|
||||
if (
|
||||
// string_starts_with(p->name, str_lit("runtime@_windows_default_alloc_or_resize")) ||
|
||||
false
|
||||
@@ -391,12 +386,27 @@ gb_internal WORKER_TASK_PROC(cg_procedure_compile_worker_proc) {
|
||||
emit_asm = true;
|
||||
}
|
||||
|
||||
TB_FunctionOutput *output = tb_module_compile_function(p->module->mod, p->func, TB_ISEL_FAST, emit_asm);
|
||||
// emit ir
|
||||
if (
|
||||
// string_starts_with(p->name, str_lit("bug@main")) ||
|
||||
// p->name == str_lit("runtime@_windows_default_alloc_or_resize") ||
|
||||
false
|
||||
) { // IR Printing
|
||||
TB_Arena *arena = cg_arena();
|
||||
TB_Passes *passes = tb_pass_enter(p->func, arena);
|
||||
defer (tb_pass_exit(passes));
|
||||
|
||||
tb_pass_print(passes);
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
if (false) { // GraphViz printing
|
||||
tb_function_print(p->func, tb_default_print_callback, stdout);
|
||||
}
|
||||
|
||||
// compile
|
||||
TB_FunctionOutput *output = tb_pass_codegen(opt, emit_asm);
|
||||
if (emit_asm) {
|
||||
TB_Assembly *assembly = tb_output_get_asm(output);
|
||||
for (TB_Assembly *node = assembly; node != nullptr; node = node->next) {
|
||||
fprintf(stdout, "%.*s", cast(int)node->length, node->data);
|
||||
}
|
||||
tb_output_print_asm(output, stdout);
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
|
||||
@@ -427,27 +437,9 @@ gb_internal void cg_procedure_generate(cgProcedure *p) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
cg_procedure_begin(p);
|
||||
cg_build_stmt(p, p->body);
|
||||
cg_procedure_end(p);
|
||||
|
||||
|
||||
if (
|
||||
// string_starts_with(p->name, str_lit("runtime@_windows_default_alloc")) ||
|
||||
// p->name == str_lit("runtime@_windows_default_alloc_or_resize") ||
|
||||
false
|
||||
) { // IR Printing
|
||||
TB_Arena *arena = tb_default_arena();
|
||||
defer (arena->free(arena));
|
||||
TB_FuncOpt *opt = tb_funcopt_enter(p->func, arena);
|
||||
defer (tb_funcopt_exit(opt));
|
||||
tb_funcopt_print(opt);
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
if (false) { // GraphViz printing
|
||||
tb_function_print(p->func, tb_default_print_callback, stdout);
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal void cg_build_nested_proc(cgProcedure *p, AstProcLit *pd, Entity *e) {
|
||||
@@ -989,3 +981,95 @@ gb_internal cgValue cg_build_call_expr_internal(cgProcedure *p, Ast *expr) {
|
||||
|
||||
return cg_emit_call(p, value, call_args);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
gb_internal cgProcedure *cg_equal_proc_for_type(cgModule *m, Type *type) {
|
||||
type = base_type(type);
|
||||
GB_ASSERT(is_type_comparable(type));
|
||||
|
||||
mutex_lock(&m->generated_procs_mutex);
|
||||
defer (mutex_unlock(&m->generated_procs_mutex));
|
||||
|
||||
cgProcedure **found = map_get(&m->equal_procs, type);
|
||||
if (found) {
|
||||
return *found;
|
||||
}
|
||||
|
||||
static std::atomic<u32> proc_index;
|
||||
|
||||
char buf[32] = {};
|
||||
isize n = gb_snprintf(buf, 32, "__$equal%u", 1+proc_index.fetch_add(1));
|
||||
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
|
||||
String proc_name = make_string_c(str);
|
||||
|
||||
|
||||
cgProcedure *p = cg_procedure_create_dummy(m, proc_name, t_equal_proc);
|
||||
map_set(&m->equal_procs, type, p);
|
||||
|
||||
cg_procedure_begin(p);
|
||||
|
||||
TB_Node *x = tb_inst_param(p->func, 0);
|
||||
TB_Node *y = tb_inst_param(p->func, 1);
|
||||
GB_ASSERT(x->dt.type == TB_PTR);
|
||||
GB_ASSERT(y->dt.type == TB_PTR);
|
||||
|
||||
TB_DataType ret_dt = TB_PROTOTYPE_RETURNS(p->proto)->dt;
|
||||
|
||||
TB_Node *node_true = tb_inst_uint(p->func, ret_dt, true);
|
||||
TB_Node *node_false = tb_inst_uint(p->func, ret_dt, false);
|
||||
|
||||
TB_Node *same_ptr_region = cg_control_region(p, "same_ptr");
|
||||
TB_Node *diff_ptr_region = cg_control_region(p, "diff_ptr");
|
||||
|
||||
TB_Node *is_same_ptr = tb_inst_cmp_eq(p->func, x, y);
|
||||
tb_inst_if(p->func, is_same_ptr, same_ptr_region, diff_ptr_region);
|
||||
|
||||
tb_inst_set_control(p->func, same_ptr_region);
|
||||
tb_inst_ret(p->func, 1, &node_true);
|
||||
|
||||
tb_inst_set_control(p->func, diff_ptr_region);
|
||||
|
||||
Type *pt = alloc_type_pointer(type);
|
||||
cgValue lhs = cg_value(x, pt);
|
||||
cgValue rhs = cg_value(y, pt);
|
||||
|
||||
if (type->kind == Type_Struct) {
|
||||
type_set_offsets(type);
|
||||
|
||||
TB_Node *false_region = cg_control_region(p, "bfalse");
|
||||
cgValue res = cg_const_bool(p, t_bool, true);
|
||||
|
||||
for_array(i, type->Struct.fields) {
|
||||
TB_Node *next_region = cg_control_region(p, "btrue");
|
||||
|
||||
cgValue plhs = cg_emit_struct_ep(p, lhs, i);
|
||||
cgValue prhs = cg_emit_struct_ep(p, rhs, i);
|
||||
cgValue left = cg_emit_load(p, plhs);
|
||||
cgValue right = cg_emit_load(p, prhs);
|
||||
cgValue ok = cg_emit_comp(p, Token_CmpEq, left, right);
|
||||
|
||||
cg_emit_if(p, ok, next_region, false_region);
|
||||
|
||||
cg_emit_goto(p, next_region);
|
||||
tb_inst_set_control(p->func, next_region);
|
||||
}
|
||||
|
||||
tb_inst_ret(p->func, 1, &node_true);
|
||||
tb_inst_set_control(p->func, false_region);
|
||||
tb_inst_ret(p->func, 1, &node_false);
|
||||
|
||||
} else if (type->kind == Type_Union) {
|
||||
GB_PANIC("TODO(bill): union comparison");
|
||||
} else {
|
||||
cgValue left = cg_lvalue_addr(x, type);
|
||||
cgValue right = cg_lvalue_addr(y, type);
|
||||
cgValue ok = cg_emit_comp(p, Token_CmpEq, left, right);
|
||||
cg_build_return_stmt_internal_single(p, ok);
|
||||
}
|
||||
|
||||
cg_procedure_end(p);
|
||||
|
||||
return p;
|
||||
}
|
||||
@@ -1047,6 +1047,14 @@ gb_internal void cg_build_assign_stmt(cgProcedure *p, AstAssignStmt *as) {
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal void cg_build_return_stmt_internal_single(cgProcedure *p, cgValue result) {
|
||||
Slice<cgValue> results = {};
|
||||
results.data = &result;
|
||||
results.count = 1;
|
||||
cg_build_return_stmt_internal(p, results);
|
||||
}
|
||||
|
||||
|
||||
gb_internal void cg_build_return_stmt_internal(cgProcedure *p, Slice<cgValue> const &results) {
|
||||
TypeTuple *tuple = &p->type->Proc.results->Tuple;
|
||||
isize return_count = p->type->Proc.result_count;
|
||||
|
||||
Reference in New Issue
Block a user