]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/internal/obj/ppc64: support alignment of prefixed insn
authorPaul E. Murphy <murp@ibm.com>
Tue, 9 Mar 2021 22:55:20 +0000 (16:55 -0600)
committerLynn Boger <laboger@linux.vnet.ibm.com>
Tue, 12 Oct 2021 12:24:09 +0000 (12:24 +0000)
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>
src/cmd/internal/obj/objfile.go
src/cmd/internal/obj/ppc64/a.out.go
src/cmd/internal/obj/ppc64/anames.go
src/cmd/internal/obj/ppc64/asm9.go
src/cmd/internal/obj/ppc64/asm_test.go

index 98af803880f2491361453555fcc77b603e4adc58..a590549f52e2cc4e01627fe8cefc4ecb42d4b5a1 100644 (file)
@@ -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")
                }
index 3a1e2d3c9ac0350119d699df6cf1d5c84376e87a..dd6d903e2867df49fb3d09e941f7edb37ecead73 100644 (file)
@@ -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
@@ -1013,6 +1014,9 @@ const (
        AXVCVUXDSP
        AXVCVUXWSP
 
+       /* ISA 3.1 opcodes */
+       APNOP
+
        ALAST
 
        // aliases
index fca4b3e35558b464710b4bce6d370e05cceeef99..0da73ca91ed6c9f8d1036e94c481c918ddc39f12 100644 (file)
@@ -613,5 +613,6 @@ var Anames = []string{
        "XVCVSXWSP",
        "XVCVUXDSP",
        "XVCVUXWSP",
+       "PNOP",
        "LAST",
 }
index 0901d647929daf984b2bc679ea3d831bf41cdac8..6ed11b3f7ec46c8b0838e60c776bcd4bfca61a48 100644 (file)
@@ -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
@@ -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},
@@ -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)
@@ -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
@@ -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])
@@ -1979,6 +2024,7 @@ func buildop(ctxt *obj.Link) {
                        AECIWX,
                        ACLRLSLWI,
                        AMTVSRDD,
+                       APNOP,
                        obj.ANOP,
                        obj.ATEXT,
                        obj.AUNDEF,
@@ -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
index 60c5f346e08b8325dee6dd331ee9dfb5da3c25ca..ec0ba19e37d9e0c0dff77af56e42aca0ab7e75a4 100644 (file)
@@ -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
@@ -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