<commit_before>/*
 * Copyright (C) 1995-2011 University of Karlsruhe.  All right reserved.
 *
 * This file is part of libFirm.
 *
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation and appearing in the file LICENSE.GPL included in the
 * packaging of this file.
 *
 * Licensees holding valid libFirm Professional Edition licenses may use
 * this file in accordance with the libFirm Commercial License.
 * Agreement provided with the Software.
 *
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE.
 */

/**
 * @file
 * @brief       Dumps global variables and constants as gas assembler.
 * @author      <NAME>, <NAME>
 * @date        04.11.2005
 */
#include "config.h"

#include "begnuas.h"

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>

#include "obst.h"
#include "tv.h"
#include "irnode.h"
#include "irprog.h"
#include "entity_t.h"
#include "error.h"
#include "util.h"
#include "execfreq.h"

#include "be_t.h"
#include "beemitter.h"
#include "bedwarf.h"

/** by default, we generate assembler code for the Linux gas */
object_file_format_t  be_gas_object_file_format = OBJECT_FILE_FORMAT_ELF;
elf_variant_t         be_gas_elf_variant        = ELF_VARIANT_NORMAL;
bool                  be_gas_emit_types         = true;
char                  be_gas_elf_type_char      = '@';

static be_gas_section_t current_section = (be_gas_section_t) -1;
static pmap            *block_numbers;
static unsigned         next_block_nr;

/**
 * An environment containing all needed dumper data.
 * Currently we create the file completely in memory first, then
 * write it to the disk. This is an artifact from the old C-generating backend
 * and even there NOT needed. So we might change it in the future.
 */
typedef struct be_gas_decl_env {
	be_gas_section_t     section;
	const be_main_env_t *main_env;
} be_gas_decl_env_t;

static void emit_section_macho(be_gas_section_t section)
{
	be_gas_section_t  base  = section & GAS_SECTION_TYPE_MASK;
	be_gas_section_t  flags = section & ~GAS_SECTION_TYPE_MASK;
	const char       *name;

	if (current_section == section)
		return;
	current_section = section;

	/* shortforms */
	if (flags == 0) {
		switch (base) {
		case GAS_SECTION_TEXT:            name = "text";          break;
		case GAS_SECTION_DATA:            name = "data";          break;
		case GAS_SECTION_RODATA:          name = "const";         break;
		case GAS_SECTION_BSS:             name = "data";          break;
		case GAS_SECTION_CONSTRUCTORS:    name = "mod_init_func"; break;
		case GAS_SECTION_DESTRUCTORS:     name = "mod_term_func"; break;
		case GAS_SECTION_PIC_TRAMPOLINES: name = "section\t__IMPORT,__jump_table,symbol_stubs,self_modifying_code+pure_instructions,5"; break;
		case GAS_SECTION_PIC_SYMBOLS:     name = "section\t__IMPORT,__pointers,non_lazy_symbol_pointers"; break;
		case GAS_SECTION_CSTRING:         name = "cstring";       break;
		case GAS_SECTION_DEBUG_INFO:      name = "section __DWARF,__debug_info,regular,debug"; break;
		case GAS_SECTION_DEBUG_ABBREV:    name = "section __DWARF,__debug_abbrev,regular,debug"; break;
		case GAS_SECTION_DEBUG_LINE:      name = "section __DWARF,__debug_line,regular,debug"; break;
		case GAS_SECTION_DEBUG_PUBNAMES:  name = "section __DWARF,__debug_pubnames,regular,debug"; break;
		case GAS_SECTION_DEBUG_FRAME:     name = "section __DWARF,__debug_frame,regular,debug"; break;
		default: panic("unsupported scetion type 0x%X", section);
		}
		be_emit_irprintf("\t.%s\n", name);
		be_emit_write_line();
	} else if (flags & GAS_SECTION_FLAG_COMDAT) {
		switch (base) {
		case GAS_SECTION_TEXT:            name = "section __TEXT,__textcoal_nt,coalesced,pure_instructions"; break;
		case GAS_SECTION_BSS:
		case GAS_SECTION_DATA:            name = "section __DATA,__datacoal_nt,coalesced"; break;
		case GAS_SECTION_RODATA:          name = "section __TEXT,__const_coal,coalesced"; break;
		case GAS_SECTION_CSTRING:         name = "section __TEXT,__const_coal,coalesced"; break;
		default: panic("unsupported scetion type 0x%X", section);
		}
	} else if (flags & GAS_SECTION_FLAG_TLS) {
		panic("thread local storage not supported on macho (section 0x%X)", section);
	} else {
		panic("unsupported section type 0x%X", section);
	}
}

static void emit_section_sparc(be_gas_section_t section, const ir_entity *entity)
{
	be_gas_section_t base = section & GAS_SECTION_TYPE_MASK;
	be_gas_section_t flags = section & ~GAS_SECTION_TYPE_MASK;
	static const char *const basename[GAS_SECTION_LAST+1] = {
		"text",
		"data",
		"rodata",
		"bss",
		"ctors",
		"dtors",
		NULL, /* cstring */
		NULL, /* pic trampolines */
		NULL, /* pic symbols */
		"debug_info",
		"debug_abbrev",
		"debug_line",
		"debug_pubnames"
		"debug_frame",
	};

	if (current_section == section && !(section & GAS_SECTION_FLAG_COMDAT))
		return;
	current_section = section;

	be_emit_cstring("\t.section\t\".");

	/* Part1: section-name */
	if (flags & GAS_SECTION_FLAG_TLS)
		be_emit_char('t');
	assert(base < (be_gas_section_t)ARRAY_SIZE(basename));
	be_emit_string(basename[base]);

	if (flags & GAS_SECTION_FLAG_COMDAT) {
		be_emit_char('.');
		be_gas_emit_entity(entity);
	}
	be_emit_char('"');

	/* for the simple sections we're done here */
	if (flags == 0)
		goto end;

	be_emit_cstring(",#alloc");

	switch (base) {
	case GAS_SECTION_TEXT: be_emit_cstring(",#execinstr"); break;
	case GAS_SECTION_DATA:
	case GAS_SECTION_BSS:  be_emit_cstring(",#write"); break;
	default:
		/* nothing */
		break;
	}
	if (flags & GAS_SECTION_FLAG_TLS) {
		be_emit_cstring(",#tls");
	}

end:
	be_emit_char('\n');
	be_emit_write_line();
}

static void emit_section(be_gas_section_t section, const ir_entity *entity)
{
	be_gas_section_t base = section & GAS_SECTION_TYPE_MASK;
	be_gas_section_t flags = section & ~GAS_SECTION_TYPE_MASK;
	const char *f;
	static const struct {
		const char *name;
		const char *type;
		const char *flags;
	} sectioninfos[GAS_SECTION_LAST+1] = {
		{ "text",           "progbits", "ax" },
		{ "data",           "progbits", "aw" },
		{ "rodata",         "progbits", "a"  },
		{ "bss",            "nobits",   "aw" },
		{ "ctors",          "progbits", "aw" },
		{ "dtors",          "progbits", "aw" },
		{ NULL,             NULL,       NULL }, /* cstring */
		{ NULL,             NULL,       NULL }, /* pic trampolines */
		{ NULL,             NULL,       NULL }, /* pic symbols */
		{ "debug_info",     "progbits", ""   },
		{ "debug_abbrev",   "progbits", ""   },
		{ "debug_line",     "progbits", ""   },
		{ "debug_pubnames", "progbits", ""   },
		{ "debug_frame",    "progbits", ""   },
	};

	if (be_gas_object_file_format == OBJECT_FILE_FORMAT_MACH_O) {
		emit_section_macho(section);
		return;
	} else if(be_gas_elf_variant == ELF_VARIANT_SPARC) {
		emit_section_sparc(section, entity);
		return;
	}

	if (current_section == section && !(section & GAS_SECTION_FLAG_COMDAT))
		return;
	current_section = section;

	/* shortforms */
	if (flags == 0) {
		switch (base) {
		case GAS_SECTION_TEXT:
			be_emit_cstring("\t.text\n");
			be_emit_write_line();
			return;
		case GAS_SECTION_DATA:
			be_emit_cstring("\t.data\n");
			be_emit_write_line();
			return;
		case GAS_SECTION_RODATA:
			be_emit_cstring("\t.section\t.rodata\n");
			be_emit_write_line();
			return;
		case GAS_SECTION_BSS:
			be_emit_cstring("\t.bss\n");
			be_emit_write_line();
			return;
		default:
			break;
		}
	}

	assert(base < (be_gas_section_t) ARRAY_SIZE(sectioninfos));
	be_emit_cstring("\t.section\t.");
	/* section name */
	if (flags & GAS_SECTION_FLAG_TLS)
		be_emit_char('t');
	be_emit_string(sectioninfos[base].name);
	if (flags & GAS_SECTION_FLAG_COMDAT) {
		be_emit_char('.');
		be_gas_emit_entity(entity);
	}

	/* section flags */
	be_emit_cstring(",\"");
	for (f = sectioninfos[base].flags; *f != '\0'; ++f) {
		be_emit_char(*f);
	}
	if (flags & GAS_SECTION_FLAG_TLS)
		be_emit_char('T');
	if (flags & GAS_SECTION_FLAG_COMDAT)
		be_emit_char('G');

	/* section type */
	if (be_gas_object_file_format != OBJECT_FILE_FORMAT_COFF) {
		be_emit_cstring("\",");
		be_emit_char(be_gas_elf_type_char);
		be_emit_string(sectioninfos[base].type);
	}

	if (flags & GAS_SECTION_FLAG_COMDAT) {
		be_emit_char(',');
		be_gas_emit_entity(entity);
		be_emit_cstring(",comdat");
	}
	be_emit_char('\n');
	be_emit_write_line();
}



void be_gas_emit_switch_section(be_gas_section_t section)
{
	/* you have to produce a switch_section call with entity manually
	 * for comdat sections */
	assert( !(section & GAS_SECTION_FLAG_COMDAT));

	emit_section(section, NULL);
}

static ir_tarval *get_initializer_tarval(const ir_initializer_t *initializer)
{
	if (initializer->kind == IR_INITIALIZER_TARVAL)
		return initializer->tarval.value;
	if (initializer->kind == IR_INITIALIZER_CONST) {
		ir_node *node = initializer->consti.value;
		if (is_Const(node)) {
			return get_Const_tarval(node);
		}
	}
	return get_tarval_undefined();
}

static bool initializer_is_string_const(const ir_initializer_t *initializer)
{
	size_t i, len;
	bool found_printable = false;

	if (initializer->kind != IR_INITIALIZER_COMPOUND)
		return false;

	len = initializer->compound.n_initializers;
	if (len < 1)
		return false;
	for (i = 0; i < len; ++i) {
		int               c;
		ir_tarval        *tv;
		ir_mode          *mode;
		ir_initializer_t *sub_initializer
			= initializer->compound.initializers[i];

		tv = get_initializer_tarval(sub_initializer);
		if (!tarval_is_constant(tv))
			return false;

		mode = get_tarval_mode(tv);
		if (!mode_is_int(mode) || get_mode_size_bits(mode) != 8)
			return false;

		c = get_tarval_long(tv);
		if (isgraph(c) || isspace(c))
			found_printable = true;
		else if (c != 0)
			return false;

		if (i == len - 1 && c != '\0')
			return false;
	}

	return found_printable;
}

static bool initializer_is_null(const ir_initializer_t *initializer)
{
	switch (initializer->kind) {
	case IR_INITIALIZER_NULL:
		return true;
	case IR_INITIALIZER_TARVAL: {
		ir_tarval *tv = initializer->tarval.value;
		return tarval_is_null(tv);
	}
	case IR_INITIALIZER_CONST: {
		ir_node *value = initializer->consti.value;
		if (!is_Const(value))
			return false;
		return is_Const_null(value);
	}
	case IR_INITIALIZER_COMPOUND: {
		size_t i;
		for (i = 0; i < initializer->compound.n_initializers; ++i) {
			ir_initializer_t *subinitializer
				= initializer->compound.initializers[i];
			if (!initializer_is_null(subinitializer))
				return false;
		}
		return true;
	}
	}
	panic("invalid initializer in initializer_is_null");
}

/**
 * Determine if an entity is a string constant
 * @param ent The entity
 * @return 1 if it is a string constant, 0 otherwise
 */
static int entity_is_string_const(const ir_entity *ent)
{
	ir_type *type, *element_type;
	ir_mode *mode;

	type = get_entity_type(ent);

	/* if it's an array */
	if (!is_Array_type(type))
		return 0;

	element_type = get_array_element_type(type);

	/* and the array's element type is primitive */
	if (!is_Primitive_type(element_type))
		return 0;

	/* and the mode of the element type is an int of
	 * the same size as the byte mode */
	mode = get_type_mode(element_type);
	if (!mode_is_int(mode) || get_mode_size_bits(mode) != 8)
		return 0;

	if (ent->initializer != NULL) {
		return initializer_is_string_const(ent->initializer);
	}

	return 0;
}

static bool entity_is_null(const ir_entity *entity)
{
	ir_initializer_t *initializer = get_entity_initializer(entity);
	return initializer == NULL || initializer_is_null(initializer);
}

static bool is_comdat(const ir_entity *entity)
{
	ir_linkage linkage = get_entity_linkage(entity);
	return (linkage & IR_LINKAGE_MERGE)
		&& (linkage & IR_LINKAGE_GARBAGE_COLLECT);
}

static be_gas_section_t determine_basic_section(const ir_entity *entity)
{
	ir_linkage linkage;

	if (is_method_entity(entity))
		return GAS_SECTION_TEXT;

	linkage = get_entity_linkage(entity);
	if (linkage & IR_LINKAGE_CONSTANT) {
		/* mach-o is the only one with a cstring section */
		if (be_gas_object_file_format == OBJECT_FILE_FORMAT_MACH_O
		    && entity_is_string_const(entity))
			return GAS_SECTION_CSTRING;

		return GAS_SECTION_RODATA;
	}
	if (entity_is_null(entity))
		return GAS_SECTION_BSS;

	return GAS_SECTION_DATA;
}

static be_gas_section_t determine_section(be_gas_decl_env_t *env,
                                          const ir_entity *entity)
{
	ir_type *owner = get_entity_owner(entity);

	if (owner == get_segment_type(IR_SEGMENT_GLOBAL)) {
		be_gas_section_t section = determine_basic_section(entity);
		if (is_comdat(entity))
			section |= GAS_SECTION_FLAG_COMDAT;
		return section;
	} else if (env != NULL && owner == env->main_env->pic_symbols_type) {
		return GAS_SECTION_PIC_SYMBOLS;
	} else if (env != NULL && owner == env->main_env->pic_trampolines_type) {
		return GAS_SECTION_PIC_TRAMPOLINES;
	} else if (owner == get_segment_type(IR_SEGMENT_CONSTRUCTORS)) {
		return GAS_SECTION_CONSTRUCTORS;
	} else if (owner == get_segment_type(IR_SEGMENT_DESTRUCTORS)) {
		return GAS_SECTION_DESTRUCTORS;
	} else if (owner == get_segment_type(IR_SEGMENT_THREAD_LOCAL)) {
		be_gas_section_t section = determine_basic_section(entity);
		if (is_comdat(entity))
			section |= GAS_SECTION_FLAG_COMDAT;

		return section | GAS_SECTION_FLAG_TLS;
	}

	/* the java frontend keeps some functions inside classes */
	if (is_Class_type(owner)) {
		return determine_basic_section(entity);
	}

	panic("Couldn't determine section for %+F?!?", entity);
}

static void emit_weak(const ir_entity *entity)
{
	if (be_gas_object_file_format == OBJECT_FILE_FORMAT_MACH_O) {
		be_emit_cstring("\t.weak_reference ");
	} else {
		be_emit_cstring("\t.weak ");
	}
	be_gas_emit_entity(entity);
	be_emit_char('\n');
	be_emit_write_line();
}

static void emit_visibility(const ir_entity *entity)
{
	ir_linkage const linkage = get_entity_linkage(entity);

	if (linkage & IR_LINKAGE_WEAK) {
		emit_weak(entity);
		/* Note: .weak seems to imply .globl so no need to output .globl */
	} else if (get_entity_visibility(entity) == ir_visibility_external
	           && entity_has_definition(entity)) {
		be_emit_cstring("\t.globl ");
		be_gas_emit_entity(entity);
		be_emit_char('\n');
		be_emit_write_line();
	}

	if (be_gas_object_file_format == OBJECT_FILE_FORMAT_MACH_O
			&& (linkage & IR_LINKAGE_HIDDEN_USER)
			&& get_entity_ld_name(entity)[0] != '\0') {
		be_emit_cstring("\t.no_dead_strip ");
		be_gas_emit_entity(entity);
		be_emit_char('\n');
		be_emit_write_line();
	}
}

void be_gas_emit_function_prolog(const ir_entity *entity, unsigned po2alignment, const parameter_dbg_info_t *parameter_infos)
{
	be_gas_section_t section;

	be_dwarf_method_before(entity, parameter_infos);

	section = determine_section(NULL, entity);
	emit_section(section, entity);

	/* write the begin line (makes the life easier for scripts parsing the
	 * assembler) */
	if (be_options.verbose_asm) {
		be_emit_cstring("# -- Begin  ");
		be_gas_emit_entity(entity);
		be_emit_char('\n');
		be_emit_write_line();
	}

	if (po2alignment > 0) {
		const char *fill_byte = "";
		unsigned    maximum_skip = (1 << po2alignment) - 1;
		/* gcc fills space between function with 0x90... */
		if (be_gas_object_file_format == OBJECT_FILE_FORMAT_MACH_O) {
			fill_byte = "0x90";
		}
		be_emit_cstring("\t.p2align ");
		be_emit_irprintf("%u,%s,%u\n", po2alignment, fill_byte, maximum_skip);
		be_emit_write_line();
	}
	emit_visibility(entity);

	switch (be_gas_object_file_format) {
	case OBJECT_FILE_FORMAT_ELF:
		be_emit_cstring("\t.type\t");
		be_gas_emit_entity(entity);
		be_emit_cstring(", ");
		be_emit_char(be_gas_elf_type_char);
		be_emit_cstring("function\n");
		be_emit_write_line();
		break;
	case OBJECT_FILE_FORMAT_COFF:
		be_emit_cstring("\t.def\t");
		be_gas_emit_entity(entity);
		be_emit_cstring(";");
		if (get_entity_visibility(entity) == ir_visibility_local) {
			be_emit_cstring("\t.scl\t3;");
		} else {
			be_emit_cstring("\t.scl\t2;");
		}
		be_emit_cstring("\t.type\t32;\t.endef\n");
		be_emit_write_line();
		break;
	case OBJECT_FILE_FORMAT_MACH_O:
		break;
	}
	be_gas_emit_entity(entity);
	be_emit_cstring(":\n");
	be_emit_write_line();

	be_dwarf_method_begin();
}

void be_gas_emit_function_epilog(const ir_entity *entity)
{
	be_dwarf_method_end();

	if (be_gas_object_file_format == OBJECT_FILE_FORMAT_ELF) {
		be_emit_cstring("\t.size\t");
		be_gas_emit_entity(entity);
		be_emit_cstring(", .-");
		be_gas_emit_entity(entity);
		be_emit_char('\n');
		be_emit_write_line();
	}

	if (be_options.verbose_asm) {
		be_emit_cstring("# -- End  ");
		be_gas_emit_entity(entity);
		be_emit_char('\n');
		be_emit_write_line();
	}

	be_emit_char('\n');
	be_emit_write_line();

	next_block_nr += 100;
}

/**
 * Output a tarval.
 *
 * @param tv     the tarval
 * @param bytes  the width of the tarvals value in bytes
 */
static void emit_arith_tarval(ir_tarval *tv, unsigned bytes)
{
	switch (bytes) {
	case 1:
		be_emit_irprintf("0x%02x", get_tarval_sub_bits(tv, 0));
		return;

	case 2:
		be_emit_irprintf("0x%02x%02x",
			get_tarval_sub_bits(tv, 1), get_tarval_sub_bits(tv, 0));
		return;

	case 4:
		be_emit_irprintf("0x%02x%02x%02x%02x",
			get_tarval_sub_bits(tv, 3), get_tarval_sub_bits(tv, 2),
			get_tarval_sub_bits(tv, 1), get_tarval_sub_bits(tv, 0));
		return;
<commit_msg>make block numbers accross functions more deterministic<commit_after>/*
 * Copyright (C) 1995-2011 University of Karlsruhe.  All right reserved.
 *
 * This file is part of libFirm.
 *
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation and appearing in the file LICENSE.GPL included in the
 * packaging of this file.
 *
 * Licensees holding valid libFirm Professional Edition licenses may use
 * this file in accordance with the libFirm Commercial License.
 * Agreement provided with the Software.
 *
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE.
 */

/**
 * @file
 * @brief       Dumps global variables and constants as gas assembler.
 * @author      <NAME>, <NAME>
 * @date        04.11.2005
 */
#include "config.h"

#include "begnuas.h"

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>

#include "obst.h"
#include "tv.h"
#include "irnode.h"
#include "irprog.h"
#include "entity_t.h"
#include "error.h"
#include "util.h"
#include "execfreq.h"

#include "be_t.h"
#include "beemitter.h"
#include "bedwarf.h"

/** by default, we generate assembler code for the Linux gas */
object_file_format_t  be_gas_object_file_format = OBJECT_FILE_FORMAT_ELF;
elf_variant_t         be_gas_elf_variant        = ELF_VARIANT_NORMAL;
bool                  be_gas_emit_types         = true;
char                  be_gas_elf_type_char      = '@';

static be_gas_section_t current_section = (be_gas_section_t) -1;
static pmap            *block_numbers;
static unsigned         next_block_nr;

/**
 * An environment containing all needed dumper data.
 * Currently we create the file completely in memory first, then
 * write it to the disk. This is an artifact from the old C-generating backend
 * and even there NOT needed. So we might change it in the future.
 */
typedef struct be_gas_decl_env {
	be_gas_section_t     section;
	const be_main_env_t *main_env;
} be_gas_decl_env_t;

static void emit_section_macho(be_gas_section_t section)
{
	be_gas_section_t  base  = section & GAS_SECTION_TYPE_MASK;
	be_gas_section_t  flags = section & ~GAS_SECTION_TYPE_MASK;
	const char       *name;

	if (current_section == section)
		return;
	current_section = section;

	/* shortforms */
	if (flags == 0) {
		switch (base) {
		case GAS_SECTION_TEXT:            name = "text";          break;
		case GAS_SECTION_DATA:            name = "data";          break;
		case GAS_SECTION_RODATA:          name = "const";         break;
		case GAS_SECTION_BSS:             name = "data";          break;
		case GAS_SECTION_CONSTRUCTORS:    name = "mod_init_func"; break;
		case GAS_SECTION_DESTRUCTORS:     name = "mod_term_func"; break;
		case GAS_SECTION_PIC_TRAMPOLINES: name = "section\t__IMPORT,__jump_table,symbol_stubs,self_modifying_code+pure_instructions,5"; break;
		case GAS_SECTION_PIC_SYMBOLS:     name = "section\t__IMPORT,__pointers,non_lazy_symbol_pointers"; break;
		case GAS_SECTION_CSTRING:         name = "cstring";       break;
		case GAS_SECTION_DEBUG_INFO:      name = "section __DWARF,__debug_info,regular,debug"; break;
		case GAS_SECTION_DEBUG_ABBREV:    name = "section __DWARF,__debug_abbrev,regular,debug"; break;
		case GAS_SECTION_DEBUG_LINE:      name = "section __DWARF,__debug_line,regular,debug"; break;
		case GAS_SECTION_DEBUG_PUBNAMES:  name = "section __DWARF,__debug_pubnames,regular,debug"; break;
		case GAS_SECTION_DEBUG_FRAME:     name = "section __DWARF,__debug_frame,regular,debug"; break;
		default: panic("unsupported scetion type 0x%X", section);
		}
		be_emit_irprintf("\t.%s\n", name);
		be_emit_write_line();
	} else if (flags & GAS_SECTION_FLAG_COMDAT) {
		switch (base) {
		case GAS_SECTION_TEXT:            name = "section __TEXT,__textcoal_nt,coalesced,pure_instructions"; break;
		case GAS_SECTION_BSS:
		case GAS_SECTION_DATA:            name = "section __DATA,__datacoal_nt,coalesced"; break;
		case GAS_SECTION_RODATA:          name = "section __TEXT,__const_coal,coalesced"; break;
		case GAS_SECTION_CSTRING:         name = "section __TEXT,__const_coal,coalesced"; break;
		default: panic("unsupported scetion type 0x%X", section);
		}
	} else if (flags & GAS_SECTION_FLAG_TLS) {
		panic("thread local storage not supported on macho (section 0x%X)", section);
	} else {
		panic("unsupported section type 0x%X", section);
	}
}

static void emit_section_sparc(be_gas_section_t section, const ir_entity *entity)
{
	be_gas_section_t base = section & GAS_SECTION_TYPE_MASK;
	be_gas_section_t flags = section & ~GAS_SECTION_TYPE_MASK;
	static const char *const basename[GAS_SECTION_LAST+1] = {
		"text",
		"data",
		"rodata",
		"bss",
		"ctors",
		"dtors",
		NULL, /* cstring */
		NULL, /* pic trampolines */
		NULL, /* pic symbols */
		"debug_info",
		"debug_abbrev",
		"debug_line",
		"debug_pubnames"
		"debug_frame",
	};

	if (current_section == section && !(section & GAS_SECTION_FLAG_COMDAT))
		return;
	current_section = section;

	be_emit_cstring("\t.section\t\".");

	/* Part1: section-name */
	if (flags & GAS_SECTION_FLAG_TLS)
		be_emit_char('t');
	assert(base < (be_gas_section_t)ARRAY_SIZE(basename));
	be_emit_string(basename[base]);

	if (flags & GAS_SECTION_FLAG_COMDAT) {
		be_emit_char('.');
		be_gas_emit_entity(entity);
	}
	be_emit_char('"');

	/* for the simple sections we're done here */
	if (flags == 0)
		goto end;

	be_emit_cstring(",#alloc");

	switch (base) {
	case GAS_SECTION_TEXT: be_emit_cstring(",#execinstr"); break;
	case GAS_SECTION_DATA:
	case GAS_SECTION_BSS:  be_emit_cstring(",#write"); break;
	default:
		/* nothing */
		break;
	}
	if (flags & GAS_SECTION_FLAG_TLS) {
		be_emit_cstring(",#tls");
	}

end:
	be_emit_char('\n');
	be_emit_write_line();
}

static void emit_section(be_gas_section_t section, const ir_entity *entity)
{
	be_gas_section_t base = section & GAS_SECTION_TYPE_MASK;
	be_gas_section_t flags = section & ~GAS_SECTION_TYPE_MASK;
	const char *f;
	static const struct {
		const char *name;
		const char *type;
		const char *flags;
	} sectioninfos[GAS_SECTION_LAST+1] = {
		{ "text",           "progbits", "ax" },
		{ "data",           "progbits", "aw" },
		{ "rodata",         "progbits", "a"  },
		{ "bss",            "nobits",   "aw" },
		{ "ctors",          "progbits", "aw" },
		{ "dtors",          "progbits", "aw" },
		{ NULL,             NULL,       NULL }, /* cstring */
		{ NULL,             NULL,       NULL }, /* pic trampolines */
		{ NULL,             NULL,       NULL }, /* pic symbols */
		{ "debug_info",     "progbits", ""   },
		{ "debug_abbrev",   "progbits", ""   },
		{ "debug_line",     "progbits", ""   },
		{ "debug_pubnames", "progbits", ""   },
		{ "debug_frame",    "progbits", ""   },
	};

	if (be_gas_object_file_format == OBJECT_FILE_FORMAT_MACH_O) {
		emit_section_macho(section);
		return;
	} else if(be_gas_elf_variant == ELF_VARIANT_SPARC) {
		emit_section_sparc(section, entity);
		return;
	}

	if (current_section == section && !(section & GAS_SECTION_FLAG_COMDAT))
		return;
	current_section = section;

	/* shortforms */
	if (flags == 0) {
		switch (base) {
		case GAS_SECTION_TEXT:
			be_emit_cstring("\t.text\n");
			be_emit_write_line();
			return;
		case GAS_SECTION_DATA:
			be_emit_cstring("\t.data\n");
			be_emit_write_line();
			return;
		case GAS_SECTION_RODATA:
			be_emit_cstring("\t.section\t.rodata\n");
			be_emit_write_line();
			return;
		case GAS_SECTION_BSS:
			be_emit_cstring("\t.bss\n");
			be_emit_write_line();
			return;
		default:
			break;
		}
	}

	assert(base < (be_gas_section_t) ARRAY_SIZE(sectioninfos));
	be_emit_cstring("\t.section\t.");
	/* section name */
	if (flags & GAS_SECTION_FLAG_TLS)
		be_emit_char('t');
	be_emit_string(sectioninfos[base].name);
	if (flags & GAS_SECTION_FLAG_COMDAT) {
		be_emit_char('.');
		be_gas_emit_entity(entity);
	}

	/* section flags */
	be_emit_cstring(",\"");
	for (f = sectioninfos[base].flags; *f != '\0'; ++f) {
		be_emit_char(*f);
	}
	if (flags & GAS_SECTION_FLAG_TLS)
		be_emit_char('T');
	if (flags & GAS_SECTION_FLAG_COMDAT)
		be_emit_char('G');

	/* section type */
	if (be_gas_object_file_format != OBJECT_FILE_FORMAT_COFF) {
		be_emit_cstring("\",");
		be_emit_char(be_gas_elf_type_char);
		be_emit_string(sectioninfos[base].type);
	}

	if (flags & GAS_SECTION_FLAG_COMDAT) {
		be_emit_char(',');
		be_gas_emit_entity(entity);
		be_emit_cstring(",comdat");
	}
	be_emit_char('\n');
	be_emit_write_line();
}



void be_gas_emit_switch_section(be_gas_section_t section)
{
	/* you have to produce a switch_section call with entity manually
	 * for comdat sections */
	assert( !(section & GAS_SECTION_FLAG_COMDAT));

	emit_section(section, NULL);
}

static ir_tarval *get_initializer_tarval(const ir_initializer_t *initializer)
{
	if (initializer->kind == IR_INITIALIZER_TARVAL)
		return initializer->tarval.value;
	if (initializer->kind == IR_INITIALIZER_CONST) {
		ir_node *node = initializer->consti.value;
		if (is_Const(node)) {
			return get_Const_tarval(node);
		}
	}
	return get_tarval_undefined();
}

static bool initializer_is_string_const(const ir_initializer_t *initializer)
{
	size_t i, len;
	bool found_printable = false;

	if (initializer->kind != IR_INITIALIZER_COMPOUND)
		return false;

	len = initializer->compound.n_initializers;
	if (len < 1)
		return false;
	for (i = 0; i < len; ++i) {
		int               c;
		ir_tarval        *tv;
		ir_mode          *mode;
		ir_initializer_t *sub_initializer
			= initializer->compound.initializers[i];

		tv = get_initializer_tarval(sub_initializer);
		if (!tarval_is_constant(tv))
			return false;

		mode = get_tarval_mode(tv);
		if (!mode_is_int(mode) || get_mode_size_bits(mode) != 8)
			return false;

		c = get_tarval_long(tv);
		if (isgraph(c) || isspace(c))
			found_printable = true;
		else if (c != 0)
			return false;

		if (i == len - 1 && c != '\0')
			return false;
	}

	return found_printable;
}

static bool initializer_is_null(const ir_initializer_t *initializer)
{
	switch (initializer->kind) {
	case IR_INITIALIZER_NULL:
		return true;
	case IR_INITIALIZER_TARVAL: {
		ir_tarval *tv = initializer->tarval.value;
		return tarval_is_null(tv);
	}
	case IR_INITIALIZER_CONST: {
		ir_node *value = initializer->consti.value;
		if (!is_Const(value))
			return false;
		return is_Const_null(value);
	}
	case IR_INITIALIZER_COMPOUND: {
		size_t i;
		for (i = 0; i < initializer->compound.n_initializers; ++i) {
			ir_initializer_t *subinitializer
				= initializer->compound.initializers[i];
			if (!initializer_is_null(subinitializer))
				return false;
		}
		return true;
	}
	}
	panic("invalid initializer in initializer_is_null");
}

/**
 * Determine if an entity is a string constant
 * @param ent The entity
 * @return 1 if it is a string constant, 0 otherwise
 */
static int entity_is_string_const(const ir_entity *ent)
{
	ir_type *type, *element_type;
	ir_mode *mode;

	type = get_entity_type(ent);

	/* if it's an array */
	if (!is_Array_type(type))
		return 0;

	element_type = get_array_element_type(type);

	/* and the array's element type is primitive */
	if (!is_Primitive_type(element_type))
		return 0;

	/* and the mode of the element type is an int of
	 * the same size as the byte mode */
	mode = get_type_mode(element_type);
	if (!mode_is_int(mode) || get_mode_size_bits(mode) != 8)
		return 0;

	if (ent->initializer != NULL) {
		return initializer_is_string_const(ent->initializer);
	}

	return 0;
}

static bool entity_is_null(const ir_entity *entity)
{
	ir_initializer_t *initializer = get_entity_initializer(entity);
	return initializer == NULL || initializer_is_null(initializer);
}

static bool is_comdat(const ir_entity *entity)
{
	ir_linkage linkage = get_entity_linkage(entity);
	return (linkage & IR_LINKAGE_MERGE)
		&& (linkage & IR_LINKAGE_GARBAGE_COLLECT);
}

static be_gas_section_t determine_basic_section(const ir_entity *entity)
{
	ir_linkage linkage;

	if (is_method_entity(entity))
		return GAS_SECTION_TEXT;

	linkage = get_entity_linkage(entity);
	if (linkage & IR_LINKAGE_CONSTANT) {
		/* mach-o is the only one with a cstring section */
		if (be_gas_object_file_format == OBJECT_FILE_FORMAT_MACH_O
		    && entity_is_string_const(entity))
			return GAS_SECTION_CSTRING;

		return GAS_SECTION_RODATA;
	}
	if (entity_is_null(entity))
		return GAS_SECTION_BSS;

	return GAS_SECTION_DATA;
}

static be_gas_section_t determine_section(be_gas_decl_env_t *env,
                                          const ir_entity *entity)
{
	ir_type *owner = get_entity_owner(entity);

	if (owner == get_segment_type(IR_SEGMENT_GLOBAL)) {
		be_gas_section_t section = determine_basic_section(entity);
		if (is_comdat(entity))
			section |= GAS_SECTION_FLAG_COMDAT;
		return section;
	} else if (env != NULL && owner == env->main_env->pic_symbols_type) {
		return GAS_SECTION_PIC_SYMBOLS;
	} else if (env != NULL && owner == env->main_env->pic_trampolines_type) {
		return GAS_SECTION_PIC_TRAMPOLINES;
	} else if (owner == get_segment_type(IR_SEGMENT_CONSTRUCTORS)) {
		return GAS_SECTION_CONSTRUCTORS;
	} else if (owner == get_segment_type(IR_SEGMENT_DESTRUCTORS)) {
		return GAS_SECTION_DESTRUCTORS;
	} else if (owner == get_segment_type(IR_SEGMENT_THREAD_LOCAL)) {
		be_gas_section_t section = determine_basic_section(entity);
		if (is_comdat(entity))
			section |= GAS_SECTION_FLAG_COMDAT;

		return section | GAS_SECTION_FLAG_TLS;
	}

	/* the java frontend keeps some functions inside classes */
	if (is_Class_type(owner)) {
		return determine_basic_section(entity);
	}

	panic("Couldn't determine section for %+F?!?", entity);
}

static void emit_weak(const ir_entity *entity)
{
	if (be_gas_object_file_format == OBJECT_FILE_FORMAT_MACH_O) {
		be_emit_cstring("\t.weak_reference ");
	} else {
		be_emit_cstring("\t.weak ");
	}
	be_gas_emit_entity(entity);
	be_emit_char('\n');
	be_emit_write_line();
}

static void emit_visibility(const ir_entity *entity)
{
	ir_linkage const linkage = get_entity_linkage(entity);

	if (linkage & IR_LINKAGE_WEAK) {
		emit_weak(entity);
		/* Note: .weak seems to imply .globl so no need to output .globl */
	} else if (get_entity_visibility(entity) == ir_visibility_external
	           && entity_has_definition(entity)) {
		be_emit_cstring("\t.globl ");
		be_gas_emit_entity(entity);
		be_emit_char('\n');
		be_emit_write_line();
	}

	if (be_gas_object_file_format == OBJECT_FILE_FORMAT_MACH_O
			&& (linkage & IR_LINKAGE_HIDDEN_USER)
			&& get_entity_ld_name(entity)[0] != '\0') {
		be_emit_cstring("\t.no_dead_strip ");
		be_gas_emit_entity(entity);
		be_emit_char('\n');
		be_emit_write_line();
	}
}

void be_gas_emit_function_prolog(const ir_entity *entity, unsigned po2alignment, const parameter_dbg_info_t *parameter_infos)
{
	be_gas_section_t section;

	be_dwarf_method_before(entity, parameter_infos);

	section = determine_section(NULL, entity);
	emit_section(section, entity);

	/* write the begin line (makes the life easier for scripts parsing the
	 * assembler) */
	if (be_options.verbose_asm) {
		be_emit_cstring("# -- Begin  ");
		be_gas_emit_entity(entity);
		be_emit_char('\n');
		be_emit_write_line();
	}

	if (po2alignment > 0) {
		const char *fill_byte = "";
		unsigned    maximum_skip = (1 << po2alignment) - 1;
		/* gcc fills space between function with 0x90... */
		if (be_gas_object_file_format == OBJECT_FILE_FORMAT_MACH_O) {
			fill_byte = "0x90";
		}
		be_emit_cstring("\t.p2align ");
		be_emit_irprintf("%u,%s,%u\n", po2alignment, fill_byte, maximum_skip);
		be_emit_write_line();
	}
	emit_visibility(entity);

	switch (be_gas_object_file_format) {
	case OBJECT_FILE_FORMAT_ELF:
		be_emit_cstring("\t.type\t");
		be_gas_emit_entity(entity);
		be_emit_cstring(", ");
		be_emit_char(be_gas_elf_type_char);
		be_emit_cstring("function\n");
		be_emit_write_line();
		break;
	case OBJECT_FILE_FORMAT_COFF:
		be_emit_cstring("\t.def\t");
		be_gas_emit_entity(entity);
		be_emit_cstring(";");
		if (get_entity_visibility(entity) == ir_visibility_local) {
			be_emit_cstring("\t.scl\t3;");
		} else {
			be_emit_cstring("\t.scl\t2;");
		}
		be_emit_cstring("\t.type\t32;\t.endef\n");
		be_emit_write_line();
		break;
	case OBJECT_FILE_FORMAT_MACH_O:
		break;
	}
	be_gas_emit_entity(entity);
	be_emit_cstring(":\n");
	be_emit_write_line();

	be_dwarf_method_begin();
}

void be_gas_emit_function_epilog(const ir_entity *entity)
{
	be_dwarf_method_end();

	if (be_gas_object_file_format == OBJECT_FILE_FORMAT_ELF) {
		be_emit_cstring("\t.size\t");
		be_gas_emit_entity(entity);
		be_emit_cstring(", .-");
		be_gas_emit_entity(entity);
		be_emit_char('\n');
		be_emit_write_line();
	}

	if (be_options.verbose_asm) {
		be_emit_cstring("# -- End  ");
		be_gas_emit_entity(entity);
		be_emit_char('\n');
		be_emit_write_line();
	}

	be_emit_char('\n');
	be_emit_write_line();

	next_block_nr += 199;
	next_block_nr = next_block_nr/100*100;
}

/**
 * Output a tarval.
 *
 * @param tv     the tarval
 * @param bytes  the width of the tarvals value in bytes
 */
static void emit_arith_tarval(ir_tarval *tv, unsigned bytes)
{
	switch (bytes) {
	case 1:
		be_emit_irprintf("0x%02x", get_tarval_sub_bits(tv, 0));
		return;

	case 2:
		be_emit_irprintf("0x%02x%02x",
			get_tarval_sub_bits(tv, 1), get_tarval_sub_bits(tv, 0));
		return;

	case 4:
		be_emit_irprintf("0x%02x%02x%02x%02x",
			get_tarval_sub_bits(tv, 3), get_tarval_sub_bits(tv, 2),
			get_tarval_sub_bits(tv, 1), get_tarval_sub_bits(tv, 0));
		return;

{'fraction_non_alphanumeric': 0.12129719818942303, 'fraction_numerical': 0.0055577837621039365, 'mean_word_length': 4.31956720512039, 'pattern_counts': {'":': 2, '<': 29, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5474236', 'n_tokens_mistral': 13967, 'n_tokens_neox': 13107, 'n_words': 2630}