+/**
+ * Build the Mulh replacement code for n / tv.
+ *
+ * Note that 'div' might be a mod or DivMod operation as well
+ */
+static ir_node *replace_div_by_mulh(ir_node *div, tarval *tv) {
+ dbg_info *dbg = get_irn_dbg_info(div);
+ ir_node *n = get_binop_left(div);
+ ir_node *block = get_irn_n(div, -1);
+ ir_mode *mode = get_irn_mode(n);
+ int bits = get_mode_size_bits(mode);
+ ir_node *q, *t, *c;
+
+ /* Beware: do not transform bad code */
+ if (is_Bad(n) || is_Bad(block))
+ return div;
+
+ if (mode_is_signed(mode)) {
+ struct ms mag = magic(tv);
+
+ /* generate the Mulh instruction */
+ c = new_r_Const(current_ir_graph, block, mode, mag.M);
+ q = new_rd_Mulh(dbg, current_ir_graph, block, n, c, mode);
+
+ /* do we need an Add or Sub */
+ if (mag.need_add)
+ q = new_rd_Add(dbg, current_ir_graph, block, q, n, mode);
+ else if (mag.need_sub)
+ q = new_rd_Sub(dbg, current_ir_graph, block, q, n, mode);
+
+ /* Do we need the shift */
+ if (mag.s > 0) {
+ c = new_r_Const_long(current_ir_graph, block, mode_Iu, mag.s);
+ q = new_rd_Shrs(dbg, current_ir_graph, block, q, c, mode);
+ }
+
+ /* final */
+ c = new_r_Const_long(current_ir_graph, block, mode_Iu, bits-1);
+ t = new_rd_Shr(dbg, current_ir_graph, block, q, c, mode);
+
+ q = new_rd_Add(dbg, current_ir_graph, block, q, t, mode);
+ } else {
+ struct mu mag = magicu(tv);
+ ir_node *c;
+
+ /* generate the Mulh instruction */
+ c = new_r_Const(current_ir_graph, block, mode, mag.M);
+ q = new_rd_Mulh(dbg, current_ir_graph, block, n, c, mode);
+
+ if (mag.need_add) {
+ if (mag.s > 0) {
+ /* use the GM scheme */
+ t = new_rd_Sub(dbg, current_ir_graph, block, n, q, mode);
+
+ c = new_r_Const(current_ir_graph, block, mode_Iu, get_mode_one(mode_Iu));
+ t = new_rd_Shr(dbg, current_ir_graph, block, t, c, mode);
+
+ t = new_rd_Add(dbg, current_ir_graph, block, t, q, mode);
+
+ c = new_r_Const_long(current_ir_graph, block, mode_Iu, mag.s-1);
+ q = new_rd_Shr(dbg, current_ir_graph, block, t, c, mode);
+ } else {
+ /* use the default scheme */
+ q = new_rd_Add(dbg, current_ir_graph, block, q, n, mode);
+ }
+ } else if (mag.s > 0) { /* default scheme, shift needed */
+ c = new_r_Const_long(current_ir_graph, block, mode_Iu, mag.s);
+ q = new_rd_Shr(dbg, current_ir_graph, block, q, c, mode);
+ }
+ }
+ return q;
+}