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

/**
 * @file
 * @brief   Write textual representation of firm to file.
 * @author  <NAME>, <NAME>
 */
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdarg.h>

#include "irio.h"

#include "irnode_t.h"
#include "irprog.h"
#include "irgraph_t.h"
#include "irprintf.h"
#include "ircons_t.h"
#include "irgmod.h"
#include "irflag_t.h"
#include "irgwalk.h"
#include "tv_t.h"
#include "array.h"
#include "error.h"
#include "typerep.h"
#include "set.h"
#include "obst.h"
#include "pmap.h"
#include "pdeq.h"

#define SYMERROR ((unsigned) ~0)

static void register_generated_node_readers(void);
static void register_generated_node_writers(void);

typedef struct delayed_initializer_t {
	ir_initializer_t *initializer;
	long              node_nr;
} delayed_initializer_t;

typedef struct delayed_pred_t {
	ir_node *node;
	int      n_preds;
	long     preds[];
} delayed_pred_t;

typedef struct read_env_t {
	int            c;           /**< currently read char */
	FILE          *file;
	const char    *inputname;
	unsigned       line;

	ir_graph      *irg;
	set           *idset;       /**< id_entry set, which maps from file ids to
	                                 new Firm elements */
	ir_type      **fixedtypes;
	bool           read_errors;
	struct obstack obst;
	struct obstack preds_obst;
	delayed_initializer_t *delayed_initializers;
	const delayed_pred_t **delayed_preds;
} read_env_t;

typedef struct write_env_t {
	FILE *file;
	pdeq *write_queue;
	pdeq *entity_queue;
} write_env_t;

typedef enum typetag_t {
	tt_align,
	tt_builtin_kind,
	tt_cond_jmp_predicate,
	tt_initializer,
	tt_keyword,
	tt_linkage,
	tt_mode_arithmetic,
	tt_pin_state,
	tt_segment,
	tt_throws,
	tt_tpo,
	tt_type_state,
	tt_visibility,
	tt_volatility,
} typetag_t;

typedef enum keyword_t {
	kw_asm,
	kw_compound_member,
	kw_constirg,
	kw_entity,
	kw_float_mode,
	kw_int_mode,
	kw_irg,
	kw_alias,
	kw_gotentry,
	kw_label,
	kw_method,
	kw_modes,
	kw_parameter,
	kw_program,
	kw_reference_mode,
	kw_segment_type,
	kw_type,
	kw_typegraph,
	kw_unknown,
} keyword_t;

typedef struct symbol_t {
	const char *str;      /**< The name of this symbol. */
	typetag_t   typetag;  /**< The type tag of this symbol. */
	unsigned    code;     /**< The value of this symbol. */
} symbol_t;

typedef struct id_entry {
	long id;
	void *elem;
} id_entry;

/** The symbol table, a set of symbol_t elements. */
static set *symtbl;

/**
 * Compare two symbol table entries.
 */
static int symbol_cmp(const void *elt, const void *key, size_t size)
{
	int res;
	const symbol_t *entry = (const symbol_t *) elt;
	const symbol_t *keyentry = (const symbol_t *) key;
	(void) size;
	res = entry->typetag - keyentry->typetag;
	if (res) return res;
	return strcmp(entry->str, keyentry->str);
}

static int id_cmp(const void *elt, const void *key, size_t size)
{
	const id_entry *entry = (const id_entry *) elt;
	const id_entry *keyentry = (const id_entry *) key;
	(void) size;
	return entry->id - keyentry->id;
}

static void __attribute__((format(printf, 2, 3)))
parse_error(read_env_t *env, const char *fmt, ...)
{
	va_list  ap;
	unsigned line = env->line;

	/* workaround read_c "feature" that a '\n' triggers the line++
	 * instead of the character after the '\n' */
	if (env->c == '\n') {
		line--;
	}

	fprintf(stderr, "%s:%u: error ", env->inputname, line);
	env->read_errors = true;

	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	va_end(ap);
}

/** Initializes the symbol table. May be called more than once without problems. */
static void symtbl_init(void)
{
	symbol_t key;

	/* Only initialize once */
	if (symtbl != NULL)
		return;

	symtbl = new_set(symbol_cmp, 256);

#define INSERT(tt, s, cod)                                       \
	key.str = (s);                                               \
	key.typetag = (tt);                                          \
	key.code = (cod);                                            \
	(void)set_insert(symbol_t, symtbl, &key, sizeof(key), hash_str(s) + tt * 17)

#define INSERTENUM(tt, e) INSERT(tt, #e, e)
#define INSERTKEYWORD(k) INSERT(tt_keyword, #k, kw_##k)

	INSERT(tt_tpo, "array", tpo_array);
	INSERT(tt_tpo, "class", tpo_class);
	INSERT(tt_tpo, "method", tpo_method);
	INSERT(tt_tpo, "pointer", tpo_pointer);
	INSERT(tt_tpo, "primitive", tpo_primitive);
	INSERT(tt_tpo, "struct", tpo_struct);
	INSERT(tt_tpo, "union", tpo_union);
	INSERT(tt_tpo, "Unknown", tpo_unknown);

	INSERT(tt_segment, "global", IR_SEGMENT_GLOBAL);
	INSERT(tt_segment, "thread_local", IR_SEGMENT_THREAD_LOCAL);
	INSERT(tt_segment, "constructors", IR_SEGMENT_CONSTRUCTORS);
	INSERT(tt_segment, "destructors", IR_SEGMENT_DESTRUCTORS);

	INSERT(tt_linkage, "constant", IR_LINKAGE_CONSTANT);
	INSERT(tt_linkage, "weak", IR_LINKAGE_WEAK);
	INSERT(tt_linkage, "garbage_collect", IR_LINKAGE_GARBAGE_COLLECT);
	INSERT(tt_linkage, "merge", IR_LINKAGE_MERGE);
	INSERT(tt_linkage, "hidden_user", IR_LINKAGE_HIDDEN_USER);

	INSERT(tt_visibility, "local", ir_visibility_local);
	INSERT(tt_visibility, "external", ir_visibility_external);
	INSERT(tt_visibility, "private", ir_visibility_private);

	INSERT(tt_throws, "throw",   true);
	INSERT(tt_throws, "nothrow", false);

	INSERTKEYWORD(alias);
	INSERTKEYWORD(asm);
	INSERTKEYWORD(compound_member);
	INSERTKEYWORD(constirg);
	INSERTKEYWORD(entity);
	INSERTKEYWORD(float_mode);
	INSERTKEYWORD(gotentry);
	INSERTKEYWORD(int_mode);
	INSERTKEYWORD(irg);
	INSERTKEYWORD(label);
	INSERTKEYWORD(method);
	INSERTKEYWORD(modes);
	INSERTKEYWORD(parameter);
	INSERTKEYWORD(program);
	INSERTKEYWORD(reference_mode);
	INSERTKEYWORD(segment_type);
	INSERTKEYWORD(type);
	INSERTKEYWORD(typegraph);
	INSERTKEYWORD(unknown);

	INSERTENUM(tt_align, align_non_aligned);
	INSERTENUM(tt_align, align_is_aligned);

	INSERTENUM(tt_builtin_kind, ir_bk_trap);
	INSERTENUM(tt_builtin_kind, ir_bk_debugbreak);
	INSERTENUM(tt_builtin_kind, ir_bk_return_address);
	INSERTENUM(tt_builtin_kind, ir_bk_frame_address);
	INSERTENUM(tt_builtin_kind, ir_bk_prefetch);
	INSERTENUM(tt_builtin_kind, ir_bk_ffs);
	INSERTENUM(tt_builtin_kind, ir_bk_clz);
	INSERTENUM(tt_builtin_kind, ir_bk_ctz);
	INSERTENUM(tt_builtin_kind, ir_bk_popcount);
	INSERTENUM(tt_builtin_kind, ir_bk_parity);
	INSERTENUM(tt_builtin_kind, ir_bk_bswap);
	INSERTENUM(tt_builtin_kind, ir_bk_inport);
	INSERTENUM(tt_builtin_kind, ir_bk_outport);
	INSERTENUM(tt_builtin_kind, ir_bk_inner_trampoline);
	INSERTENUM(tt_builtin_kind, ir_bk_saturating_increment);
	INSERTENUM(tt_builtin_kind, ir_bk_compare_swap);

	INSERTENUM(tt_cond_jmp_predicate, COND_JMP_PRED_NONE);
	INSERTENUM(tt_cond_jmp_predicate, COND_JMP_PRED_TRUE);
	INSERTENUM(tt_cond_jmp_predicate, COND_JMP_PRED_FALSE);

	INSERTENUM(tt_initializer, IR_INITIALIZER_CONST);
	INSERTENUM(tt_initializer, IR_INITIALIZER_TARVAL);
	INSERTENUM(tt_initializer, IR_INITIALIZER_NULL);
	INSERTENUM(tt_initializer, IR_INITIALIZER_COMPOUND);

	INSERT(tt_mode_arithmetic, "none",               irma_none);
	INSERT(tt_mode_arithmetic, "twos_complement",    irma_twos_complement);
	INSERT(tt_mode_arithmetic, "ieee754",            irma_ieee754);
	INSERT(tt_mode_arithmetic, "x86_extended_float", irma_x86_extended_float);

	INSERTENUM(tt_pin_state, op_pin_state_floats);
	INSERTENUM(tt_pin_state, op_pin_state_pinned);
	INSERTENUM(tt_pin_state, op_pin_state_exc_pinned);

	INSERTENUM(tt_type_state, layout_undefined);
	INSERTENUM(tt_type_state, layout_fixed);

	INSERTENUM(tt_volatility, volatility_non_volatile);
	INSERTENUM(tt_volatility, volatility_is_volatile);

#undef INSERTKEYWORD
#undef INSERTENUM
#undef INSERT
}

static const char *get_segment_name(ir_segment_t segment)
{
	switch (segment) {
	case IR_SEGMENT_GLOBAL:       return "global";
	case IR_SEGMENT_THREAD_LOCAL: return "thread_local";
	case IR_SEGMENT_CONSTRUCTORS: return "constructors";
	case IR_SEGMENT_DESTRUCTORS:  return "destructors";
	}
	panic("INVALID_SEGMENT");
}

static const char *get_visibility_name(ir_visibility visibility)
{
	switch (visibility) {
	case ir_visibility_local:    return "local";
	case ir_visibility_external: return "external";
	case ir_visibility_private:  return "private";
	}
	panic("INVALID_VISIBILITY");
}

static const char *get_mode_arithmetic_name(ir_mode_arithmetic arithmetic)
{
	switch (arithmetic) {
	case irma_none:               return "none";
	case irma_twos_complement:    return "twos_complement";
	case irma_ieee754:            return "ieee754";
	case irma_x86_extended_float: return "x86_extended_float";
	}
	panic("invalid mode_arithmetic");
}

/** Returns the according symbol value for the given string and tag, or SYMERROR if none was found. */
static unsigned symbol(const char *str, typetag_t typetag)
{
	symbol_t key, *entry;

	key.str = str;
	key.typetag = typetag;

	entry = set_find(symbol_t, symtbl, &key, sizeof(key), hash_str(str) + typetag * 17);
	return entry ? entry->code : SYMERROR;
}

static void write_long(write_env_t *env, long value)
{
	fprintf(env->file, "%ld ", value);
}

static void write_int(write_env_t *env, int value)
{
	fprintf(env->file, "%d ", value);
}

static void write_unsigned(write_env_t *env, unsigned value)
{
	fprintf(env->file, "%u ", value);
}

static void write_size_t(write_env_t *env, size_t value)
{
	ir_fprintf(env->file, "%zu ", value);
}

static void write_symbol(write_env_t *env, const char *symbol)
{
	fputs(symbol, env->file);
	fputc(' ', env->file);
}

static void write_entity_ref(write_env_t *env, ir_entity *entity)
{
	write_long(env, get_entity_nr(entity));
}

static void write_type_ref(write_env_t *env, ir_type *type)
{
	switch (get_type_tpop_code(type)) {
	case tpo_unknown:
		write_symbol(env, "unknown");
		return;
	case tpo_code:
		write_symbol(env, "code");
		return;
	default:
		break;
	}
	write_long(env, get_type_nr(type));
}

static void write_string(write_env_t *env, const char *string)
{
	const char *c;
	fputc('"', env->file);
	for (c = string; *c != '\0'; ++c) {
		switch (*c) {
		case '\n':
			fputc('\\', env->file);
			fputc('n', env->file);
			break;
		case '"':
		case '\\':
			fputc('\\', env->file);
			/* FALLTHROUGH */
		default:
			fputc(*c, env->file);
			break;
		}
	}
	fputc('"', env->file);
	fputc(' ', env->file);
}

static void write_ident(write_env_t *env, ident *id)
{
	write_string(env, get_id_str(id));
}

static void write_ident_null(write_env_t *env, ident *id)
{
	if (id == NULL) {
		fputs("NULL ", env->file);
	} else {
		write_ident(env, id);
	}
}

static void write_mode_ref(write_env_t *env, ir_mode *mode)
{
	write_string(env, get_mode_name(mode));
}

static void write_tarval(write_env_t *env, ir_tarval *tv)
{
	ir_mode *mode = get_tarval_mode(tv);
	write_mode_ref(env, mode);
	char buf[128];
	const char *ascii = ir_tarval_to_ascii(buf, sizeof(buf), tv);
	fputs(ascii, env->file);
	fputc(' ', env->file);
}

static void write_align(write_env_t *env, ir_align align)
{
	fputs(get_align_name(align), env->file);
	fputc(' ', env->file);
}

static void write_builtin_kind(write_env_t *env, const ir_node *node)
{
	fputs(get_builtin_kind_name(get_Builtin_kind(node)), env->file);
	fputc(' ', env->file);
}

static void write_cond_jmp_predicate(write_env_t *env, const ir_node *node)
{
	fputs(get_cond_jmp_predicate_name(get_Cond_jmp_pred(node)), env->file);
	fputc(' ', env->file);
}

static void write_relation(write_env_t *env, ir_relation relation)
{
	write_long(env, (long)relation);
}

static void write_throws(write_env_t *env, bool throws)
{
	write_symbol(env, throws ? "throw" : "nothrow");
}

static void write_list_begin(write_env_t *env)
{
	fputs("[", env->file);
}

static void write_list_end(write_env_t *env)
{
	fputs("] ", env->file);
}

static void write_scope_begin(write_env_t *env)
{
	fputs("{\n", env->file);
}

static void write_scope_end(write_env_t *env)
{
	fputs("}\n\n", env->file);
}

static void write_node_ref(write_env_t *env, const ir_node *node)
{
	write_long(env, get_irn_node_nr(node));
}

static void write_initializer(write_env_t *env, ir_initializer_t *ini)
{
	FILE *f = env->file;
	ir_initializer_kind_t ini_kind = get_initializer_kind(ini);

	fputs(get_initializer_kind_name(ini_kind), f);
	fputc(' ', f);

	switch (ini_kind) {
	case IR_INITIALIZER_CONST:
		write_node_ref(env, get_initializer_const_value(ini));
		return;

	case IR_INITIALIZER_TARVAL:
		write_tarval(env, get_initializer_tarval_value(ini));
		return;

	case IR_INITIALIZER_NULL:
		return;

	case IR_INITIALIZER_COMPOUND: {
		size_t i, n = get_initializer_compound_n_entries(ini);
		write_size_t(env, n);
		for (i = 0; i < n; ++i)
			write_initializer(env, get_initializer_compound_value(ini, i));
		return;
	}
	}
	panic("Unknown initializer kind");
}

static void write_pin_state(write_env_t *env, op_pin_state state)
{
	fputs(get_op_pin_state_name(state), env->file);
	fputc(' ', env->file);
}

static void write_volatility(write_env_t *env, ir_volatility vol)
{
	fputs(get_volatility_name(vol), env->file);
	fputc(' ', env->file);
}

static void write_type_state(write_env_t *env, ir_type_state state)
{
	fputs(get_type_state_name(state), env->file);
	fputc(' ', env->file);
}

static void write_visibility(write_env_t *env, ir_visibility visibility)
{
	fputs(get_visibility_name(visibility), env->file);
	fputc(' ', env->file);
}

static void write_mode_arithmetic(write_env_t *env, ir_mode_arithmetic arithmetic)
{
	fputs(get_mode_arithmetic_name(arithmetic), env->file);
	fputc(' ', env->file);
}

static void write_type_common(write_env_t *env, ir_type *tp)
{
	fputc('\t', env->file);
	write_symbol(env, "type");
	write_long(env, get_type_nr(tp));
	write_symbol(env, get_type_tpop_name(tp));
	write_unsigned(env, get_type_size_bytes(tp));
	write_unsigned(env, get_type_alignment_bytes(tp));
	write_type_state(env, get_type_state(tp));
	write_unsigned(env, tp->flags);
}

static void write_type(write_env_t *env, ir_type *tp);

static void write_type_primitive(write_env_t *env, ir_type *tp)
{
	write_type_common(env, tp);
	write_mode_ref(env, get_type_mode(tp));
	fputc('\n', env->file);
}

static void write_type_compound(write_env_t *env, ir_type *tp)
{
	size_t n_members = get_compound_n_members(tp);
	size_t i;

	if (is_Class_type(tp)) {
		if (get_class_n_subtypes(tp) > 0 || get_class_n_supertypes(tp) > 0 || get_class_vtable_size(tp) > 0) {
			/* sub/superclass export not implemented yet, it's unclear whether
			 * class types will stay in libfirm anyway */
			panic("can't export class types yet");
		}
	}
	write_type_common(env, tp);
	write_ident_null(env, get_compound_ident(tp));
	fputc('\n', env->file);

	for (i = 0; i < n_members; ++i) {
		ir_entity *member = get_compound_member(tp, i);
		pdeq_putr(env->entity_queue, member);
	}
}

static void write_type_array(write_env_t *env, ir_type *tp)
{
	size_t     n_dimensions   = get_array_n_dimensions(tp);
	ir_type   *element_type   = get_array_element_type(tp);
	ir_entity *element_entity = get_array_element_entity(tp);
	size_t   i;

	write_type(env, element_type);

	write_type_common(env, tp);
	write_size_t(env, n_dimensions);
	write_type_ref(env, get_array_element_type(tp));
	for (i = 0; i < n_dimensions; i++) {
		ir_node *lower = get_array_lower_bound(tp, i);
		ir_node *upper = get_array_upper_bound(tp, i);

		if (is_Const(lower))
			write_long(env, get_tarval_long(get_Const_tarval(lower)));
		else
			panic("Lower array bound is not constant");

		if (is_Const(upper))
			write_long(env, get_tarval_long(get_Const_tarval(upper)));
		else if (is_Unknown(upper))
			write_symbol(env, "unknown");
		else
			panic("Upper array bound is not constant");
	}
	/* note that we just write a reference to the element entity
	 * but never the entity itself */
	write_entity_ref(env, element_entity);
	fputc('\n', env->file);
}

static void write_type_method(write_env_t *env, ir_type *tp)
{
	size_t nparams  = get_method_n_params(tp);
	size_t nresults = get_method_n_ress(tp);
	size_t i;

	for (i = 0; i < nparams; i++)
		write_type(env, get_method_param_type(tp, i));
	for (i = 0; i < nresults; i++)
		write_type(env, get_method_res_type(tp, i));

	write_type_common(env, tp);
	write_unsigned(env, get_method_calling_convention(tp));
	write_unsigned(env, get_method_additional_properties(tp));
	write_size_t(env, nparams);
	write_size_t(env, nresults);
	for (i = 0; i < nparams; i++)
		write_type_ref(env, get_method_param_type(tp, i));
	for (i = 0; i < nresults; i++)
		write_type_ref(env, get_method_res_type(tp, i));
	write_unsigned(env, get_method_variadicity(tp));
	fputc('\n', env->file);
}

static void write_type_pointer(write_env_t *env, ir_type *tp)
{
	ir_type *points_to = get_pointer_points_to_type(tp);

	write_type(env, points_to);

	write_type_common(env, tp);
	write_mode_ref(env, get_type_mode(tp));
	write_type_ref(env, points_to);
	fputc('\n', env->file);
}

static void write_type(write_env_t *env, ir_type *tp)
{
	if (type_visited(tp))
		return;
	mark_type_visited(tp);

	switch ((tp_opcode)get_type_tpop_code(tp)) {
	case tpo_unknown:
	case tpo_code:
	case tpo_uninitialized:
		/* no need to write special builtin types */
		return;

	case tpo_union:
	case tpo_struct:
	case tpo_class:
		write_type_compound(env, tp);
		return;

	case tpo_primitive:    write_type_primitive(env, tp);   return;
	case tpo_method:       write_type_method(env, tp);      return;
	case tpo_pointer:      write_type_pointer(env, tp);     return;
	case tpo_array:        write_type_array(env, tp);       return;
	}
	panic("can't write invalid type %+F\n", tp);
}

static void write_entity(write_env_t *env, ir_entity *ent)
{
	ir_type       *type       = get_entity_type(ent);
	ir_type       *owner      = get_entity_owner(ent);
	ir_visibility  visibility = get_entity_visibility(ent);
	ir_linkage     linkage    = get_entity_linkage(ent);

	if (entity_visited(ent))
		return;
	mark_entity_visited(ent);

	write_type(env, type);
	write_type(env, owner);

	fputc('\t', env->file);
	switch ((ir_entity_kind)ent->entity_kind) {
	case IR_ENTITY_ALIAS:           write_symbol(env, "alias");           break;
	case IR_ENTITY_GOTENTRY:        write_symbol(env, "gotentry");        break;
	case IR_ENTITY_NORMAL:          write_symbol(env, "entity");          break;
	case IR_ENTITY_METHOD:          write_symbol(env, "method");          break;
	case IR_ENTITY_LABEL:           write_symbol(env, "label");           break;
	case IR_ENTITY_COMPOUND_MEMBER: write_symbol(env, "compound_member"); break;
	case IR_ENTITY_PARAMETER:       write_symbol(env, "parameter");       break;
	case IR_ENTITY_UNKNOWN:
		write_symbol(env, "unknown");
		write_long(env, get_entity_nr(ent));
		return;
	}
	write_long(env, get_entity_nr(ent));

	if (ent->entity_kind != IR_ENTITY_LABEL<commit_msg>irio: fix writing of alias entities<commit_after>/*
 * This file is part of libFirm.
 * Copyright (C) 2012 University of Karlsruhe.
 */

/**
 * @file
 * @brief   Write textual representation of firm to file.
 * @author  <NAME>, <NAME>
 */
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdarg.h>

#include "irio.h"

#include "irnode_t.h"
#include "irprog.h"
#include "irgraph_t.h"
#include "irprintf.h"
#include "ircons_t.h"
#include "irgmod.h"
#include "irflag_t.h"
#include "irgwalk.h"
#include "tv_t.h"
#include "array.h"
#include "error.h"
#include "typerep.h"
#include "set.h"
#include "obst.h"
#include "pmap.h"
#include "pdeq.h"

#define SYMERROR ((unsigned) ~0)

static void register_generated_node_readers(void);
static void register_generated_node_writers(void);

typedef struct delayed_initializer_t {
	ir_initializer_t *initializer;
	long              node_nr;
} delayed_initializer_t;

typedef struct delayed_pred_t {
	ir_node *node;
	int      n_preds;
	long     preds[];
} delayed_pred_t;

typedef struct read_env_t {
	int            c;           /**< currently read char */
	FILE          *file;
	const char    *inputname;
	unsigned       line;

	ir_graph      *irg;
	set           *idset;       /**< id_entry set, which maps from file ids to
	                                 new Firm elements */
	ir_type      **fixedtypes;
	bool           read_errors;
	struct obstack obst;
	struct obstack preds_obst;
	delayed_initializer_t *delayed_initializers;
	const delayed_pred_t **delayed_preds;
} read_env_t;

typedef struct write_env_t {
	FILE *file;
	pdeq *write_queue;
	pdeq *entity_queue;
} write_env_t;

typedef enum typetag_t {
	tt_align,
	tt_builtin_kind,
	tt_cond_jmp_predicate,
	tt_initializer,
	tt_keyword,
	tt_linkage,
	tt_mode_arithmetic,
	tt_pin_state,
	tt_segment,
	tt_throws,
	tt_tpo,
	tt_type_state,
	tt_visibility,
	tt_volatility,
} typetag_t;

typedef enum keyword_t {
	kw_asm,
	kw_compound_member,
	kw_constirg,
	kw_entity,
	kw_float_mode,
	kw_int_mode,
	kw_irg,
	kw_alias,
	kw_gotentry,
	kw_label,
	kw_method,
	kw_modes,
	kw_parameter,
	kw_program,
	kw_reference_mode,
	kw_segment_type,
	kw_type,
	kw_typegraph,
	kw_unknown,
} keyword_t;

typedef struct symbol_t {
	const char *str;      /**< The name of this symbol. */
	typetag_t   typetag;  /**< The type tag of this symbol. */
	unsigned    code;     /**< The value of this symbol. */
} symbol_t;

typedef struct id_entry {
	long id;
	void *elem;
} id_entry;

/** The symbol table, a set of symbol_t elements. */
static set *symtbl;

/**
 * Compare two symbol table entries.
 */
static int symbol_cmp(const void *elt, const void *key, size_t size)
{
	int res;
	const symbol_t *entry = (const symbol_t *) elt;
	const symbol_t *keyentry = (const symbol_t *) key;
	(void) size;
	res = entry->typetag - keyentry->typetag;
	if (res) return res;
	return strcmp(entry->str, keyentry->str);
}

static int id_cmp(const void *elt, const void *key, size_t size)
{
	const id_entry *entry = (const id_entry *) elt;
	const id_entry *keyentry = (const id_entry *) key;
	(void) size;
	return entry->id - keyentry->id;
}

static void __attribute__((format(printf, 2, 3)))
parse_error(read_env_t *env, const char *fmt, ...)
{
	va_list  ap;
	unsigned line = env->line;

	/* workaround read_c "feature" that a '\n' triggers the line++
	 * instead of the character after the '\n' */
	if (env->c == '\n') {
		line--;
	}

	fprintf(stderr, "%s:%u: error ", env->inputname, line);
	env->read_errors = true;

	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	va_end(ap);
}

/** Initializes the symbol table. May be called more than once without problems. */
static void symtbl_init(void)
{
	symbol_t key;

	/* Only initialize once */
	if (symtbl != NULL)
		return;

	symtbl = new_set(symbol_cmp, 256);

#define INSERT(tt, s, cod)                                       \
	key.str = (s);                                               \
	key.typetag = (tt);                                          \
	key.code = (cod);                                            \
	(void)set_insert(symbol_t, symtbl, &key, sizeof(key), hash_str(s) + tt * 17)

#define INSERTENUM(tt, e) INSERT(tt, #e, e)
#define INSERTKEYWORD(k) INSERT(tt_keyword, #k, kw_##k)

	INSERT(tt_tpo, "array", tpo_array);
	INSERT(tt_tpo, "class", tpo_class);
	INSERT(tt_tpo, "method", tpo_method);
	INSERT(tt_tpo, "pointer", tpo_pointer);
	INSERT(tt_tpo, "primitive", tpo_primitive);
	INSERT(tt_tpo, "struct", tpo_struct);
	INSERT(tt_tpo, "union", tpo_union);
	INSERT(tt_tpo, "Unknown", tpo_unknown);

	INSERT(tt_segment, "global", IR_SEGMENT_GLOBAL);
	INSERT(tt_segment, "thread_local", IR_SEGMENT_THREAD_LOCAL);
	INSERT(tt_segment, "constructors", IR_SEGMENT_CONSTRUCTORS);
	INSERT(tt_segment, "destructors", IR_SEGMENT_DESTRUCTORS);

	INSERT(tt_linkage, "constant", IR_LINKAGE_CONSTANT);
	INSERT(tt_linkage, "weak", IR_LINKAGE_WEAK);
	INSERT(tt_linkage, "garbage_collect", IR_LINKAGE_GARBAGE_COLLECT);
	INSERT(tt_linkage, "merge", IR_LINKAGE_MERGE);
	INSERT(tt_linkage, "hidden_user", IR_LINKAGE_HIDDEN_USER);

	INSERT(tt_visibility, "local", ir_visibility_local);
	INSERT(tt_visibility, "external", ir_visibility_external);
	INSERT(tt_visibility, "private", ir_visibility_private);

	INSERT(tt_throws, "throw",   true);
	INSERT(tt_throws, "nothrow", false);

	INSERTKEYWORD(alias);
	INSERTKEYWORD(asm);
	INSERTKEYWORD(compound_member);
	INSERTKEYWORD(constirg);
	INSERTKEYWORD(entity);
	INSERTKEYWORD(float_mode);
	INSERTKEYWORD(gotentry);
	INSERTKEYWORD(int_mode);
	INSERTKEYWORD(irg);
	INSERTKEYWORD(label);
	INSERTKEYWORD(method);
	INSERTKEYWORD(modes);
	INSERTKEYWORD(parameter);
	INSERTKEYWORD(program);
	INSERTKEYWORD(reference_mode);
	INSERTKEYWORD(segment_type);
	INSERTKEYWORD(type);
	INSERTKEYWORD(typegraph);
	INSERTKEYWORD(unknown);

	INSERTENUM(tt_align, align_non_aligned);
	INSERTENUM(tt_align, align_is_aligned);

	INSERTENUM(tt_builtin_kind, ir_bk_trap);
	INSERTENUM(tt_builtin_kind, ir_bk_debugbreak);
	INSERTENUM(tt_builtin_kind, ir_bk_return_address);
	INSERTENUM(tt_builtin_kind, ir_bk_frame_address);
	INSERTENUM(tt_builtin_kind, ir_bk_prefetch);
	INSERTENUM(tt_builtin_kind, ir_bk_ffs);
	INSERTENUM(tt_builtin_kind, ir_bk_clz);
	INSERTENUM(tt_builtin_kind, ir_bk_ctz);
	INSERTENUM(tt_builtin_kind, ir_bk_popcount);
	INSERTENUM(tt_builtin_kind, ir_bk_parity);
	INSERTENUM(tt_builtin_kind, ir_bk_bswap);
	INSERTENUM(tt_builtin_kind, ir_bk_inport);
	INSERTENUM(tt_builtin_kind, ir_bk_outport);
	INSERTENUM(tt_builtin_kind, ir_bk_inner_trampoline);
	INSERTENUM(tt_builtin_kind, ir_bk_saturating_increment);
	INSERTENUM(tt_builtin_kind, ir_bk_compare_swap);

	INSERTENUM(tt_cond_jmp_predicate, COND_JMP_PRED_NONE);
	INSERTENUM(tt_cond_jmp_predicate, COND_JMP_PRED_TRUE);
	INSERTENUM(tt_cond_jmp_predicate, COND_JMP_PRED_FALSE);

	INSERTENUM(tt_initializer, IR_INITIALIZER_CONST);
	INSERTENUM(tt_initializer, IR_INITIALIZER_TARVAL);
	INSERTENUM(tt_initializer, IR_INITIALIZER_NULL);
	INSERTENUM(tt_initializer, IR_INITIALIZER_COMPOUND);

	INSERT(tt_mode_arithmetic, "none",               irma_none);
	INSERT(tt_mode_arithmetic, "twos_complement",    irma_twos_complement);
	INSERT(tt_mode_arithmetic, "ieee754",            irma_ieee754);
	INSERT(tt_mode_arithmetic, "x86_extended_float", irma_x86_extended_float);

	INSERTENUM(tt_pin_state, op_pin_state_floats);
	INSERTENUM(tt_pin_state, op_pin_state_pinned);
	INSERTENUM(tt_pin_state, op_pin_state_exc_pinned);

	INSERTENUM(tt_type_state, layout_undefined);
	INSERTENUM(tt_type_state, layout_fixed);

	INSERTENUM(tt_volatility, volatility_non_volatile);
	INSERTENUM(tt_volatility, volatility_is_volatile);

#undef INSERTKEYWORD
#undef INSERTENUM
#undef INSERT
}

static const char *get_segment_name(ir_segment_t segment)
{
	switch (segment) {
	case IR_SEGMENT_GLOBAL:       return "global";
	case IR_SEGMENT_THREAD_LOCAL: return "thread_local";
	case IR_SEGMENT_CONSTRUCTORS: return "constructors";
	case IR_SEGMENT_DESTRUCTORS:  return "destructors";
	}
	panic("INVALID_SEGMENT");
}

static const char *get_visibility_name(ir_visibility visibility)
{
	switch (visibility) {
	case ir_visibility_local:    return "local";
	case ir_visibility_external: return "external";
	case ir_visibility_private:  return "private";
	}
	panic("INVALID_VISIBILITY");
}

static const char *get_mode_arithmetic_name(ir_mode_arithmetic arithmetic)
{
	switch (arithmetic) {
	case irma_none:               return "none";
	case irma_twos_complement:    return "twos_complement";
	case irma_ieee754:            return "ieee754";
	case irma_x86_extended_float: return "x86_extended_float";
	}
	panic("invalid mode_arithmetic");
}

/** Returns the according symbol value for the given string and tag, or SYMERROR if none was found. */
static unsigned symbol(const char *str, typetag_t typetag)
{
	symbol_t key, *entry;

	key.str = str;
	key.typetag = typetag;

	entry = set_find(symbol_t, symtbl, &key, sizeof(key), hash_str(str) + typetag * 17);
	return entry ? entry->code : SYMERROR;
}

static void write_long(write_env_t *env, long value)
{
	fprintf(env->file, "%ld ", value);
}

static void write_int(write_env_t *env, int value)
{
	fprintf(env->file, "%d ", value);
}

static void write_unsigned(write_env_t *env, unsigned value)
{
	fprintf(env->file, "%u ", value);
}

static void write_size_t(write_env_t *env, size_t value)
{
	ir_fprintf(env->file, "%zu ", value);
}

static void write_symbol(write_env_t *env, const char *symbol)
{
	fputs(symbol, env->file);
	fputc(' ', env->file);
}

static void write_entity_ref(write_env_t *env, ir_entity *entity)
{
	write_long(env, get_entity_nr(entity));
}

static void write_type_ref(write_env_t *env, ir_type *type)
{
	switch (get_type_tpop_code(type)) {
	case tpo_unknown:
		write_symbol(env, "unknown");
		return;
	case tpo_code:
		write_symbol(env, "code");
		return;
	default:
		break;
	}
	write_long(env, get_type_nr(type));
}

static void write_string(write_env_t *env, const char *string)
{
	const char *c;
	fputc('"', env->file);
	for (c = string; *c != '\0'; ++c) {
		switch (*c) {
		case '\n':
			fputc('\\', env->file);
			fputc('n', env->file);
			break;
		case '"':
		case '\\':
			fputc('\\', env->file);
			/* FALLTHROUGH */
		default:
			fputc(*c, env->file);
			break;
		}
	}
	fputc('"', env->file);
	fputc(' ', env->file);
}

static void write_ident(write_env_t *env, ident *id)
{
	write_string(env, get_id_str(id));
}

static void write_ident_null(write_env_t *env, ident *id)
{
	if (id == NULL) {
		fputs("NULL ", env->file);
	} else {
		write_ident(env, id);
	}
}

static void write_mode_ref(write_env_t *env, ir_mode *mode)
{
	write_string(env, get_mode_name(mode));
}

static void write_tarval(write_env_t *env, ir_tarval *tv)
{
	ir_mode *mode = get_tarval_mode(tv);
	write_mode_ref(env, mode);
	char buf[128];
	const char *ascii = ir_tarval_to_ascii(buf, sizeof(buf), tv);
	fputs(ascii, env->file);
	fputc(' ', env->file);
}

static void write_align(write_env_t *env, ir_align align)
{
	fputs(get_align_name(align), env->file);
	fputc(' ', env->file);
}

static void write_builtin_kind(write_env_t *env, const ir_node *node)
{
	fputs(get_builtin_kind_name(get_Builtin_kind(node)), env->file);
	fputc(' ', env->file);
}

static void write_cond_jmp_predicate(write_env_t *env, const ir_node *node)
{
	fputs(get_cond_jmp_predicate_name(get_Cond_jmp_pred(node)), env->file);
	fputc(' ', env->file);
}

static void write_relation(write_env_t *env, ir_relation relation)
{
	write_long(env, (long)relation);
}

static void write_throws(write_env_t *env, bool throws)
{
	write_symbol(env, throws ? "throw" : "nothrow");
}

static void write_list_begin(write_env_t *env)
{
	fputs("[", env->file);
}

static void write_list_end(write_env_t *env)
{
	fputs("] ", env->file);
}

static void write_scope_begin(write_env_t *env)
{
	fputs("{\n", env->file);
}

static void write_scope_end(write_env_t *env)
{
	fputs("}\n\n", env->file);
}

static void write_node_ref(write_env_t *env, const ir_node *node)
{
	write_long(env, get_irn_node_nr(node));
}

static void write_initializer(write_env_t *env, ir_initializer_t *ini)
{
	FILE *f = env->file;
	ir_initializer_kind_t ini_kind = get_initializer_kind(ini);

	fputs(get_initializer_kind_name(ini_kind), f);
	fputc(' ', f);

	switch (ini_kind) {
	case IR_INITIALIZER_CONST:
		write_node_ref(env, get_initializer_const_value(ini));
		return;

	case IR_INITIALIZER_TARVAL:
		write_tarval(env, get_initializer_tarval_value(ini));
		return;

	case IR_INITIALIZER_NULL:
		return;

	case IR_INITIALIZER_COMPOUND: {
		size_t i, n = get_initializer_compound_n_entries(ini);
		write_size_t(env, n);
		for (i = 0; i < n; ++i)
			write_initializer(env, get_initializer_compound_value(ini, i));
		return;
	}
	}
	panic("Unknown initializer kind");
}

static void write_pin_state(write_env_t *env, op_pin_state state)
{
	fputs(get_op_pin_state_name(state), env->file);
	fputc(' ', env->file);
}

static void write_volatility(write_env_t *env, ir_volatility vol)
{
	fputs(get_volatility_name(vol), env->file);
	fputc(' ', env->file);
}

static void write_type_state(write_env_t *env, ir_type_state state)
{
	fputs(get_type_state_name(state), env->file);
	fputc(' ', env->file);
}

static void write_visibility(write_env_t *env, ir_visibility visibility)
{
	fputs(get_visibility_name(visibility), env->file);
	fputc(' ', env->file);
}

static void write_mode_arithmetic(write_env_t *env, ir_mode_arithmetic arithmetic)
{
	fputs(get_mode_arithmetic_name(arithmetic), env->file);
	fputc(' ', env->file);
}

static void write_type_common(write_env_t *env, ir_type *tp)
{
	fputc('\t', env->file);
	write_symbol(env, "type");
	write_long(env, get_type_nr(tp));
	write_symbol(env, get_type_tpop_name(tp));
	write_unsigned(env, get_type_size_bytes(tp));
	write_unsigned(env, get_type_alignment_bytes(tp));
	write_type_state(env, get_type_state(tp));
	write_unsigned(env, tp->flags);
}

static void write_type(write_env_t *env, ir_type *tp);

static void write_type_primitive(write_env_t *env, ir_type *tp)
{
	write_type_common(env, tp);
	write_mode_ref(env, get_type_mode(tp));
	fputc('\n', env->file);
}

static void write_type_compound(write_env_t *env, ir_type *tp)
{
	size_t n_members = get_compound_n_members(tp);
	size_t i;

	if (is_Class_type(tp)) {
		if (get_class_n_subtypes(tp) > 0 || get_class_n_supertypes(tp) > 0 || get_class_vtable_size(tp) > 0) {
			/* sub/superclass export not implemented yet, it's unclear whether
			 * class types will stay in libfirm anyway */
			panic("can't export class types yet");
		}
	}
	write_type_common(env, tp);
	write_ident_null(env, get_compound_ident(tp));
	fputc('\n', env->file);

	for (i = 0; i < n_members; ++i) {
		ir_entity *member = get_compound_member(tp, i);
		pdeq_putr(env->entity_queue, member);
	}
}

static void write_type_array(write_env_t *env, ir_type *tp)
{
	size_t     n_dimensions   = get_array_n_dimensions(tp);
	ir_type   *element_type   = get_array_element_type(tp);
	ir_entity *element_entity = get_array_element_entity(tp);
	size_t   i;

	write_type(env, element_type);

	write_type_common(env, tp);
	write_size_t(env, n_dimensions);
	write_type_ref(env, get_array_element_type(tp));
	for (i = 0; i < n_dimensions; i++) {
		ir_node *lower = get_array_lower_bound(tp, i);
		ir_node *upper = get_array_upper_bound(tp, i);

		if (is_Const(lower))
			write_long(env, get_tarval_long(get_Const_tarval(lower)));
		else
			panic("Lower array bound is not constant");

		if (is_Const(upper))
			write_long(env, get_tarval_long(get_Const_tarval(upper)));
		else if (is_Unknown(upper))
			write_symbol(env, "unknown");
		else
			panic("Upper array bound is not constant");
	}
	/* note that we just write a reference to the element entity
	 * but never the entity itself */
	write_entity_ref(env, element_entity);
	fputc('\n', env->file);
}

static void write_type_method(write_env_t *env, ir_type *tp)
{
	size_t nparams  = get_method_n_params(tp);
	size_t nresults = get_method_n_ress(tp);
	size_t i;

	for (i = 0; i < nparams; i++)
		write_type(env, get_method_param_type(tp, i));
	for (i = 0; i < nresults; i++)
		write_type(env, get_method_res_type(tp, i));

	write_type_common(env, tp);
	write_unsigned(env, get_method_calling_convention(tp));
	write_unsigned(env, get_method_additional_properties(tp));
	write_size_t(env, nparams);
	write_size_t(env, nresults);
	for (i = 0; i < nparams; i++)
		write_type_ref(env, get_method_param_type(tp, i));
	for (i = 0; i < nresults; i++)
		write_type_ref(env, get_method_res_type(tp, i));
	write_unsigned(env, get_method_variadicity(tp));
	fputc('\n', env->file);
}

static void write_type_pointer(write_env_t *env, ir_type *tp)
{
	ir_type *points_to = get_pointer_points_to_type(tp);

	write_type(env, points_to);

	write_type_common(env, tp);
	write_mode_ref(env, get_type_mode(tp));
	write_type_ref(env, points_to);
	fputc('\n', env->file);
}

static void write_type(write_env_t *env, ir_type *tp)
{
	if (type_visited(tp))
		return;
	mark_type_visited(tp);

	switch ((tp_opcode)get_type_tpop_code(tp)) {
	case tpo_unknown:
	case tpo_code:
	case tpo_uninitialized:
		/* no need to write special builtin types */
		return;

	case tpo_union:
	case tpo_struct:
	case tpo_class:
		write_type_compound(env, tp);
		return;

	case tpo_primitive:    write_type_primitive(env, tp);   return;
	case tpo_method:       write_type_method(env, tp);      return;
	case tpo_pointer:      write_type_pointer(env, tp);     return;
	case tpo_array:        write_type_array(env, tp);       return;
	}
	panic("can't write invalid type %+F\n", tp);
}

static void write_entity(write_env_t *env, ir_entity *ent)
{
	ir_type       *type       = get_entity_type(ent);
	ir_type       *owner      = get_entity_owner(ent);
	ir_visibility  visibility = get_entity_visibility(ent);
	ir_linkage     linkage    = get_entity_linkage(ent);

	if (entity_visited(ent))
		return;
	mark_entity_visited(ent);

	write_type(env, type);
	write_type(env, owner);
	if (is_alias_entity(ent)) {
		ir_entity *aliased = get_entity_alias(ent);
		write_entity(env, aliased);
	}

	fputc('\t', env->file);
	switch ((ir_entity_kind)ent->entity_kind) {
	case IR_ENTITY_ALIAS:           write_symbol(env, "alias");           break;
	case IR_ENTITY_GOTENTRY:        write_symbol(env, "gotentry");        break;
	case IR_ENTITY_NORMAL:          write_symbol(env, "entity");          break;
	case IR_ENTITY_METHOD:          write_symbol(env, "method");          break;
	case IR_ENTITY_LABEL:           write_symbol(env, "label");           break;
	case IR_ENTITY_COMPOUND_MEMBER: write_symbol(env, "compound_member"); break;
	case IR_ENTITY_PARAMETER:       write_symbol(env, "parameter");       break;
	case IR_ENTITY_UNKNOWN:
		write_symbol(env, "unknown");
		write_long(env, get_entity_nr(ent));
		return;
	}
	write_long(env, get_entity_nr(ent));

	if (ent->entity_kind != IR_ENTITY_LABEL
{'fraction_non_alphanumeric': 0.12768680996319803, 'fraction_numerical': 0.0025601365406154996, 'mean_word_length': 5.093435164120897, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 139, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1618539', 'n_tokens_mistral': 14994, 'n_tokens_neox': 14613, 'n_words': 2907}