From 1bf58cf51c9bff076485e249ef5b708483055b4a Mon Sep 17 00:00:00 2001 From: George Koehler Date: Wed, 8 Feb 2017 11:46:31 -0500 Subject: [PATCH] Add RELOLIS for PowerPC lis with ha16 or hi16. 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 | 6 +-- mach/powerpc/as/mach1.c | 5 ++- mach/powerpc/as/mach4.c | 41 +++++++++--------- mach/powerpc/as/mach5.c | 95 ++++++++++++++++++++++++++++------------- util/amisc/ashow.c | 3 ++ util/led/relocate.c | 48 +++++++++++++++++++-- 6 files changed, 140 insertions(+), 58 deletions(-) diff --git a/h/out.h b/h/out.h index 45289f313..6aeffc446 100644 --- 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. */ diff --git a/mach/powerpc/as/mach1.c b/mach/powerpc/as/mach1.c index 44b415ff8..50f799684 100644 --- a/mach/powerpc/as/mach1.c +++ b/mach/powerpc/as/mach1.c @@ -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); diff --git a/mach/powerpc/as/mach4.c b/mach/powerpc/as/mach4.c index 640573fa1..8a0cca9de 100644 --- a/mach/powerpc/as/mach4.c +++ b/mach/powerpc/as/mach4.c @@ -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 diff --git a/mach/powerpc/as/mach5.c b/mach/powerpc/as/mach5.c index 47460790c..d72b8514a 100644 --- a/mach/powerpc/as/mach5.c +++ b/mach/powerpc/as/mach5.c @@ -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); } diff --git a/util/amisc/ashow.c b/util/amisc/ashow.c index 498ae3980..ec85de30d 100644 --- a/util/amisc/ashow.c +++ b/util/amisc/ashow.c @@ -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; diff --git a/util/led/relocate.c b/util/led/relocate.c index 036b7dbb8..1b8960938 100644 --- a/util/led/relocate.c +++ b/util/led/relocate.c @@ -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; -- 2.34.1