Fix regular expression at doc/rdsrc.pl
[platform/upstream/nasm.git] / assemble.c
index e2e24c3..ff3cea7 100644 (file)
@@ -1,6 +1,6 @@
 /* ----------------------------------------------------------------------- *
  *
- *   Copyright 1996-2013 The NASM Authors - All Rights Reserved
+ *   Copyright 1996-2014 The NASM Authors - All Rights Reserved
  *   See the file AUTHORS included with the NASM distribution for
  *   the specific copyright holders.
  *
 #include "assemble.h"
 #include "insns.h"
 #include "tables.h"
+#include "disp8.h"
 
 enum match_result {
     /*
@@ -189,11 +190,13 @@ enum match_result {
     MERR_INVALOP,
     MERR_OPSIZEMISSING,
     MERR_OPSIZEMISMATCH,
+    MERR_BRNUMMISMATCH,
     MERR_BADCPU,
     MERR_BADMODE,
     MERR_BADHLE,
     MERR_ENCMISMATCH,
     MERR_BADBND,
+    MERR_BADREPNE,
     /*
      * Matching success; the conditional ones first
      */
@@ -216,7 +219,7 @@ typedef struct {
 #define GEN_MODRM(mod, reg, rm)                     \
         (((mod) << 6) | (((reg) & 7) << 3) | ((rm) & 7))
 
-static iflags_t cpu;            /* cpu level received from nasm.c */
+static iflag_t cpu;             /* cpu level received from nasm.c */
 static efunc errfunc;
 static struct ofmt *outfmt;
 static ListGen *list;
@@ -359,6 +362,7 @@ static bool jmp_match(int32_t segment, int64_t offset, int bits,
     int64_t isize;
     const uint8_t *code = temp->code;
     uint8_t c = code[0];
+    bool is_byte;
 
     if (((c & ~1) != 0370) || (ins->oprs[0].type & STRICT))
         return false;
@@ -377,10 +381,19 @@ static bool jmp_match(int32_t segment, int64_t offset, int bits,
         return false;
 
     isize = ins->oprs[0].offset - offset - isize; /* isize is delta */
-    return (isize >= -128 && isize <= 127); /* is it byte size? */
+    is_byte = (isize >= -128 && isize <= 127); /* is it byte size? */
+
+    if (is_byte && c == 0371 && ins->prefixes[PPS_REP] == P_BND) {
+        /* jmp short (opcode eb) cannot be used with bnd prefix. */
+        ins->prefixes[PPS_REP] = P_none;
+        errfunc(ERR_WARNING | ERR_WARN_BND | ERR_PASS2 ,
+                "jmp short does not init bnd regs - bnd prefix dropped.");
+    }
+
+    return is_byte;
 }
 
-int64_t assemble(int32_t segment, int64_t offset, int bits, iflags_t cp,
+int64_t assemble(int32_t segment, int64_t offset, int bits, iflag_t cp,
                  insn * instruction, struct ofmt *output, efunc error,
                  ListGen * listgen)
 {
@@ -632,6 +645,10 @@ int64_t assemble(int32_t segment, int64_t offset, int bits, iflags_t cp,
                     case P_OSP:
                         c = 0x66;
                         break;
+                    case P_EVEX:
+                    case P_VEX3:
+                    case P_VEX2:
+                    case P_NOBND:
                     case P_none:
                         break;
                     default:
@@ -668,6 +685,10 @@ int64_t assemble(int32_t segment, int64_t offset, int bits, iflags_t cp,
         case MERR_OPSIZEMISMATCH:
             error(ERR_NONFATAL, "mismatch in operand sizes");
             break;
+        case MERR_BRNUMMISMATCH:
+            error(ERR_NONFATAL,
+                  "mismatch in the number of broadcasting elements");
+            break;
         case MERR_BADCPU:
             error(ERR_NONFATAL, "no instruction for this cpu level");
             break;
@@ -675,6 +696,17 @@ int64_t assemble(int32_t segment, int64_t offset, int bits, iflags_t cp,
             error(ERR_NONFATAL, "instruction not supported in %d-bit mode",
                   bits);
             break;
+        case MERR_ENCMISMATCH:
+            error(ERR_NONFATAL, "specific encoding scheme not available");
+            break;
+        case MERR_BADBND:
+            error(ERR_NONFATAL, "bnd prefix is not allowed");
+            break;
+        case MERR_BADREPNE:
+            error(ERR_NONFATAL, "%s prefix is not allowed",
+                  (has_prefix(instruction, PPS_REP, P_REPNE) ?
+                   "repne" : "repnz"));
+            break;
         default:
             error(ERR_NONFATAL,
                   "invalid combination of opcode and operands");
@@ -684,7 +716,7 @@ int64_t assemble(int32_t segment, int64_t offset, int bits, iflags_t cp,
     return 0;
 }
 
-int64_t insn_size(int32_t segment, int64_t offset, int bits, iflags_t cp,
+int64_t insn_size(int32_t segment, int64_t offset, int bits, iflag_t cp,
                   insn * instruction, efunc error)
 {
     const struct itemplate *temp;
@@ -786,6 +818,10 @@ int64_t insn_size(int32_t segment, int64_t offset, int bits, iflags_t cp,
                 break;
             case P_A64:
             case P_O64:
+            case P_EVEX:
+            case P_VEX3:
+            case P_VEX2:
+            case P_NOBND:
             case P_none:
                 break;
             default:
@@ -1188,33 +1224,25 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
                 } else {
                     /* set EVEX.L'L (vector length) */
                     ins->evex_p[2] |= ((ins->vex_wlp << (5 - 2)) & EVEX_P2LL);
+                    ins->evex_p[1] |= ((ins->vex_wlp << (7 - 4)) & EVEX_P1W);
                     if (opy->decoflags & BRDCAST_MASK) {
                         /* set EVEX.b */
                         ins->evex_p[2] |= EVEX_P2B;
                     }
                 }
 
-                /*
-                 * if a separate form of MIB (ICC style) is used,
-                 * the index reg info is merged into mem operand
-                 */
-                if (mib_index != R_none) {
-                    opy->indexreg = mib_index;
-                    opy->scale = 1;
-                    opy->hintbase = mib_index;
-                    opy->hinttype = EAH_NOTBASE;
-                }
-
-                /*
-                 * only for mib operands, make a single reg index [reg*1].
-                 * gas uses this form to explicitly denote index register.
-                 */
-                if ((temp->flags & IF_MIB) &&
-                    (opy->indexreg == -1 && opy->hintbase == opy->basereg &&
-                     opy->hinttype == EAH_NOTBASE)) {
-                    opy->indexreg = opy->basereg;
-                    opy->basereg  = -1;
-                    opy->scale    = 1;
+                if (itemp_has(temp, IF_MIB)) {
+                    opy->eaflags |= EAF_MIB;
+                    /*
+                     * if a separate form of MIB (ICC style) is used,
+                     * the index reg info is merged into mem operand
+                     */
+                    if (mib_index != R_none) {
+                        opy->indexreg = mib_index;
+                        opy->scale = 1;
+                        opy->hintbase = mib_index;
+                        opy->hinttype = EAH_NOTBASE;
+                    }
                 }
 
                 if (process_ea(opy, &ea_data, bits,
@@ -1245,6 +1273,20 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
         ins->rex &= ~REX_P;        /* Don't force REX prefix due to high reg */
     }
 
+    switch (ins->prefixes[PPS_VEX]) {
+    case P_EVEX:
+        if (!(ins->rex & REX_EV))
+            return -1;
+        break;
+    case P_VEX3:
+    case P_VEX2:
+        if (!(ins->rex & REX_V))
+            return -1;
+        break;
+    default:
+        break;
+    }
+
     if (ins->rex & (REX_V | REX_EV)) {
         int bad32 = REX_R|REX_W|REX_X|REX_B;
 
@@ -1276,7 +1318,8 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
         }
         if (ins->rex & REX_EV)
             length += 4;
-        else if (ins->vex_cm != 1 || (ins->rex & (REX_W|REX_X|REX_B)))
+        else if (ins->vex_cm != 1 || (ins->rex & (REX_W|REX_X|REX_B)) ||
+                 ins->prefixes[PPS_VEX] == P_VEX3)
             length += 3;
         else
             length += 2;
@@ -1288,7 +1331,7 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
             length++;
         } else if ((ins->rex & REX_L) &&
                    !(ins->rex & (REX_P|REX_W|REX_X|REX_B)) &&
-                   cpu >= IF_X86_64) {
+                   iflag_ffs(&cpu) >= IF_X86_64) {
             /* LOCK-as-REX.R */
             assert_no_prefix(ins, PPS_LOCK);
             lockcheck = false;  /* Already errored, no need for warning */
@@ -1300,23 +1343,34 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
     }
 
     if (has_prefix(ins, PPS_LOCK, P_LOCK) && lockcheck &&
-        (!(temp->flags & IF_LOCK) || !is_class(MEMORY, ins->oprs[0].type))) {
+        (!itemp_has(temp,IF_LOCK) || !is_class(MEMORY, ins->oprs[0].type))) {
         errfunc(ERR_WARNING | ERR_WARN_LOCK | ERR_PASS2 ,
                 "instruction is not lockable");
     }
 
     bad_hle_warn(ins, hleok);
 
+    /*
+     * when BND prefix is set by DEFAULT directive,
+     * BND prefix is added to every appropriate instruction line
+     * unless it is overridden by NOBND prefix.
+     */
+    if (globalbnd &&
+        (itemp_has(temp, IF_BND) && !has_prefix(ins, PPS_REP, P_NOBND)))
+            ins->prefixes[PPS_REP] = P_BND;
+
     return length;
 }
 
 static inline unsigned int emit_rex(insn *ins, int32_t segment, int64_t offset, int bits)
 {
     if (bits == 64) {
-        if ((ins->rex & REX_REAL) && !(ins->rex & (REX_V | REX_EV))) {
-            ins->rex = (ins->rex & REX_REAL) | REX_P;
-            out(offset, segment, &ins->rex, OUT_RAWDATA, 1, NO_SEG, NO_SEG);
-            ins->rex = 0;
+        if ((ins->rex & REX_REAL) &&
+            !(ins->rex & (REX_V | REX_EV)) &&
+            !ins->rex_done) {
+            int rex = (ins->rex & REX_REAL) | REX_P;
+            out(offset, segment, &rex, OUT_RAWDATA, 1, NO_SEG, NO_SEG);
+            ins->rex_done = true;
             return 1;
         }
     }
@@ -1338,6 +1392,8 @@ static void gencode(int32_t segment, int64_t offset, int bits,
     uint8_t opex = 0;
     enum ea_type eat = EA_SCALAR;
 
+    ins->rex_done = false;
+
     while (*codes) {
         c = *codes++;
         op1 = (c & 3) + ((opex & 1) << 2);
@@ -1575,7 +1631,8 @@ static void gencode(int32_t segment, int64_t offset, int bits,
         case4(0260):
         case 0270:
             codes += 2;
-            if (ins->vex_cm != 1 || (ins->rex & (REX_W|REX_X|REX_B))) {
+            if (ins->vex_cm != 1 || (ins->rex & (REX_W|REX_X|REX_B)) ||
+                ins->prefixes[PPS_VEX] == P_VEX3) {
                 bytes[0] = (ins->vex_cm >> 6) ? 0x8f : 0xc4;
                 bytes[1] = (ins->vex_cm & 31) | ((~ins->rex & 7) << 5);
                 bytes[2] = ((ins->rex & REX_W) << (7-3)) |
@@ -1945,9 +2002,6 @@ static int op_evexflags(const operand * o, int mask, uint8_t byte)
 {
     int val;
 
-    if (!is_register(o->basereg))
-        errfunc(ERR_PANIC, "invalid operand passed to op_evexflags()");
-
     val = nasm_regvals[o->basereg];
 
     return evexflags(val, o->decoflags, mask, byte);
@@ -1981,8 +2035,7 @@ static enum match_result find_match(const struct itemplate **tempp,
                 m = MOK_GOOD;
             else
                 m = MERR_INVALOP;
-        } else if (m == MERR_OPSIZEMISSING &&
-                   (temp->flags & IF_SMASK) != IF_SX) {
+        } else if (m == MERR_OPSIZEMISSING && !itemp_has(temp, IF_SX)) {
             /*
              * Missing operand size and a candidate for fuzzy matching...
              */
@@ -2016,10 +2069,13 @@ static enum match_result find_match(const struct itemplate **tempp,
         if ((xsizeflags[i] & (xsizeflags[i]-1)))
             goto done;                /* No luck */
 
-        if (i == broadcast)
+        if (i == broadcast) {
             instruction->oprs[i].decoflags |= xsizeflags[i];
-        else
+            instruction->oprs[i].type |= (xsizeflags[i] == BR_BITS32 ?
+                                          BITS32 : BITS64);
+        } else {
             instruction->oprs[i].type |= xsizeflags[i]; /* Set the size */
+        }
     }
 
     /* Try matching again... */
@@ -2065,10 +2121,27 @@ static enum match_result matches(const struct itemplate *itemp,
     /*
      * Is it legal?
      */
-    if (!(optimizing > 0) && (itemp->flags & IF_OPT))
+    if (!(optimizing > 0) && itemp_has(itemp, IF_OPT))
        return MERR_INVALOP;
 
     /*
+     * {evex} available?
+     */
+    switch (instruction->prefixes[PPS_VEX]) {
+    case P_EVEX:
+        if (!itemp_has(itemp, IF_EVEX))
+            return MERR_ENCMISMATCH;
+        break;
+    case P_VEX3:
+    case P_VEX2:
+        if (!itemp_has(itemp, IF_VEX))
+            return MERR_ENCMISMATCH;
+        break;
+    default:
+        break;
+    }
+
+    /*
      * Check that no spurious colons or TOs are present
      */
     for (i = 0; i < itemp->operands; i++)
@@ -2078,29 +2151,29 @@ static enum match_result matches(const struct itemplate *itemp,
     /*
      * Process size flags
      */
-    switch (itemp->flags & IF_SMASK) {
-    case IF_SB:
+    switch (itemp_smask(itemp)) {
+    case IF_GENBIT(IF_SB):
         asize = BITS8;
         break;
-    case IF_SW:
+    case IF_GENBIT(IF_SW):
         asize = BITS16;
         break;
-    case IF_SD:
+    case IF_GENBIT(IF_SD):
         asize = BITS32;
         break;
-    case IF_SQ:
+    case IF_GENBIT(IF_SQ):
         asize = BITS64;
         break;
-    case IF_SO:
+    case IF_GENBIT(IF_SO):
         asize = BITS128;
         break;
-    case IF_SY:
+    case IF_GENBIT(IF_SY):
         asize = BITS256;
         break;
-    case IF_SZ:
+    case IF_GENBIT(IF_SZ):
         asize = BITS512;
         break;
-    case IF_SIZE:
+    case IF_GENBIT(IF_SIZE):
         switch (bits) {
         case 16:
             asize = BITS16;
@@ -2121,9 +2194,9 @@ static enum match_result matches(const struct itemplate *itemp,
         break;
     }
 
-    if (itemp->flags & IF_ARMASK) {
+    if (itemp_armask(itemp)) {
         /* S- flags only apply to a specific operand */
-        i = ((itemp->flags & IF_ARMASK) >> IF_ARSHFT) - 1;
+        i = itemp_arg(itemp);
         memset(size, 0, sizeof size);
         size[i] = asize;
     } else {
@@ -2155,37 +2228,59 @@ static enum match_result matches(const struct itemplate *itemp,
     for (i = 0; i < itemp->operands; i++) {
         opflags_t type = instruction->oprs[i].type;
         decoflags_t deco = instruction->oprs[i].decoflags;
+        bool is_broadcast = deco & BRDCAST_MASK;
+        uint8_t brcast_num = 0;
+        opflags_t template_opsize, insn_opsize;
+
         if (!(type & SIZE_MASK))
             type |= size[i];
 
+        insn_opsize     = type & SIZE_MASK;
+        if (!is_broadcast) {
+            template_opsize = itemp->opd[i] & SIZE_MASK;
+        } else {
+            decoflags_t deco_brsize = itemp->deco[i] & BRSIZE_MASK;
+            /*
+             * when broadcasting, the element size depends on
+             * the instruction type. decorator flag should match.
+             */
+
+            if (deco_brsize) {
+                template_opsize = (deco_brsize == BR_BITS32 ? BITS32 : BITS64);
+                /* calculate the proper number : {1to<brcast_num>} */
+                brcast_num = (itemp->opd[i] & SIZE_MASK) / BITS128 *
+                                BITS64 / template_opsize * 2;
+            } else {
+                template_opsize = 0;
+            }
+        }
+
         if ((itemp->opd[i] & ~type & ~SIZE_MASK) ||
-            (itemp->deco[i] & deco) != deco) {
+            (deco & ~itemp->deco[i] & ~BRNUM_MASK)) {
             return MERR_INVALOP;
-        } else if ((itemp->opd[i] & SIZE_MASK) &&
-                   (itemp->opd[i] & SIZE_MASK) != (type & SIZE_MASK)) {
-            if (type & SIZE_MASK) {
-                /*
-                 * when broadcasting, the element size depends on
-                 * the instruction type. decorator flag should match.
-                 */
-#define MATCH_BRSZ(bits) (((type & SIZE_MASK) == BITS##bits) &&             \
-                          ((itemp->deco[i] & BRSIZE_MASK) == BR_BITS##bits))
-                if (!((deco & BRDCAST_MASK) &&
-                      (MATCH_BRSZ(32) || MATCH_BRSZ(64)))) {
+        } else if (template_opsize) {
+            if (template_opsize != insn_opsize) {
+                if (insn_opsize) {
                     return MERR_INVALOP;
+                } else if (!is_class(REGISTER, type)) {
+                    /*
+                     * Note: we don't honor extrinsic operand sizes for registers,
+                     * so "missing operand size" for a register should be
+                     * considered a wildcard match rather than an error.
+                     */
+                    opsizemissing = true;
                 }
-            } else if (!is_class(REGISTER, type)) {
+            } else if (is_broadcast &&
+                       (brcast_num !=
+                        (8U << ((deco & BRNUM_MASK) >> BRNUM_SHIFT)))) {
                 /*
-                 * Note: we don't honor extrinsic operand sizes for registers,
-                 * so "missing operand size" for a register should be
-                 * considered a wildcard match rather than an error.
+                 * broadcasting opsize matches but the number of repeated memory
+                 * element does not match.
+                 * if 64b double precision float is broadcasted to zmm (512b),
+                 * broadcasting decorator must be {1to8}.
                  */
-                opsizemissing = true;
+                return MERR_BRNUMMISMATCH;
             }
-        } else if (is_register(instruction->oprs[i].basereg) &&
-                   nasm_regvals[instruction->oprs[i].basereg] >= 16 &&
-                   !(itemp->flags & IF_AVX512)) {
-            return MERR_ENCMISMATCH;
         }
     }
 
@@ -2195,8 +2290,8 @@ static enum match_result matches(const struct itemplate *itemp,
     /*
      * Check operand sizes
      */
-    if (itemp->flags & (IF_SM | IF_SM2)) {
-        oprs = (itemp->flags & IF_SM2 ? 2 : itemp->operands);
+    if (itemp_has(itemp, IF_SM) || itemp_has(itemp, IF_SM2)) {
+        oprs = (itemp_has(itemp, IF_SM2) ? 2 : itemp->operands);
         for (i = 0; i < oprs; i++) {
             asize = itemp->opd[i] & SIZE_MASK;
             if (asize) {
@@ -2218,19 +2313,19 @@ static enum match_result matches(const struct itemplate *itemp,
     /*
      * Check template is okay at the set cpu level
      */
-    if (((itemp->flags & IF_PLEVEL) > cpu))
+    if (iflag_cmp_cpu_level(&insns_flags[itemp->iflag_idx], &cpu) > 0)
         return MERR_BADCPU;
 
     /*
      * Verify the appropriate long mode flag.
      */
-    if ((itemp->flags & (bits == 64 ? IF_NOLONG : IF_LONG)))
+    if (itemp_has(itemp, (bits == 64 ? IF_NOLONG : IF_LONG)))
         return MERR_BADMODE;
 
     /*
      * If we have a HLE prefix, look for the NOHLE flag
      */
-    if ((itemp->flags & IF_NOHLE) &&
+    if (itemp_has(itemp, IF_NOHLE) &&
         (has_prefix(instruction, PPS_REP, P_XACQUIRE) ||
          has_prefix(instruction, PPS_REP, P_XRELEASE)))
         return MERR_BADHLE;
@@ -2242,99 +2337,22 @@ static enum match_result matches(const struct itemplate *itemp,
         return MOK_JUMP;
 
     /*
-     * Check if BND prefix is allowed
+     * Check if BND prefix is allowed.
+     * Other 0xF2 (REPNE/REPNZ) prefix is prohibited.
      */
-    if ((IF_BND & ~itemp->flags) &&
-        has_prefix(instruction, PPS_REP, P_BND))
+    if (!itemp_has(itemp, IF_BND) &&
+        (has_prefix(instruction, PPS_REP, P_BND) ||
+         has_prefix(instruction, PPS_REP, P_NOBND)))
         return MERR_BADBND;
+    else if (itemp_has(itemp, IF_BND) &&
+             (has_prefix(instruction, PPS_REP, P_REPNE) ||
+              has_prefix(instruction, PPS_REP, P_REPNZ)))
+        return MERR_BADREPNE;
 
     return MOK_GOOD;
 }
 
 /*
- * Check if offset is a multiple of N with corresponding tuple type
- * if Disp8*N is available, compressed displacement is stored in compdisp
- */
-static bool is_disp8n(operand *input, insn *ins, int8_t *compdisp)
-{
-    const uint8_t fv_n[2][2][VLMAX] = {{{16, 32, 64}, {4, 4, 4}},
-                                       {{16, 32, 64}, {8, 8, 8}}};
-    const uint8_t hv_n[2][VLMAX]    =  {{8, 16, 32}, {4, 4, 4}};
-    const uint8_t dup_n[VLMAX]      =   {8, 32, 64};
-
-    bool evex_b           = input->decoflags & BRDCAST_MASK;
-    enum ttypes   tuple   = ins->evex_tuple;
-    /* vex_wlp composed as [wwllpp] */
-    enum vectlens vectlen = (ins->vex_wlp & 0x0c) >> 2;
-    /* wig(=2) is treated as w0(=0) */
-    bool evex_w           = (ins->vex_wlp & 0x10) >> 4;
-    int32_t off           = input->offset;
-    uint8_t n = 0;
-    int32_t disp8;
-
-    switch(tuple) {
-    case FV:
-        n = fv_n[evex_w][evex_b][vectlen];
-        break;
-    case HV:
-        n = hv_n[evex_b][vectlen];
-        break;
-
-    case FVM:
-        /* 16, 32, 64 for VL 128, 256, 512 respectively*/
-        n = 1 << (vectlen + 4);
-        break;
-    case T1S8:  /* N = 1 */
-    case T1S16: /* N = 2 */
-        n = tuple - T1S8 + 1;
-        break;
-    case T1S:
-        /* N = 4 for 32bit, 8 for 64bit */
-        n = evex_w ? 8 : 4;
-        break;
-    case T1F32:
-    case T1F64:
-        /* N = 4 for 32bit, 8 for 64bit */
-        n = (tuple == T1F32 ? 4 : 8);
-        break;
-    case T2:
-    case T4:
-    case T8:
-        if (vectlen + 7 <= (evex_w + 5) + (tuple - T2 + 1))
-            n = 0;
-        else
-            n = 1 << (tuple - T2 + evex_w + 3);
-        break;
-    case HVM:
-    case QVM:
-    case OVM:
-        n = 1 << (OVM - tuple + vectlen + 1);
-        break;
-    case M128:
-        n = 16;
-        break;
-    case DUP:
-        n = dup_n[vectlen];
-        break;
-
-    default:
-        break;
-    }
-
-    if (n && !(off & (n - 1))) {
-        disp8 = off / n;
-        /* if it fits in Disp8 */
-        if (disp8 >= -128 && disp8 <= 127) {
-            *compdisp = disp8;
-            return true;
-        }
-    }
-
-    *compdisp = 0;
-    return false;
-}
-
-/*
  * Check if ModR/M.mod should/can be 01.
  * - EAF_BYTEOFFS is set
  * - offset can fit in a byte when EVEX is not used
@@ -2353,6 +2371,7 @@ static enum ea_type process_ea(operand *input, ea *output, int bits,
 {
     bool forw_ref = !!(input->opflags & OPFLAG_UNKNOWN);
     int addrbits = ins->addr_size;
+    int eaflags = input->eaflags;
 
     output->type    = EA_SCALAR;
     output->rip     = false;
@@ -2408,8 +2427,14 @@ static enum ea_type process_ea(operand *input, ea *output, int bits,
                 input->type |= MEMORY;
             }
 
-            if (input->eaflags & EAF_BYTEOFFS ||
-                (input->eaflags & EAF_WORDOFFS &&
+            if (bits == 64 &&
+                !(IP_REL & ~input->type) && (eaflags & EAF_MIB)) {
+                nasm_error(ERR_NONFATAL, "RIP-relative addressing is prohibited for mib.");
+                return -1;
+            }
+
+            if (eaflags & EAF_BYTEOFFS ||
+                (eaflags & EAF_WORDOFFS &&
                  input->disp_size != (addrbits != 16 ? 32 : 16))) {
                 nasm_error(ERR_WARNING | ERR_PASS1, "displacement size ignored on absolute address");
             }
@@ -2530,7 +2555,7 @@ static enum ea_type process_ea(operand *input, ea *output, int bits,
                     base = (bt & 7);
                     if (base != REG_NUM_EBP && o == 0 &&
                         seg == NO_SEG && !forw_ref &&
-                        !(input->eaflags & (EAF_BYTEOFFS | EAF_WORDOFFS)))
+                        !(eaflags & (EAF_BYTEOFFS | EAF_WORDOFFS)))
                         mod = 0;
                     else if (IS_MOD_01())
                         mod = 1;
@@ -2585,19 +2610,41 @@ static enum ea_type process_ea(operand *input, ea *output, int bits,
                     t = bt, bt = it, it = t;
                     x = bx, bx = ix, ix = x;
                 }
-                if (bt == it)     /* convert EAX+2*EAX to 3*EAX */
-                    bt = -1, bx = 0, s++;
+
                 if (bt == -1 && s == 1 && !(hb == i && ht == EAH_NOTBASE)) {
                     /* make single reg base, unless hint */
                     bt = it, bx = ix, it = -1, ix = 0;
                 }
-                if (((s == 2 && it != REG_NUM_ESP && !(input->eaflags & EAF_TIMESTWO)) ||
-                      s == 3 || s == 5 || s == 9) && bt == -1)
-                    bt = it, bx = ix, s--; /* convert 3*EAX to EAX+2*EAX */
-                if (it == -1 && (bt & 7) != REG_NUM_ESP &&
-                    (input->eaflags & EAF_TIMESTWO))
-                    it = bt, ix = bx, bt = -1, bx = 0, s = 1;
-                /* convert [NOSPLIT EAX] to sib format with 0x0 displacement */
+                if (eaflags & EAF_MIB) {
+                    /* only for mib operands */
+                    if (it == -1 && (hb == b && ht == EAH_NOTBASE)) {
+                        /*
+                         * make a single reg index [reg*1].
+                         * gas uses this form for an explicit index register.
+                         */
+                        it = bt, ix = bx, bt = -1, bx = 0, s = 1;
+                    }
+                    if ((ht == EAH_SUMMED) && bt == -1) {
+                        /* separate once summed index into [base, index] */
+                        bt = it, bx = ix, s--;
+                    }
+                } else {
+                    if (((s == 2 && it != REG_NUM_ESP &&
+                          (!(eaflags & EAF_TIMESTWO) || (ht == EAH_SUMMED))) ||
+                         s == 3 || s == 5 || s == 9) && bt == -1) {
+                        /* convert 3*EAX to EAX+2*EAX */
+                        bt = it, bx = ix, s--;
+                    }
+                    if (it == -1 && (bt & 7) != REG_NUM_ESP &&
+                        (eaflags & EAF_TIMESTWO) &&
+                        (hb == b && ht == EAH_NOTBASE)) {
+                        /*
+                         * convert [NOSPLIT EAX*1]
+                         * to sib format with 0x0 displacement - [EAX*1+0].
+                         */
+                        it = bt, ix = bx, bt = -1, bx = 0, s = 1;
+                    }
+                }
                 if (s == 1 && it == REG_NUM_ESP) {
                     /* swap ESP into base if scale is 1 */
                     t = it, it = bt, bt = t;
@@ -2621,7 +2668,7 @@ static enum ea_type process_ea(operand *input, ea *output, int bits,
                         rm = (bt & 7);
                         if (rm != REG_NUM_EBP && o == 0 &&
                             seg == NO_SEG && !forw_ref &&
-                            !(input->eaflags & (EAF_BYTEOFFS | EAF_WORDOFFS)))
+                            !(eaflags & (EAF_BYTEOFFS | EAF_WORDOFFS)))
                             mod = 0;
                         else if (IS_MOD_01())
                             mod = 1;
@@ -2665,7 +2712,7 @@ static enum ea_type process_ea(operand *input, ea *output, int bits,
                         base = (bt & 7);
                         if (base != REG_NUM_EBP && o == 0 &&
                             seg == NO_SEG && !forw_ref &&
-                            !(input->eaflags & (EAF_BYTEOFFS | EAF_WORDOFFS)))
+                            !(eaflags & (EAF_BYTEOFFS | EAF_WORDOFFS)))
                             mod = 0;
                         else if (IS_MOD_01())
                             mod = 1;
@@ -2750,7 +2797,7 @@ static enum ea_type process_ea(operand *input, ea *output, int bits,
                     goto err;        /* so panic if it does */
 
                 if (o == 0 && seg == NO_SEG && !forw_ref && rm != 6 &&
-                    !(input->eaflags & (EAF_BYTEOFFS | EAF_WORDOFFS)))
+                    !(eaflags & (EAF_BYTEOFFS | EAF_WORDOFFS)))
                     mod = 0;
                 else if (IS_MOD_01())
                     mod = 1;