/*
- * We use RELOPPC to relocate hi16[expr] or ha16[expr]. This requires
- * a matching lo16[expr] in the next instruction, because RELOPPC
- * handles a hi16/lo16 or ha16/lo16 pair. RELOPPC only works with
- * certain specific opcodes.
+ * To relocate hi16[expr] or ha16[expr], we must find a matching
+ * lo16[expr] in a later instruction. This is because RELOPPC needs a
+ * hi16/lo16 or ha16/lo16 pair. Also, RELOPPC only works with certain
+ * specific opcodes.
*/
static item_t *last_und_item;
word_t eval_hl(expr_t* expr, int token)
{
struct hirel* hr;
+ ADDR_T distance;
word_t val = expr->val;
uint16_t hi = val >> 16;
uint16_t lo = val & 0xffff;
else
hl_item = NULL;
+ if (pass == PASS_3) {
+ hr = hr_head;
+ if (hr && hr->hr_hidot == DOTVAL && hr->hr_dottyp == DOTTYP) {
+ /*
+ * We have a hirel from emit_hl() PASS_2. Encode distance
+ * to the matching lo16 in top 6 bits of hi.
+ */
+ distance = hr->hr_lodot - DOTVAL;
+ if (distance & 0x0003)
+ serror("matching lo16 is misaligned");
+ if (distance & ~0x00fc)
+ serror("matching lo16 is too far away");
+ fit(fitx((int16_t)hi, 10));
+ hi = (distance << 8) | (hi & 0x03ff);
+
+ /* Remove hirel from list. */
+ hr_head = hr->hr_next;
+ free(hr);
+
+ /* No sign adjustment; linker will do it later. */
+ return hi;
+ }
+ }
+
switch (token) {
case OP_HA: /* ha16[expr] */
/*
hi++;
/* FALLTHROUGH */
case OP_HI: /* hi16[expr] */
- if (pass == PASS_3) {
- /* Get the hirel from emit_hl() PASS_2. */
- hr = hr_head;
- if (hr && hr->hr_hidot == DOTVAL && hr->hr_dottyp == DOTTYP) {
- /* For now, the lo16 must be in the next instruction. */
- if (DOTVAL + 4 != hr->hr_lodot)
- serror("%s needs lo16 in next instruction",
- token == OP_HI ? "hi16" : "ha16");
-
- hr_head = hr->hr_next;
- free(hr);
- }
- }
return hi;
case OP_LO: /* lo16[expr] */
return lo;
if (hr_match(hr, und)) {
/* Found hr, remove it from list. */
*hrlink = hr->hr_inext;
+ hr->hr_inext = NULL;
break;
}
hrlink = &(hr->hr_inext);
return false;
}
-/* PowerPC fixups are complex as we need to patch up to the next two
- * instructions in one of several different ways, depending on what the
- * instructions area.
+static bool is_powerpc_ori(uint32_t opcode)
+{
+ return (opcode & 0xfc000000) == 0x60000000;
+}
+
+/* PowerPC fixups are complex as we need to patch one or two
+ * instructions in one of several different ways.
*/
static uint32_t get_powerpc_valu(char* addr, uint16_t type)
{
- uint32_t opcode1 = read4(addr+0, type);
- uint32_t opcode2 = read4(addr+4, type);
+ uint32_t opcode1 = read4(addr, type);
+ uint32_t opcode2 = 0;
if ((opcode1 & 0xfc000000) == 0x48000000)
{
/* branch instruction */
return opcode1 & 0x03fffffd;
}
- else if (((opcode1 & 0xfc1f0000) == 0x3c000000) &&
- ((opcode2 & 0xfc000000) == 0x60000000))
+ else if ((opcode1 & 0xfc1f0000) == 0x3c000000)
{
- /* addis / ori instruction pair */
- return ((opcode1 & 0xffff) << 16) | (opcode2 & 0xffff);
- }
- else if (((opcode1 & 0xfc1f0000) == 0x3c000000) &&
- is_powerpc_memory_op(opcode2))
- {
- /* addis / memoryop instruction pair */
- uint16_t hi = opcode1 & 0xffff;
- uint16_t lo = opcode2 & 0xffff;
+ /* addis, paired with a later instruction */
+ uint16_t distance, hi, lo;
- /* Undo the sign adjustment (see mach/powerpc/as/mach5.c). */
+ distance = (opcode1 & 0xfc00) >> 8;
+ /* XXX addr + distance might be too big */
+ if (distance)
+ opcode2 = read4(addr + distance, type);
- if (lo > 0x7fff)
- hi--;
+ hi = opcode1 & 0x03ff;
+ lo = opcode2 & 0xffff;
+ if (hi & 0x0200)
+ hi |= 0xfc00; /* sign extension */
- return ((hi << 16) | lo);
+ if (is_powerpc_memory_op(opcode2) || is_powerpc_ori(opcode2))
+ 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);
}
/*
assert(0 && "unrecognised VC4 instruction");
}
-/* PowerPC fixups are complex as we need to patch up to the next two
- * instructions in one of several different ways, depending on what the
- * instructions area.
+/* PowerPC fixups are complex as we need to patch one or two
+ * instructions in one of several different ways.
*/
static void put_powerpc_valu(char* addr, uint32_t value, uint16_t type)
{
- uint32_t opcode1 = read4(addr+0, type);
- uint32_t opcode2 = read4(addr+4, type);
+ uint32_t opcode1 = read4(addr, type);
+ uint32_t opcode2 = 0;
if ((opcode1 & 0xfc000000) == 0x48000000)
{
uint32_t i = opcode1 & ~0x03fffffd;
i |= value & 0x03fffffd;
write4(i, addr, type);
+ return;
}
- else if (((opcode1 & 0xfc1f0000) == 0x3c000000) &&
- ((opcode2 & 0xfc000000) == 0x60000000))
+ else if ((opcode1 & 0xfc1f0000) == 0x3c000000)
{
- /* addis / ori instruction pair */
- uint16_t hi = value >> 16;
- uint16_t lo = value & 0xffff;
+ /* addis, paired with a later instruction */
+ uint16_t distance, hi, lo;
+ bool adj;
- write4((opcode1 & 0xffff0000) | hi, addr+0, type);
- write4((opcode2 & 0xffff0000) | lo, addr+4, type);
- }
- else if (((opcode1 & 0xfc1f0000) == 0x3c000000) &&
- is_powerpc_memory_op(opcode2))
- {
- /* addis / memoryop instruction pair */
- uint16_t hi = value >> 16;
- uint16_t lo = value & 0xffff;
+ distance = (opcode1 & 0xfc00) >> 8;
+ /* XXX addr + distance might be too big */
+ if (distance)
+ opcode2 = read4(addr + distance, type);
- /* Apply the sign adjustment (see mach/powerpc/as/mach5.c). */
+ hi = value >> 16;
+ lo = value & 0xffff;
- if (lo > 0x7fff)
+ /* Apply the sign adjustment (see mach/powerpc/as/mach5.c). */
+ adj = is_powerpc_memory_op(opcode2);
+ if (adj && lo > 0x7fff)
hi++;
- write4((opcode1 & 0xffff0000) | hi, addr+0, type);
- write4((opcode2 & 0xffff0000) | lo, addr+4, type);
+ if (adj || is_powerpc_ori(opcode2)) {
+ opcode1 = (opcode1 & 0xffff0000) | hi;
+ opcode2 = (opcode2 & 0xffff0000) | lo;
+ write4(opcode1, addr, type);
+ write4(opcode2, addr + distance, type);
+ return;
+ }
}
- 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);
}
/*