Skip to content

Commit

Permalink
cmd/internal/obj/ppc64: support alignment of prefixed insn
Browse files Browse the repository at this point in the history
Insert machine NOPs when a prefixed instruction crosses a 64B boundary.
ISA 3.1 prohibits prefixed instructions being placed across them. Such
instructions generate SIGILL if executed.

Likewise, adjust the function alignment to guarantee such instructions
can never cross one. And, don't pad the PC based on alignment. The
linker can fit these more optimally.

Likewise, include the function alignment when printing function debug
information. This is needed to verify function alignment happens.

Updates #44549

Change-Id: I434fb0ee4e984ca00dc4566f7569c3bcdf93f910
Reviewed-on: https://go-review.googlesource.com/c/go/+/347050
Run-TryBot: Paul Murphy <murp@ibm.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Lynn Boger <laboger@linux.vnet.ibm.com>
  • Loading branch information
pmur authored and laboger committed Oct 12, 2021
1 parent 9c1dbdf commit 4679670
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 28 deletions.
2 changes: 1 addition & 1 deletion src/cmd/internal/obj/objfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,7 @@ func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) {
fmt.Fprintf(ctxt.Bso, "size=%d", s.Size)
if s.Type == objabi.STEXT {
fn := s.Func()
fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x", uint64(fn.Args), uint64(fn.Locals), uint64(fn.FuncID))
fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x align=%#x", uint64(fn.Args), uint64(fn.Locals), uint64(fn.FuncID), uint64(fn.Align))
if s.Leaf() {
fmt.Fprintf(ctxt.Bso, " leaf")
}
Expand Down
24 changes: 14 additions & 10 deletions src/cmd/internal/obj/ppc64/a.out.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,16 +295,17 @@ const (

const (
/* mark flags */
LABEL = 1 << 0
LEAF = 1 << 1
FLOAT = 1 << 2
BRANCH = 1 << 3
LOAD = 1 << 4
FCMP = 1 << 5
SYNC = 1 << 6
LIST = 1 << 7
FOLL = 1 << 8
NOSCHED = 1 << 9
LABEL = 1 << 0
LEAF = 1 << 1
FLOAT = 1 << 2
BRANCH = 1 << 3
LOAD = 1 << 4
FCMP = 1 << 5
SYNC = 1 << 6
LIST = 1 << 7
FOLL = 1 << 8
NOSCHED = 1 << 9
PFX_X64B = 1 << 10 // A prefixed instruction crossing a 64B boundary
)

// Values for use in branch instruction BC
Expand Down Expand Up @@ -1013,6 +1014,9 @@ const (
AXVCVUXDSP
AXVCVUXWSP

/* ISA 3.1 opcodes */
APNOP

ALAST

// aliases
Expand Down
1 change: 1 addition & 0 deletions src/cmd/internal/obj/ppc64/anames.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 61 additions & 11 deletions src/cmd/internal/obj/ppc64/asm9.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ type Optab struct {
a6 uint8 // p.To (obj.Addr)
type_ int8 // cases in asmout below. E.g., 44 = st r,(ra+rb); 45 = ld (ra+rb), r
size int8 // Text space in bytes to lay operation

// A prefixed instruction is generated by this opcode. This cannot be placed
// across a 64B PC address. Opcodes should not translate to more than one
// prefixed instruction. The prefixed instruction should be written first
// (e.g when Optab.size > 8).
ispfx bool
}

// optab contains an array to be sliced of accepted operand combinations for an
Expand Down Expand Up @@ -509,6 +515,9 @@ var optab = []Optab{
{as: ASTSW, a1: C_REG, a3: C_LCON, a6: C_ZOREG, type_: 41, size: 4},
{as: ALSW, a1: C_ZOREG, a6: C_REG, type_: 45, size: 4},
{as: ALSW, a1: C_ZOREG, a3: C_LCON, a6: C_REG, type_: 42, size: 4},

{as: APNOP, type_: 105, size: 8, ispfx: true},

{as: obj.AUNDEF, type_: 78, size: 4},
{as: obj.APCDATA, a1: C_LCON, a6: C_LCON, type_: 0, size: 0},
{as: obj.AFUNCDATA, a1: C_SCON, a6: C_ADDR, type_: 0, size: 0},
Expand Down Expand Up @@ -642,9 +651,11 @@ func span9(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
var otxt int64
var q *obj.Prog
var out [6]uint32
var falign int32 // Track increased alignment requirements for prefix.
for bflag != 0 {
bflag = 0
pc = 0
falign = 0 // Note, linker bumps function symbols to funcAlign.
for p = c.cursym.Func().Text.Link; p != nil; p = p.Link {
p.Pc = pc
o = c.oplook(p)
Expand Down Expand Up @@ -738,25 +749,56 @@ func span9(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
}
}

// Prefixed instructions cannot be placed across a 64B boundary.
// Mark and adjust the PC of those which do. A nop will be
// inserted during final assembly.
if o.ispfx {
mark := p.Mark &^ PFX_X64B
if pc&63 == 60 {
p.Pc += 4
m += 4
mark |= PFX_X64B
}

// Marks may be adjusted if a too-far conditional branch is
// fixed up above. Likewise, inserting a NOP may cause a
// branch target to become too far away. We need to run
// another iteration and verify no additional changes
// are needed.
if mark != p.Mark {
bflag = 1
p.Mark = mark
}

// Check for 16 or 32B crossing of this prefixed insn.
// These do no require padding, but do require increasing
// the function alignment to prevent them from potentially
// crossing a 64B boundary when the linker assigns the final
// PC.
switch p.Pc & 31 {
case 28: // 32B crossing
falign = 64
case 12: // 16B crossing
if falign < 64 {
falign = 32
}
}
}

pc += int64(m)
}

c.cursym.Size = pc
}

if r := pc & funcAlignMask; r != 0 {
pc += funcAlign - r
}

c.cursym.Size = pc

/*
* lay out the code, emitting code and data relocations.
*/

c.cursym.Func().Align = falign
c.cursym.Grow(c.cursym.Size)

// lay out the code, emitting code and data relocations.

bp := c.cursym.P
nop := LOP_IRR(OP_ORI, REGZERO, REGZERO, 0)
var i int32
for p := c.cursym.Func().Text.Link; p != nil; p = p.Link {
c.pc = p.Pc
Expand All @@ -766,17 +808,20 @@ func span9(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
}
// asmout is not set up to add large amounts of padding
if o.type_ == 0 && p.As == obj.APCALIGN {
pad := LOP_RRR(OP_OR, REGZERO, REGZERO, REGZERO)
aln := c.vregoff(&p.From)
v := addpad(p.Pc, aln, c.ctxt, c.cursym)
if v > 0 {
// Same padding instruction for all
for i = 0; i < int32(v/4); i++ {
c.ctxt.Arch.ByteOrder.PutUint32(bp, pad)
c.ctxt.Arch.ByteOrder.PutUint32(bp, nop)
bp = bp[4:]
}
}
} else {
if p.Mark&PFX_X64B != 0 {
c.ctxt.Arch.ByteOrder.PutUint32(bp, nop)
bp = bp[4:]
}
c.asmout(p, o, out[:])
for i = 0; i < int32(o.size/4); i++ {
c.ctxt.Arch.ByteOrder.PutUint32(bp, out[i])
Expand Down Expand Up @@ -1979,6 +2024,7 @@ func buildop(ctxt *obj.Link) {
AECIWX,
ACLRLSLWI,
AMTVSRDD,
APNOP,
obj.ANOP,
obj.ATEXT,
obj.AUNDEF,
Expand Down Expand Up @@ -3695,6 +3741,10 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) {

case 104: /* VSX mtvsr* instructions, XX1-form RA,RB,XT */
o1 = AOP_XX1(c.oprrr(p.As), uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.Reg))

case 105: /* PNOP */
o1 = 0x07000000
o2 = 0x00000000
}

out[0] = o1
Expand Down
174 changes: 168 additions & 6 deletions src/cmd/internal/obj/ppc64/asm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,20 @@ import (
"testing"
)

var invalidPCAlignSrc = `
var platformEnvs = [][]string{
{"GOOS=aix", "GOARCH=ppc64"},
{"GOOS=linux", "GOARCH=ppc64"},
{"GOOS=linux", "GOARCH=ppc64le"},
}

const invalidPCAlignSrc = `
TEXT test(SB),0,$0-0
ADD $2, R3
PCALIGN $64
RET
`

var validPCAlignSrc = `
const validPCAlignSrc = `
TEXT test(SB),0,$0-0
ADD $2, R3
PCALIGN $16
Expand All @@ -37,10 +43,166 @@ ADD $4, R8
RET
`

var platformEnvs = [][]string{
{"GOOS=aix", "GOARCH=ppc64"},
{"GOOS=linux", "GOARCH=ppc64"},
{"GOOS=linux", "GOARCH=ppc64le"},
const x64pgm = `
TEXT test(SB),0,$0-0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
PNOP
`
const x32pgm = `
TEXT test(SB),0,$0-0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
PNOP
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
`

const x16pgm = `
TEXT test(SB),0,$0-0
OR R0, R0
OR R0, R0
OR R0, R0
PNOP
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
`

const x0pgm = `
TEXT test(SB),0,$0-0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
PNOP
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
`
const x64pgmA64 = `
TEXT test(SB),0,$0-0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
PNOP
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
PNOP
`

const x64pgmA32 = `
TEXT test(SB),0,$0-0
OR R0, R0
OR R0, R0
OR R0, R0
PNOP
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
OR R0, R0
PNOP
`

// Test that nops are inserted when crossing 64B boundaries, and
// alignment is adjusted to avoid crossing.
func TestPfxAlign(t *testing.T) {
testenv.MustHaveGoBuild(t)

dir, err := ioutil.TempDir("", "testpfxalign")
if err != nil {
t.Fatalf("could not create directory: %v", err)
}
defer os.RemoveAll(dir)

pgms := []struct {
text []byte
align string
hasNop bool
}{
{[]byte(x0pgm), "align=0x0", false}, // No alignment or nop adjustments needed
{[]byte(x16pgm), "align=0x20", false}, // Increased alignment needed
{[]byte(x32pgm), "align=0x40", false}, // Worst case alignment needed
{[]byte(x64pgm), "align=0x0", true}, // 0 aligned is default (16B) alignment
{[]byte(x64pgmA64), "align=0x40", true}, // extra alignment + nop
{[]byte(x64pgmA32), "align=0x20", true}, // extra alignment + nop
}

for _, pgm := range pgms {
tmpfile := filepath.Join(dir, "x.s")
err = ioutil.WriteFile(tmpfile, pgm.text, 0644)
if err != nil {
t.Fatalf("can't write output: %v\n", err)
}
cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-S", "-o", filepath.Join(dir, "test.o"), tmpfile)
cmd.Env = append(os.Environ(), "GOOS=linux", "GOARCH=ppc64le")
out, err := cmd.CombinedOutput()
if err != nil {
t.Errorf("Failed to compile %v: %v\n", pgm, err)
}
if !strings.Contains(string(out), pgm.align) {
t.Errorf(fmt.Sprintf("Fatal, misaligned text with prefixed instructions:\n%s\n", string(out)))
}
hasNop := strings.Contains(string(out), "00 00 00 60")
if hasNop != pgm.hasNop {
t.Errorf(fmt.Sprintf("Fatal, prefixed instruction is missing nop padding:\n%s\n", string(out)))
}
}
}

// TestLarge generates a very large file to verify that large
Expand Down

0 comments on commit 4679670

Please sign in to comment.