+/*********************************************************
+ * _ _ _
+ * (_) | (_)
+ * ___ _ __ ___ _| |_ _ _ _ _ __ ___ _ __ ___
+ * / _ \ '_ ` _ \| | __| | | | | | '_ ` _ \| '_ \/ __|
+ * | __/ | | | | | | |_ | | |_| | | | | | | |_) \__ \
+ * \___|_| |_| |_|_|\__| | |\__,_|_| |_| |_| .__/|___/
+ * _/ | | |
+ * |__/ |_|
+ *********************************************************/
+
+/* jump table entry (target and corresponding number) */
+typedef struct _branch_t {
+ ir_node *target;
+ int value;
+} branch_t;
+
+/* jump table for switch generation */
+typedef struct _jmp_tbl_t {
+ ir_node *defProj; /**< default target */
+ int min_value; /**< smallest switch case */
+ int max_value; /**< largest switch case */
+ int num_branches; /**< number of jumps */
+ char *label; /**< label of the jump table */
+ branch_t *branches; /**< jump array */
+} jmp_tbl_t;
+
+/**
+ * Compare two variables of type branch_t. Used to sort all switch cases
+ */
+static int ia32_cmp_branch_t(const void *a, const void *b) {
+ branch_t *b1 = (branch_t *)a;
+ branch_t *b2 = (branch_t *)b;
+
+ if (b1->value <= b2->value)
+ return -1;
+ else
+ return 1;
+}
+
+/**
+ * Emits code for a SwitchJmp (creates a jump table if
+ * possible otherwise a cmp-jmp cascade). Port from
+ * cggg ia32 backend
+ */
+void emit_ia32_SwitchJmp(const ir_node *irn, emit_env_t *emit_env) {
+ unsigned long interval;
+ char buf[SNPRINTF_BUF_LEN];
+ int last_value, i, pn, do_jmp_tbl = 1;
+ jmp_tbl_t tbl;
+ ir_node *proj;
+ const ir_edge_t *edge;
+ const lc_arg_env_t *env = ia32_get_arg_env();
+ FILE *F = emit_env->out;
+
+ /* fill the table structure */
+ tbl.label = xmalloc(SNPRINTF_BUF_LEN);
+ tbl.label = get_unique_label(tbl.label, SNPRINTF_BUF_LEN, "JMPTBL_");
+ tbl.defProj = NULL;
+ tbl.num_branches = get_irn_n_edges(irn);
+ tbl.branches = xcalloc(tbl.num_branches, sizeof(tbl.branches[0]));
+ tbl.min_value = INT_MAX;
+ tbl.max_value = INT_MIN;
+
+ i = 0;
+ /* go over all proj's and collect them */
+ foreach_out_edge(irn, edge) {
+ proj = get_edge_src_irn(edge);
+ assert(is_Proj(proj) && "Only proj allowed at SwitchJmp");
+
+ pn = get_Proj_proj(proj);
+
+ /* create branch entry */
+ tbl.branches[i].target = proj;
+ tbl.branches[i].value = pn;
+
+ tbl.min_value = pn < tbl.min_value ? pn : tbl.min_value;
+ tbl.max_value = pn > tbl.max_value ? pn : tbl.max_value;
+
+ /* check for default proj */
+ if (pn == get_ia32_pncode(irn)) {
+ assert(tbl.defProj == NULL && "found two defProjs at SwitchJmp");
+ tbl.defProj = proj;
+ }
+
+ i++;
+ }
+
+ /* sort the branches by their number */
+ qsort(tbl.branches, tbl.num_branches, sizeof(tbl.branches[0]), ia32_cmp_branch_t);
+
+ /* two-complement's magic make this work without overflow */
+ interval = tbl.max_value - tbl.min_value;
+
+ /* check value interval */
+ if (interval > 16 * 1024) {
+ do_jmp_tbl = 0;
+ }
+
+ /* check ratio of value interval to number of branches */
+ if ((float)(interval + 1) / (float)tbl.num_branches > 8.0) {
+ do_jmp_tbl = 0;
+ }
+
+ if (do_jmp_tbl) {
+ /* emit the table */
+ if (tbl.min_value != 0) {
+ fprintf(F, "\tcmpl %lu, -%d", interval, tbl.min_value);
+ lc_efprintf(env, F, "(%1S)\t\t/* first switch value is not 0 */\n", irn);
+ }
+ else {
+ fprintf(F, "\tcmpl %lu, ", interval);
+ lc_efprintf(env, F, "%1S\t\t\t/* compare for switch */\n", irn);
+ }
+
+ fprintf(F, "\tja %s\t\t\t/* default jump if out of range */\n", get_cfop_target(tbl.defProj, buf));
+
+ if (tbl.num_branches > 1) {
+ /* create table */
+
+ //fprintf(F, "\tjmp *%s", tbl.label);
+ lc_efprintf(env, F, "\tjmp *%s(,%1S,4)\t\t/* get jump table entry as target */\n", tbl.label, irn);
+
+ fprintf(F, "\t.section\t.rodata\t\t/* start jump table */\n");
+ fprintf(F, "\t.align 4\n");
+
+ fprintf(F, "%s:\n", tbl.label);
+ fprintf(F, "\t.long %s\t\t\t/* case %d */\n", get_cfop_target(tbl.branches[0].target, buf), tbl.branches[0].value);
+
+ last_value = tbl.branches[0].value;
+ for (i = 1; i < tbl.num_branches; ++i) {
+ while (++last_value < tbl.branches[i].value) {
+ fprintf(F, "\t.long %s\t\t/* default case */\n", get_cfop_target(tbl.defProj, buf));
+ }
+ fprintf(F, "\t.long %s\t\t\t/* case %d */\n", get_cfop_target(tbl.branches[i].target, buf), last_value);
+ }
+
+ fprintf(F, "\t.text\t\t\t\t/* end of jump table */\n");
+ }
+ else {
+ /* one jump is enough */
+ fprintf(F, "\tjmp %s\t\t/* only one case given */\n", get_cfop_target(tbl.branches[0].target, buf));
+ }
+ }
+ else { // no jump table
+ for (i = 0; i < tbl.num_branches; ++i) {
+ fprintf(F, "\tcmpl %d, ", tbl.branches[i].value);
+ lc_efprintf(env, F, "%1S", irn);
+ fprintf(F, "\t\t\t/* case %d */\n", tbl.branches[i].value);
+ fprintf(F, "\tje %s\n", get_cfop_target(tbl.branches[i].target, buf));
+ }
+
+ fprintf(F, "\tjmp %s\t\t\t/* default case */\n", get_cfop_target(tbl.defProj, buf));
+ }
+
+ if (tbl.label)
+ free(tbl.label);
+ if (tbl.branches)
+ free(tbl.branches);
+}
+
+/**
+ * Emits code for a unconditional jump.
+ */
+void emit_Jmp(ir_node *irn, emit_env_t *env) {
+ FILE *F = env->out;
+
+ char buf[SNPRINTF_BUF_LEN];
+ ir_fprintf(F, "\tjmp %s\t\t\t/* Jmp(%+F) */\n", get_cfop_target(irn, buf), get_irn_link(irn));
+}
+
+
+
+/****************************
+ * _
+ * (_)
+ * _ __ _ __ ___ _ ___
+ * | '_ \| '__/ _ \| |/ __|
+ * | |_) | | | (_) | |\__ \
+ * | .__/|_| \___/| ||___/
+ * | | _/ |
+ * |_| |__/
+ ****************************/
+
+/**
+ * Emits code for a proj -> node
+ */
+void emit_Proj(ir_node *irn, emit_env_t *env) {
+ ir_node *pred = get_Proj_pred(irn);
+
+ if (get_irn_op(pred) == op_Start) {
+ switch(get_Proj_proj(irn)) {
+ case pn_Start_X_initial_exec:
+ emit_Jmp(irn, env);
+ break;
+ default:
+ break;
+ }
+ }