Browse Source

Correct (experimental) System V AMD64 ABI support

gingerBill 5 years ago
parent
commit
5b52fed268
3 changed files with 317 additions and 52 deletions
  1. 173 1
      src/check_type.cpp
  2. 85 26
      src/ir.cpp
  3. 59 25
      src/ir_print.cpp

+ 173 - 1
src/check_type.cpp

@@ -1837,6 +1837,168 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
 	return tuple;
 	return tuple;
 }
 }
 
 
+Array<Type *> systemv_distribute_struct_fields(Type *t, i32 level=0) {
+	t = base_type(t);
+	GB_ASSERT_MSG(t->kind == Type_Struct, "%s %d", type_to_string(t), level);
+	TypeStruct *ts = &t->Struct;
+
+	auto distributed = array_make<Type *>(heap_allocator(), 0, ts->fields.count);
+
+	for_array(field_index, ts->fields) {
+		Entity *f = ts->fields[field_index];
+		Type *bt = core_type(f->type);
+		switch (bt->kind) {
+		case Type_Struct:
+			if (bt->Struct.is_raw_union) {
+				goto DEFAULT;
+			} else {
+				// IMPORTANT TOOD(bill): handle #packed structs correctly
+				// IMPORTANT TODO(bill): handle #align structs correctly
+				auto nested = systemv_distribute_struct_fields(f->type, level+1);
+				for_array(i, nested) {
+					array_add(&distributed, nested[i]);
+				}
+				array_free(&nested);
+			}
+			break;
+
+		case Type_Array:
+			for (i64 i = 0; i < bt->Array.count; i++) {
+				array_add(&distributed, bt->Array.elem);
+			}
+			break;
+
+		case Type_BitSet:
+			array_add(&distributed, bit_set_to_int(bt));
+			break;
+
+		case Type_Tuple:
+			GB_PANIC("Invalid struct field type");
+			break;
+
+		case Type_Slice:
+		case Type_DynamicArray:
+		case Type_Map:
+		case Type_Union:
+		case Type_BitField: // TODO(bill): Ignore?
+			// NOTE(bill, 2019-10-10): Odin specific, don't worry about C calling convention yet
+			goto DEFAULT;
+
+		case Type_Pointer:
+		case Type_Proc:
+		case Type_SimdVector: // TODO(bill): Is this correct logic?
+		default:
+		DEFAULT:;
+			if (type_size_of(bt) > 0) {
+				array_add(&distributed, bt);
+			}
+			break;
+		}
+	}
+
+	return distributed;
+}
+
+Type *struct_type_from_systemv_distribute_struct_fields(Type *abi_type) {
+	GB_ASSERT(is_type_tuple(abi_type));
+	Type *final_type = alloc_type_struct();
+	final_type->Struct.fields = abi_type->Tuple.variables;
+	return final_type;
+}
+
+
+Type *handle_single_distributed_type_parameter(Array<Type *> const &types, bool packed, isize *offset) {
+	GB_ASSERT(types.count > 0);
+
+	if (types.count == 1) {
+		if (offset) *offset = 1;
+		if (is_type_float(types[0])) {
+			return types[0];
+		} else if (type_size_of(types[0]) == 8) {
+			return types[0];
+		} else {
+			return t_u64;
+		}
+	} else if (types.count >= 2) {
+	    if (types[0] == t_f32 && types[1] == t_f32) {
+	    	if (offset) *offset = 2;
+			return alloc_type_simd_vector(2, t_f32);
+		} else if (type_size_of(types[0]) == 8) {
+	    	if (offset) *offset = 1;
+			return types[0];
+		}
+
+		i64 total_size = 0;
+		isize i = 0;
+		if (packed) {
+			for (; i < types.count && total_size < 8; i += 1) {
+				Type *t = types[i];
+				i64 s = type_size_of(t);
+				total_size += s;
+			}
+		} else {
+			for (; i < types.count && total_size < 8; i += 1) {
+				Type *t = types[i];
+				i64 s = gb_max(type_size_of(t), 0);
+				i64 a = gb_max(type_align_of(t), 1);
+				isize ts = align_formula(total_size, a);
+				if (ts >= 8) {
+					break;
+				}
+				total_size = ts + s;
+			}
+		}
+		if (offset) *offset = i;
+		return t_u64;
+	}
+
+	return nullptr;
+}
+
+Type *handle_struct_system_v_amd64_abi_type(Type *t) {
+	GB_ASSERT(is_type_struct(t));
+	Type *bt = core_type(t);
+	i64 size = type_size_of(bt);
+
+	if (!bt->Struct.is_raw_union) {
+		auto field_types = systemv_distribute_struct_fields(bt);
+		defer (array_free(&field_types));
+
+		GB_ASSERT(field_types.count <= 16);
+
+		Type *final_type = nullptr;
+
+		if (field_types.count == 0) {
+			// Do nothing
+		} else if (field_types.count == 1) {
+			final_type = field_types[0];
+		} else {
+			if (size <= 8) {
+				isize offset = 0;
+				final_type = handle_single_distributed_type_parameter(field_types, bt->Struct.is_packed, &offset);
+			} else {
+				isize offset = 0;
+				isize next_offset = 0;
+				Type *two_types[2] = {};
+
+				two_types[0] = handle_single_distributed_type_parameter(field_types, bt->Struct.is_packed, &offset);
+				auto remaining = array_slice(field_types, offset, field_types.count);
+				two_types[1] = handle_single_distributed_type_parameter(remaining, bt->Struct.is_packed, &next_offset);
+				GB_ASSERT(offset + next_offset == field_types.count);
+
+				auto variables = array_make<Entity *>(heap_allocator(), 2);
+				variables[0] = alloc_entity_param(nullptr, empty_token, two_types[0], false, false);
+				variables[1] = alloc_entity_param(nullptr, empty_token, two_types[1], false, false);
+				final_type = alloc_type_tuple();
+				final_type->Tuple.variables = variables;
+			}
+		}
+		return final_type;
+	} else {
+		return t;
+	}
+}
+
 Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCallingConvention cc) {
 Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCallingConvention cc) {
 	Type *new_type = original_type;
 	Type *new_type = original_type;
 
 
@@ -1901,6 +2063,7 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall
 		{
 		{
 			i64 align = type_align_of(original_type);
 			i64 align = type_align_of(original_type);
 			i64 size  = type_size_of(original_type);
 			i64 size  = type_size_of(original_type);
+
 			switch (8*size) {
 			switch (8*size) {
 			case 8:  new_type = t_u8;  break;
 			case 8:  new_type = t_u8;  break;
 			case 16: new_type = t_u16; break;
 			case 16: new_type = t_u16; break;
@@ -1944,6 +2107,15 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall
 			i64 size  = type_size_of(original_type);
 			i64 size  = type_size_of(original_type);
 			if (8*size > 16) {
 			if (8*size > 16) {
 				new_type = alloc_type_pointer(original_type);
 				new_type = alloc_type_pointer(original_type);
+			} else if (build_context.ODIN_ARCH == "amd64") {
+				// NOTE(bill): System V AMD64 ABI
+				if (bt->Struct.is_raw_union) {
+					// TODO(bill): Handle raw union correctly for
+					break;
+				}
+
+				new_type = handle_struct_system_v_amd64_abi_type(bt);
+				return new_type;
 			}
 			}
 
 
 			break;
 			break;
@@ -2018,7 +2190,7 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCal
 		}
 		}
 	} else if (build_context.ODIN_OS == "linux" || build_context.ODIN_OS == "darwin") {
 	} else if (build_context.ODIN_OS == "linux" || build_context.ODIN_OS == "darwin") {
 		if (build_context.ODIN_ARCH == "amd64") {
 		if (build_context.ODIN_ARCH == "amd64") {
-			
+
 		}
 		}
 	} else {
 	} else {
 		// IMPORTANT TODO(bill): figure out the ABI settings for Linux, OSX etc. for
 		// IMPORTANT TODO(bill): figure out the ABI settings for Linux, OSX etc. for

+ 85 - 26
src/ir.cpp

@@ -147,6 +147,7 @@ struct irProcedure {
 
 
 	Array<irContextData>  context_stack;
 	Array<irContextData>  context_stack;
 
 
+	i32      parameter_count;
 
 
 	irValue *return_ptr_hint_value;
 	irValue *return_ptr_hint_value;
 	Ast *    return_ptr_hint_ast;
 	Ast *    return_ptr_hint_ast;
@@ -425,6 +426,7 @@ enum irParamPasskind {
 	irParamPass_Integer,  // Pass as an integer of the same size
 	irParamPass_Integer,  // Pass as an integer of the same size
 	irParamPass_ConstRef, // Pass as a pointer but the value is immutable
 	irParamPass_ConstRef, // Pass as a pointer but the value is immutable
 	irParamPass_BitCast,  // Pass by value and bit cast to the correct type
 	irParamPass_BitCast,  // Pass by value and bit cast to the correct type
+	irParamPass_Tuple,    // Pass across multiple parameters (System V AMD64, up to 2)
 };
 };
 
 
 struct irValueParam {
 struct irValueParam {
@@ -433,6 +435,7 @@ struct irValueParam {
 	Entity *         entity;
 	Entity *         entity;
 	Type *           type;
 	Type *           type;
 	Type *           original_type;
 	Type *           original_type;
+	i32              index;
 	Array<irValue *> referrers;
 	Array<irValue *> referrers;
 };
 };
 
 
@@ -914,15 +917,18 @@ irValue *ir_value_global(Entity *e, irValue *value) {
 	if (value) value->uses += 1;
 	if (value) value->uses += 1;
 	return v;
 	return v;
 }
 }
-irValue *ir_value_param(irProcedure *parent, Entity *e, Type *abi_type) {
+irValue *ir_value_param(irProcedure *parent, Entity *e, Type *abi_type, i32 index) {
 	irValue *v = ir_alloc_value(irValue_Param);
 	irValue *v = ir_alloc_value(irValue_Param);
 	v->Param.kind          = irParamPass_Value;
 	v->Param.kind          = irParamPass_Value;
 	v->Param.parent        = parent;
 	v->Param.parent        = parent;
-	v->Param.entity        = e;
-	v->Param.original_type = e->type;
+	if (e != nullptr) {
+		v->Param.entity        = e;
+		v->Param.original_type = e->type;
+	}
 	v->Param.type          = abi_type;
 	v->Param.type          = abi_type;
+	v->Param.index         = index;
 
 
-	if (abi_type != e->type) {
+	if (e != nullptr && abi_type != e->type) {
 		if (is_type_pointer(abi_type)) {
 		if (is_type_pointer(abi_type)) {
 			GB_ASSERT(e->kind == Entity_Variable);
 			GB_ASSERT(e->kind == Entity_Variable);
 			v->Param.kind = irParamPass_Pointer;
 			v->Param.kind = irParamPass_Pointer;
@@ -935,8 +941,12 @@ irValue *ir_value_param(irProcedure *parent, Entity *e, Type *abi_type) {
 			v->Param.kind = irParamPass_Value;
 			v->Param.kind = irParamPass_Value;
 		} else if (is_type_simd_vector(abi_type)) {
 		} else if (is_type_simd_vector(abi_type)) {
 			v->Param.kind = irParamPass_BitCast;
 			v->Param.kind = irParamPass_BitCast;
+		} else if (is_type_float(abi_type)) {
+			v->Param.kind = irParamPass_BitCast;
+		} else if (is_type_tuple(abi_type)) {
+			v->Param.kind = irParamPass_Tuple;
 		} else {
 		} else {
-			GB_PANIC("Invalid abi type pass kind");
+			GB_PANIC("Invalid abi type pass kind %s", type_to_string(abi_type));
 		}
 		}
 	}
 	}
 	array_init(&v->Param.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here
 	array_init(&v->Param.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here
@@ -1429,7 +1439,7 @@ irValue *ir_value_procedure(irModule *m, Entity *entity, Type *type, Ast *type_e
 
 
 	Type *t = base_type(type);
 	Type *t = base_type(type);
 	GB_ASSERT(is_type_proc(t));
 	GB_ASSERT(is_type_proc(t));
-	array_init(&v->Proc.params, ir_allocator(), 0, t->Proc.param_count);
+	array_init(&v->Proc.params, heap_allocator(), 0, t->Proc.param_count);
 
 
 	return v;
 	return v;
 }
 }
@@ -1499,7 +1509,9 @@ void ir_start_block(irProcedure *proc, irBlock *block) {
 }
 }
 
 
 
 
-
+irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t);
+irValue *ir_address_from_load_or_generate_local(irProcedure *proc, irValue *val);
+irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index);
 
 
 
 
 
 
@@ -1713,9 +1725,12 @@ irValue *ir_add_global_generated(irModule *m, Type *type, irValue *value) {
 
 
 
 
 irValue *ir_add_param(irProcedure *proc, Entity *e, Ast *expr, Type *abi_type, i32 index) {
 irValue *ir_add_param(irProcedure *proc, Entity *e, Ast *expr, Type *abi_type, i32 index) {
-	irValue *v = ir_value_param(proc, e, abi_type);
+	irValue *v = ir_value_param(proc, e, abi_type, index);
+	array_add(&proc->params, v);
 	irValueParam *p = &v->Param;
 	irValueParam *p = &v->Param;
 
 
+	irValue *res = nullptr;
+
 	ir_push_debug_location(proc->module, e ? e->identifier : nullptr, proc->debug_scope, e);
 	ir_push_debug_location(proc->module, e ? e->identifier : nullptr, proc->debug_scope, e);
 	defer (ir_pop_debug_location(proc->module));
 	defer (ir_pop_debug_location(proc->module));
 
 
@@ -1750,6 +1765,24 @@ irValue *ir_add_param(irProcedure *proc, Entity *e, Ast *expr, Type *abi_type, i
 		ir_emit_store(proc, l, x);
 		ir_emit_store(proc, l, x);
 		return x;
 		return x;
 	}
 	}
+	case irParamPass_Tuple: {
+		irValue *l = ir_add_local(proc, e, expr, true, index);
+		Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type);
+		irValue *ptr = ir_emit_bitcast(proc, l, alloc_type_pointer(st));
+		if (abi_type->Tuple.variables.count > 0) {
+			array_pop(&proc->params);
+		}
+		for_array(i, abi_type->Tuple.variables) {
+			Type *t = abi_type->Tuple.variables[i]->type;
+
+			irValue *elem = ir_value_param(proc, nullptr, t, index+cast(i32)i);
+			array_add(&proc->params, elem);
+
+			irValue *dst = ir_emit_struct_ep(proc, ptr, cast(i32)i);
+			ir_emit_store(proc, dst, elem);
+		}
+		return ir_emit_load(proc, l);
+	}
 
 
 	}
 	}
 
 
@@ -2945,10 +2978,6 @@ void ir_emit_unreachable(irProcedure *proc) {
 	ir_emit(proc, ir_instr_unreachable(proc));
 	ir_emit(proc, ir_instr_unreachable(proc));
 }
 }
 
 
-irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t);
-irValue *ir_address_from_load_or_generate_local(irProcedure *proc, irValue *val);
-irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index);
-
 
 
 irValue *ir_get_package_value(irModule *m, String package_name, String entity_name) {
 irValue *ir_get_package_value(irModule *m, String package_name, String entity_name) {
 	AstPackage *rt_pkg = get_core_package(m->info, package_name);
 	AstPackage *rt_pkg = get_core_package(m->info, package_name);
@@ -2998,7 +3027,7 @@ Array<irValue *> ir_value_to_array(irProcedure *p, irValue *value) {
 }
 }
 
 
 
 
-irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, ProcInlining inlining = ProcInlining_none, bool use_return_ptr_hint = false) {
+irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> const &args, ProcInlining inlining = ProcInlining_none, bool use_return_ptr_hint = false) {
 	Type *pt = base_type(ir_type(value));
 	Type *pt = base_type(ir_type(value));
 	GB_ASSERT(pt->kind == Type_Proc);
 	GB_ASSERT(pt->kind == Type_Proc);
 	Type *results = pt->Proc.results;
 	Type *results = pt->Proc.results;
@@ -3008,6 +3037,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro
 		context_ptr = ir_find_or_generate_context_ptr(p);
 		context_ptr = ir_find_or_generate_context_ptr(p);
 	}
 	}
 
 
+
 	bool is_c_vararg = pt->Proc.c_vararg;
 	bool is_c_vararg = pt->Proc.c_vararg;
 	isize param_count = pt->Proc.param_count;
 	isize param_count = pt->Proc.param_count;
 	if (is_c_vararg) {
 	if (is_c_vararg) {
@@ -3016,9 +3046,13 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro
 	} else {
 	} else {
 		GB_ASSERT_MSG(param_count == args.count, "%td == %td", param_count, args.count);
 		GB_ASSERT_MSG(param_count == args.count, "%td == %td", param_count, args.count);
 	}
 	}
+
+	auto processed_args = array_make<irValue *>(heap_allocator(), 0, args.count);
+
 	for (isize i = 0; i < param_count; i++) {
 	for (isize i = 0; i < param_count; i++) {
 		Entity *e = pt->Proc.params->Tuple.variables[i];
 		Entity *e = pt->Proc.params->Tuple.variables[i];
 		if (e->kind != Entity_Variable) {
 		if (e->kind != Entity_Variable) {
+			array_add(&processed_args, args[i]);
 			continue;
 			continue;
 		}
 		}
 		GB_ASSERT(e->flags & EntityFlag_Param);
 		GB_ASSERT(e->flags & EntityFlag_Param);
@@ -3027,20 +3061,29 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro
 		Type *new_type = pt->Proc.abi_compat_params[i];
 		Type *new_type = pt->Proc.abi_compat_params[i];
 		Type *arg_type = ir_type(args[i]);
 		Type *arg_type = ir_type(args[i]);
 		if (are_types_identical(arg_type, new_type)) {
 		if (are_types_identical(arg_type, new_type)) {
+			array_add(&processed_args, args[i]);
 			// NOTE(bill): Done
 			// NOTE(bill): Done
 		} else if (!are_types_identical(original_type, new_type)) {
 		} else if (!are_types_identical(original_type, new_type)) {
 			if (is_type_pointer(new_type) && !is_type_pointer(original_type)) {
 			if (is_type_pointer(new_type) && !is_type_pointer(original_type)) {
 				if (e->flags&EntityFlag_ImplicitReference) {
 				if (e->flags&EntityFlag_ImplicitReference) {
-					args[i] = ir_address_from_load_or_generate_local(p, args[i]);
+					array_add(&processed_args, ir_address_from_load_or_generate_local(p, args[i]));
 				} else if (!is_type_pointer(arg_type)) {
 				} else if (!is_type_pointer(arg_type)) {
-					args[i] = ir_copy_value_to_ptr(p, args[i], original_type, 16);
+					array_add(&processed_args, ir_copy_value_to_ptr(p, args[i], original_type, 16));
 				}
 				}
 			} else if (is_type_integer(new_type)) {
 			} else if (is_type_integer(new_type)) {
-				args[i] = ir_emit_transmute(p, args[i], new_type);
+				array_add(&processed_args, ir_emit_transmute(p, args[i], new_type));
 			} else if (new_type == t_llvm_bool) {
 			} else if (new_type == t_llvm_bool) {
-				args[i] = ir_emit_conv(p, args[i], new_type);
+				array_add(&processed_args, ir_emit_conv(p, args[i], new_type));
 			} else if (is_type_simd_vector(new_type)) {
 			} else if (is_type_simd_vector(new_type)) {
-				args[i] = ir_emit_bitcast(p, args[i], new_type);
+				array_add(&processed_args, ir_emit_bitcast(p, args[i], new_type));
+			} else if (is_type_tuple(new_type)) {
+				Type *abi_type = pt->Proc.abi_compat_params[i];
+				Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type);
+				irValue *x = ir_emit_transmute(p, args[i], st);
+				for (isize j = 0; j < new_type->Tuple.variables.count; j++) {
+					irValue *xx = ir_emit_struct_ev(p, x, cast(i32)j);
+					array_add(&processed_args, xx);
+				}
 			}
 			}
 		}
 		}
 	}
 	}
@@ -3066,10 +3109,10 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro
 			return_ptr = ir_add_local_generated(p, rt, true);
 			return_ptr = ir_add_local_generated(p, rt, true);
 		}
 		}
 		GB_ASSERT(is_type_pointer(ir_type(return_ptr)));
 		GB_ASSERT(is_type_pointer(ir_type(return_ptr)));
-		ir_emit(p, ir_instr_call(p, value, return_ptr, args, nullptr, context_ptr, inlining));
+		ir_emit(p, ir_instr_call(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining));
 		result = ir_emit_load(p, return_ptr);
 		result = ir_emit_load(p, return_ptr);
 	} else {
 	} else {
-		result = ir_emit(p, ir_instr_call(p, value, nullptr, args, abi_rt, context_ptr, inlining));
+		result = ir_emit(p, ir_instr_call(p, value, nullptr, processed_args, abi_rt, context_ptr, inlining));
 		if (abi_rt != results) {
 		if (abi_rt != results) {
 			result = ir_emit_transmute(p, result, rt);
 			result = ir_emit_transmute(p, result, rt);
 		}
 		}
@@ -9540,6 +9583,7 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) {
 ////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////
 
 
 void ir_number_proc_registers(irProcedure *proc) {
 void ir_number_proc_registers(irProcedure *proc) {
+	// i32 reg_index = proc->parameter_count;
 	i32 reg_index = 0;
 	i32 reg_index = 0;
 	for_array(i, proc->blocks) {
 	for_array(i, proc->blocks) {
 		irBlock *b = proc->blocks[i];
 		irBlock *b = proc->blocks[i];
@@ -9595,13 +9639,15 @@ void ir_begin_procedure_body(irProcedure *proc) {
 	proc->entry_block = ir_new_block(proc, proc->type_expr, "entry");
 	proc->entry_block = ir_new_block(proc, proc->type_expr, "entry");
 	ir_start_block(proc, proc->entry_block);
 	ir_start_block(proc, proc->entry_block);
 
 
+	i32 parameter_index = 0;
+
 	if (proc->type->Proc.return_by_pointer) {
 	if (proc->type->Proc.return_by_pointer) {
 		// NOTE(bill): this must be the first parameter stored
 		// NOTE(bill): this must be the first parameter stored
 		Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(proc->type->Proc.results));
 		Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(proc->type->Proc.results));
 		Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false);
 		Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false);
 		e->flags |= EntityFlag_Sret | EntityFlag_NoAlias;
 		e->flags |= EntityFlag_Sret | EntityFlag_NoAlias;
 
 
-		irValue *param = ir_value_param(proc, e, ptr_type);
+		irValue *param = ir_value_param(proc, e, ptr_type, -1);
 		param->Param.kind = irParamPass_Pointer;
 		param->Param.kind = irParamPass_Pointer;
 
 
 		ir_module_add_value(proc->module, e, param);
 		ir_module_add_value(proc->module, e, param);
@@ -9630,13 +9676,19 @@ void ir_begin_procedure_body(irProcedure *proc) {
 
 
 				Entity *e = params->variables[i];
 				Entity *e = params->variables[i];
 				if (e->kind != Entity_Variable) {
 				if (e->kind != Entity_Variable) {
+					parameter_index += 1;
 					continue;
 					continue;
 				}
 				}
 
 
 				Type *abi_type = proc->type->Proc.abi_compat_params[i];
 				Type *abi_type = proc->type->Proc.abi_compat_params[i];
 				if (e->token.string != "" && !is_blank_ident(e->token)) {
 				if (e->token.string != "" && !is_blank_ident(e->token)) {
-					irValue *param = ir_add_param(proc, e, name, abi_type, cast(i32)(i+1));
-					array_add(&proc->params, param);
+					ir_add_param(proc, e, name, abi_type, parameter_index);
+				}
+
+				if (is_type_tuple(abi_type)) {
+					parameter_index += cast(i32)abi_type->Tuple.variables.count;
+				} else {
+					parameter_index += 1;
 				}
 				}
 			}
 			}
 		} else {
 		} else {
@@ -9645,6 +9697,7 @@ void ir_begin_procedure_body(irProcedure *proc) {
 			for_array(i, params->variables) {
 			for_array(i, params->variables) {
 				Entity *e = params->variables[i];
 				Entity *e = params->variables[i];
 				if (e->kind != Entity_Variable) {
 				if (e->kind != Entity_Variable) {
+					parameter_index += 1;
 					continue;
 					continue;
 				}
 				}
 				Type *abi_type = e->type;
 				Type *abi_type = e->type;
@@ -9652,8 +9705,12 @@ void ir_begin_procedure_body(irProcedure *proc) {
 					abi_type = abi_types[i];
 					abi_type = abi_types[i];
 				}
 				}
 				if (e->token.string != "" && !is_blank_ident(e->token)) {
 				if (e->token.string != "" && !is_blank_ident(e->token)) {
-					irValue *param = ir_add_param(proc, e, nullptr, abi_type, cast(i32)(i+1));
-					array_add(&proc->params, param);
+					ir_add_param(proc, e, nullptr, abi_type, parameter_index);
+				}
+				if (is_type_tuple(abi_type)) {
+					parameter_index += cast(i32)abi_type->Tuple.variables.count;
+				} else {
+					parameter_index += 1;
 				}
 				}
 			}
 			}
 		}
 		}
@@ -9695,11 +9752,13 @@ void ir_begin_procedure_body(irProcedure *proc) {
 	if (proc->type->Proc.calling_convention == ProcCC_Odin) {
 	if (proc->type->Proc.calling_convention == ProcCC_Odin) {
 		Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false);
 		Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false);
 		e->flags |= EntityFlag_NoAlias;
 		e->flags |= EntityFlag_NoAlias;
-		irValue *param = ir_value_param(proc, e, e->type);
+		irValue *param = ir_value_param(proc, e, e->type, -1);
 		ir_module_add_value(proc->module, e, param);
 		ir_module_add_value(proc->module, e, param);
 		irContextData ctx = {param, proc->scope_index};
 		irContextData ctx = {param, proc->scope_index};
 		array_add(&proc->context_stack, ctx);
 		array_add(&proc->context_stack, ctx);
 	}
 	}
+
+	proc->parameter_count = parameter_index;
 }
 }
 
 
 
 

+ 59 - 25
src/ir_print.cpp

@@ -1130,7 +1130,11 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin
 		break;
 		break;
 	}
 	}
 	case irValue_Param:
 	case irValue_Param:
-		ir_print_encoded_local(f, value->Param.entity->token.string);
+		if (value->Param.index >= 0) {
+			ir_fprintf(f, "%%_.%d", value->Param.index);
+		} else {
+			ir_print_encoded_local(f, value->Param.entity->token.string);
+		}
 		break;
 		break;
 	case irValue_SourceCodeLocation: {
 	case irValue_SourceCodeLocation: {
 		irValue *file      = value->SourceCodeLocation.file;
 		irValue *file      = value->SourceCodeLocation.file;
@@ -1936,9 +1940,6 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 					}
 					}
 					ir_write_byte(f, ' ');
 					ir_write_byte(f, ' ');
 					irValue *arg = call->args[i];
 					irValue *arg = call->args[i];
-					if (is_type_boolean(t)) {
-
-					}
 					ir_print_value(f, m, arg, t);
 					ir_print_value(f, m, arg, t);
 					param_index++;
 					param_index++;
 				}
 				}
@@ -1953,24 +1954,43 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 					param_index++;
 					param_index++;
 				}
 				}
 			} else {
 			} else {
-				GB_ASSERT(call->args.count == params->variables.count);
+				// GB_ASSERT(call->args.count == params->variables.count);
+				isize arg_index = 0;
 				for_array(i, params->variables) {
 				for_array(i, params->variables) {
 					Entity *e = params->variables[i];
 					Entity *e = params->variables[i];
 					GB_ASSERT(e != nullptr);
 					GB_ASSERT(e != nullptr);
-					if (e->kind != Entity_Variable) continue;
+					if (e->kind != Entity_Variable) {
+						arg_index++;
+						continue;
+					}
 
 
 					if (param_index > 0) ir_write_str_lit(f, ", ");
 					if (param_index > 0) ir_write_str_lit(f, ", ");
 
 
-					irValue *arg = call->args[i];
 					Type *t = proc_type->Proc.abi_compat_params[i];
 					Type *t = proc_type->Proc.abi_compat_params[i];
-
-					ir_print_type(f, m, t);
-					if (e->flags&EntityFlag_NoAlias) {
-						ir_write_str_lit(f, " noalias");
+					if (is_type_tuple(t)) {
+						for_array(j, t->Tuple.variables) {
+							if (j > 0) ir_write_str_lit(f, ", ");
+
+							irValue *arg = call->args[arg_index++];
+
+							ir_print_type(f, m, t->Tuple.variables[j]->type);
+							if (e->flags&EntityFlag_NoAlias) {
+								ir_write_str_lit(f, " noalias");
+							}
+							ir_write_byte(f, ' ');
+							ir_print_value(f, m, arg, t);
+							param_index++;
+						}
+					} else {
+						irValue *arg = call->args[arg_index++];
+						ir_print_type(f, m, t);
+						if (e->flags&EntityFlag_NoAlias) {
+							ir_write_str_lit(f, " noalias");
+						}
+						ir_write_byte(f, ' ');
+						ir_print_value(f, m, arg, t);
+						param_index++;
 					}
 					}
-					ir_write_byte(f, ' ');
-					ir_print_value(f, m, arg, t);
-					param_index++;
 				}
 				}
 			}
 			}
 		}
 		}
@@ -2089,7 +2109,8 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
 
 
 	if (param_count > 0) {
 	if (param_count > 0) {
 		TypeTuple *params = &proc_type->params->Tuple;
 		TypeTuple *params = &proc_type->params->Tuple;
-		for (isize i = 0; i < param_count; i++) {
+		isize parameter_index = 0;
+		for (isize i = 0; i < param_count; i++, parameter_index++) {
 			Entity *e = params->variables[i];
 			Entity *e = params->variables[i];
 			Type *original_type = e->type;
 			Type *original_type = e->type;
 			Type *abi_type = proc_type->abi_compat_params[i];
 			Type *abi_type = proc_type->abi_compat_params[i];
@@ -2099,16 +2120,29 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
 			if (i+1 == params->variables.count && proc_type->c_vararg) {
 			if (i+1 == params->variables.count && proc_type->c_vararg) {
 				ir_write_str_lit(f, " ...");
 				ir_write_str_lit(f, " ...");
 			} else {
 			} else {
-				ir_print_type(f, m, abi_type);
-				if (e->flags&EntityFlag_NoAlias) {
-					ir_write_str_lit(f, " noalias");
-				}
-				if (proc->body != nullptr) {
-					if (e->token.string != "" && !is_blank_ident(e->token)) {
-						ir_write_byte(f, ' ');
-						ir_print_encoded_local(f, e->token.string);
-					} else {
-						ir_fprintf(f, " %%_.param_%td", i);
+				if (is_type_tuple(abi_type)) {
+					for_array(j, abi_type->Tuple.variables) {
+						if (j > 0) ir_write_string(f, str_lit(", "));
+
+						Type *tft = abi_type->Tuple.variables[j]->type;
+						ir_print_type(f, m, tft);
+						if (e->flags&EntityFlag_NoAlias) {
+							ir_write_str_lit(f, " noalias");
+						}
+
+						if (proc->body != nullptr) {
+							ir_fprintf(f, " %%_.%td", parameter_index+j);
+						}
+					}
+					parameter_index += abi_type->Tuple.variables.count-1;
+					param_index += abi_type->Tuple.variables.count-1;
+				} else {
+					ir_print_type(f, m, abi_type);
+					if (e->flags&EntityFlag_NoAlias) {
+						ir_write_str_lit(f, " noalias");
+					}
+					if (proc->body != nullptr) {
+						ir_fprintf(f, " %%_.%td", parameter_index);
 					}
 					}
 				}
 				}
 			}
 			}