diff options
| author | Ginger Bill <bill@gingerbill.org> | 2017-04-16 10:38:42 +0100 |
|---|---|---|
| committer | Ginger Bill <bill@gingerbill.org> | 2017-04-16 10:38:42 +0100 |
| commit | abb9930725c10d8bf8cb5560bca9afe6aad204e1 (patch) | |
| tree | 812ffced8e15d7e6615d4425b9670d6d4a3ece0c /src | |
| parent | 169310a9f61f3d9a9d420fd3a73fe3115d995595 (diff) | |
IR emit C ABI compatible types for calling conventions (Only for x86/amd64 like processors at the moment)
Diffstat (limited to 'src')
| -rw-r--r-- | src/check_expr.c | 58 | ||||
| -rw-r--r-- | src/ir.c | 101 | ||||
| -rw-r--r-- | src/ir_print.c | 54 | ||||
| -rw-r--r-- | src/types.c | 2 |
4 files changed, 176 insertions, 39 deletions
diff --git a/src/check_expr.c b/src/check_expr.c index 1dba6dbbd..9d4eb0905 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -1014,6 +1014,50 @@ Type *check_get_results(Checker *c, Scope *scope, AstNode *_results) { return tuple; } +Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type) { + Type *new_type = original_type; + // NOTE(bill): Changing the passing parameter value type is to match C's ABI + // IMPORTANT TODO(bill): This only matches the ABI on MSVC at the moment + Type *bt = core_type(original_type); + switch (bt->kind) { + // Okay to pass by value + // Especially the only Odin types + case Type_Basic: break; + case Type_Pointer: break; + case Type_Proc: break; // NOTE(bill): Just a pointer + + // Odin only types + case Type_Slice: + case Type_DynamicArray: + case Type_Map: + break; + + // Odin specific + case Type_Array: + case Type_Vector: + // Could be in C too + case Type_Record: { + i64 size = type_size_of(a, original_type); + switch (8*size) { + case 8: new_type = t_u8; break; + case 16: new_type = t_u16; break; + case 32: new_type = t_u32; break; + case 64: new_type = t_u64; break; + default: + // NOTE(bill): It could be an empty struct that is passed + // and if that is the case, no need to pass by pointer + // (I think..) + if (size > 0) { + new_type = make_type_pointer(a, original_type); + } + break; + } + } break; + } + + return new_type; +} + void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) { ast_node(pt, ProcType, proc_type_node); @@ -1034,6 +1078,20 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) { type->Proc.result_count = result_count; type->Proc.variadic = variadic; type->Proc.calling_convention = pt->calling_convention; + + + type->Proc.abi_compat_params = gb_alloc_array(c->allocator, Type *, param_count); + for (isize i = 0; i < param_count; i++) { + Type *original_type = type->Proc.params->Tuple.variables[i]->type; + Type *new_type = type_to_abi_compat_param_type(c->allocator, original_type); + type->Proc.abi_compat_params[i] = new_type; + } + + // NOTE(bill): The types are the same + type->Proc.abi_compat_results = gb_alloc_array(c->allocator, Type *, result_count); + for (isize i = 0; i < result_count; i++) { + type->Proc.abi_compat_results[i] = type->Proc.results->Tuple.variables[i]->type; + } } @@ -351,11 +351,20 @@ typedef struct irValueGlobal { bool is_unnamed_addr; } irValueGlobal; + +typedef enum irParamPasskind { + irParamPass_Value, // Pass by value + irParamPass_Pointer, // Pass as a pointer rather than by value + irParamPass_Integer, // Pass as an integer of the same size +} irParamPasskind; + typedef struct irValueParam { - irProcedure *parent; - Entity * entity; - Type * type; - irValueArray referrers; + irParamPasskind kind; + irProcedure * parent; + Entity * entity; + Type * type; + Type * original_type; + irValueArray referrers; } irValueParam; typedef struct irValue { @@ -755,11 +764,23 @@ irValue *ir_value_global(gbAllocator a, Entity *e, irValue *value) { array_init(&v->Global.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here return v; } -irValue *ir_value_param(gbAllocator a, irProcedure *parent, Entity *e) { +irValue *ir_value_param(gbAllocator a, irProcedure *parent, Entity *e, Type *abi_type) { irValue *v = ir_alloc_value(a, irValue_Param); - v->Param.parent = parent; - v->Param.entity = e; - v->Param.type = e->type; + v->Param.kind = irParamPass_Value; + v->Param.parent = parent; + v->Param.entity = e; + v->Param.original_type = e->type; + v->Param.type = abi_type; + + if (abi_type != e->type) { + if (is_type_pointer(abi_type)) { + v->Param.kind = irParamPass_Pointer; + } else if (is_type_integer(abi_type)) { + v->Param.kind = irParamPass_Integer; + } else { + GB_PANIC("Invalid abi type pass kind"); + } + } array_init(&v->Param.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here return v; } @@ -1280,16 +1301,30 @@ irValue *ir_add_local_generated(irProcedure *proc, Type *type) { } -irValue *ir_add_param(irProcedure *proc, Entity *e, AstNode *expr) { - irValue *v = ir_value_param(proc->module->allocator, proc, e); -#if 1 - irValue *l = ir_add_local(proc, e, expr); - ir_emit_store(proc, l, v); +irValue *ir_add_param(irProcedure *proc, Entity *e, AstNode *expr, Type *abi_type) { + irValue *v = ir_value_param(proc->module->allocator, proc, e, abi_type); + irValueParam *p = &v->Param; -#else - ir_module_add_value(proc->module, e, v); -#endif - return v; + switch (p->kind) { + case irParamPass_Value: { + irValue *l = ir_add_local(proc, e, expr); + ir_emit_store(proc, l, v); + return v; + } + case irParamPass_Pointer: { + ir_module_add_value(proc->module, e, v); + return ir_emit_load(proc, v); + } + case irParamPass_Integer: { + irValue *l = ir_add_local(proc, e, expr); + irValue *iptr = ir_emit_conv(proc, l, make_type_pointer(proc->module->allocator, p->type)); + ir_emit_store(proc, iptr, v); + return ir_emit_load(proc, l); + } + } + + GB_PANIC("Unreachable"); + return NULL; } @@ -1383,11 +1418,36 @@ irValue *ir_emit_comment(irProcedure *p, String text) { return ir_emit(p, ir_instr_comment(p, text)); } +irValue *ir_copy_value_to_ptr(irProcedure *proc, irValue *val) { + Type *t = ir_type(val); + irValue *ptr = ir_add_local_generated(proc, t); + ir_emit_store(proc, ptr, val); + return ptr; +} + +irValue *ir_emit_bitcast(irProcedure *proc, irValue *data, Type *type) { + return ir_emit(proc, ir_instr_conv(proc, irConv_bitcast, data, ir_type(data), type)); +} irValue *ir_emit_call(irProcedure *p, irValue *value, irValue **args, isize arg_count) { Type *pt = base_type(ir_type(value)); GB_ASSERT(pt->kind == Type_Proc); Type *results = pt->Proc.results; + + isize param_count = pt->Proc.param_count; + GB_ASSERT(param_count == arg_count); + for (isize i = 0; i < param_count; i++) { + Type *original_type = pt->Proc.params->Tuple.variables[i]->type; + Type *new_type = pt->Proc.abi_compat_params[i]; + if (original_type != new_type) { + if (is_type_pointer(new_type)) { + args[i] = ir_copy_value_to_ptr(p, args[i]); + } else if (is_type_integer(new_type)) { + args[i] = ir_emit_bitcast(p, args[i], new_type); + } + } + } + return ir_emit(p, ir_instr_call(p, value, args, arg_count, results)); } @@ -1475,9 +1535,7 @@ void ir_emit_startup_runtime(irProcedure *proc) { ir_emit(proc, ir_alloc_instr(proc, irInstr_StartupRuntime)); } -irValue *ir_emit_bitcast(irProcedure *proc, irValue *data, Type *type) { - return ir_emit(proc, ir_instr_conv(proc, irConv_bitcast, data, ir_type(data), type)); -} + irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index); @@ -6436,9 +6494,10 @@ void ir_begin_procedure_body(irProcedure *proc) { AstNode *name = field->names.e[q_index++]; Entity *e = params->variables[i]; + Type *abi_type = proc->type->Proc.abi_compat_params[i]; if (!str_eq(e->token.string, str_lit("")) && !str_eq(e->token.string, str_lit("_"))) { - irValue *param = ir_add_param(proc, e, name); + irValue *param = ir_add_param(proc, e, name, abi_type); array_add(&proc->params, param); } } diff --git a/src/ir_print.c b/src/ir_print.c index 560983537..a80cf73c9 100644 --- a/src/ir_print.c +++ b/src/ir_print.c @@ -135,6 +135,28 @@ void ir_print_encoded_global(irFileBuffer *f, String name, bool remove_prefix) { ir_print_escape_string(f, name, true); } +void ir_print_type(irFileBuffer *f, irModule *m, Type *t); + +void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) { + GB_ASSERT(is_type_proc(t)); + t = base_type(t); + isize result_count = t->Proc.result_count; + if (result_count == 0) { + ir_fprintf(f, "void"); + } else if (result_count == 1) { + ir_print_type(f, m, t->Proc.abi_compat_results[0]); + } else { + ir_fprintf(f, "{"); + for (isize i = 0; i < result_count; i++) { + if (i > 0) { + ir_fprintf(f, ", "); + } + ir_print_type(f, m, t->Proc.abi_compat_results[i]); + } + ir_fprintf(f, "}"); + } +} + void ir_print_type(irFileBuffer *f, irModule *m, Type *t) { i64 word_bits = 8*build_context.word_size; @@ -273,18 +295,15 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) { } return; case Type_Proc: { - if (t->Proc.result_count == 0) { - ir_fprintf(f, "void"); - } else { - ir_print_type(f, m, t->Proc.results); - } + isize param_count = t->Proc.param_count; + isize result_count = t->Proc.result_count; + ir_print_proc_results(f, m, t); ir_fprintf(f, " ("); - TypeTuple *params = &t->Proc.params->Tuple; - for (isize i = 0; i < t->Proc.param_count; i++) { + for (isize i = 0; i < param_count; i++) { if (i > 0) { ir_fprintf(f, ", "); } - ir_print_type(f, m, params->variables[i]->type); + ir_print_type(f, m, t->Proc.abi_compat_params[i]); } ir_fprintf(f, ")*"); } return; @@ -1176,7 +1195,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { ir_fprintf(f, "call "); ir_print_calling_convention(f, m, proc_type->Proc.calling_convention); if (result_type) { - ir_print_type(f, m, result_type); + ir_print_proc_results(f, m, proc_type); } else { ir_fprintf(f, "void"); } @@ -1192,7 +1211,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { for (isize i = 0; i < call->arg_count; i++) { Entity *e = params->variables[i]; GB_ASSERT(e != NULL); - Type *t = e->type; + Type *t = proc_type->Proc.abi_compat_params[i]; if (i > 0) { ir_fprintf(f, ", "); } @@ -1405,12 +1424,9 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { ir_print_calling_convention(f, m, proc_type->calling_convention); - if (proc_type->result_count == 0) { - ir_fprintf(f, "void"); - } else { - ir_print_type(f, m, proc_type->results); - } - + isize param_count = proc_type->param_count; + isize result_count = proc_type->result_count; + ir_print_proc_results(f, m, proc->type); ir_fprintf(f, " "); // #ifndef GB_SYSTEM_WINDOWS @@ -1423,14 +1439,16 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { ir_fprintf(f, "("); - if (proc_type->param_count > 0) { + if (param_count > 0) { TypeTuple *params = &proc_type->params->Tuple; for (isize i = 0; i < params->variable_count; i++) { Entity *e = params->variables[i]; + Type *original_type = e->type; + Type *abi_type = proc_type->abi_compat_params[i]; if (i > 0) { ir_fprintf(f, ", "); } - ir_print_type(f, m, e->type); + ir_print_type(f, m, abi_type); if (e->flags&EntityFlag_NoAlias) { ir_fprintf(f, " noalias"); } diff --git a/src/types.c b/src/types.c index 02c01d4c9..0dc91f246 100644 --- a/src/types.c +++ b/src/types.c @@ -132,6 +132,8 @@ typedef struct TypeRecord { Type * results; /* Type_Tuple */ \ i32 param_count; \ i32 result_count; \ + Type **abi_compat_params; \ + Type **abi_compat_results; \ bool variadic; \ ProcCallingConvention calling_convention; \ }) \ |