| 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)); }
| 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)); }
{ 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); }
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
+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);
}
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;
}
/*
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:
}
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);
}
/*
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;