/*
 * This file is part of libFirm.
 * Copyright (C) 2012 University of Karlsruhe.
 */

/**
 * @file
 * @brief   Representation of types.
 * @author  Goetz Lindenmaier, Michael Beck
 * @brief
 *
 *  Implementation of the data structure to hold
 *  type information.
 *
 *  This module supplies a data structure to represent all types
 *  known in the compiled program.  This includes types specified
 *  in the program as well as types defined by the language.  In the
 *  view of the intermediate representation there is no difference
 *  between these types.
 *
 *  There exist several kinds of types, arranged by the structure of
 *  the type.  A type is described by a set of attributes.  Some of
 *  these attributes are common to all types, others depend on the
 *  kind of the type.
 *
 *  Types are different from the modes defined in irmode:  Types are
 *  on the level of the programming language, modes at the level of
 *  the target processor.
 */
#include "type_t.h"

#include "array.h"
#include "bitfiddle.h"
#include "dbginfo.h"
#include "entity_t.h"
#include "ircons.h"
#include "irhooks.h"
#include "irnode_t.h"
#include "irprog_t.h"
#include "irprog_t.h"
#include "panic.h"
#include "tv_t.h"
#include "util.h"
#include "xmalloc.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>

static ir_type *new_type(tp_opcode opcode, size_t attr_size, ir_mode *mode);
static void free_compound_entities(ir_type *type);

const char *get_type_opcode_name(tp_opcode const opcode)
{
	switch (opcode) {
	case tpo_array:         return "array";
	case tpo_class:         return "class";
	case tpo_code:          return "code";
	case tpo_method:        return "method";
	case tpo_pointer:       return "pointer";
	case tpo_primitive:     return "primitive";
	case tpo_segment:       return "segment";
	case tpo_struct:        return "struct";
	case tpo_uninitialized: return "uninitialized";
	case tpo_union:         return "union";
	case tpo_unknown:       return "unknown";
	}
	panic("Invalid type opcode");
}

ir_type *get_code_type(void)
{
	return irp->code_type;
}

ir_type *get_unknown_type(void)
{
	return irp->unknown_type;
}

void ir_init_type(ir_prog *irp)
{
	irp->code_type = new_type(tpo_code, 0, mode_ANY);
	set_type_state(irp->code_type, layout_fixed);

	irp->unknown_type = new_type(tpo_unknown, 0, mode_ANY);
	set_type_state (irp->unknown_type, layout_fixed);

	irp->dummy_owner = new_type_struct(new_id_from_str("$dummy_owner$"));
}

void ir_finish_type(ir_prog *irp)
{
	/** nothing to do. (The code, unknown types are in the global type list
	 * and freed there */
	(void)irp;
}

ir_visited_t firm_type_visited;

void set_master_type_visited(ir_visited_t val)
{
	firm_type_visited = val;
}

ir_visited_t (get_master_type_visited)(void)
{
	return get_master_type_visited_();
}

void inc_master_type_visited(void)
{
	++firm_type_visited;
}

/**
 *   Creates a new type representation:
 *   @return A new type of the given type.  The remaining private attributes are
 *           not initialized.  The type is in state layout_undefined.
 */
static ir_type *new_type(tp_opcode const opcode, size_t attr_size,
                         ir_mode *const mode)
{
	size_t   const node_size = offsetof(ir_type, attr) +  attr_size;
	ir_type *const res       = (ir_type*)xmalloc(node_size);
	memset(res, 0, node_size);

	res->kind   = k_type;
	res->opcode = opcode;
	res->mode   = mode;
	res->flags  = tf_none;
	res->size   = 0;
	res->align  = 0;
	res->visit  = 0;
	res->link   = NULL;
	res->nr     = get_irp_new_node_nr();

	add_irp_type(res);   /* Remember the new type global. */

	return res;
}

void free_type_entities(ir_type *const type)
{
	if (is_compound_type(type))
		free_compound_entities(type);
}

static void free_type_attrs(ir_type *const type)
{
	switch (get_type_opcode(type)) {
	case tpo_class:
		free_class_attrs(type);
		return;
	case tpo_segment:
	case tpo_struct:
	case tpo_union:
		free_compound_attrs(type);
		return;
	case tpo_method:
		free_method_attrs(type);
		return;
	case tpo_code:
	case tpo_primitive:
	case tpo_pointer:
	case tpo_array:
	case tpo_unknown:
	case tpo_uninitialized:
		return;
	}
	panic("Invalid type");
}

void free_type(ir_type *tp)
{
	free_type_entities(tp);
	/* Remove from list of all types */
	remove_irp_type(tp);
	/* Free the attributes of the type. */
	free_type_attrs(tp);
	/* And now the type itself... */
#ifdef DEBUG_libfirm
	tp->kind = k_BAD;
#endif
	free(tp);
}

void *(get_type_link)(const ir_type *tp)
{
	return get_type_link_(tp);
}

void (set_type_link)(ir_type *tp, void *l)
{
	set_type_link_(tp, l);
}

static inline bool is_type(const void *thing)
{
	return get_kind(thing) == k_type;
}

tp_opcode (get_type_opcode)(ir_type const *const type)
{
	return get_type_opcode_(type);
}

ir_mode *(get_type_mode)(const ir_type *tp)
{
	return get_type_mode_(tp);
}

long get_type_nr(const ir_type *tp)
{
	assert(is_type(tp));
	return tp->nr;
}

unsigned (get_type_size)(const ir_type *tp)
{
	return get_type_size_(tp);
}

void set_type_size(ir_type *tp, unsigned size)
{
	tp->size = size;
}

unsigned (get_type_alignment)(const ir_type *type)
{
	return get_type_alignment_(type);
}

void set_type_alignment(ir_type *type, unsigned align)
{
	assert(is_type(type));
	assert(align > 0);
	type->align = align;
}

const char *get_type_state_name(ir_type_state s)
{
#define X(a)    case a: return #a
	switch (s) {
		X(layout_undefined);
		X(layout_fixed);
	}
	return "<unknown>";
#undef X
}

ir_type_state (get_type_state)(const ir_type *tp)
{
	return get_type_state_(tp);
}

void set_type_state(ir_type *tp, ir_type_state state)
{
	assert(is_type(tp));

	tp_opcode opcode = get_type_opcode(tp);
	if (opcode == tpo_pointer || opcode == tpo_primitive
	 || opcode == tpo_method)
		return;

#ifndef NDEBUG
	/* Just a correctness check: */
	if (state == layout_fixed && is_compound_type(tp)
	 && !is_segment_type(tp)) {
		for (size_t i = 0, n_mem = get_compound_n_members(tp);
			 i < n_mem; i++) {
			ir_entity *entity = get_compound_member(tp, i);
			if (is_method_entity(entity))
				continue;
			assert(get_entity_offset(entity) != INVALID_OFFSET);
		}
	}
#endif
	if (state == layout_fixed)
		tp->flags |= tf_layout_fixed;
	else
		tp->flags &= ~tf_layout_fixed;
}

ir_visited_t (get_type_visited)(const ir_type *tp)
{
	return get_type_visited_(tp);
}

void (set_type_visited)(ir_type *tp, ir_visited_t num)
{
	set_type_visited_(tp, num);
}

void (mark_type_visited)(ir_type *tp)
{
	mark_type_visited_(tp);
}

int (type_visited)(const ir_type *tp)
{
	return type_visited_(tp);
}

type_dbg_info *(get_type_dbg_info)(const ir_type *tp)
{
	return get_type_dbg_info_(tp);
}

void (set_type_dbg_info)(ir_type *tp, type_dbg_info *db)
{
	set_type_dbg_info_(tp, db);
}

static void compound_init(ir_type *const type, ident *const name)
{
	type->flags                |= tf_compound;
	type->name                  = name;
	type->attr.compound.members = NEW_ARR_F(ir_entity*, 0);
}

void free_compound_attrs(ir_type *type)
{
	DEL_ARR_F(type->attr.compound.members);
}

static void free_compound_entities(ir_type *type)
{
	for (size_t i = get_compound_n_members(type); i-- > 0; )
		free_entity(get_compound_member(type, i));
}

ir_type *new_type_class(ident *name)
{
	ir_type *res = new_type(tpo_class, sizeof(class_attr), NULL);
	compound_init(res, name);
	res->attr.cls.subtypes   = NEW_ARR_F(ir_type*, 0);
	res->attr.cls.supertypes = NEW_ARR_F(ir_type*, 0);
	hook_new_type(res);
	return res;
}

void free_class_attrs(ir_type *clss)
{
	assert(is_Class_type(clss));
	free_compound_attrs(clss);
	DEL_ARR_F(clss->attr.cls.subtypes);
	DEL_ARR_F(clss->attr.cls.supertypes);
}

size_t get_class_n_members(const ir_type *clss)
{
	assert(is_Class_type(clss));
	return get_compound_n_members(clss);
}

ir_entity *get_class_member(ir_type const *const clss, size_t const pos)
{
	assert(is_Class_type(clss));
	return get_compound_member(clss, pos);
}

size_t get_class_member_index(ir_type const *clss, ir_entity const *const mem)
{
	assert(is_Class_type(clss));
	return get_compound_member_index(clss, mem);
}

void add_class_subtype(ir_type *clss, ir_type *subtype)
{
	assert(is_Class_type(clss));
	ARR_APP1(ir_type *, clss->attr.cls.subtypes, subtype);
	for (size_t i = 0, n_supertypes = get_class_n_supertypes(subtype);
	     i < n_supertypes; i++) {
		if (get_class_supertype(subtype, i) == clss)
			/* Class already registered */
			return;
	}
	ARR_APP1(ir_type *, subtype->attr.cls.supertypes, clss);
}

size_t get_class_n_subtypes(const ir_type *clss)
{
	assert(is_Class_type(clss));
	return ARR_LEN(clss->attr.cls.subtypes);
}

ir_type *get_class_subtype(const ir_type *clss, size_t pos)
{
	assert(is_Class_type(clss));
	assert(pos < get_class_n_subtypes(clss));
	return clss->attr.cls.subtypes[pos];
}

size_t get_class_subtype_index(const ir_type *clss, const ir_type *subclass)
{
	assert(is_Class_type(clss) && is_Class_type(subclass));
	for (size_t i = 0, n_subtypes = get_class_n_subtypes(clss);
	     i < n_subtypes; ++i) {
		if (get_class_subtype(clss, i) == subclass)
			return i;
	}
	return (size_t)-1;
}

void set_class_subtype(ir_type *clss, ir_type *subtype, size_t pos)
{
	assert(is_Class_type(clss));
	assert(pos < get_class_n_subtypes(clss));
	clss->attr.cls.subtypes[pos] = subtype;
}

void remove_class_subtype(ir_type *clss, ir_type *subtype)
{
	assert(is_Class_type(clss));
	for (size_t i = 0; i < ARR_LEN(clss->attr.cls.subtypes); ++i) {
		if (clss->attr.cls.subtypes[i] == subtype) {
			for (; i < ARR_LEN(clss->attr.cls.subtypes) - 1; ++i)
				clss->attr.cls.subtypes[i] = clss->attr.cls.subtypes[i+1];
			ARR_SETLEN(ir_type*, clss->attr.cls.subtypes, ARR_LEN(clss->attr.cls.subtypes) - 1);
			break;
		}
	}
}

void add_class_supertype(ir_type *clss, ir_type *supertype)
{
	assert(is_Class_type(clss));
	assert(is_Class_type(supertype));
	ARR_APP1(ir_type *, clss->attr.cls.supertypes, supertype);
	for (size_t i = 0, n = get_class_n_subtypes(supertype); i < n; ++i) {
		if (get_class_subtype(supertype, i) == clss)
			/* Class already registered */
			return;
	}
	ARR_APP1(ir_type *, supertype->attr.cls.subtypes, clss);
}

size_t get_class_n_supertypes(const ir_type *clss)
{
	assert(is_Class_type(clss));
	return ARR_LEN(clss->attr.cls.supertypes);
}

size_t get_class_supertype_index(const ir_type *clss, const ir_type *super_clss)
{
	assert(is_Class_type(clss) && is_Class_type(super_clss));
	for (size_t i = 0, n_supertypes = get_class_n_supertypes(clss);
	     i < n_supertypes; i++) {
		if (get_class_supertype(clss, i) == super_clss)
			return i;
	}
	return (size_t)-1;
}

ir_type *get_class_supertype(const ir_type *clss, size_t pos)
{
	assert(is_Class_type(clss));
	assert(pos < get_class_n_supertypes(clss));
	return clss->attr.cls.supertypes[pos];
}

void set_class_supertype(ir_type *clss, ir_type *supertype, size_t pos)
{
	assert(is_Class_type(clss));
	assert(pos < get_class_n_supertypes(clss));
	clss->attr.cls.supertypes[pos] = supertype;
}

void remove_class_supertype(ir_type *clss, ir_type *supertype)
{
	assert(is_Class_type(clss));
	for (size_t i = 0; i < ARR_LEN(clss->attr.cls.supertypes); ++i) {
		if (clss->attr.cls.supertypes[i] == supertype) {
			for (; i < ARR_LEN(clss->attr.cls.supertypes) - 1; ++i)
				clss->attr.cls.supertypes[i] = clss->attr.cls.supertypes[i+1];
			ARR_SETLEN(ir_type*, clss->attr.cls.supertypes, ARR_LEN(clss->attr.cls.supertypes) - 1);
			break;
		}
	}
}

int (is_Class_type)(const ir_type *clss)
{
	return is_class_type_(clss);
}


ir_type *new_type_struct(ident *name)
{
	ir_type *res = new_type(tpo_struct, sizeof(compound_attr), NULL);
	compound_init(res, name);
	hook_new_type(res);
	return res;
}

size_t get_struct_n_members(const ir_type *strct)
{
	assert(is_Struct_type(strct));
	return get_compound_n_members(strct);
}

ir_entity *get_struct_member(const ir_type *strct, size_t pos)
{
	assert(is_Struct_type(strct));
	return get_compound_member(strct, pos);
}

size_t get_struct_member_index(ir_type const *strct, ir_entity const *const mem)
{
	assert(is_Struct_type(strct));
	return get_compound_member_index(strct, mem);
}

int (is_Struct_type)(const ir_type *strct)
{
	return is_struct_type_(strct);
}

ir_type *new_type_method(size_t const n_param, size_t const n_res, int const is_variadic, unsigned const cc_mask, mtp_additional_properties const property_mask)
{
	ir_type *const res = new_type(tpo_method, sizeof(method_attr), NULL);
	res->flags                       |= tf_layout_fixed;
	res->attr.method.n_params         = n_param;
	res->attr.method.params           = XMALLOCNZ(ir_type*, n_param);
	res->attr.method.n_res            = n_res;
	res->attr.method.res_type         = XMALLOCNZ(ir_type*, n_res);
	res->attr.method.variadic         = is_variadic ? (int)n_param : -1;
	res->attr.method.irg_calling_conv = cc_mask;
	res->attr.method.properties       = property_mask;
	set_type_alignment(res, 1);
	hook_new_type(res);
	return res;
}

ir_type *clone_type_method(ir_type *const tp, int const variadic_index, mtp_additional_properties const property_mask)
{
	assert(is_Method_type(tp));
	ir_mode       *mode     = tp->mode;
	size_t         n_params = tp->attr.method.n_params;
	size_t         n_res    = tp->attr.method.n_res;
	type_dbg_info *db       = tp->dbi;
	ir_type       *res      = new_type(tpo_method, sizeof(method_attr), mode);
	set_type_dbg_info(res, db);

	res->flags                        = tp->flags;
	res->size                         = tp->size;
	res->attr.method.n_params         = n_params;
	res->attr.method.params           = XMALLOCN(ir_type*, n_params);
	MEMCPY(res->attr.method.params, tp->attr.method.params, n_params);
	res->attr.method.n_res            = n_res;
	res->attr.method.res_type         = XMALLOCN(ir_type*, n_res);
	MEMCPY(res->attr.method.res_type, tp->attr.method.res_type, n_res);
	res->attr.method.variadic         = variadic_index;
	res->attr.method.properties       = property_mask;
	res->attr.method.irg_calling_conv = tp->attr.method.irg_calling_conv;
	set_type_alignment(res, get_type_alignment(tp));
	hook_new_type(res);
	return res;
}

void free_method_attrs(ir_type *method)
{
	assert(is_Method_type(method));
	free(method->attr.method.params);
	free(method->attr.method.res_type);
}

size_t (get_method_n_params)(const ir_type *method)
{
	return get_method_n_params_(method);
}

ir_type *get_method_param_type(const ir_type *method, size_t pos)
{
	assert(is_Method_type(method));
	assert(pos < get_method_n_params(method));
	ir_type *res = method->attr.method.params[pos];
	return res;
}

void set_method_param_type(ir_type *method, size_t pos, ir_type *tp)
{
	assert(is_Method_type(method));
	assert(pos < get_method_n_params(method));
	method->attr.method.params[pos] = tp;
}

size_t (get_method_n_ress)(const ir_type *method)
{
	return get_method_n_ress_(method);
}

ir_type *get_method_res_type(const ir_type *method, size_t pos)
{
	assert(is_Method_type(method));
	assert(pos < get_method_n_ress(method));
	ir_type *res = method->attr.method.res_type[pos];
	return res;
}

void set_method_res_type(ir_type *method, size_t pos, ir_type *tp)
{
	assert(is_Method_type(method));
	assert(pos < get_method_n_ress(method));
	method->attr.method.res_type[pos] = tp;
}

int is_method_variadic(ir_type const *const method)
{
	assert(is_Method_type(method));
	return method->attr.method.variadic > -1;
}

int get_method_variadic_index(ir_type const *const method) {
	assert(is_Method_type(method));
	return method->attr.method.variadic;
}

void set_method_variadic_index(ir_type *const method, int variadic_index) {
	assert(is_Method_type(method) && variadic_index >= -1);
	method->attr.method.variadic = variadic_index;
}

mtp_additional_properties (get_method_additional_properties)(const ir_type *method)
{
	return get_method_additional_properties_(method);
}

unsigned (get_method_calling_convention)(const ir_type *method)
{
	return get_method_calling_convention_(method);
}

unsigned get_method_n_regparams(ir_type *method)
{
	unsigned cc = get_method_calling_convention(method);
	assert(IS_FASTCALL(cc));

	return cc & ~cc_bits;
}

int (is_Method_type)(const ir_type *method)
{
	return is_method_type_(method);
}

ir_type *new_type_union(ident *name)
{
	ir_type *res = new_type(tpo_union, sizeof(compound_attr), NULL);
	compound_init(res, name);
	hook_new_type(res);
	return res;
}

size_t get_union_n_members(const ir_type *uni)
{
	assert(is_Union_type(uni));
	return get_compound_n_members(uni);
}

ir_entity *get_union_member(const ir_type *uni, size_t pos)
{
	assert(is_Union_type(uni));
	return get_compound_member(uni, pos);
}

size_t get_union_member_index(ir_type const *uni, ir_entity const *const mem)
{
	assert(is_Union_type(uni));
	return get_compound_member_index(uni, mem);
}

int (is_Union_type)(const ir_type *uni)
{
	return is_union_type_(uni);
}

ir_type *new_type_segment(ident *const name, type_flags const flags)
{
	ir_type *const res = new_type(tpo_segment, sizeof(compound_attr), NULL);
	compound_init(res, name);
	res->flags |= flags;
	return res;
}

int (is_segment_type)(ir_type const *const type)
{
	return is_segment_type_(type);
}

ir_type *new_type_array(ir_type *const element_type, unsigned const n_elements)
{
	assert(!is_Method_type(element_type));

	ir_type *const res = new_type(tpo_array, sizeof(array_attr), NULL);
	res->attr.array.element_type = element_type;
	res->attr.array.size         = n_elements;
	set_type_alignment(res, get_type_alignment(element_type));
	if (n_elements != 0) {
		set_type_size(res, n_elements * get_type_size(element_type));
		set_type_state(res, layout_fixed);
	}

	hook_new_type(res);
	return res;
}

unsigned get_array_size(const ir_type *array)
{
	assert(is_Array_type(array));
	return array->attr.array.size;
}

ir_type *get_array_element_type(const ir_type *array)
{
	assert(is_Array_type(array));
	return array->attr.array.element_type;
}

int (is_Array_type)(const ir_type *array)
{
	return is_array_type_(array);
}


ir_type *new_type_pointer(ir_type *points_to)
{
	ir_mode *const mode = mode_P;
	ir_type *const res  = new_type(tpo_pointer, sizeof(pointer_attr), mode);
	res->attr.pointer.points_to = points_to;
	unsigned size = get_mode_size_bytes(mode);
	res->size = size;
	res->flags |= tf_layout_fixed;
	set_type_alignment(res, size);
	hook_new_type(res);
	return res;
}

void set_pointer_points_to_type(ir_type *pointer, ir_type *tp)
{
	assert(is_Pointer_type(pointer));
	pointer->attr.pointer.points_to = tp;
}

ir_type *get_pointer_points_to_type(const ir_type *pointer)
{
	assert(is_Pointer_type(pointer));
	return pointer->attr.pointer.points_to;
}

int (is_Pointer_type)(const ir_type *pointer)
{
	return is_pointer_type_(pointer);
}


ir_type *new_type_primitive(ir_mode *mode)
{
	unsigned size  = get_mode_size_bytes(mode);
	unsigned align = (size > 0 && size != (unsigned)-1)
	               ? ceil_po2(size) : 1;

	ir_type *res = new_type(tpo_primitive, 0, mode);
	res->size  = size;
	res->flags |= tf_layout_fixed;
	set_type_alignment(res, align);
	hook_new_type(res);
	return res;
}

int (is_Primitive_type)(const ir_type *primitive)
{
	return is_primitive_type_(primitive);
}

int (is_atomic_type)(const ir_type *tp)
{
	return is_atomic_type_(tp);
}

size_t (get_compound_n_members)(ir_type const *const type)
{
	return get_compound_n_members_(type);
}

ir_entity *(get_compound_member)(ir_type const *const type, size_t const pos)
{
	return get_compound_member_(type, pos);
}

size_t get_compound_member_index(ir_type const *const type,
                                 ir_entity const *const entity)
{
	assert(is_compound_type(type));
	for (size_t i = 0, n = get_compound_n_members(type); i < n; ++i) {
		if (get_compound_member(type, i) == entity)
			return i;
	}
	return INVALID_MEMBER_INDEX;
}

int is_compound_type(const ir_type *tp)
{
	assert(tp->kind == k_type);
	return (tp->flags & tf_compound) != 0;
}

ident *get_compound_ident(const ir_type *tp)
{
	assert(is_compound_type(tp));
	return tp->name;
}

const char *get_compound_name(const ir_type *tp)
{
	if (get_compound_ident(tp) == NULL)
		return NULL;
	return get_id_str(get_compound_ident(tp));
}

void remove_compound_member(ir_type *type, ir_entity *member)
{
	assert(is_compound_type(type));
	for (size_t i = 0, n = ARR_LEN(type->attr.compound.members); i < n; ++i) {
		if (get_compound_member(type, i) != member)
			continue;
		for (; i < n - 1; ++i)
			type->attr.compound.members[i] = type->attr.compound.members[i+1];
		ARR_SETLEN(ir_entity*, type->attr.compound.members, n-1);
		/* members of global type must also be removed from map */
		if (is_segment_type(type) && !(type->flags & tf_info)
		 && get_entity_visibility(member) != ir_visibility_private) {
			pmap *globals = irp->globals;
			pmap_insert(globals, get_entity_ld_ident(member), NULL);
		}
		break;
	}
}

void add_compound_member(ir_type *type, ir_entity *entity)
{
	assert(is_compound_type(type));
	/* try to detect double-add */
	ARR_APP1(ir_entity *, type->attr.compound.members, entity);
	/* Add segment members to globals map. */
	if (is_segment_type(type) && !(type->flags & tf_info)
	 && get_entity_visibility(entity) != ir_visibility_private) {
		ident *id = get_entity_ld_ident(entity);
		pmap *globals = irp->globals;
		/* Globals must have unique names. */
		assert(NULL == pmap_get(ir_entity, globals, id));
		pmap_insert(globals, id, entity);
	}
}

int is_code_type(ir_type const *const type)
{
	return get_type_opcode(type) == tpo_code;
}

int is_unknown_type(ir_type const *const type)
{
	return get_type_opcode(type) == tpo_unknown;
}

int is_frame_type(ir_type const *const type)
{
	assert(type->kind == k_type);
	return (type->flags & tf_frame_type) != 0;
}

ir_type *new_type_frame(void)
{
	ir_type *res = new_type_class(new_id_from_str("<frame_type>"));
	res->flags |= tf_frame_type;

	return res;
}

ir_type *clone_frame_type(ir_type *type)
{
	assert(is_frame_type(type));
	/* the entity link resource should be allocated if this function is called */
	assert(irp_resources_reserved(irp) & IRP_RESOURCE_ENTITY_LINK);

	ir_type *res = new_type_frame();
	for (size_t i = 0, n = get_compound_n_members(type); i < n; ++i) {
		ir_entity *ent  = get_compound_member(type, i);
		ident     *name = get_entity_ident(ent);
		ir_entity *nent = clone_entity(ent, name, res);
		set_entity_link(ent, nent);
		set_entity_link(nent, ent);
	}
	return res;
}

void default_layout_compound_type(ir_type *type)
{
	unsigned size      = 0;
	unsigned align_all = 1;
	for (size_t i = 0, n = get_compound_n_members(type); i < n; ++i) {
		ir_entity *entity      = get_compound_member(type, i);
		ir_type   *entity_type = get_entity_type(entity);
		if (is_Method_type(entity_type))
			continue;

		if (get_entity_bitfield_size(entity) > 0) {
			panic("default_layout_compound_type() cannot handle bitfield members (in %+F)", type);
		}

		unsigned entity_size;
		if (i + 1 == n && is_Array_type(entity_type) && get_array_size(entity_type) == 0) {
			entity_size = 0;
		} else {
			assert(get_type_state(entity_type) == layout_fixed);
			entity_size = get_type_size(entity_type);
		}

		unsigned const align = get_type_alignment(entity_type);
		align_all = MAX(align, align_all);

		unsigned offset;
		if (is_Union_type(type)) {
			offset = 0;
			size   = MAX(size, entity_size);
		} else {
			if (align != 0) {
				unsigned const misalign = size % align;
				if (misalign != 0)
					size += align - misalign;
			}
			offset = size;
			size  += entity_size;
		}
		set_entity_offset(entity, offset);
	}

	unsigned const misalign = size % align_all;
	if (misalign != 0)
		size += align_all - misalign;
	set_type_alignment(type, align_all);
	set_type_size(type, size);
	set_type_state(type, layout_fixed);
}

void ir_print_type(char *buffer, size_t buffer_size, const ir_type *type)
{
	type_dbg_info *tdbgi = get_type_dbg_info(type);
	if (tdbgi != NULL) {
		ir_retrieve_type_dbg_info(buffer, buffer_size, tdbgi);
		return;
	}

	/* we have to construct some name... */
	switch (get_type_opcode(type)) {
	case tpo_uninitialized:
		break;
	case tpo_code:
		snprintf(buffer, buffer_size, "code");
		return;

	{
		char const *kind_name;
	case tpo_class:   kind_name = "class";   goto compound;
	case tpo_struct:  kind_name = "struct";  goto compound;
	case tpo_union:   kind_name = "union";   goto compound;
	case tpo_segment: kind_name = "segment"; goto compound;
compound:
		snprintf(buffer, buffer_size, "%s '%s'", kind_name, get_compound_name(type));
		return;
	}

	case tpo_unknown:
		snprintf(buffer, buffer_size, "unknown type");
		return;

	case tpo_pointer: {
		int p = snprintf(buffer, buffer_size, "pointer to ");
		buffer      += p;
		buffer_size -= p;
		ir_print_type(buffer, buffer_size, get_pointer_points_to_type(type));
		return;
	}

	case tpo_array: {
		int p = snprintf(buffer, buffer_size, "array of ");
		buffer      += p;
		buffer_size -= p;
		ir_print_type(buffer, buffer_size, get_array_element_type(type));
		return;
	}

	case tpo_primitive: {
		ident *id = get_mode_ident(get_type_mode(type));
		snprintf(buffer, buffer_size, "%s", get_id_str(id));
		return;
	}

	case tpo_method:
		/* TODO: we should print argument and return types here... */
		snprintf(buffer, buffer_size, "method type");
		return;
	}
	snprintf(buffer, buffer_size, "invalid type");
}
