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.
This commit is contained in:
David Given 2017-01-15 11:51:37 +01:00
parent 14aab21204
commit f80acfe9f5
9 changed files with 108 additions and 45 deletions

View file

@ -3,3 +3,7 @@
* $State$ * $State$
*/ */
#include <stdbool.h>
extern quad emit_ha(struct expr_t* expr, bool is_signed);
extern quad emit_lo(struct expr_t* expr);

View file

@ -85,7 +85,7 @@
%token <y_word> OP_LI32 %token <y_word> OP_LI32
%token <y_word> OP_POWERPC_FIXUP %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 */ /* Other token types */

View file

@ -102,8 +102,8 @@
0, OP_LA, 0, "la", 0, OP_LA, 0, "la",
0, OP_LA, 0, "li", 0, OP_LA, 0, "li",
0, OP_RS_RA_RA_C, 31<<26 | 444<<1, "mr", 0, OP_RS_RA_RA_C, 31<<26 | 444<<1, "mr",
0, OP_POWERPC_FIXUP, 0, ".powerpcfixup", 0, OP_HA, 0, "ha16",
0, OP_HI, 0, "ha16", 0, OP_HAS, 0, "has16",
0, OP_LO, 0, "lo16", 0, OP_LO, 0, "lo16",
/* Branch processor instructions (page 20) */ /* Branch processor instructions (page 20) */

View file

@ -60,7 +60,6 @@ operation
| OP_LIA lia { emit4($1 | $2); } | OP_LIA lia { emit4($1 | $2); }
| OP_LIL lil { emit4($1 | $2); } | OP_LIL lil { emit4($1 | $2); }
| OP_LI32 li32 /* emitted in subrule */ | OP_LI32 li32 /* emitted in subrule */
| OP_POWERPC_FIXUP powerpcfixup /* emitted in subrule */
; ;
c c
@ -76,32 +75,9 @@ e16
serror("16-bit value out of range"); serror("16-bit value out of range");
$$ = (uint16_t) $1; $$ = (uint16_t) $1;
} }
| OP_HI ASC_LPAR expr ASC_RPAR | OP_HA ASC_LPAR expr ASC_RPAR { $$ = emit_ha(&$3, false); }
{ | OP_HAS ASC_LPAR expr ASC_RPAR { $$ = emit_ha(&$3, true); }
/* If this is a symbol reference, discard the symbol and keep only the | OP_LO ASC_LPAR expr ASC_RPAR { $$ = emit_lo(&$3); }
* 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;
}
; ;
u8 u8

View file

@ -1,4 +1,38 @@
/*
* $Source$ quad emit_ha(struct expr_t* expr, bool is_signed)
* $State$ {
/* 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;
}

View file

@ -175,7 +175,8 @@ TOKENS
/* Primitives */ /* Primitives */
LABEL = { ADDR adr; } 4 adr. 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 "]". LABEL_OFFSET_LO = { ADDR adr; } 4 "lo16[" adr "]".
LOCAL = { INT off; } 4. LOCAL = { INT off; } 4.
@ -285,7 +286,7 @@ INSTRUCTIONS
add GPR:wo, GPR:ro, GPR:ro. add GPR:wo, GPR:ro, GPR:ro.
addX "add." GPR:wo, GPR:ro, GPR:ro. addX "add." GPR:wo, GPR:ro, GPR:ro.
addi GPR:wo, GPR:ro, CONST: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. and GPR:wo, GPR:ro, GPR:ro.
andc GPR:wo, GPR:ro, GPR:ro. andc GPR:wo, GPR:ro, GPR:ro.
andiX "andi." GPR:wo:cc, GPR:ro, CONST:ro. andiX "andi." GPR:wo:cc, GPR:ro, CONST:ro.
@ -370,7 +371,6 @@ INSTRUCTIONS
xori GPR:wo, GPR:ro, CONST:ro. xori GPR:wo, GPR:ro, CONST:ro.
xoris GPR:wo, GPR:ro, CONST:ro. xoris GPR:wo, GPR:ro, CONST:ro.
fixup ".powerpcfixup" LABEL:ro.
comment "!" LABEL:ro cost(0, 0). comment "!" LABEL:ro cost(0, 0).
@ -408,8 +408,7 @@ MOVES
from LABEL to GPR from LABEL to GPR
gen gen
COMMENT("move LABEL->GPR") COMMENT("move LABEL->GPR")
fixup {LABEL, %1.adr} addis %2, R0, {LABEL_OFFSET_HA, %1.adr}
addis %2, R0, {LABEL_OFFSET_HI, %1.adr}
ori %2, %2, {LABEL_OFFSET_LO, %1.adr} ori %2, %2, {LABEL_OFFSET_LO, %1.adr}
/* Sign extension */ /* Sign extension */
@ -1150,8 +1149,7 @@ PATTERNS
with LABEL with LABEL
uses REG uses REG
gen gen
fixup {LABEL, %1.adr} addis %a, R0, {LABEL_OFFSET_HAS, %1.adr}
addis %a, R0, {LABEL_OFFSET_HI, %1.adr}
lwz %a, {GPRINDIRECT_OFFSET_LO, %a, %1.adr} lwz %a, {GPRINDIRECT_OFFSET_LO, %a, %1.adr}
yields %a yields %a
with GPR with GPR

View file

@ -4,8 +4,6 @@
*/ */
/* $Id$ */ /* $Id$ */
typedef int bool;
#define FALSE 0 #define FALSE 0
#define TRUE 1 #define TRUE 1

View file

@ -17,3 +17,5 @@ extern int DEB;
extern int Verbose; extern int Verbose;
#define verbose(s, a1, a2, a3, a4) (Verbose && do_verbose(s, a1, a2, a3, a4)) #define verbose(s, a1, a2, a3, a4) (Verbose && do_verbose(s, a1, a2, a3, a4))
extern void fatal(char* format, ...);

View file

@ -9,6 +9,7 @@ static char rcsid[] = "$Id$";
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <assert.h> #include <assert.h>
#include "out.h" #include "out.h"
#include "const.h" #include "const.h"
@ -104,6 +105,24 @@ static uint32_t get_vc4_valu(char* addr)
assert(0 && "unrecognised VC4 instruction"); 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 /* 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 in one of several different ways, depending on what the
* instructions area. * instructions area.
@ -125,8 +144,23 @@ static uint32_t get_powerpc_valu(char* addr, uint16_t type)
/* addis / ori instruction pair */ /* addis / ori instruction pair */
return ((opcode1 & 0xffff) << 16) | (opcode2 & 0xffff); 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((opcode1 & 0xffff0000) | hi, addr+0, type);
write4((opcode2 & 0xffff0000) | lo, addr+4, 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 else
assert(0 && "unrecognised PowerPC instruction"); fatal("Don't know how to write a PowerPC fixup to instructions 0x%08x+0x%08x",
opcode1, opcode2);
} }
/* /*