* $State$
*/
+#include <stdbool.h>
+
+extern quad emit_ha(struct expr_t* expr, bool is_signed);
+extern quad emit_lo(struct expr_t* expr);
%token <y_word> OP_LI32
%token <y_word> OP_POWERPC_FIXUP
-%token <y_word> OP_HI OP_LO
+%token <y_word> OP_HA OP_HAS OP_LO
/* Other token types */
0, OP_LA, 0, "la",
0, OP_LA, 0, "li",
0, OP_RS_RA_RA_C, 31<<26 | 444<<1, "mr",
-0, OP_POWERPC_FIXUP, 0, ".powerpcfixup",
-0, OP_HI, 0, "ha16",
+0, OP_HA, 0, "ha16",
+0, OP_HAS, 0, "has16",
0, OP_LO, 0, "lo16",
/* Branch processor instructions (page 20) */
| OP_LIA lia { emit4($1 | $2); }
| OP_LIL lil { emit4($1 | $2); }
| OP_LI32 li32 /* emitted in subrule */
- | OP_POWERPC_FIXUP powerpcfixup /* emitted in subrule */
;
c
serror("16-bit value out of range");
$$ = (uint16_t) $1;
}
- | OP_HI ASC_LPAR expr ASC_RPAR
- {
- /* If this is a symbol reference, discard the symbol and keep only the
- * offset part. */
- quad type = $3.typ & S_TYP;
- quad val = $3.val;
-
- /* If the assembler stored a symbol for relocation later, we need to
- * abandon it (because we're not going to generate a relocation). */
- if (type != S_ABS)
- relonami = 0;
-
- $$ = ((quad)val) >> 16;
- }
- | OP_LO ASC_LPAR expr ASC_RPAR
- {
- quad type = $3.typ & S_TYP;
- quad val = $3.val;
-
- /* If the assembler stored a symbol for relocation later, we need to
- * abandon it (because we're not going to generate a relocation). */
- if (type != S_ABS)
- relonami = 0;
-
- $$ = val & 0xffff;
- }
+ | OP_HA ASC_LPAR expr ASC_RPAR { $$ = emit_ha(&$3, false); }
+ | OP_HAS ASC_LPAR expr ASC_RPAR { $$ = emit_ha(&$3, true); }
+ | OP_LO ASC_LPAR expr ASC_RPAR { $$ = emit_lo(&$3); }
;
u8
-/*
- * $Source$
- * $State$
- */
+
+quad emit_ha(struct expr_t* expr, bool is_signed)
+{
+ /* If this is a symbol reference, discard the symbol and keep only the
+ * offset part. */
+ quad type = expr->typ & S_TYP;
+ quad 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;
+}
+
+quad emit_lo(struct expr_t* expr)
+{
+ quad type = expr->typ & S_TYP;
+ quad val = expr->val;
+
+ /* If the assembler stored a symbol for relocation later, we need to
+ * abandon it (because the relocation was generated by emit_ha). */
+
+ if (type != S_ABS)
+ relonami = 0;
+
+ return val & 0xffff;
+}
/* Primitives */
LABEL = { ADDR adr; } 4 adr.
- LABEL_OFFSET_HI = { ADDR adr; } 4 "ha16[" adr "]".
+ LABEL_OFFSET_HA = { ADDR adr; } 4 "ha16[" adr "]".
+ LABEL_OFFSET_HAS = { ADDR adr; } 4 "has16[" adr "]".
LABEL_OFFSET_LO = { ADDR adr; } 4 "lo16[" adr "]".
LOCAL = { INT off; } 4.
add GPR:wo, GPR:ro, GPR:ro.
addX "add." GPR:wo, GPR:ro, GPR:ro.
addi GPR:wo, GPR:ro, CONST:ro.
- addis GPR:wo, GPR:ro, CONST+LABEL_OFFSET_HI:ro.
+ addis GPR:wo, GPR:ro, CONST+LABEL_OFFSET_HA+LABEL_OFFSET_HAS:ro.
and GPR:wo, GPR:ro, GPR:ro.
andc GPR:wo, GPR:ro, GPR:ro.
andiX "andi." GPR:wo:cc, GPR:ro, CONST:ro.
xori GPR:wo, GPR:ro, CONST:ro.
xoris GPR:wo, GPR:ro, CONST:ro.
- fixup ".powerpcfixup" LABEL:ro.
comment "!" LABEL:ro cost(0, 0).
from LABEL to GPR
gen
COMMENT("move LABEL->GPR")
- fixup {LABEL, %1.adr}
- addis %2, R0, {LABEL_OFFSET_HI, %1.adr}
+ addis %2, R0, {LABEL_OFFSET_HA, %1.adr}
ori %2, %2, {LABEL_OFFSET_LO, %1.adr}
/* Sign extension */
with LABEL
uses REG
gen
- fixup {LABEL, %1.adr}
- addis %a, R0, {LABEL_OFFSET_HI, %1.adr}
+ addis %a, R0, {LABEL_OFFSET_HAS, %1.adr}
lwz %a, {GPRINDIRECT_OFFSET_LO, %a, %1.adr}
yields %a
with GPR
*/
/* $Id$ */
-typedef int bool;
-
#define FALSE 0
#define TRUE 1
extern int Verbose;
#define verbose(s, a1, a2, a3, a4) (Verbose && do_verbose(s, a1, a2, a3, a4))
+
+extern void fatal(char* format, ...);
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
+#include <stdbool.h>
#include <assert.h>
#include "out.h"
#include "const.h"
assert(0 && "unrecognised VC4 instruction");
}
+static bool is_powerpc_memory_op(uint32_t opcode)
+{
+ /* Tests for any PowerPC memory indirection instruction where the payload
+ * is a *signed* 16-bit value. */
+ switch ((opcode & 0xfc000000) >> 26)
+ {
+ case 34: /* lbz */
+ case 40: /* lhz */
+ case 32: /* lwz */
+ case 38: /* stb */
+ case 44: /* sth */
+ case 36: /* stw */
+ return true;
+ }
+
+ 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.
/* 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;
+
+ /* Undo the sign adjustment (see mach/powerpc/as/mach5.c). */
+
+ if (lo > 0x7fff)
+ hi--;
+
+ return ((hi << 16) | lo);
+ }
- assert(0 && "unrecognised PowerPC instruction");
+ fatal("Don't know how to read from PowerPC fixup on instructions 0x%08x+0x%08x",
+ opcode1, opcode2);
}
/*
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;
+
+ /* Apply the sign adjustment (see mach/powerpc/as/mach5.c). */
+
+ if (lo > 0x7fff)
+ hi++;
+
+ write4((opcode1 & 0xffff0000) | hi, addr+0, type);
+ write4((opcode2 & 0xffff0000) | lo, addr+4, type);
+ }
+
else
- assert(0 && "unrecognised PowerPC instruction");
+ fatal("Don't know how to write a PowerPC fixup to instructions 0x%08x+0x%08x",
+ opcode1, opcode2);
}
/*