From f80acfe9f5cdbdea954df4d553fd347129b34038 Mon Sep 17 00:00:00 2001 From: David Given Date: Sun, 15 Jan 2017 11:51:37 +0100 Subject: [PATCH] Signed vs unsigned lower halves of powerpc fixups are now handled by having two assembler directives, ha16() and has16(), for the upper half; has16() applies the sign adjustment. .powerpcfixup is now gone, as we generate the relocation in ha*() instead. Add special logic to the linker for undoing and redoing the sign adjustment when reading/writing fixups. Tests still pass. --- mach/powerpc/as/mach1.c | 4 +++ mach/powerpc/as/mach2.c | 2 +- mach/powerpc/as/mach3.c | 4 +-- mach/powerpc/as/mach4.c | 30 +++------------------- mach/powerpc/as/mach5.c | 42 ++++++++++++++++++++++++++++--- mach/powerpc/ncg/table | 12 ++++----- util/led/const.h | 2 -- util/led/debug.h | 2 ++ util/led/relocate.c | 55 +++++++++++++++++++++++++++++++++++++++-- 9 files changed, 108 insertions(+), 45 deletions(-) diff --git a/mach/powerpc/as/mach1.c b/mach/powerpc/as/mach1.c index 998cd8d9c..8cf9ca282 100644 --- a/mach/powerpc/as/mach1.c +++ b/mach/powerpc/as/mach1.c @@ -3,3 +3,7 @@ * $State$ */ +#include + +extern quad emit_ha(struct expr_t* expr, bool is_signed); +extern quad emit_lo(struct expr_t* expr); diff --git a/mach/powerpc/as/mach2.c b/mach/powerpc/as/mach2.c index a07f21b99..f64d148cd 100644 --- a/mach/powerpc/as/mach2.c +++ b/mach/powerpc/as/mach2.c @@ -85,7 +85,7 @@ %token OP_LI32 %token OP_POWERPC_FIXUP -%token OP_HI OP_LO +%token OP_HA OP_HAS OP_LO /* Other token types */ diff --git a/mach/powerpc/as/mach3.c b/mach/powerpc/as/mach3.c index a40e2495d..74415aa9f 100644 --- a/mach/powerpc/as/mach3.c +++ b/mach/powerpc/as/mach3.c @@ -102,8 +102,8 @@ 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) */ diff --git a/mach/powerpc/as/mach4.c b/mach/powerpc/as/mach4.c index 016877576..bb6f5163b 100644 --- a/mach/powerpc/as/mach4.c +++ b/mach/powerpc/as/mach4.c @@ -60,7 +60,6 @@ operation | 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 @@ -76,32 +75,9 @@ e16 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 diff --git a/mach/powerpc/as/mach5.c b/mach/powerpc/as/mach5.c index 5b8eb9167..a8e4cd043 100644 --- a/mach/powerpc/as/mach5.c +++ b/mach/powerpc/as/mach5.c @@ -1,4 +1,38 @@ -/* - * $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; +} diff --git a/mach/powerpc/ncg/table b/mach/powerpc/ncg/table index a907e4294..d37bed8f0 100644 --- a/mach/powerpc/ncg/table +++ b/mach/powerpc/ncg/table @@ -175,7 +175,8 @@ TOKENS /* 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. @@ -285,7 +286,7 @@ INSTRUCTIONS 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. @@ -370,7 +371,6 @@ INSTRUCTIONS xori GPR:wo, GPR:ro, CONST:ro. xoris GPR:wo, GPR:ro, CONST:ro. - fixup ".powerpcfixup" LABEL:ro. comment "!" LABEL:ro cost(0, 0). @@ -408,8 +408,7 @@ MOVES 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 */ @@ -1150,8 +1149,7 @@ PATTERNS 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 diff --git a/util/led/const.h b/util/led/const.h index 40ad395b5..d2d27f51a 100644 --- a/util/led/const.h +++ b/util/led/const.h @@ -4,8 +4,6 @@ */ /* $Id$ */ -typedef int bool; - #define FALSE 0 #define TRUE 1 diff --git a/util/led/debug.h b/util/led/debug.h index 0e493185d..715409d25 100644 --- a/util/led/debug.h +++ b/util/led/debug.h @@ -17,3 +17,5 @@ extern int DEB; extern int Verbose; #define verbose(s, a1, a2, a3, a4) (Verbose && do_verbose(s, a1, a2, a3, a4)) + +extern void fatal(char* format, ...); diff --git a/util/led/relocate.c b/util/led/relocate.c index 9e5c92b7c..613a0afa7 100644 --- a/util/led/relocate.c +++ b/util/led/relocate.c @@ -9,6 +9,7 @@ static char rcsid[] = "$Id$"; #include #include #include +#include #include #include "out.h" #include "const.h" @@ -104,6 +105,24 @@ static uint32_t get_vc4_valu(char* addr) 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. @@ -125,8 +144,23 @@ static uint32_t get_powerpc_valu(char* addr, uint16_t type) /* 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; - assert(0 && "unrecognised PowerPC instruction"); + /* Undo the sign adjustment (see mach/powerpc/as/mach5.c). */ + + if (lo > 0x7fff) + hi--; + + return ((hi << 16) | lo); + } + + fatal("Don't know how to read from PowerPC fixup on instructions 0x%08x+0x%08x", + opcode1, opcode2); } /* @@ -269,8 +303,25 @@ static void put_powerpc_valu(char* addr, uint32_t value, uint16_t type) 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); } /*