+static void print_help(const char *argv0, help_sections_t sections)
+{
+ if (sections & HELP_BASIC) print_help_basic(argv0);
+ if (sections & HELP_PREPROCESSOR) print_help_preprocessor();
+ if (sections & HELP_PARSER) print_help_parser();
+ if (sections & HELP_WARNINGS) print_help_warnings();
+ if (sections & HELP_OPTIMIZATION) print_help_optimization();
+ if (sections & HELP_CODEGEN) print_help_codegeneration();
+ if (sections & HELP_LINKER) print_help_linker();
+ if (sections & HELP_LANGUAGETOOLS) print_help_language_tools();
+ if (sections & HELP_DEBUG) print_help_debug();
+ if (sections & HELP_FIRM) print_help_firm();
+}
+
+static void set_be_option(const char *arg)
+{
+ int res = be_parse_arg(arg);
+ (void) res;
+ assert(res);
+}
+
+static compilation_unit_type_t get_unit_type_from_string(const char *string)
+{
+ if (streq(string, "c") || streq(string, "c-header"))
+ return COMPILATION_UNIT_C;
+ if (streq(string, "c++") || streq(string, "c++-header"))
+ return COMPILATION_UNIT_CXX;
+ if (streq(string, "assembler"))
+ return COMPILATION_UNIT_PREPROCESSED_ASSEMBLER;
+ if (streq(string, "assembler-with-cpp"))
+ return COMPILATION_UNIT_ASSEMBLER;
+ if (streq(string, "none"))
+ return COMPILATION_UNIT_AUTODETECT;
+
+ return COMPILATION_UNIT_UNKNOWN;
+}
+
+static bool init_os_support(void)
+{
+ wchar_atomic_kind = ATOMIC_TYPE_INT;
+ enable_main_collect2_hack = false;
+ define_intmax_types = false;
+
+ if (firm_is_unixish_os(target_machine)) {
+ set_create_ld_ident(create_name_linux_elf);
+ } else if (firm_is_darwin_os(target_machine)) {
+ set_create_ld_ident(create_name_macho);
+ define_intmax_types = true;
+ } else if (firm_is_windows_os(target_machine)) {
+ wchar_atomic_kind = ATOMIC_TYPE_USHORT;
+ enable_main_collect2_hack = true;
+ set_create_ld_ident(create_name_win32);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+static bool parse_target_triple(const char *arg)
+{
+ machine_triple_t *triple = firm_parse_machine_triple(arg);
+ if (triple == NULL) {
+ errorf(NULL, "target-triple '%s' is not in the form 'cpu_type-manufacturer-operating_system'", arg);
+ return false;
+ }
+ target_machine = triple;
+ return true;
+}
+
+static unsigned decide_modulo_shift(unsigned type_size)
+{
+ if (architecture_modulo_shift == 0)
+ return 0;
+ if (type_size < architecture_modulo_shift)
+ return architecture_modulo_shift;
+ return type_size;
+}
+
+static bool is_ia32_cpu(const char *architecture)
+{
+ return streq(architecture, "i386")
+ || streq(architecture, "i486")
+ || streq(architecture, "i586")
+ || streq(architecture, "i686")
+ || streq(architecture, "i786");
+}
+
+static const char *setup_isa_from_tripel(const machine_triple_t *machine)
+{
+ const char *cpu = machine->cpu_type;
+
+ if (is_ia32_cpu(cpu)) {
+ return "ia32";
+ } else if (streq(cpu, "x86_64")) {
+ return "amd64";
+ } else if (streq(cpu, "sparc")) {
+ return "sparc";
+ } else if (streq(cpu, "arm")) {
+ return "arm";
+ } else {
+ errorf(NULL, "unknown cpu '%s' in target-triple", cpu);
+ return NULL;
+ }
+}
+
+static const char *setup_target_machine(void)
+{
+ if (!setup_firm_for_machine(target_machine))
+ exit(1);
+
+ const char *isa = setup_isa_from_tripel(target_machine);
+
+ if (isa == NULL)
+ exit(1);
+
+ init_os_support();
+
+ return isa;
+}
+
+/**
+ * initialize cparser type properties based on a firm type
+ */
+static void set_typeprops_type(atomic_type_properties_t* props, ir_type *type)
+{
+ props->size = get_type_size_bytes(type);
+ props->alignment = get_type_alignment_bytes(type);
+ props->struct_alignment = props->alignment;
+}
+
+/**
+ * Copy atomic type properties except the integer conversion rank
+ */
+static void copy_typeprops(atomic_type_properties_t *dest,
+ const atomic_type_properties_t *src)
+{
+ dest->size = src->size;
+ dest->alignment = src->alignment;
+ dest->struct_alignment = src->struct_alignment;
+ dest->flags = src->flags;
+}
+
+static void init_types_and_adjust(void)
+{
+ const backend_params *be_params = be_get_backend_param();
+ unsigned machine_size = be_params->machine_size;
+ init_types(machine_size);
+
+ atomic_type_properties_t *props = atomic_type_properties;
+
+ /* adjust types as requested by target architecture */
+ ir_type *const type_ld = be_params->type_long_double;
+ if (type_ld) {
+ set_typeprops_type(&props[ATOMIC_TYPE_LONG_DOUBLE], type_ld);
+ atomic_modes[ATOMIC_TYPE_LONG_DOUBLE] = get_type_mode(type_ld);
+ }
+
+ ir_type *const type_ll = be_params->type_long_long;
+ if (type_ll)
+ set_typeprops_type(&props[ATOMIC_TYPE_LONGLONG], type_ll);
+
+ ir_type *const type_ull = be_params->type_unsigned_long_long;
+ if (type_ull)
+ set_typeprops_type(&props[ATOMIC_TYPE_ULONGLONG], type_ull);
+
+ /* operating system ABI specifics */
+ if (firm_is_darwin_os(target_machine)) {
+ if (is_ia32_cpu(target_machine->cpu_type)) {
+ props[ATOMIC_TYPE_LONGLONG].struct_alignment = 4;
+ props[ATOMIC_TYPE_ULONGLONG].struct_alignment = 4;
+ props[ATOMIC_TYPE_DOUBLE].struct_alignment = 4;
+ props[ATOMIC_TYPE_LONG_DOUBLE].size = 16;
+ props[ATOMIC_TYPE_LONG_DOUBLE].alignment = 16;
+ props[ATOMIC_TYPE_LONG_DOUBLE].struct_alignment = 16;
+ }
+ } else if (firm_is_windows_os(target_machine)) {
+ if (is_ia32_cpu(target_machine->cpu_type)) {
+ props[ATOMIC_TYPE_LONGLONG].struct_alignment = 8;
+ props[ATOMIC_TYPE_ULONGLONG].struct_alignment = 8;
+ props[ATOMIC_TYPE_DOUBLE].struct_alignment = 8;
+ } else if (machine_size == 64) {
+ /* to ease porting of old c-code microsoft decided to use 32bits
+ * even for long */
+ props[ATOMIC_TYPE_LONG] = props[ATOMIC_TYPE_INT];
+ props[ATOMIC_TYPE_ULONG] = props[ATOMIC_TYPE_UINT];
+ }
+
+ /* on windows long double is not supported */
+ props[ATOMIC_TYPE_LONG_DOUBLE] = props[ATOMIC_TYPE_DOUBLE];
+ } else if (firm_is_unixish_os(target_machine)) {
+ if (is_ia32_cpu(target_machine->cpu_type)) {
+ props[ATOMIC_TYPE_DOUBLE].struct_alignment = 4;
+ props[ATOMIC_TYPE_LONGLONG].struct_alignment = 4;
+ props[ATOMIC_TYPE_ULONGLONG].struct_alignment = 4;
+ }
+ }
+
+ /* stuff decided after processing operating system specifics and
+ * commandline flags */
+ if (char_is_signed) {
+ props[ATOMIC_TYPE_CHAR].flags |= ATOMIC_TYPE_FLAG_SIGNED;
+ } else {
+ props[ATOMIC_TYPE_CHAR].flags &= ~ATOMIC_TYPE_FLAG_SIGNED;
+ }
+ /* copy over wchar_t properties (including rank) */
+ props[ATOMIC_TYPE_WCHAR_T] = props[wchar_atomic_kind];
+
+ /* initialize defaults for unsupported types */
+ if (!type_ll) {
+ copy_typeprops(&props[ATOMIC_TYPE_LONGLONG], &props[ATOMIC_TYPE_LONG]);
+ }
+ if (!type_ull) {
+ copy_typeprops(&props[ATOMIC_TYPE_ULONGLONG],
+ &props[ATOMIC_TYPE_ULONG]);
+ }
+ if (!type_ld) {
+ copy_typeprops(&props[ATOMIC_TYPE_LONG_DOUBLE],
+ &props[ATOMIC_TYPE_DOUBLE]);
+ }
+
+ /* initialize firm pointer modes */
+ char name[64];
+ unsigned bit_size = machine_size;
+ unsigned modulo_shift = decide_modulo_shift(bit_size);
+
+ snprintf(name, sizeof(name), "p%u", machine_size);
+ ir_mode *ptr_mode = new_reference_mode(name, irma_twos_complement, bit_size, modulo_shift);
+
+ if (machine_size == 16) {
+ set_reference_mode_signed_eq(ptr_mode, mode_Hs);
+ set_reference_mode_unsigned_eq(ptr_mode, mode_Hu);
+ } else if (machine_size == 32) {
+ set_reference_mode_signed_eq(ptr_mode, mode_Is);
+ set_reference_mode_unsigned_eq(ptr_mode, mode_Iu);
+ } else if (machine_size == 64) {
+ set_reference_mode_signed_eq(ptr_mode, mode_Ls);
+ set_reference_mode_unsigned_eq(ptr_mode, mode_Lu);
+ } else {
+ panic("strange machine_size when determining pointer modes");
+ }
+
+ /* Hmm, pointers should be machine size */
+ set_modeP_data(ptr_mode);
+ set_modeP_code(ptr_mode);
+
+ byte_order_big_endian = be_params->byte_order_big_endian;
+ if (be_params->modulo_shift_efficient) {
+ architecture_modulo_shift = machine_size;
+ } else {
+ architecture_modulo_shift = 0;
+ }
+}
+
+static void setup_cmode(const compilation_unit_t *unit)
+{
+ compilation_unit_type_t type = unit->type;
+ lang_standard_t standard = unit->standard;
+ if (type == COMPILATION_UNIT_PREPROCESSED_C || type == COMPILATION_UNIT_C) {
+ switch (standard) {
+ case STANDARD_C89: c_mode = _C89; break;
+ /* TODO determine difference between these two */
+ case STANDARD_C89AMD1: c_mode = _C89; break;
+ case STANDARD_C99: c_mode = _C89 | _C99; break;
+ case STANDARD_C11: c_mode = _C89 | _C99 | _C11; break;
+ case STANDARD_GNU89: c_mode = _C89 | _GNUC; break;
+ case STANDARD_GNU11: c_mode = _C89 | _C99 | _C11 | _GNUC; break;
+
+ case STANDARD_ANSI:
+ case STANDARD_CXX98:
+ case STANDARD_GNUXX98:
+ case STANDARD_DEFAULT:
+ fprintf(stderr, "warning: command line option \"-std=%s\" is not valid for C\n", str_lang_standard(standard));
+ /* FALLTHROUGH */
+ case STANDARD_GNU99: c_mode = _C89 | _C99 | _GNUC; break;
+ }
+ } else if (type == COMPILATION_UNIT_PREPROCESSED_CXX
+ || type == COMPILATION_UNIT_CXX) {
+ switch (standard) {
+ case STANDARD_CXX98: c_mode = _CXX; break;
+
+ case STANDARD_ANSI:
+ case STANDARD_C89:
+ case STANDARD_C89AMD1:
+ case STANDARD_C99:
+ case STANDARD_C11:
+ case STANDARD_GNU89:
+ case STANDARD_GNU99:
+ case STANDARD_GNU11:
+ case STANDARD_DEFAULT:
+ fprintf(stderr, "warning: command line option \"-std=%s\" is not valid for C++\n", str_lang_standard(standard));
+ /* FALLTHROUGH */
+ case STANDARD_GNUXX98: c_mode = _CXX | _GNUC; break;
+ }
+ }
+
+ c_mode |= features_on;
+ c_mode &= ~features_off;
+}
+
+static void determine_unit_standard(compilation_unit_t *unit,
+ lang_standard_t standard)
+{
+ unit->standard = standard;
+ switch (standard) {
+ case STANDARD_ANSI:
+ switch (unit->type) {
+ case COMPILATION_UNIT_C:
+ case COMPILATION_UNIT_PREPROCESSED_C:
+ unit->standard = STANDARD_C89;
+ break;
+ case COMPILATION_UNIT_CXX:
+ case COMPILATION_UNIT_PREPROCESSED_CXX:
+ unit->standard = STANDARD_CXX98;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case STANDARD_DEFAULT:
+ switch (unit->type) {
+ case COMPILATION_UNIT_C:
+ case COMPILATION_UNIT_PREPROCESSED_C:
+ unit->standard = STANDARD_GNU99;
+ break;
+ case COMPILATION_UNIT_CXX:
+ case COMPILATION_UNIT_PREPROCESSED_CXX:
+ unit->standard = STANDARD_GNUXX98;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static bool output_preprocessor_tokens(compilation_unit_t *unit, FILE *out)
+{
+ /* just here for gcc compatibility */
+ fprintf(out, "# 1 \"%s\"\n", unit->name);
+ fprintf(out, "# 1 \"<built-in>\"\n");
+ fprintf(out, "# 1 \"<command-line>\"\n");
+
+ set_preprocessor_output(out);
+ switch_pp_input(unit->input, unit->name, NULL, false);
+
+ for (;;) {
+ next_preprocessing_token();
+ if (pp_token.kind == T_EOF)
+ break;
+ emit_pp_token();
+ }
+
+ fputc('\n', out);
+ check_unclosed_conditionals();
+ close_pp_input();
+ print_error_summary();
+ set_preprocessor_output(NULL);
+
+ if (unit->type == COMPILATION_UNIT_C) {
+ unit->type = COMPILATION_UNIT_PREPROCESSED_C;
+ } else if (unit->type == COMPILATION_UNIT_CXX) {
+ unit->type = COMPILATION_UNIT_PREPROCESSED_CXX;
+ }
+ bool res = close_input(unit);
+ return res && error_count == 0;
+}
+
+static void copy_file(FILE *dest, FILE *input)
+{
+ char buf[16384];
+
+ while (!feof(input) && !ferror(dest)) {
+ size_t read = fread(buf, 1, sizeof(buf), input);
+ if (fwrite(buf, 1, read, dest) != read) {
+ perror("could not write output");
+ }
+ }
+}
+
+static bool open_input(compilation_unit_t *unit)
+{
+ /* input already available as FILE? */
+ if (unit->input != NULL)
+ return true;
+
+ const char *const inputname = unit->name;
+ unit->input_is_pipe = false;
+ if (streq(inputname, "-")) {
+ unit->input = stdin;
+ } else {
+ unit->input = fopen(inputname, "r");
+ if (unit->input == NULL) {
+ position_t const pos = { inputname, 0, 0, 0 };
+ errorf(&pos, "could not open: %s", strerror(errno));
+ return false;
+ }
+ }
+ return true;
+}
+
+static void node_counter(ir_node *node, void *env)
+{
+ (void)node;
+ unsigned long long *count = (unsigned long long*)env;
+ ++(*count);
+}
+
+static unsigned long long count_firm_nodes(void)
+{
+ unsigned long long count = 0;
+
+ int n_irgs = get_irp_n_irgs();
+ for (int i = 0; i < n_irgs; ++i) {
+ ir_graph *irg = get_irp_irg(i);
+ irg_walk_graph(irg, node_counter, NULL, &count);
+ }
+ return count;
+}
+
+static int compilation_loop(compile_mode_t mode, compilation_unit_t *units,
+ lang_standard_t standard, FILE *out)
+{
+ int result = EXIT_SUCCESS;
+ bool already_constructed_firm = false;
+ for (compilation_unit_t *unit = units; unit != NULL; unit = unit->next) {
+ const char *const inputname = unit->name;
+
+ determine_unit_standard(unit, standard);
+ setup_cmode(unit);
+
+ stat_ev_ctx_push_str("compilation_unit", inputname);
+
+again:
+ switch (unit->type) {
+ case COMPILATION_UNIT_IR: {
+ if (!open_input(unit)) {
+ result = EXIT_FAILURE;
+ break;
+ }
+ if (ir_import_file(unit->input, unit->name)) {
+ position_t const pos = { inputname, 0, 0, 0 };
+ errorf(&pos, "import of firm graph failed");
+ result = EXIT_FAILURE;
+ break;
+ }
+ unit->type = COMPILATION_UNIT_INTERMEDIATE_REPRESENTATION;
+ goto again;
+ }
+ case COMPILATION_UNIT_ASSEMBLER: {
+ if (external_preprocessor == NULL) {
+ panic("preprocessed assembler not possible with internal preprocessor yet");
+ }
+ if (!run_external_preprocessor(unit)) {
+ result = EXIT_FAILURE;
+ break;
+ }
+ /* write file to output... */
+ FILE *asm_out;
+ if (mode == PreprocessOnly) {
+ asm_out = out;
+ } else {
+ asm_out = make_temp_file("ccs", &unit->name);
+ }
+ assert(unit->input != NULL);
+ assert(unit->input_is_pipe);
+ copy_file(asm_out, unit->input);
+ if (asm_out != out)
+ fclose(asm_out);
+ unit->type = COMPILATION_UNIT_PREPROCESSED_ASSEMBLER;
+ goto again;
+ }
+ case COMPILATION_UNIT_C:
+ case COMPILATION_UNIT_CXX:
+ if (external_preprocessor != NULL) {
+ if (!run_external_preprocessor(unit)) {
+ result = EXIT_FAILURE;
+ break;
+ }
+ goto again;
+ }
+ /* FALLTHROUGH */
+
+ case COMPILATION_UNIT_PREPROCESSED_C:
+ case COMPILATION_UNIT_PREPROCESSED_CXX: {
+ if (!open_input(unit)) {
+ result = EXIT_FAILURE;
+ break;
+ }
+ init_tokens();
+
+ if (mode == PreprocessOnly) {
+ if (!output_preprocessor_tokens(unit, out)) {
+ result = EXIT_FAILURE;
+ break;
+ }
+ break;
+ }
+
+ /* do the actual parsing */
+ do_parsing(unit);
+ goto again;
+ }
+ case COMPILATION_UNIT_AST:
+ /* prints the AST even if errors occurred */
+ if (mode == PrintAst) {
+ print_to_file(out);
+ print_ast(unit->ast);
+ }
+ if (unit->parse_errors) {
+ result = EXIT_FAILURE;
+ break;
+ }
+
+ if (mode == BenchmarkParser) {
+ break;
+ } else if (mode == PrintFluffy) {
+ write_fluffy_decls(out, unit->ast);
+ break;
+ } else if (mode == PrintJna) {
+ write_jna_decls(out, unit->ast);
+ break;
+ } else if (mode == PrintCompoundSizes) {
+ write_compoundsizes(out, unit->ast);
+ break;
+ }
+
+ /* build the firm graph */
+ ir_timer_t *t_construct = ir_timer_new();
+ timer_register(t_construct, "Frontend: Graph construction");
+ timer_start(t_construct);
+ if (already_constructed_firm) {
+ panic("compiling multiple files/translation units not possible");
+ }
+ init_implicit_optimizations();
+ translation_unit_to_firm(unit->ast);
+ already_constructed_firm = true;
+ timer_stop(t_construct);
+ if (stat_ev_enabled) {
+ stat_ev_dbl("time_graph_construction", ir_timer_elapsed_sec(t_construct));
+ stat_ev_int("size_graph_construction", count_firm_nodes());
+ }
+ unit->type = COMPILATION_UNIT_INTERMEDIATE_REPRESENTATION;
+ goto again;
+
+ case COMPILATION_UNIT_INTERMEDIATE_REPRESENTATION:
+ if (mode == ParseOnly)
+ break;
+
+ if (mode == CompileDump) {
+ /* find irg */
+ ident *id = new_id_from_str(dumpfunction);
+ ir_graph *irg = NULL;
+ int n_irgs = get_irp_n_irgs();
+ for (int i = 0; i < n_irgs; ++i) {
+ ir_graph *tirg = get_irp_irg(i);
+ ident *irg_id = get_entity_ident(get_irg_entity(tirg));
+ if (irg_id == id) {
+ irg = tirg;
+ break;
+ }
+ }
+
+ if (irg == NULL) {
+ errorf(NULL, "no graph for function '%s' found", dumpfunction);
+ return EXIT_FAILURE;
+ }
+
+ dump_ir_graph_file(out, irg);
+ fclose(out);
+ return EXIT_SUCCESS;
+ }
+
+ if (mode == CompileExportIR) {
+ ir_export_file(out);
+ if (ferror(out) != 0) {
+ errorf(NULL, "writing to output failed");
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+ }
+
+ FILE *asm_out;
+ if (mode == Compile) {
+ asm_out = out;
+ } else {
+ asm_out = make_temp_file("ccs", &unit->name);
+ }
+ ir_timer_t *t_opt_codegen = ir_timer_new();
+ timer_register(t_opt_codegen, "Optimization and Codegeneration");
+ timer_start(t_opt_codegen);
+ generate_code(asm_out, inputname);
+ timer_stop(t_opt_codegen);
+ if (stat_ev_enabled) {
+ stat_ev_dbl("time_opt_codegen", ir_timer_elapsed_sec(t_opt_codegen));
+ }
+ if (asm_out != out) {
+ fclose(asm_out);
+ }
+ unit->type = COMPILATION_UNIT_PREPROCESSED_ASSEMBLER;
+ goto again;
+ case COMPILATION_UNIT_PREPROCESSED_ASSEMBLER:
+ if (mode != CompileAssemble && mode != CompileAssembleLink)
+ break;