mirror of
https://github.com/odin-lang/Odin.git
synced 2026-02-14 23:33:15 +00:00
Mock out instructions emit calls
This commit is contained in:
@@ -119,6 +119,7 @@ void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name
|
||||
void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint);
|
||||
void check_or_return_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_);
|
||||
|
||||
bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y);
|
||||
|
||||
void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") {
|
||||
auto results = did_you_mean_results(d);
|
||||
@@ -850,6 +851,14 @@ bool internal_check_is_assignable_to(Type *src, Type *dst) {
|
||||
return check_is_assignable_to(nullptr, &x, dst);
|
||||
}
|
||||
|
||||
bool internal_check_is_castable_to(Type *src, Type *dst) {
|
||||
Operand x = {};
|
||||
x.type = src;
|
||||
x.mode = Addressing_Value;
|
||||
return check_is_castable_to(nullptr, &x, dst);
|
||||
}
|
||||
|
||||
|
||||
AstPackage *get_package_of_type(Type *type) {
|
||||
for (;;) {
|
||||
if (type == nullptr) {
|
||||
|
||||
@@ -2182,9 +2182,6 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
|
||||
} else {
|
||||
Type *lt = left.type;
|
||||
Type *rt = right.type;
|
||||
|
||||
lt = left.type;
|
||||
rt = right.type;
|
||||
i64 ls = type_size_of(lt);
|
||||
i64 rs = type_size_of(rt);
|
||||
|
||||
|
||||
@@ -1,272 +1,5 @@
|
||||
#include "middle_end.hpp"
|
||||
|
||||
struct meGenerator {
|
||||
CheckerInfo *info;
|
||||
|
||||
Array<String> output_object_paths;
|
||||
Array<String> output_temp_paths;
|
||||
String output_base;
|
||||
String output_name;
|
||||
PtrMap<AstPackage *, meModule *> modules;
|
||||
meModule default_module;
|
||||
|
||||
PtrMap<Ast *, meProcedure *> anonymous_proc_lits;
|
||||
|
||||
std::atomic<u32> global_array_index;
|
||||
std::atomic<u32> global_generated_index;
|
||||
};
|
||||
|
||||
gb_internal meGenerator me_gen;
|
||||
|
||||
gb_global Arena global_me_arena = {};
|
||||
gbAllocator me_allocator() {
|
||||
return arena_allocator(&global_me_arena);
|
||||
}
|
||||
|
||||
#define me_new(TYPE) gb_alloc_item(me_allocator(), TYPE)
|
||||
|
||||
meValue me_value(meInstruction *instr) {
|
||||
meValue value = {meValue_Instruction};
|
||||
value.instr = instr;
|
||||
return value;
|
||||
}
|
||||
meValue me_value(ExactValue *constant) {
|
||||
meValue value = {meValue_ConstantValue};
|
||||
value.constant = constant;
|
||||
return value;
|
||||
}
|
||||
meValue me_value(meBlock *block) {
|
||||
meValue value = {meValue_Block};
|
||||
value.block = block;
|
||||
return value;
|
||||
}
|
||||
meValue me_value(meProcedure *proc) {
|
||||
meValue value = {meValue_Procedure};
|
||||
value.proc = proc;
|
||||
return value;
|
||||
}
|
||||
meValue me_value(meGlobalVariable *global) {
|
||||
meValue value = {meValue_GlobalVariable};
|
||||
value.global = global;
|
||||
return value;
|
||||
}
|
||||
meValue me_value(meParameter *param) {
|
||||
meValue value = {meValue_Parameter};
|
||||
value.param = param;
|
||||
return value;
|
||||
}
|
||||
|
||||
meModule *me_pkg_module(AstPackage *pkg) {
|
||||
if (pkg != nullptr) {
|
||||
auto *found = map_get(&me_gen.modules, pkg);
|
||||
if (found) {
|
||||
return *found;
|
||||
}
|
||||
}
|
||||
return &me_gen.default_module;
|
||||
}
|
||||
|
||||
|
||||
void me_add_entity(meModule *m, Entity *e, meValue val) {
|
||||
if (e != nullptr) {
|
||||
map_set(&m->values, e, val);
|
||||
}
|
||||
}
|
||||
void me_add_member(meModule *m, String const &name, meValue val) {
|
||||
if (name.len > 0) {
|
||||
string_map_set(&m->members, name, val);
|
||||
}
|
||||
}
|
||||
void me_add_member(meModule *m, StringHashKey const &key, meValue val) {
|
||||
string_map_set(&m->members, key, val);
|
||||
}
|
||||
void me_add_procedure_value(meModule *m, meProcedure *p) {
|
||||
if (p->entity != nullptr) {
|
||||
map_set(&m->procedure_values, p, p->entity);
|
||||
}
|
||||
string_map_set(&m->procedures, p->name, p);
|
||||
}
|
||||
|
||||
|
||||
void me_add_foreign_library_path(meModule *m, Entity *e) {
|
||||
if (e == nullptr) {
|
||||
return;
|
||||
}
|
||||
GB_ASSERT(e->kind == Entity_LibraryName);
|
||||
GB_ASSERT(e->flags & EntityFlag_Used);
|
||||
|
||||
for_array(i, e->LibraryName.paths) {
|
||||
String library_path = e->LibraryName.paths[i];
|
||||
if (library_path.len == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
for_array(path_index, m->foreign_library_paths) {
|
||||
String path = m->foreign_library_paths[path_index];
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
if (str_eq_ignore_case(path, library_path)) {
|
||||
#else
|
||||
if (str_eq(path, library_path)) {
|
||||
#endif
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
array_add(&m->foreign_library_paths, library_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String me_mangle_name(meModule *m, Entity *e) {
|
||||
String name = e->token.string;
|
||||
|
||||
AstPackage *pkg = e->pkg;
|
||||
GB_ASSERT_MSG(pkg != nullptr, "Missing package for '%.*s'", LIT(name));
|
||||
String pkgn = pkg->name;
|
||||
GB_ASSERT(!rune_is_digit(pkgn[0]));
|
||||
if (pkgn == "llvm") {
|
||||
pkgn = str_lit("llvm$");
|
||||
}
|
||||
|
||||
isize max_len = pkgn.len + 1 + name.len + 1;
|
||||
bool require_suffix_id = is_type_polymorphic(e->type, true);
|
||||
|
||||
if ((e->scope->flags & (ScopeFlag_File | ScopeFlag_Pkg)) == 0) {
|
||||
require_suffix_id = true;
|
||||
} else if (is_blank_ident(e->token)) {
|
||||
require_suffix_id = true;
|
||||
}if (e->flags & EntityFlag_NotExported) {
|
||||
require_suffix_id = true;
|
||||
}
|
||||
|
||||
if (require_suffix_id) {
|
||||
max_len += 21;
|
||||
}
|
||||
|
||||
char *new_name = gb_alloc_array(permanent_allocator(), char, max_len);
|
||||
isize new_name_len = gb_snprintf(
|
||||
new_name, max_len,
|
||||
"%.*s.%.*s", LIT(pkgn), LIT(name)
|
||||
);
|
||||
if (require_suffix_id) {
|
||||
char *str = new_name + new_name_len-1;
|
||||
isize len = max_len-new_name_len;
|
||||
isize extra = gb_snprintf(str, len, "-%llu", cast(unsigned long long)e->id);
|
||||
new_name_len += extra-1;
|
||||
}
|
||||
|
||||
String mangled_name = make_string((u8 const *)new_name, new_name_len-1);
|
||||
return mangled_name;
|
||||
}
|
||||
|
||||
String me_set_nested_type_name_ir_mangled_name(Entity *e, meProcedure *p) {
|
||||
// NOTE(bill, 2020-03-08): A polymorphic procedure may take a nested type declaration
|
||||
// and as a result, the declaration does not have time to determine what it should be
|
||||
|
||||
GB_ASSERT(e != nullptr && e->kind == Entity_TypeName);
|
||||
if (e->TypeName.ir_mangled_name.len != 0) {
|
||||
return e->TypeName.ir_mangled_name;
|
||||
}
|
||||
GB_ASSERT((e->scope->flags & ScopeFlag_File) == 0);
|
||||
|
||||
if (p == nullptr) {
|
||||
Entity *proc = nullptr;
|
||||
if (e->parent_proc_decl != nullptr) {
|
||||
proc = e->parent_proc_decl->entity;
|
||||
} else {
|
||||
Scope *scope = e->scope;
|
||||
while (scope != nullptr && (scope->flags & ScopeFlag_Proc) == 0) {
|
||||
scope = scope->parent;
|
||||
}
|
||||
GB_ASSERT(scope != nullptr);
|
||||
GB_ASSERT(scope->flags & ScopeFlag_Proc);
|
||||
proc = scope->procedure_entity;
|
||||
}
|
||||
GB_ASSERT(proc->kind == Entity_Procedure);
|
||||
if (proc->me_procedure != nullptr) {
|
||||
p = proc->me_procedure;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(bill): Generate a new name
|
||||
// parent_proc.name-guid
|
||||
String ts_name = e->token.string;
|
||||
|
||||
if (p != nullptr) {
|
||||
isize name_len = p->name.len + 1 + ts_name.len + 1 + 10 + 1;
|
||||
char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
|
||||
u32 guid = ++p->module->nested_type_name_guid;
|
||||
name_len = gb_snprintf(name_text, name_len, "%.*s.%.*s-%u", LIT(p->name), LIT(ts_name), guid);
|
||||
|
||||
String name = make_string(cast(u8 *)name_text, name_len-1);
|
||||
e->TypeName.ir_mangled_name = name;
|
||||
return name;
|
||||
} else {
|
||||
// NOTE(bill): a nested type be required before its parameter procedure exists. Just give it a temp name for now
|
||||
isize name_len = 9 + 1 + ts_name.len + 1 + 10 + 1;
|
||||
char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
|
||||
static u32 guid = 0;
|
||||
guid += 1;
|
||||
name_len = gb_snprintf(name_text, name_len, "_internal.%.*s-%u", LIT(ts_name), guid);
|
||||
|
||||
String name = make_string(cast(u8 *)name_text, name_len-1);
|
||||
e->TypeName.ir_mangled_name = name;
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String me_get_entity_name(meModule *m, Entity *e, String default_name) {
|
||||
if (e != nullptr && e->kind == Entity_TypeName && e->TypeName.ir_mangled_name.len != 0) {
|
||||
return e->TypeName.ir_mangled_name;
|
||||
}
|
||||
GB_ASSERT(e != nullptr);
|
||||
|
||||
if (e->pkg == nullptr) {
|
||||
return e->token.string;
|
||||
}
|
||||
|
||||
if (e->kind == Entity_TypeName && (e->scope->flags & ScopeFlag_File) == 0) {
|
||||
return me_set_nested_type_name_ir_mangled_name(e, nullptr);
|
||||
}
|
||||
|
||||
String name = {};
|
||||
|
||||
bool no_name_mangle = false;
|
||||
|
||||
if (e->kind == Entity_Variable) {
|
||||
bool is_foreign = e->Variable.is_foreign;
|
||||
bool is_export = e->Variable.is_export;
|
||||
no_name_mangle = e->Variable.link_name.len > 0 || is_foreign || is_export;
|
||||
if (e->Variable.link_name.len > 0) {
|
||||
return e->Variable.link_name;
|
||||
}
|
||||
} else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) {
|
||||
return e->Procedure.link_name;
|
||||
} else if (e->kind == Entity_Procedure && e->Procedure.is_export) {
|
||||
no_name_mangle = true;
|
||||
}
|
||||
|
||||
if (!no_name_mangle) {
|
||||
name = me_mangle_name(m, e);
|
||||
}
|
||||
if (name.len == 0) {
|
||||
name = e->token.string;
|
||||
}
|
||||
|
||||
if (e->kind == Entity_TypeName) {
|
||||
e->TypeName.ir_mangled_name = name;
|
||||
} else if (e->kind == Entity_Procedure) {
|
||||
e->Procedure.link_name = name;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
#include "middle_end_core.cpp"
|
||||
|
||||
|
||||
void me_module_init(meModule *m, Checker *c) {
|
||||
@@ -402,7 +135,7 @@ meProcedure *me_procedure_create(meModule *m, Entity *entity, bool ignore_body)
|
||||
Type *pt = base_type(entity->type);
|
||||
GB_ASSERT(pt->kind == Type_Proc);
|
||||
|
||||
p->type = entity->type;
|
||||
p->type = base_type(entity->type);
|
||||
p->type_expr = decl->type_expr;
|
||||
p->body = pl->body;
|
||||
switch (pl->inlining) {
|
||||
@@ -485,32 +218,6 @@ meProcedure *me_procedure_create(meModule *m, Entity *entity, bool ignore_body)
|
||||
return p;
|
||||
}
|
||||
|
||||
meBlock *me_block_create(meProcedure *p, char const *name) {
|
||||
auto *b = me_new(meBlock);
|
||||
b->scope = p->curr_scope;
|
||||
b->scope_index = p->scope_index;
|
||||
|
||||
b->preds.allocator = heap_allocator();
|
||||
b->succs.allocator = heap_allocator();
|
||||
|
||||
array_add(&p->blocks, b);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
void me_block_start(meProcedure *p, meBlock *b) {
|
||||
p->curr_block = b;
|
||||
}
|
||||
|
||||
void me_build_stmt(meProcedure *p, Ast *stmt) {
|
||||
|
||||
}
|
||||
|
||||
meContextData *me_push_context_onto_stack_from_implicit_parameter(meProcedure *p) {
|
||||
// TODO(bill): me_push_context_onto_stack_from_implicit_parameter
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void me_procedure_body_begin(meProcedure *p) {
|
||||
DeclInfo *decl = decl_info_of_entity(p->entity);
|
||||
|
||||
@@ -9,7 +9,31 @@ struct meValue;
|
||||
struct meProcedure;
|
||||
struct meBlock;
|
||||
struct meInstruction;
|
||||
struct meConstant;
|
||||
struct meGlobalVariable;
|
||||
struct meParameter;
|
||||
|
||||
|
||||
enum meValueKind : u8 {
|
||||
meValue_Invalid = 0,
|
||||
meValue_Instruction,
|
||||
meValue_ConstantValue,
|
||||
meValue_Block,
|
||||
meValue_Procedure,
|
||||
meValue_GlobalVariable,
|
||||
meValue_Parameter,
|
||||
};
|
||||
struct meValue {
|
||||
meValueKind kind;
|
||||
union {
|
||||
meInstruction *instr;
|
||||
meConstant *constant;
|
||||
meBlock *block;
|
||||
meProcedure *proc;
|
||||
meGlobalVariable *global;
|
||||
meParameter *param;
|
||||
};
|
||||
};
|
||||
|
||||
enum meOpKind : u8 {
|
||||
meOp_Invalid = 0,
|
||||
@@ -22,7 +46,9 @@ enum meOpKind : u8 {
|
||||
meOp_Phi,
|
||||
|
||||
// Unary Operators
|
||||
meOp_FNeg,
|
||||
meOp_Neg,
|
||||
meOp_LogicalNot,
|
||||
meOp_BitwiseNot,
|
||||
|
||||
// Binary Arithmetic Operators
|
||||
meOp_Add,
|
||||
@@ -43,7 +69,11 @@ enum meOpKind : u8 {
|
||||
meOp_Alloca,
|
||||
meOp_Load,
|
||||
meOp_Store,
|
||||
meOp_UnalignedLoad,
|
||||
meOp_UnalignedStore,
|
||||
meOp_GetElementPtr,
|
||||
meOp_PtrOffset,
|
||||
meOp_PtrSub,
|
||||
|
||||
// Cast Operators
|
||||
meOp_Cast,
|
||||
@@ -56,6 +86,7 @@ enum meOpKind : u8 {
|
||||
meOp_LtEq,
|
||||
meOp_Gt,
|
||||
meOp_GtEq,
|
||||
|
||||
meOp_Min,
|
||||
meOp_Max,
|
||||
|
||||
@@ -69,16 +100,20 @@ enum meOpKind : u8 {
|
||||
meOp_ExtractValue,
|
||||
meOp_Swizzle,
|
||||
|
||||
meOp_Alias, // alias of another value
|
||||
|
||||
// Atomics
|
||||
meOp_Fence,
|
||||
meOp_AtomicXchg,
|
||||
meOp_AtomicCmpXchg,
|
||||
};
|
||||
|
||||
enum meInstructionFlags : u16 {
|
||||
meInstructionFlag_Volatile = 1<<0,
|
||||
meInstructionFlag_AtomicRMW = 1<<1,
|
||||
meInstructionFlag_ForceInline = 1<<2,
|
||||
meInstructionFlag_ForceNoInline = 1<<3,
|
||||
meInstructionFlag_Volatile = 1<<0,
|
||||
meInstructionFlag_AtomicRMW = 1<<1,
|
||||
meInstructionFlag_ForceInline = 1<<2,
|
||||
meInstructionFlag_ForceNoInline = 1<<3,
|
||||
meInstructionFlag_HasSideEffects = 1<<4,
|
||||
};
|
||||
|
||||
enum meAtomicOrderingKind : u8 {
|
||||
@@ -110,14 +145,19 @@ struct meInstruction {
|
||||
u16 alignment;
|
||||
u16 uses;
|
||||
|
||||
Type * type;
|
||||
Type * type;
|
||||
meProcedure *parent;
|
||||
TokenPos pos;
|
||||
TokenPos pos;
|
||||
|
||||
meValue *operands[me_INSTRUCTION_MAX_ARG_COUNT];
|
||||
isize operand_count;
|
||||
meValue ops[me_INSTRUCTION_MAX_ARG_COUNT];
|
||||
isize op_count;
|
||||
|
||||
Slice<meValue> *extra_operands; // non-null if used
|
||||
Slice<meValue> *extra_ops; // non-null if used
|
||||
};
|
||||
|
||||
struct meConstant {
|
||||
ExactValue value;
|
||||
Type *type;
|
||||
};
|
||||
|
||||
struct meBlock {
|
||||
@@ -165,32 +205,10 @@ struct meGlobalVariable {
|
||||
};
|
||||
|
||||
struct meParameter {
|
||||
String name;
|
||||
Entity * entity;
|
||||
String name;
|
||||
Entity * entity;
|
||||
meProcedure *parent;
|
||||
i32 uses;
|
||||
};
|
||||
|
||||
|
||||
enum meValueKind : u8 {
|
||||
meValue_Invalid = 0,
|
||||
meValue_Instruction,
|
||||
meValue_ConstantValue,
|
||||
meValue_Block,
|
||||
meValue_Procedure,
|
||||
meValue_GlobalVariable,
|
||||
meValue_Parameter,
|
||||
};
|
||||
struct meValue {
|
||||
meValueKind kind;
|
||||
union {
|
||||
meInstruction *instr;
|
||||
ExactValue *constant;
|
||||
meBlock *block;
|
||||
meProcedure *proc;
|
||||
meGlobalVariable *global;
|
||||
meParameter *param;
|
||||
};
|
||||
i32 uses;
|
||||
};
|
||||
|
||||
enum meAddrKind : u32 {
|
||||
@@ -360,11 +378,12 @@ String me_get_entity_name(meModule *m, Entity *e, String default_name = {});
|
||||
meProcedure *me_procedure_create(meModule *m, Entity *entity, bool ignore_body=false);
|
||||
|
||||
|
||||
|
||||
|
||||
meValue me_value(meInstruction *instr);
|
||||
meValue me_value(ExactValue *constant);
|
||||
meValue me_value(meConstant *constant);
|
||||
meValue me_value(meBlock *block);
|
||||
meValue me_value(meProcedure *proc);
|
||||
meValue me_value(meGlobalVariable *global);
|
||||
meValue me_value(meParameter *param);
|
||||
|
||||
|
||||
meValue me_emit_conv(meProcedure *p, meValue value, Type *type);
|
||||
|
||||
925
src/middle_end_core.cpp
Normal file
925
src/middle_end_core.cpp
Normal file
@@ -0,0 +1,925 @@
|
||||
|
||||
struct meGenerator {
|
||||
CheckerInfo *info;
|
||||
|
||||
Array<String> output_object_paths;
|
||||
Array<String> output_temp_paths;
|
||||
String output_base;
|
||||
String output_name;
|
||||
PtrMap<AstPackage *, meModule *> modules;
|
||||
meModule default_module;
|
||||
|
||||
PtrMap<Ast *, meProcedure *> anonymous_proc_lits;
|
||||
|
||||
std::atomic<u32> global_array_index;
|
||||
std::atomic<u32> global_generated_index;
|
||||
};
|
||||
|
||||
gb_internal meGenerator me_gen;
|
||||
|
||||
gb_global Arena global_me_arena = {};
|
||||
gbAllocator me_allocator() {
|
||||
return arena_allocator(&global_me_arena);
|
||||
}
|
||||
|
||||
#define me_new(TYPE) gb_alloc_item(me_allocator(), TYPE)
|
||||
|
||||
meValue me_value(meInstruction *instr) {
|
||||
meValue value = {meValue_Instruction};
|
||||
value.instr = instr;
|
||||
return value;
|
||||
}
|
||||
meValue me_value(meConstant *constant) {
|
||||
meValue value = {meValue_ConstantValue};
|
||||
value.constant = constant;
|
||||
return value;
|
||||
}
|
||||
meValue me_value(meBlock *block) {
|
||||
meValue value = {meValue_Block};
|
||||
value.block = block;
|
||||
return value;
|
||||
}
|
||||
meValue me_value(meProcedure *proc) {
|
||||
meValue value = {meValue_Procedure};
|
||||
value.proc = proc;
|
||||
return value;
|
||||
}
|
||||
meValue me_value(meGlobalVariable *global) {
|
||||
meValue value = {meValue_GlobalVariable};
|
||||
value.global = global;
|
||||
return value;
|
||||
}
|
||||
meValue me_value(meParameter *param) {
|
||||
meValue value = {meValue_Parameter};
|
||||
value.param = param;
|
||||
return value;
|
||||
}
|
||||
|
||||
bool me_is_const(meValue value) {
|
||||
return value.kind == meValue_ConstantValue;
|
||||
}
|
||||
|
||||
bool me_is_const_nil(meValue value) {
|
||||
if (value.kind == meValue_ConstantValue) {
|
||||
return value.constant->value.kind == ExactValue_Invalid;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
meValue me_use(meValue const &value) {
|
||||
switch (value.kind) {
|
||||
case meValue_Instruction: value.instr->uses += 1; break;
|
||||
case meValue_Procedure: value.proc->uses += 1; break;
|
||||
case meValue_GlobalVariable: value.global->uses += 1; break;
|
||||
case meValue_Parameter: value.param->uses += 1; break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
i32 me_uses(meValue const &value) {
|
||||
switch (value.kind) {
|
||||
case meValue_Instruction: return value.instr->uses;
|
||||
case meValue_Procedure: return value.proc->uses;
|
||||
case meValue_GlobalVariable: return value.global->uses;
|
||||
case meValue_Parameter: return value.param->uses;
|
||||
}
|
||||
GB_PANIC("invalid value to call on uses");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void me_remove_use(meValue const &value) {
|
||||
switch (value.kind) {
|
||||
case meValue_Instruction: GB_ASSERT(value.instr->uses > 0); value.instr->uses -= 1; break;
|
||||
case meValue_Procedure: GB_ASSERT(value.proc->uses > 0); value.proc->uses -= 1; break;
|
||||
case meValue_GlobalVariable: GB_ASSERT(value.global->uses > 0); value.global->uses -= 1; break;
|
||||
case meValue_Parameter: GB_ASSERT(value.param->uses > 0); value.param->uses -= 1; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
meAddr me_addr(meValue value) {
|
||||
meAddr addr = {};
|
||||
addr.kind = meAddr_Default;
|
||||
addr.addr = value;
|
||||
return addr;
|
||||
}
|
||||
|
||||
Type *me_type(meValue value) {
|
||||
switch (value.kind) {
|
||||
case meValue_Invalid:
|
||||
return nullptr;
|
||||
case meValue_Instruction:
|
||||
return value.instr->type;
|
||||
case meValue_ConstantValue:
|
||||
return value.constant->type;
|
||||
case meValue_Block:
|
||||
return nullptr;
|
||||
case meValue_Procedure:
|
||||
return value.proc->type;
|
||||
case meValue_GlobalVariable:
|
||||
return value.global->type;
|
||||
case meValue_Parameter:
|
||||
return value.param->entity->type;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
meModule *me_pkg_module(AstPackage *pkg) {
|
||||
if (pkg != nullptr) {
|
||||
auto *found = map_get(&me_gen.modules, pkg);
|
||||
if (found) {
|
||||
return *found;
|
||||
}
|
||||
}
|
||||
return &me_gen.default_module;
|
||||
}
|
||||
|
||||
|
||||
void me_add_entity(meModule *m, Entity *e, meValue val) {
|
||||
if (e != nullptr) {
|
||||
map_set(&m->values, e, val);
|
||||
}
|
||||
}
|
||||
void me_add_member(meModule *m, String const &name, meValue val) {
|
||||
if (name.len > 0) {
|
||||
string_map_set(&m->members, name, val);
|
||||
}
|
||||
}
|
||||
void me_add_member(meModule *m, StringHashKey const &key, meValue val) {
|
||||
string_map_set(&m->members, key, val);
|
||||
}
|
||||
void me_add_procedure_value(meModule *m, meProcedure *p) {
|
||||
if (p->entity != nullptr) {
|
||||
map_set(&m->procedure_values, p, p->entity);
|
||||
}
|
||||
string_map_set(&m->procedures, p->name, p);
|
||||
}
|
||||
|
||||
|
||||
void me_add_foreign_library_path(meModule *m, Entity *e) {
|
||||
if (e == nullptr) {
|
||||
return;
|
||||
}
|
||||
GB_ASSERT(e->kind == Entity_LibraryName);
|
||||
GB_ASSERT(e->flags & EntityFlag_Used);
|
||||
|
||||
for_array(i, e->LibraryName.paths) {
|
||||
String library_path = e->LibraryName.paths[i];
|
||||
if (library_path.len == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
for_array(path_index, m->foreign_library_paths) {
|
||||
String path = m->foreign_library_paths[path_index];
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
if (str_eq_ignore_case(path, library_path)) {
|
||||
#else
|
||||
if (str_eq(path, library_path)) {
|
||||
#endif
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
array_add(&m->foreign_library_paths, library_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String me_mangle_name(meModule *m, Entity *e) {
|
||||
String name = e->token.string;
|
||||
|
||||
AstPackage *pkg = e->pkg;
|
||||
GB_ASSERT_MSG(pkg != nullptr, "Missing package for '%.*s'", LIT(name));
|
||||
String pkgn = pkg->name;
|
||||
GB_ASSERT(!rune_is_digit(pkgn[0]));
|
||||
if (pkgn == "llvm") {
|
||||
pkgn = str_lit("llvm$");
|
||||
}
|
||||
|
||||
isize max_len = pkgn.len + 1 + name.len + 1;
|
||||
bool require_suffix_id = is_type_polymorphic(e->type, true);
|
||||
|
||||
if ((e->scope->flags & (ScopeFlag_File | ScopeFlag_Pkg)) == 0) {
|
||||
require_suffix_id = true;
|
||||
} else if (is_blank_ident(e->token)) {
|
||||
require_suffix_id = true;
|
||||
}if (e->flags & EntityFlag_NotExported) {
|
||||
require_suffix_id = true;
|
||||
}
|
||||
|
||||
if (require_suffix_id) {
|
||||
max_len += 21;
|
||||
}
|
||||
|
||||
char *new_name = gb_alloc_array(permanent_allocator(), char, max_len);
|
||||
isize new_name_len = gb_snprintf(
|
||||
new_name, max_len,
|
||||
"%.*s.%.*s", LIT(pkgn), LIT(name)
|
||||
);
|
||||
if (require_suffix_id) {
|
||||
char *str = new_name + new_name_len-1;
|
||||
isize len = max_len-new_name_len;
|
||||
isize extra = gb_snprintf(str, len, "-%llu", cast(unsigned long long)e->id);
|
||||
new_name_len += extra-1;
|
||||
}
|
||||
|
||||
String mangled_name = make_string((u8 const *)new_name, new_name_len-1);
|
||||
return mangled_name;
|
||||
}
|
||||
|
||||
String me_set_nested_type_name_ir_mangled_name(Entity *e, meProcedure *p) {
|
||||
// NOTE(bill, 2020-03-08): A polymorphic procedure may take a nested type declaration
|
||||
// and as a result, the declaration does not have time to determine what it should be
|
||||
|
||||
GB_ASSERT(e != nullptr && e->kind == Entity_TypeName);
|
||||
if (e->TypeName.ir_mangled_name.len != 0) {
|
||||
return e->TypeName.ir_mangled_name;
|
||||
}
|
||||
GB_ASSERT((e->scope->flags & ScopeFlag_File) == 0);
|
||||
|
||||
if (p == nullptr) {
|
||||
Entity *proc = nullptr;
|
||||
if (e->parent_proc_decl != nullptr) {
|
||||
proc = e->parent_proc_decl->entity;
|
||||
} else {
|
||||
Scope *scope = e->scope;
|
||||
while (scope != nullptr && (scope->flags & ScopeFlag_Proc) == 0) {
|
||||
scope = scope->parent;
|
||||
}
|
||||
GB_ASSERT(scope != nullptr);
|
||||
GB_ASSERT(scope->flags & ScopeFlag_Proc);
|
||||
proc = scope->procedure_entity;
|
||||
}
|
||||
GB_ASSERT(proc->kind == Entity_Procedure);
|
||||
if (proc->me_procedure != nullptr) {
|
||||
p = proc->me_procedure;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(bill): Generate a new name
|
||||
// parent_proc.name-guid
|
||||
String ts_name = e->token.string;
|
||||
|
||||
if (p != nullptr) {
|
||||
isize name_len = p->name.len + 1 + ts_name.len + 1 + 10 + 1;
|
||||
char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
|
||||
u32 guid = ++p->module->nested_type_name_guid;
|
||||
name_len = gb_snprintf(name_text, name_len, "%.*s.%.*s-%u", LIT(p->name), LIT(ts_name), guid);
|
||||
|
||||
String name = make_string(cast(u8 *)name_text, name_len-1);
|
||||
e->TypeName.ir_mangled_name = name;
|
||||
return name;
|
||||
} else {
|
||||
// NOTE(bill): a nested type be required before its parameter procedure exists. Just give it a temp name for now
|
||||
isize name_len = 9 + 1 + ts_name.len + 1 + 10 + 1;
|
||||
char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
|
||||
static u32 guid = 0;
|
||||
guid += 1;
|
||||
name_len = gb_snprintf(name_text, name_len, "_internal.%.*s-%u", LIT(ts_name), guid);
|
||||
|
||||
String name = make_string(cast(u8 *)name_text, name_len-1);
|
||||
e->TypeName.ir_mangled_name = name;
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String me_get_entity_name(meModule *m, Entity *e, String default_name) {
|
||||
if (e != nullptr && e->kind == Entity_TypeName && e->TypeName.ir_mangled_name.len != 0) {
|
||||
return e->TypeName.ir_mangled_name;
|
||||
}
|
||||
GB_ASSERT(e != nullptr);
|
||||
|
||||
if (e->pkg == nullptr) {
|
||||
return e->token.string;
|
||||
}
|
||||
|
||||
if (e->kind == Entity_TypeName && (e->scope->flags & ScopeFlag_File) == 0) {
|
||||
return me_set_nested_type_name_ir_mangled_name(e, nullptr);
|
||||
}
|
||||
|
||||
String name = {};
|
||||
|
||||
bool no_name_mangle = false;
|
||||
|
||||
if (e->kind == Entity_Variable) {
|
||||
bool is_foreign = e->Variable.is_foreign;
|
||||
bool is_export = e->Variable.is_export;
|
||||
no_name_mangle = e->Variable.link_name.len > 0 || is_foreign || is_export;
|
||||
if (e->Variable.link_name.len > 0) {
|
||||
return e->Variable.link_name;
|
||||
}
|
||||
} else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) {
|
||||
return e->Procedure.link_name;
|
||||
} else if (e->kind == Entity_Procedure && e->Procedure.is_export) {
|
||||
no_name_mangle = true;
|
||||
}
|
||||
|
||||
if (!no_name_mangle) {
|
||||
name = me_mangle_name(m, e);
|
||||
}
|
||||
if (name.len == 0) {
|
||||
name = e->token.string;
|
||||
}
|
||||
|
||||
if (e->kind == Entity_TypeName) {
|
||||
e->TypeName.ir_mangled_name = name;
|
||||
} else if (e->kind == Entity_Procedure) {
|
||||
e->Procedure.link_name = name;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
meInstruction *me_last_instruction(meBlock *block) {
|
||||
if (block && block->instructions.count > 0) {
|
||||
return block->instructions[block->instructions.count-1];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool me_is_instruction_terminator(meOpKind op) {
|
||||
switch (op) {
|
||||
case meOp_Unreachable:
|
||||
case meOp_Return:
|
||||
case meOp_Jump:
|
||||
case meOp_CondJump:
|
||||
case meOp_Switch:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool me_is_last_instruction_terminator(meBlock *b) {
|
||||
meInstruction *instr = me_last_instruction(b);
|
||||
return instr != nullptr && me_is_instruction_terminator(instr->op);
|
||||
}
|
||||
|
||||
meBlock *me_block_create(meProcedure *p, char const *name) {
|
||||
auto *b = me_new(meBlock);
|
||||
b->scope = p->curr_scope;
|
||||
b->scope_index = p->scope_index;
|
||||
|
||||
b->preds.allocator = heap_allocator();
|
||||
b->succs.allocator = heap_allocator();
|
||||
|
||||
array_add(&p->blocks, b);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
void me_block_add_edge(meBlock *from, meBlock *to) {
|
||||
if (!me_is_last_instruction_terminator(from)) {
|
||||
array_add(&from->succs, to);
|
||||
array_add(&to->preds, from);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void me_block_start(meProcedure *p, meBlock *b) {
|
||||
p->curr_block = b;
|
||||
}
|
||||
|
||||
void me_build_stmt(meProcedure *p, Ast *stmt) {
|
||||
|
||||
}
|
||||
|
||||
meContextData *me_push_context_onto_stack_from_implicit_parameter(meProcedure *p) {
|
||||
// TODO(bill): me_push_context_onto_stack_from_implicit_parameter
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
meInstruction *me_create_instruction(meProcedure *p, meOpKind op) {
|
||||
meInstruction *instr = me_new(meInstruction);
|
||||
instr->op = op;
|
||||
|
||||
GB_ASSERT(p->curr_block != nullptr);
|
||||
|
||||
if (!me_is_last_instruction_terminator(p->curr_block)) {
|
||||
if (instr->parent != nullptr) {
|
||||
GB_ASSERT(instr->parent == p);
|
||||
} else {
|
||||
instr->parent = p;
|
||||
}
|
||||
array_add(&p->curr_block->instructions, instr);
|
||||
}
|
||||
|
||||
return instr;
|
||||
}
|
||||
|
||||
void me_emit_unreachable(meProcedure *p) {
|
||||
me_create_instruction(p, meOp_Unreachable);
|
||||
}
|
||||
|
||||
void me_emit_return_empty(meProcedure *p) {
|
||||
GB_ASSERT(p->type->Proc.result_count == 0);
|
||||
|
||||
me_create_instruction(p, meOp_Return);
|
||||
}
|
||||
void me_emit_return(meProcedure *p, meValue value) {
|
||||
auto *instr = me_create_instruction(p, meOp_Return);
|
||||
if (value.kind != meValue_Invalid) {
|
||||
instr->ops[0] = me_use(value);
|
||||
instr->op_count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void me_emit_jump(meProcedure *p, meBlock *block) {
|
||||
auto *jump = me_create_instruction(p, meOp_Jump);
|
||||
jump->ops[0] = me_use(me_value(block));
|
||||
jump->op_count = 1;
|
||||
|
||||
me_block_add_edge(p->curr_block, block);
|
||||
}
|
||||
|
||||
void me_emit_cond_jump(meProcedure *p, meValue cond, meBlock *true_block, meBlock *false_block) {
|
||||
if (p->curr_block == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cond.kind == meValue_ConstantValue) {
|
||||
GB_ASSERT(cond.constant->value.kind == ExactValue_Bool);
|
||||
if (cond.constant->value.value_bool) {
|
||||
me_emit_jump(p, true_block);
|
||||
} else {
|
||||
me_emit_jump(p, false_block);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto *jump = me_create_instruction(p, meOp_CondJump);
|
||||
jump->ops[0] = me_use(cond);
|
||||
jump->ops[1] = me_use(me_value(true_block));
|
||||
jump->ops[2] = me_use(me_value(false_block));
|
||||
jump->op_count = 3;
|
||||
|
||||
me_block_add_edge(p->curr_block, true_block);
|
||||
me_block_add_edge(p->curr_block, false_block);
|
||||
}
|
||||
|
||||
|
||||
meValue me_emit_neg(meProcedure *p, meValue value) {
|
||||
Type *type = me_type(value);
|
||||
GB_ASSERT(type != nullptr);
|
||||
type = base_type(core_array_type(type));
|
||||
GB_ASSERT(is_type_numeric(type));
|
||||
|
||||
auto *n = me_create_instruction(p, meOp_Neg);
|
||||
n->type = me_type(value);
|
||||
n->ops[0] = me_use(value);
|
||||
n->op_count = 1;
|
||||
|
||||
return me_value(n);
|
||||
}
|
||||
|
||||
meValue me_emit_logical_not(meProcedure *p, meValue value) {
|
||||
Type *type = me_type(value);
|
||||
GB_ASSERT(type != nullptr);
|
||||
type = base_type(core_array_type(type));
|
||||
GB_ASSERT(is_type_boolean(type));
|
||||
|
||||
auto *n = me_create_instruction(p, meOp_LogicalNot);
|
||||
n->type = me_type(value);
|
||||
n->ops[0] = me_use(value);
|
||||
n->op_count = 1;
|
||||
|
||||
return me_value(n);
|
||||
}
|
||||
|
||||
meValue me_emit_bitwise_not(meProcedure *p, meValue value) {
|
||||
Type *type = me_type(value);
|
||||
GB_ASSERT(type != nullptr);
|
||||
type = base_type(core_array_type(type));
|
||||
GB_ASSERT(is_type_integer(type) || is_type_boolean(type) || is_type_bit_set(type));
|
||||
|
||||
auto *n = me_create_instruction(p, meOp_BitwiseNot);
|
||||
n->type = me_type(value);
|
||||
n->ops[0] = me_use(value);
|
||||
n->op_count = 1;
|
||||
|
||||
return me_value(n);
|
||||
}
|
||||
|
||||
|
||||
meValue me_emit_binary_op(meProcedure *p, meOpKind op, meValue left, meValue right, Type *type) {
|
||||
GB_ASSERT(type != nullptr);
|
||||
|
||||
switch (op) {
|
||||
case meOp_Add:
|
||||
case meOp_Sub:
|
||||
case meOp_Mul:
|
||||
case meOp_Div:
|
||||
case meOp_Rem:
|
||||
case meOp_Shl:
|
||||
case meOp_LShr:
|
||||
case meOp_AShr:
|
||||
case meOp_And:
|
||||
case meOp_Or:
|
||||
case meOp_Xor:
|
||||
case meOp_Eq:
|
||||
case meOp_NotEq:
|
||||
case meOp_Lt:
|
||||
case meOp_LtEq:
|
||||
case meOp_Gt:
|
||||
case meOp_GtEq:
|
||||
case meOp_Min:
|
||||
case meOp_Max:
|
||||
break;
|
||||
default:
|
||||
GB_PANIC("Unsupported binary op");
|
||||
}
|
||||
|
||||
auto *b = me_create_instruction(p, op);
|
||||
b->type = type;
|
||||
b->ops[0] = me_use(left);
|
||||
b->ops[1] = me_use(right);
|
||||
b->op_count = 2;
|
||||
|
||||
return me_value(b);
|
||||
}
|
||||
|
||||
|
||||
meAddr me_add_local(meProcedure *p, Type *type, Entity *e, bool zero_init) {
|
||||
meInstruction *var = nullptr;
|
||||
meBlock *curr_block = p->curr_block;
|
||||
p->curr_block = p->decl_block;
|
||||
var = me_create_instruction(p, meOp_Alloca);
|
||||
p->curr_block = curr_block;
|
||||
|
||||
var->type = alloc_type_pointer(type);
|
||||
|
||||
u16 alignment = cast(u16)type_align_of(type);
|
||||
if (is_type_matrix(type)) {
|
||||
alignment *= 2; // NOTE(bill): Just in case
|
||||
}
|
||||
var->alignment = alignment;
|
||||
|
||||
// TODO(bill): ZERO me_add_local
|
||||
|
||||
return me_addr(me_value(var));
|
||||
}
|
||||
|
||||
|
||||
meValue me_emit_inline_alloca(meProcedure *p, Type *type, u16 alignment) {
|
||||
meInstruction *var = me_create_instruction(p, meOp_Alloca);
|
||||
var->type = alloc_type_pointer(type);
|
||||
var->alignment = alignment;
|
||||
return me_value(var);
|
||||
}
|
||||
|
||||
meValue me_emit_load_with_alignment_hint(meProcedure *p, meValue const &value, u16 alignment) {
|
||||
GB_ASSERT(alignment == 0 || gb_is_power_of_two(alignment));
|
||||
Type *type = me_type(value);
|
||||
GB_ASSERT(type != nullptr);
|
||||
GB_ASSERT(is_type_pointer(type));
|
||||
|
||||
meInstruction *v = me_create_instruction(p, meOp_Load);
|
||||
v->type = type;
|
||||
v->ops[0] = me_use(value);
|
||||
v->op_count = 1;
|
||||
v->alignment = alignment;
|
||||
|
||||
return me_value(v);
|
||||
}
|
||||
|
||||
meValue me_emit_load(meProcedure *p, meValue const &value) {
|
||||
return me_emit_load_with_alignment_hint(p, value, 0);
|
||||
}
|
||||
|
||||
meValue me_emit_unaligned_load_with_alignment_hint(meProcedure *p, meValue const &value, u16 alignment) {
|
||||
GB_ASSERT(alignment == 0 || gb_is_power_of_two(alignment));
|
||||
Type *type = me_type(value);
|
||||
GB_ASSERT(type != nullptr);
|
||||
GB_ASSERT(is_type_pointer(type));
|
||||
|
||||
meInstruction *v = me_create_instruction(p, meOp_UnalignedLoad);
|
||||
v->type = type;
|
||||
v->ops[0] = me_use(value);
|
||||
v->op_count = 1;
|
||||
v->alignment = alignment;
|
||||
|
||||
return me_value(v);
|
||||
}
|
||||
|
||||
meValue me_emit_unaligned_load(meProcedure *p, meValue const &value) {
|
||||
return me_emit_unaligned_load_with_alignment_hint(p, value, 0);
|
||||
}
|
||||
|
||||
void me_emit_store(meProcedure *p, meValue dst, meValue src) {
|
||||
Type *dst_type = me_type(dst);
|
||||
GB_ASSERT(is_type_pointer(dst_type));
|
||||
src = me_emit_conv(p, src, type_deref(dst_type));
|
||||
|
||||
meInstruction *v = me_create_instruction(p, meOp_Store);
|
||||
v->ops[0] = me_use(dst);
|
||||
v->ops[1] = me_use(src);
|
||||
v->op_count = 2;
|
||||
}
|
||||
|
||||
void me_emit_unaligned_store(meProcedure *p, meValue dst, meValue src) {
|
||||
Type *dst_type = me_type(dst);
|
||||
GB_ASSERT(is_type_pointer(dst_type));
|
||||
src = me_emit_conv(p, src, type_deref(dst_type));
|
||||
|
||||
meInstruction *v = me_create_instruction(p, meOp_UnalignedStore);
|
||||
v->ops[0] = me_use(dst);
|
||||
v->ops[1] = me_use(src);
|
||||
v->op_count = 2;
|
||||
}
|
||||
|
||||
|
||||
meValue me_const_int(i64 value, Type *type) {
|
||||
meConstant *constant = me_new(meConstant);
|
||||
constant->value = exact_value_i64(value);
|
||||
constant->type = type;
|
||||
return me_value(constant);
|
||||
}
|
||||
|
||||
meValue me_emit_gep(meProcedure *p, meValue value, isize index) {
|
||||
Type *ptr_type = me_type(value);
|
||||
GB_ASSERT(is_type_pointer(ptr_type));
|
||||
Type *t = base_type(type_deref(ptr_type));
|
||||
gb_unused(t);
|
||||
GB_ASSERT(index >= 0);
|
||||
|
||||
Type *type = nullptr; // TODO(bill): type determination
|
||||
meInstruction *v = me_create_instruction(p, meOp_GetElementPtr);
|
||||
v->type = type;
|
||||
v->ops[0] = me_use(value);
|
||||
v->ops[1] = me_use(me_const_int(cast(i64)index, t_int));
|
||||
v->op_count = 2;
|
||||
|
||||
return me_value(v);
|
||||
}
|
||||
|
||||
meValue me_emit_ev(meProcedure *p, meValue value, isize index) {
|
||||
Type *value_type = me_type(value);
|
||||
GB_ASSERT(!is_type_pointer(value_type));
|
||||
Type *t = base_type(value_type);
|
||||
gb_unused(t);
|
||||
GB_ASSERT(index >= 0);
|
||||
|
||||
Type *type = nullptr; // TODO(bill): type determination
|
||||
meInstruction *v = me_create_instruction(p, meOp_ExtractValue);
|
||||
v->type = type;
|
||||
v->ops[0] = me_use(value);
|
||||
v->ops[1] = me_use(me_const_int(cast(i64)index, t_int));
|
||||
v->op_count = 2;
|
||||
|
||||
return me_value(v);
|
||||
}
|
||||
|
||||
|
||||
meValue me_emit_ptr_offset(meProcedure *p, meValue value, meValue offset) {
|
||||
Type *ptr_type = me_type(value);
|
||||
GB_ASSERT(is_type_pointer(ptr_type));
|
||||
|
||||
meInstruction *v = me_create_instruction(p, meOp_PtrOffset);
|
||||
v->type = ptr_type;
|
||||
v->ops[0] = me_use(value);
|
||||
v->ops[1] = me_use(offset);
|
||||
v->op_count = 2;
|
||||
|
||||
return me_value(v);
|
||||
}
|
||||
|
||||
meValue me_emit_ptr_sub(meProcedure *p, meValue ptr0, meValue ptr1) {
|
||||
Type *p0 = me_type(ptr0);
|
||||
Type *p1 = me_type(ptr1);
|
||||
GB_ASSERT(is_type_pointer(p0));
|
||||
GB_ASSERT(is_type_pointer(p1));
|
||||
GB_ASSERT(are_types_identical(p0, p1));
|
||||
|
||||
meInstruction *v = me_create_instruction(p, meOp_PtrSub);
|
||||
v->type = t_int;
|
||||
v->ops[0] = me_use(ptr0);
|
||||
v->ops[1] = me_use(ptr1);
|
||||
v->op_count = 2;
|
||||
|
||||
return me_value(v);
|
||||
}
|
||||
|
||||
meValue me_emit_conv(meProcedure *p, meValue value, Type *dst_type) {
|
||||
Type *src_type = me_type(value);
|
||||
GB_ASSERT(src_type != nullptr);
|
||||
GB_ASSERT(dst_type != nullptr);
|
||||
|
||||
if (are_types_identical(src_type, dst_type)) {
|
||||
return value;
|
||||
}
|
||||
GB_ASSERT(internal_check_is_castable_to(src_type, dst_type));
|
||||
|
||||
|
||||
meInstruction *v = me_create_instruction(p, meOp_Cast);
|
||||
v->type = dst_type;
|
||||
v->ops[0] = me_use(value);
|
||||
v->op_count = 1;
|
||||
|
||||
return me_value(v);
|
||||
}
|
||||
|
||||
meValue me_emit_transmute(meProcedure *p, meValue value, Type *dst_type) {
|
||||
Type *src_type = me_type(value);
|
||||
GB_ASSERT(src_type != nullptr);
|
||||
GB_ASSERT(dst_type != nullptr);
|
||||
|
||||
if (are_types_identical(src_type, dst_type)) {
|
||||
return value;
|
||||
}
|
||||
i64 src_sz = type_size_of(src_type);
|
||||
i64 dst_sz = type_size_of(dst_type);
|
||||
GB_ASSERT_MSG(src_sz == dst_sz, "%lld != %lld", cast(long long)src_sz, cast(long long)dst_sz);
|
||||
|
||||
meInstruction *v = me_create_instruction(p, meOp_Transmute);
|
||||
v->type = dst_type;
|
||||
v->ops[0] = me_use(value);
|
||||
v->op_count = 1;
|
||||
|
||||
return me_value(v);
|
||||
}
|
||||
|
||||
meValue me_emit_comp_against_nil(meProcedure *p, meOpKind op, meValue value) {
|
||||
switch (op) {
|
||||
case meOp_Eq:
|
||||
case meOp_NotEq:
|
||||
break;
|
||||
default:
|
||||
GB_PANIC("Invalid comparison against nil op");
|
||||
}
|
||||
|
||||
// TODO(bill): me_emit_comp_against_nil
|
||||
meInstruction *v = me_create_instruction(p, op);
|
||||
v->type = t_untyped_bool;
|
||||
v->ops[0] = me_use(value);
|
||||
v->op_count = 1;
|
||||
|
||||
return me_value(v);
|
||||
}
|
||||
|
||||
|
||||
meValue me_emit_comp(meProcedure *p, meOpKind op, meValue left, meValue right) {
|
||||
switch (op) {
|
||||
case meOp_Eq:
|
||||
case meOp_NotEq:
|
||||
case meOp_Lt:
|
||||
case meOp_LtEq:
|
||||
case meOp_Gt:
|
||||
case meOp_GtEq:
|
||||
break;
|
||||
default:
|
||||
GB_PANIC("Invalid comparison op");
|
||||
}
|
||||
|
||||
Type *lt = me_type(left);
|
||||
Type *rt = me_type(right);
|
||||
|
||||
Type *a = core_type(lt);
|
||||
Type *b = core_type(rt);
|
||||
|
||||
meValue nil_check = {};
|
||||
if (is_type_untyped_nil(lt)) {
|
||||
nil_check = me_emit_comp_against_nil(p, op, right);
|
||||
} else if (is_type_untyped_nil(rt)) {
|
||||
nil_check = me_emit_comp_against_nil(p, op, left);
|
||||
}
|
||||
if (nil_check.kind != meValue_Invalid) {
|
||||
return nil_check;
|
||||
}
|
||||
|
||||
|
||||
if (are_types_identical(a, b)) {
|
||||
// NOTE(bill): No need for a conversion
|
||||
} else if (me_is_const(left) || me_is_const_nil(left)) {
|
||||
left = me_emit_conv(p, left, rt);
|
||||
} else if (me_is_const(right) || me_is_const_nil(right)) {
|
||||
right = me_emit_conv(p, right, lt);
|
||||
} else {
|
||||
i64 ls = type_size_of(lt);
|
||||
i64 rs = type_size_of(rt);
|
||||
|
||||
// NOTE(bill): Quick heuristic, larger types are usually the target type
|
||||
if (ls < rs) {
|
||||
left = me_emit_conv(p, left, rt);
|
||||
} else if (ls > rs) {
|
||||
right = me_emit_conv(p, right, lt);
|
||||
} else {
|
||||
if (is_type_union(rt)) {
|
||||
left = me_emit_conv(p, left, rt);
|
||||
} else {
|
||||
right = me_emit_conv(p, right, lt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(bill): me_emit_comp
|
||||
|
||||
meInstruction *v = me_create_instruction(p, op);
|
||||
v->type = t_untyped_bool;
|
||||
v->ops[0] = me_use(left);
|
||||
v->ops[1] = me_use(right);
|
||||
v->op_count = 2;
|
||||
|
||||
return me_value(v);
|
||||
}
|
||||
|
||||
|
||||
|
||||
meValue me_emit_min(meProcedure *p, meValue left, meValue right) {
|
||||
Type *lt = me_type(left);
|
||||
Type *rt = me_type(right);
|
||||
GB_ASSERT(are_types_identical(lt, rt));
|
||||
Type *type = lt;
|
||||
GB_ASSERT(is_type_ordered(type) && (is_type_numeric(type) || is_type_string(type)));
|
||||
|
||||
// TODO(bill): optimization
|
||||
meInstruction *v = me_create_instruction(p, meOp_Min);
|
||||
v->type = type;
|
||||
v->ops[0] = me_use(left);
|
||||
v->ops[1] = me_use(right);
|
||||
v->op_count = 2;
|
||||
|
||||
return me_value(v);
|
||||
}
|
||||
|
||||
meValue me_emit_max(meProcedure *p, meValue left, meValue right) {
|
||||
Type *lt = me_type(left);
|
||||
Type *rt = me_type(right);
|
||||
GB_ASSERT(are_types_identical(lt, rt));
|
||||
Type *type = lt;
|
||||
GB_ASSERT(is_type_ordered(type) && (is_type_numeric(type) || is_type_string(type)));
|
||||
|
||||
// TODO(bill): optimization
|
||||
meInstruction *v = me_create_instruction(p, meOp_Max);
|
||||
v->type = type;
|
||||
v->ops[0] = me_use(left);
|
||||
v->ops[1] = me_use(right);
|
||||
v->op_count = 2;
|
||||
|
||||
return me_value(v);
|
||||
}
|
||||
|
||||
meValue me_emit_select(meProcedure *p, meValue cond, meValue left, meValue right) {
|
||||
GB_ASSERT(is_type_boolean(me_type(cond)));
|
||||
GB_ASSERT(are_types_identical(me_type(left), me_type(right)));
|
||||
|
||||
// TODO(bill): optimization
|
||||
meInstruction *v = me_create_instruction(p, meOp_Select);
|
||||
v->type = me_type(left);
|
||||
v->ops[0] = me_use(cond);
|
||||
v->ops[1] = me_use(left);
|
||||
v->ops[2] = me_use(right);
|
||||
v->op_count = 3;
|
||||
|
||||
return me_value(v);
|
||||
}
|
||||
|
||||
meValue me_emit_call(meProcedure *p, meValue proc, Slice<meValue> const &arguments, u16 instruction_flags) {
|
||||
GB_PANIC("TODO");
|
||||
return {};
|
||||
}
|
||||
meValue me_emit_built_call(meProcedure *p, BuiltinProcId id, Slice<meValue> const &arguments) {
|
||||
GB_PANIC("TODO");
|
||||
return {};
|
||||
}
|
||||
|
||||
meValue me_emit_swizzle(meProcedure *p, meValue value, Slice<i32> const &arguments) {
|
||||
GB_PANIC("TODO");
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
meValue me_emit_fence(meProcedure *p, meAtomicOrderingKind atomic_ordering) {
|
||||
GB_PANIC("TODO");
|
||||
return {};
|
||||
}
|
||||
|
||||
meValue me_emit_atomic_exchange(meProcedure *p, meValue left, meValue right, meAtomicOrderingKind atomic_ordering) {
|
||||
GB_PANIC("TODO");
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
meValue me_emit_atomic_compare_exchange(meProcedure *p, meValue ptr, meValue left, meValue right, meAtomicOrderingKind atomic_ordering) {
|
||||
GB_PANIC("TODO");
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
meValue me_emit_alias(meProcedure *p, meValue value) {
|
||||
if (value.kind == meValue_Instruction && value.instr->op == meOp_Alias) {
|
||||
return me_emit_alias(p, value.instr->ops[0]);
|
||||
}
|
||||
meInstruction *v = me_create_instruction(p, meOp_Alias);
|
||||
v->type = me_type(value);
|
||||
v->ops[0] = me_use(value);
|
||||
v->op_count = 1;
|
||||
return me_value(v);
|
||||
}
|
||||
Reference in New Issue
Block a user