+ be_emit_irprintf(",%d", 1 << scale);
+ }
+ }
+ be_emit_char(')');
+ }
+}
+
+/**
+ * fmt parameter output
+ * ---- ---------------------- ---------------------------------------------
+ * %% %
+ * %AM <node> address mode of the node
+ * %AR const arch_register_t* address mode of the node or register
+ * %ASx <node> address mode of the node or source register x
+ * %Dx <node> destination register x
+ * %I <node> immediate of the node
+ * %L <node> control flow target of the node
+ * %M <node> mode suffix of the node
+ * %P int condition code
+ * %R const arch_register_t* register
+ * %Sx <node> source register x
+ * %s const char* string
+ * %u unsigned int unsigned int
+ * %d signed int signed int
+ *
+ * x starts at 0
+ * # modifier for %ASx, %D and %S uses ls mode of node to alter register width
+ * * modifier does not prefix immediates with $, but AM with *
+ * l modifier for %lu and %ld
+ */
+static void ia32_emitf(const ir_node *node, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+
+ for (;;) {
+ const char *start = fmt;
+ ia32_emit_mod_t mod = 0;
+
+ while (*fmt != '%' && *fmt != '\n' && *fmt != '\0')
+ ++fmt;
+ if (fmt != start) {
+ be_emit_string_len(start, fmt - start);
+ }
+
+ if (*fmt == '\n') {
+ be_emit_finish_line_gas(node);
+ ++fmt;
+ if (*fmt == '\0')
+ break;
+ continue;
+ }
+
+ if (*fmt == '\0')
+ break;
+
+ ++fmt;
+ if (*fmt == '*') {
+ mod |= EMIT_ALTERNATE_AM;
+ ++fmt;
+ }
+
+ if (*fmt == '#') {
+ mod |= EMIT_RESPECT_LS;
+ ++fmt;
+ }
+
+ if (*fmt == 'l') {
+ mod |= EMIT_LONG;
+ ++fmt;
+ }
+
+ switch (*fmt++) {
+ case '%':
+ be_emit_char('%');
+ break;
+
+ case 'A': {
+ switch (*fmt++) {
+ case 'M':
+ if (mod & EMIT_ALTERNATE_AM)
+ be_emit_char('*');
+
+ ia32_emit_am(node);
+ break;
+
+ case 'R': {
+ const arch_register_t *reg = va_arg(ap, const arch_register_t*);
+ if (mod & EMIT_ALTERNATE_AM)
+ be_emit_char('*');
+ if (get_ia32_op_type(node) == ia32_AddrModeS) {
+ ia32_emit_am(node);
+ } else {
+ emit_register(reg, NULL);
+ }
+ break;
+ }
+
+ case 'S':
+ if (get_ia32_op_type(node) == ia32_AddrModeS) {
+ if (mod & EMIT_ALTERNATE_AM)
+ be_emit_char('*');
+ ia32_emit_am(node);
+ ++fmt;
+ } else {
+ assert(get_ia32_op_type(node) == ia32_Normal);
+ goto emit_S;
+ }
+ break;
+
+ default: goto unknown;
+ }
+ break;
+ }
+
+ case 'D': {
+ unsigned pos;
+ const arch_register_t *reg;
+
+ if (*fmt < '0' || '9' <= *fmt)
+ goto unknown;
+
+ pos = *fmt++ - '0';
+ reg = get_out_reg(node, pos);
+ emit_register(reg, mod & EMIT_RESPECT_LS ? get_ia32_ls_mode(node) : NULL);
+ break;
+ }
+
+ case 'I':
+ if (!(mod & EMIT_ALTERNATE_AM))
+ be_emit_char('$');
+ emit_ia32_Immediate_no_prefix(node);
+ break;
+
+ case 'L':
+ ia32_emit_cfop_target(node);
+ break;
+
+ case 'M': {
+ ia32_emit_mode_suffix_mode(get_ia32_ls_mode(node));
+ break;
+ }
+
+ case 'P': {
+ int pnc = va_arg(ap, int);
+ ia32_emit_cmp_suffix(pnc);
+ break;
+ }
+
+ case 'R': {
+ const arch_register_t *reg = va_arg(ap, const arch_register_t*);
+ emit_register(reg, NULL);
+ break;
+ }
+
+emit_S:
+ case 'S': {
+ unsigned pos;
+ const ir_node *in;
+
+ if (*fmt < '0' || '9' <= *fmt)
+ goto unknown;
+
+ pos = *fmt++ - '0';
+ in = get_irn_n(node, pos);
+ if (is_ia32_Immediate(in)) {
+ if (!(mod & EMIT_ALTERNATE_AM))
+ be_emit_char('$');
+ emit_ia32_Immediate_no_prefix(in);
+ } else {
+ const arch_register_t *reg;
+
+ if (mod & EMIT_ALTERNATE_AM)
+ be_emit_char('*');
+ reg = get_in_reg(node, pos);
+ emit_register(reg, mod & EMIT_RESPECT_LS ? get_ia32_ls_mode(node) : NULL);
+ }
+ break;
+ }
+
+ case 's': {
+ const char *str = va_arg(ap, const char*);
+ be_emit_string(str);
+ break;
+ }
+
+ case 'u':
+ if (mod & EMIT_LONG) {
+ unsigned long num = va_arg(ap, unsigned long);
+ be_emit_irprintf("%lu", num);
+ } else {
+ unsigned num = va_arg(ap, unsigned);
+ be_emit_irprintf("%u", num);
+ }
+ break;
+
+ case 'd':
+ if (mod & EMIT_LONG) {
+ long num = va_arg(ap, long);
+ be_emit_irprintf("%ld", num);
+ } else {
+ int num = va_arg(ap, int);
+ be_emit_irprintf("%d", num);
+ }
+ break;
+
+ default:
+unknown:
+ panic("unknown format conversion in ia32_emitf()");
+ }
+ }
+
+ va_end(ap);
+}
+
+/**
+ * Emits registers and/or address mode of a binary operation.
+ */
+void ia32_emit_binop(const ir_node *node)
+{
+ if (is_ia32_Immediate(get_irn_n(node, n_ia32_binary_right))) {
+ ia32_emitf(node, "%#S4, %#AS3");
+ } else {
+ ia32_emitf(node, "%#AS4, %#S3");
+ }
+}
+
+/**
+ * Emits registers and/or address mode of a binary operation.
+ */
+void ia32_emit_x87_binop(const ir_node *node)
+{
+ switch(get_ia32_op_type(node)) {
+ case ia32_Normal:
+ {
+ const ia32_x87_attr_t *x87_attr = get_ia32_x87_attr_const(node);
+ const arch_register_t *in1 = x87_attr->x87[0];
+ const arch_register_t *in = x87_attr->x87[1];
+ const arch_register_t *out = x87_attr->x87[2];
+
+ if (out == NULL) {
+ out = in1;
+ } else if (out == in) {
+ in = in1;
+ }
+
+ be_emit_char('%');
+ be_emit_string(arch_register_get_name(in));
+ be_emit_cstring(", %");
+ be_emit_string(arch_register_get_name(out));