Add RELOLIS for PowerPC lis with ha16 or hi16.
authorGeorge Koehler <xkernigh@netscape.net>
Wed, 8 Feb 2017 16:46:31 +0000 (11:46 -0500)
committerGeorge Koehler <xkernigh@netscape.net>
Wed, 8 Feb 2017 16:46:31 +0000 (11:46 -0500)
The new relocation type RELOLIS handles these instructions:

    lis RT, ha16[expr] == addis RT, r0, ha16[expr]
    lis RT, hi16[expr] == addis RT, r0, hi16[expr]

RELOLIS stores a 32-bit value in the program text.  In this value, the
high bit is a ha16 flag, the next 5 bits are the target register RT,
and the low bits are a signed 26-bit offset.  The linker replaces this
value with the lis instruction.

The old RELOPPC relocated a ha16/lo16 or hi16/lo16 pair.  The new
RELOLIS relocates only a ha16 or hi16, so it is no longer necessary to
have a matching lo16 in the next instruction.  The disadvantage is
that RELOLIS has only a signed 26-bit offset, not a 32-bit offset.

Switch the assembler to use RELOLIS for ha16 or hi16 and RELO2 for
lo16.  The li32 instruction still uses the old RELOPPC relocation.

This is not the same as my RELOPPC change from my recent mail to
tack-devel (https://sourceforge.net/p/tack/mailman/message/35651528/).
This commit is on a different branch.  Here I am throwing away my
RELOPPC change and instead trying RELOLIS.

h/out.h
mach/powerpc/as/mach1.c
mach/powerpc/as/mach4.c
mach/powerpc/as/mach5.c
util/amisc/ashow.c
util/led/relocate.c

diff --git a/h/out.h b/h/out.h
index 45289f3..6aeffc4 100644 (file)
--- a/h/out.h
+++ b/h/out.h
@@ -65,9 +65,9 @@ struct outname {
 #define RELO1     1                    /* 1 byte */
 #define RELO2     2                    /* 2 bytes */
 #define RELO4     3                    /* 4 bytes */
-#define RELOPPC    4            /* PowerPC 26-bit address */
-/* relo 5 is unused */
-#define RELOVC4    6            /* VideoCore IV address in 32-bit instruction */
+#define RELOPPC    4                   /* PowerPC 26-bit address */
+#define RELOLIS    5                   /* PowerPC lis */
+#define RELOVC4    6   /* VideoCore IV address in 32-bit instruction */
 
 #define RELPC  0x2000                  /* pc relative */
 #define RELBR  0x4000                  /* High order byte lowest address. */
index 44b415f..50f7996 100644 (file)
@@ -7,5 +7,6 @@
  * Do not #include anything here.  Do it in mach/proto/as/comm0.h
  */
 
-extern word_t emit_hi(struct expr_t* expr, int is_signed);
-extern word_t emit_lo(struct expr_t* expr);
+void no_hl(void);
+word_t eval_hl(struct expr_t* expr, int token);
+void emit_hl(word_t in);
index 640573f..8a0cca9 100644 (file)
@@ -10,11 +10,11 @@ operation
        | OP_BF_BFA            CR ',' CR                  { emit4($1 | ($2<<23) | ($4<<18)); }
        | OP_BF_FRA_FRB        CR ',' FPR ',' FPR         { emit4($1 | ($2<<23) | ($4<<16) | ($6<<11)); }
        | OP_BF_L_RA_RB        CR ',' u1 ',' GPR ',' GPR  { emit4($1 | ($2<<23) | ($4<<21) | ($6<<16) | ($8<<11)); }
-       | OP_BF_L_RA_SI        CR ',' u1 ',' GPR ',' e16  { emit4($1 | ($2<<23) | ($4<<21) | ($6<<16) | $8); }
-       | OP_BF_L_RA_UI        CR ',' u1 ',' GPR ',' e16  { emit4($1 | ($2<<23) | ($4<<21) | ($6<<16) | $8); }
+       | OP_BF_L_RA_SI        CR ',' u1 ',' GPR ',' e16  { emit_hl($1 | ($2<<23) | ($4<<21) | ($6<<16) | $8); }
+       | OP_BF_L_RA_UI        CR ',' u1 ',' GPR ',' e16  { emit_hl($1 | ($2<<23) | ($4<<21) | ($6<<16) | $8); }
        | OP_BF_RA_RB          cr_opt GPR ',' GPR         { emit4($1 | ($2<<23) | ($3<<16) | ($5<<11)); }
-       | OP_BF_RA_SI          cr_opt GPR ',' e16         { emit4($1 | ($2<<23) | ($3<<16) | $5); }
-       | OP_BF_RA_UI          cr_opt GPR ',' e16         { emit4($1 | ($2<<23) | ($3<<16) | $5); }
+       | OP_BF_RA_SI          cr_opt GPR ',' e16         { emit_hl($1 | ($2<<23) | ($3<<16) | $5); }
+       | OP_BF_RA_UI          cr_opt GPR ',' e16         { emit_hl($1 | ($2<<23) | ($3<<16) | $5); }
        | OP_BF_U_C            c CR ',' u4                { emit4($1 | $2 | ($3<<23) | ($5<<12)); }
        | OP_BH                                           { emit4($1); }
        | OP_BH                u2                         { emit4($1 | ($2<<11)); }
@@ -33,13 +33,13 @@ operation
        | OP_BT_BT_BT          u5                         { emit4($1 | ($2<<21) | ($2<<16) | ($2<<11)); }
        | OP_BT_C              c u5                       { emit4($1 | $2 | ($3<<21)); }
        | OP_FLM_FRB_C         c u8 ',' FPR               { emit4($1 | $2 | ($3<<17) | ($5<<11)); }
-       | OP_FRS_RA_D          FPR ',' e16 '(' GPR ')'    { emit4($1 | ($2<<21) | ($6<<16) | $4); }
+       | OP_FRS_RA_D          FPR ',' e16 '(' GPR ')'    { emit_hl($1 | ($2<<21) | ($6<<16) | $4); }
        | OP_FRS_RA_RB         FPR ',' GPR ',' GPR        { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); }
        | OP_FRT_FRA_FRB_C     c FPR ',' FPR ',' FPR      { emit4($1 | $2 | ($3<<21) | ($5<<16) | ($7<<11)); }
        | OP_FRT_FRA_FRC_FRB_C c FPR ',' FPR ',' FPR ',' FPR { emit4($1 | $2 | ($3<<21) | ($5<<16) | ($9<<11) | ($7<<6)); }
        | OP_FRT_FRA_FRC_C     c FPR ',' FPR ',' FPR      { emit4($1 | $2 | ($3<<21) | ($5<<16) | ($7<<6)); }
        | OP_FRT_FRB_C         c FPR ',' FPR              { emit4($1 | $2 | ($3<<21) | ($5<<11)); }
-       | OP_FRT_RA_D          FPR ',' e16 '(' GPR ')'    { emit4($1 | ($2<<21) | ($6<<16) | $4); }
+       | OP_FRT_RA_D          FPR ',' e16 '(' GPR ')'    { emit_hl($1 | ($2<<21) | ($6<<16) | $4); }
        | OP_FRT_RA_RB         FPR ',' GPR ',' GPR        { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); }
        | OP_FRT_C             c FPR                      { emit4($1 | $2 | ($3<<21)); }
        | OP_RA_RS_C           c GPR ',' GPR              { emit4($1 | $2 | ($5<<21) | ($3<<16)); }
@@ -59,34 +59,34 @@ operation
        { emit4($1 | $2 | ($5<<21) | ($3<<16) | SH6($7)); }
        | OP_RA_RS_SH6_MB6_C   c GPR ',' GPR ',' u6 ',' u6
        { emit4($1 | $2 | ($5<<21) | ($3<<16) | SH6($7) | MB6($9)); }
-       | OP_RA_RS_UI          GPR ',' GPR ',' e16        { emit4($1 | ($4<<21) | ($2<<16) | $6); }
-       | OP_RA_RS_UI_CC       C GPR ',' GPR ',' e16      { emit4($1 | ($5<<21) | ($3<<16) | $7); }
+       | OP_RA_RS_UI          GPR ',' GPR ',' e16        { emit_hl($1 | ($4<<21) | ($2<<16) | $6); }
+       | OP_RA_RS_UI_CC       C GPR ',' GPR ',' e16      { emit_hl($1 | ($5<<21) | ($3<<16) | $7); }
        | OP_RT                GPR                        { emit4($1 | ($2<<21)); }
        | OP_RT_RA_C           c GPR ',' GPR              { emit4($1 | $2 | ($3<<21) | ($5<<16)); }
-       | OP_RT_RA_D           GPR ',' e16 '(' GPR ')'    { emit4($1 | ($2<<21) | ($6<<16) | $4); }
-       | OP_RT_RA_DS          GPR ',' ds '(' GPR ')'     { emit4($1 | ($2<<21) | ($6<<16) | $4); }
+       | OP_RT_RA_D           GPR ',' e16 '(' GPR ')'    { emit_hl($1 | ($2<<21) | ($6<<16) | $4); }
+       | OP_RT_RA_DS          GPR ',' ds '(' GPR ')'     { emit_hl($1 | ($2<<21) | ($6<<16) | $4); }
        | OP_RT_RA_NB          GPR ',' GPR ',' nb         { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); }
        | OP_RT_RA_RB          GPR ',' GPR ',' GPR        { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); }
        | OP_RT_RA_RB_C        c GPR ',' GPR ',' GPR      { emit4($1 | $2 | ($3<<21) | ($5<<16) | ($7<<11)); }
-       | OP_RT_RA_SI          GPR ',' GPR ',' e16        { emit4($1 | ($2<<21) | ($4<<16) | $6); }
-       | OP_RT_RA_SI_addic    c GPR ',' GPR ',' e16      { emit4($1 | ($2<<26) | ($3<<21) | ($5<<16) | $7); }
+       | OP_RT_RA_SI          GPR ',' GPR ',' e16        { emit_hl($1 | ($2<<21) | ($4<<16) | $6); }
+       | OP_RT_RA_SI_addic    c GPR ',' GPR ',' e16      { emit_hl($1 | ($2<<26) | ($3<<21) | ($5<<16) | $7); }
        | OP_RT_RA_SI_subi     GPR ',' GPR ',' negate16   { emit4($1 | ($2<<21) | ($4<<16) | $6); }
        | OP_RT_RA_SI_subic    c GPR ',' GPR ',' negate16 { emit4($1 | ($2<<26) | ($3<<21) | ($5<<16) | $7); }
        | OP_RT_RB_RA_C        c GPR ',' GPR ',' GPR      { emit4($1 | $2 | ($3<<21) | ($7<<16) | ($5<<11)); }
-       | OP_RT_SI             GPR ',' e16                { emit4($1 | ($2<<21) | $4); }
+       | OP_RT_SI             GPR ',' e16                { emit_hl($1 | ($2<<21) | $4); }
        | OP_RT_SPR            GPR ',' spr_num            { emit4($1 | ($2<<21) | ($4<<11)); }
        | OP_RS_FXM            u7 ',' GPR                 { emit4($1 | ($4<<21) | ($2<<12)); }
-       | OP_RS_RA_D           GPR ',' e16 '(' GPR ')'    { emit4($1 | ($2<<21) | ($6<<16) | $4); }
-       | OP_RS_RA_DS          GPR ',' ds '(' GPR ')'     { emit4($1 | ($2<<21) | ($6<<16) | $4); }
+       | OP_RS_RA_D           GPR ',' e16 '(' GPR ')'    { emit_hl($1 | ($2<<21) | ($6<<16) | $4); }
+       | OP_RS_RA_DS          GPR ',' ds '(' GPR ')'     { emit_hl($1 | ($2<<21) | ($6<<16) | $4); }
        | OP_RS_RA_NB          GPR ',' GPR ',' nb         { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); }
        | OP_RS_RA_RB          GPR ',' GPR ',' GPR        { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); }
        | OP_RS_RA_RB_C        c GPR ',' GPR ',' GPR      { emit4($1 | $2 | ($5<<21) | ($3<<16) | ($7<<11)); }
        | OP_RS_RA_RA_C        c GPR ',' GPR              { emit4($1 | $2 | ($5<<21) | ($3<<16) | ($5<<11)); }
        | OP_RS_SPR            spr_num ',' GPR            { emit4($1 | ($4<<21) | ($2<<11)); }
        | OP_TO_RA_RB          u5 ',' GPR ',' GPR         { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); }
-       | OP_TO_RA_SI          u5 ',' GPR ',' e16         { emit4($1 | ($2<<21) | ($4<<16) | $6); }
+       | OP_TO_RA_SI          u5 ',' GPR ',' e16         { emit_hl($1 | ($2<<21) | ($4<<16) | $6); }
        | OP_TOX_RA_RB         GPR ',' GPR                { emit4($1 | ($2<<16) | ($4<<11)); }
-       | OP_TOX_RA_SI         GPR ',' e16                { emit4($1 | ($2<<16) | $4); }
+       | OP_TOX_RA_SI         GPR ',' e16                { emit_hl($1 | ($2<<16) | $4); }
        | OP_LEV                                          { emit4($1); }
        | OP_LEV               u7                         { emit4($1 | ($2<<5)); }
        | OP_LIA               lia                        { emit4($1 | $2); }
@@ -217,10 +217,11 @@ e16
                if (($1 < -0x8000) || ($1 > 0xffff))
                        serror("16-bit value out of range");
                $$ = (uint16_t) $1;
+               no_hl();
        }
-       | OP_HI ASC_LPAR expr ASC_RPAR           { $$ = emit_hi(&$3, 0); }
-       | OP_HA ASC_LPAR expr ASC_RPAR           { $$ = emit_hi(&$3, 1); }
-       | OP_LO ASC_LPAR expr ASC_RPAR           { $$ = emit_lo(&$3); }
+       | OP_HI ASC_LPAR expr ASC_RPAR           { $$ = eval_hl(&$3, OP_HI); }
+       | OP_HA ASC_LPAR expr ASC_RPAR           { $$ = eval_hl(&$3, OP_HA); }
+       | OP_LO ASC_LPAR expr ASC_RPAR           { $$ = eval_hl(&$3, OP_LO); }
        ;
 
 negate16
index 4746079..d72b851 100644 (file)
@@ -1,38 +1,75 @@
+static int hl_token;
+static expr_t hl_expr;
 
-word_t emit_hi(struct expr_t* expr, int is_signed)
+void no_hl(void) {
+       hl_token = 0;
+}
+
+word_t eval_hl(expr_t* expr, int token)
 {
-    /* If this is a symbol reference, discard the symbol and keep only the
-        * offset part. */
-    word_t type = expr->typ & S_TYP;
-    word_t val = expr->val;
-    uint16_t hi = val >> 16;
-    uint16_t lo = val & 0xffff;
-
-    if (type != S_ABS)
-               newrelo(expr->typ, RELOPPC | FIXUPFLAGS);
-
-    /* If the low half of this relocation is going to be a memory operation,
-     * then it'll be treated as a signed value. That means that values greater
-     * than 0x7fff will cause the high word to have 1 subtracted from it; so
-     * we apply an adjustment here.
-     */
-
-    if (is_signed && (lo > 0x7fff))
-        hi++;
-
-    return hi;
+       word_t val = expr->val;
+       uint16_t hi = val >> 16;
+       uint16_t lo = val & 0xffff;
+
+       hl_token = token;
+       hl_expr = *expr;
+
+       switch (token) {
+       case OP_HI:  /* hi16[expr] */
+               return hi;
+       case OP_HA:  /* ha16[expr]*/
+               /*
+                * If the low half will be treated as a signed value,
+                * then values greater than 0x7fff will cause the high
+                * half to have 1 subtracted from it; so we apply an
+                * adjustment here.
+                */
+               if (lo > 0x7fff)
+                       hi++;
+               return hi;
+       case OP_LO:  /* lo16[expr] */
+               return lo;
+       }
 }
 
-word_t emit_lo(struct expr_t* expr)
+void emit_hl(word_t in)
 {
-    word_t type = expr->typ & S_TYP;
-    word_t val = expr->val;
+       word_t reg;
+       int type;
 
-    /* If the assembler stored a symbol for relocation later, we need to
-     * abandon it (because the relocation was generated by emit_ha). */
+       switch (hl_token) {
+       case OP_HI:  /* hi16[expr] */
+       case OP_HA:  /* ha16[expr] */
+               if (PASS_RELO && (hl_expr.typ & S_TYP) != S_ABS) {
+                       /*
+                        * RELOLIS only works with lis _, _ (same as
+                        * addis _, r0, _).  Check if instruction
+                        * isn't addis or register RA isn't r0.
+                        */
+                       if ((in & 0xfc1f0000) != (0x3c000000))
+                               serror("relocation only works with lis");
 
-    if (type != S_ABS)
-        relonami = 0;
+                       /*
+                        * High bit: ha16 flag
+                        * Next 5 bits: register RT
+                        * Low 26 bits: signed offset
+                        */
+                       fit(fitx(hl_expr.val, 26));
+                       newrelo(hl_expr.typ, RELOLIS | FIXUPFLAGS);
+                       reg = (in >> 21) & 0x1f;
+                       in = (hl_token == OP_HA) << 31;
+                       in |= reg << 26;
+                       in |= hl_expr.val & 0x03ffffff;
+               }
+               break;
+       case OP_LO:  /* lo16[expr] */
+               if (PASS_RELO && (hl_expr.typ & S_TYP) != S_ABS) {
+                       DOTVAL += 2;
+                       newrelo(hl_expr.typ, RELO2 | FIXUPFLAGS);
+                       DOTVAL -= 2;
+               }
+               break;
+       }
 
-    return val & 0xffff;
+       emit4(in);
 }
index 498ae39..ec85de3 100644 (file)
@@ -140,6 +140,9 @@ showrelo()
        case RELOPPC:
                printf("\tPowerPC 26-bit address\n");
                break;
+       case RELOLIS:
+               printf("\tPowerPC lis instruction\n");
+               break;
        case RELOVC4:
                printf("\tVideoCore IV address in 32-bit instruction\n");
                break;
index 036b7db..1b89609 100644 (file)
@@ -165,8 +165,17 @@ static uint32_t get_powerpc_valu(char* addr, uint16_t type)
                return ((hi << 16) | lo);
        }
 
-       fatal("Don't know how to read from PowerPC fixup on instructions 0x%08x+0x%08x",
-               opcode1, opcode2);
+       fatal("Don't know how to read from PowerPC fixup on instructions 0x%08lx+0x%08lx",
+               (unsigned long)opcode1, (unsigned long)opcode2);
+}
+
+/* RELOLIS stores a signed 26-bit offset in the low bits. */
+static uint32_t get_lis_valu(char *addr, uint16_t type)
+{
+       uint32_t valu = read4(addr, type) & 0x03ffffff;
+       if (valu & 0x02000000)
+               valu |= 0xfc000000; /* sign extension */
+       return valu;
 }
 
 /*
@@ -184,6 +193,8 @@ static uint32_t getvalu(char* addr, uint16_t type)
                return read4(addr, type);
        case RELOPPC:
                return get_powerpc_valu(addr, type);
+       case RELOLIS:
+               return get_lis_valu(addr, type);
        case RELOVC4:
                return get_vc4_valu(addr);
        default:
@@ -327,8 +338,34 @@ static void put_powerpc_valu(char* addr, uint32_t value, uint16_t type)
        }
 
        else
-               fatal("Don't know how to write a PowerPC fixup to instructions 0x%08x+0x%08x",
-                       opcode1, opcode2);
+               fatal("Don't know how to write a PowerPC fixup to instructions 0x%08lx+0x%08lx",
+                       (unsigned long)opcode1, (unsigned long)opcode2);
+}
+
+/* Writes a PowerPC lis instruction. */
+static void put_lis_valu(char* addr, uint32_t value, uint16_t type)
+{
+       uint32_t opcode, reg;
+       uint16_t hi, lo;
+       bool ha16;
+
+       /* ha16 flag in high bit, register in next 5 bits */
+       opcode = read4(addr, type);
+       ha16 = opcode >> 31;
+       reg = (opcode >> 26) & 0x1f;
+
+       /*
+        * Apply the sign adjustment if the ha16 flag is set and the
+        * low half is a negative signed 16-bit integer.
+        */
+       hi = value >> 16;
+       lo = value & 0xffff;
+       if (ha16 && lo > 0x7fff)
+               hi++;
+
+       /* Assemble lis reg, hi == addis reg, r0, hi. */
+       opcode = (15 << 26) | (reg << 21) | (0 << 16) | hi;
+       write4(opcode, addr, type);
 }
 
 /*
@@ -352,6 +389,9 @@ static putvalu(uint32_t valu, char* addr, uint16_t type)
        case RELOPPC:
                put_powerpc_valu(addr, valu, type);
                break;
+       case RELOLIS:
+               put_lis_valu(addr, valu, type);
+               break;
        case RELOVC4:
                put_vc4_valu(addr, valu);
                break;