Skip to content

Commit 4679670

Browse files
pmurlaboger
authored andcommitted
cmd/internal/obj/ppc64: support alignment of prefixed insn
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 <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Cherry Mui <[email protected]> Reviewed-by: Lynn Boger <[email protected]>
1 parent 9c1dbdf commit 4679670

File tree

5 files changed

+245
-28
lines changed

5 files changed

+245
-28
lines changed

src/cmd/internal/obj/objfile.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,7 @@ func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) {
804804
fmt.Fprintf(ctxt.Bso, "size=%d", s.Size)
805805
if s.Type == objabi.STEXT {
806806
fn := s.Func()
807-
fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x", uint64(fn.Args), uint64(fn.Locals), uint64(fn.FuncID))
807+
fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x align=%#x", uint64(fn.Args), uint64(fn.Locals), uint64(fn.FuncID), uint64(fn.Align))
808808
if s.Leaf() {
809809
fmt.Fprintf(ctxt.Bso, " leaf")
810810
}

src/cmd/internal/obj/ppc64/a.out.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -295,16 +295,17 @@ const (
295295

296296
const (
297297
/* mark flags */
298-
LABEL = 1 << 0
299-
LEAF = 1 << 1
300-
FLOAT = 1 << 2
301-
BRANCH = 1 << 3
302-
LOAD = 1 << 4
303-
FCMP = 1 << 5
304-
SYNC = 1 << 6
305-
LIST = 1 << 7
306-
FOLL = 1 << 8
307-
NOSCHED = 1 << 9
298+
LABEL = 1 << 0
299+
LEAF = 1 << 1
300+
FLOAT = 1 << 2
301+
BRANCH = 1 << 3
302+
LOAD = 1 << 4
303+
FCMP = 1 << 5
304+
SYNC = 1 << 6
305+
LIST = 1 << 7
306+
FOLL = 1 << 8
307+
NOSCHED = 1 << 9
308+
PFX_X64B = 1 << 10 // A prefixed instruction crossing a 64B boundary
308309
)
309310

310311
// Values for use in branch instruction BC
@@ -1013,6 +1014,9 @@ const (
10131014
AXVCVUXDSP
10141015
AXVCVUXWSP
10151016

1017+
/* ISA 3.1 opcodes */
1018+
APNOP
1019+
10161020
ALAST
10171021

10181022
// aliases

src/cmd/internal/obj/ppc64/anames.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/internal/obj/ppc64/asm9.go

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ type Optab struct {
7373
a6 uint8 // p.To (obj.Addr)
7474
type_ int8 // cases in asmout below. E.g., 44 = st r,(ra+rb); 45 = ld (ra+rb), r
7575
size int8 // Text space in bytes to lay operation
76+
77+
// A prefixed instruction is generated by this opcode. This cannot be placed
78+
// across a 64B PC address. Opcodes should not translate to more than one
79+
// prefixed instruction. The prefixed instruction should be written first
80+
// (e.g when Optab.size > 8).
81+
ispfx bool
7682
}
7783

7884
// optab contains an array to be sliced of accepted operand combinations for an
@@ -509,6 +515,9 @@ var optab = []Optab{
509515
{as: ASTSW, a1: C_REG, a3: C_LCON, a6: C_ZOREG, type_: 41, size: 4},
510516
{as: ALSW, a1: C_ZOREG, a6: C_REG, type_: 45, size: 4},
511517
{as: ALSW, a1: C_ZOREG, a3: C_LCON, a6: C_REG, type_: 42, size: 4},
518+
519+
{as: APNOP, type_: 105, size: 8, ispfx: true},
520+
512521
{as: obj.AUNDEF, type_: 78, size: 4},
513522
{as: obj.APCDATA, a1: C_LCON, a6: C_LCON, type_: 0, size: 0},
514523
{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) {
642651
var otxt int64
643652
var q *obj.Prog
644653
var out [6]uint32
654+
var falign int32 // Track increased alignment requirements for prefix.
645655
for bflag != 0 {
646656
bflag = 0
647657
pc = 0
658+
falign = 0 // Note, linker bumps function symbols to funcAlign.
648659
for p = c.cursym.Func().Text.Link; p != nil; p = p.Link {
649660
p.Pc = pc
650661
o = c.oplook(p)
@@ -738,25 +749,56 @@ func span9(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
738749
}
739750
}
740751

752+
// Prefixed instructions cannot be placed across a 64B boundary.
753+
// Mark and adjust the PC of those which do. A nop will be
754+
// inserted during final assembly.
755+
if o.ispfx {
756+
mark := p.Mark &^ PFX_X64B
757+
if pc&63 == 60 {
758+
p.Pc += 4
759+
m += 4
760+
mark |= PFX_X64B
761+
}
762+
763+
// Marks may be adjusted if a too-far conditional branch is
764+
// fixed up above. Likewise, inserting a NOP may cause a
765+
// branch target to become too far away. We need to run
766+
// another iteration and verify no additional changes
767+
// are needed.
768+
if mark != p.Mark {
769+
bflag = 1
770+
p.Mark = mark
771+
}
772+
773+
// Check for 16 or 32B crossing of this prefixed insn.
774+
// These do no require padding, but do require increasing
775+
// the function alignment to prevent them from potentially
776+
// crossing a 64B boundary when the linker assigns the final
777+
// PC.
778+
switch p.Pc & 31 {
779+
case 28: // 32B crossing
780+
falign = 64
781+
case 12: // 16B crossing
782+
if falign < 64 {
783+
falign = 32
784+
}
785+
}
786+
}
787+
741788
pc += int64(m)
742789
}
743790

744791
c.cursym.Size = pc
745792
}
746793

747-
if r := pc & funcAlignMask; r != 0 {
748-
pc += funcAlign - r
749-
}
750-
751794
c.cursym.Size = pc
752-
753-
/*
754-
* lay out the code, emitting code and data relocations.
755-
*/
756-
795+
c.cursym.Func().Align = falign
757796
c.cursym.Grow(c.cursym.Size)
758797

798+
// lay out the code, emitting code and data relocations.
799+
759800
bp := c.cursym.P
801+
nop := LOP_IRR(OP_ORI, REGZERO, REGZERO, 0)
760802
var i int32
761803
for p := c.cursym.Func().Text.Link; p != nil; p = p.Link {
762804
c.pc = p.Pc
@@ -766,17 +808,20 @@ func span9(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
766808
}
767809
// asmout is not set up to add large amounts of padding
768810
if o.type_ == 0 && p.As == obj.APCALIGN {
769-
pad := LOP_RRR(OP_OR, REGZERO, REGZERO, REGZERO)
770811
aln := c.vregoff(&p.From)
771812
v := addpad(p.Pc, aln, c.ctxt, c.cursym)
772813
if v > 0 {
773814
// Same padding instruction for all
774815
for i = 0; i < int32(v/4); i++ {
775-
c.ctxt.Arch.ByteOrder.PutUint32(bp, pad)
816+
c.ctxt.Arch.ByteOrder.PutUint32(bp, nop)
776817
bp = bp[4:]
777818
}
778819
}
779820
} else {
821+
if p.Mark&PFX_X64B != 0 {
822+
c.ctxt.Arch.ByteOrder.PutUint32(bp, nop)
823+
bp = bp[4:]
824+
}
780825
c.asmout(p, o, out[:])
781826
for i = 0; i < int32(o.size/4); i++ {
782827
c.ctxt.Arch.ByteOrder.PutUint32(bp, out[i])
@@ -1979,6 +2024,7 @@ func buildop(ctxt *obj.Link) {
19792024
AECIWX,
19802025
ACLRLSLWI,
19812026
AMTVSRDD,
2027+
APNOP,
19822028
obj.ANOP,
19832029
obj.ATEXT,
19842030
obj.AUNDEF,
@@ -3695,6 +3741,10 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) {
36953741

36963742
case 104: /* VSX mtvsr* instructions, XX1-form RA,RB,XT */
36973743
o1 = AOP_XX1(c.oprrr(p.As), uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.Reg))
3744+
3745+
case 105: /* PNOP */
3746+
o1 = 0x07000000
3747+
o2 = 0x00000000
36983748
}
36993749

37003750
out[0] = o1

src/cmd/internal/obj/ppc64/asm_test.go

Lines changed: 168 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,20 @@ import (
1717
"testing"
1818
)
1919

20-
var invalidPCAlignSrc = `
20+
var platformEnvs = [][]string{
21+
{"GOOS=aix", "GOARCH=ppc64"},
22+
{"GOOS=linux", "GOARCH=ppc64"},
23+
{"GOOS=linux", "GOARCH=ppc64le"},
24+
}
25+
26+
const invalidPCAlignSrc = `
2127
TEXT test(SB),0,$0-0
2228
ADD $2, R3
2329
PCALIGN $64
2430
RET
2531
`
2632

27-
var validPCAlignSrc = `
33+
const validPCAlignSrc = `
2834
TEXT test(SB),0,$0-0
2935
ADD $2, R3
3036
PCALIGN $16
@@ -37,10 +43,166 @@ ADD $4, R8
3743
RET
3844
`
3945

40-
var platformEnvs = [][]string{
41-
{"GOOS=aix", "GOARCH=ppc64"},
42-
{"GOOS=linux", "GOARCH=ppc64"},
43-
{"GOOS=linux", "GOARCH=ppc64le"},
46+
const x64pgm = `
47+
TEXT test(SB),0,$0-0
48+
OR R0, R0
49+
OR R0, R0
50+
OR R0, R0
51+
OR R0, R0
52+
OR R0, R0
53+
OR R0, R0
54+
OR R0, R0
55+
OR R0, R0
56+
OR R0, R0
57+
OR R0, R0
58+
OR R0, R0
59+
OR R0, R0
60+
OR R0, R0
61+
OR R0, R0
62+
OR R0, R0
63+
PNOP
64+
`
65+
const x32pgm = `
66+
TEXT test(SB),0,$0-0
67+
OR R0, R0
68+
OR R0, R0
69+
OR R0, R0
70+
OR R0, R0
71+
OR R0, R0
72+
OR R0, R0
73+
OR R0, R0
74+
PNOP
75+
OR R0, R0
76+
OR R0, R0
77+
OR R0, R0
78+
OR R0, R0
79+
OR R0, R0
80+
OR R0, R0
81+
OR R0, R0
82+
OR R0, R0
83+
`
84+
85+
const x16pgm = `
86+
TEXT test(SB),0,$0-0
87+
OR R0, R0
88+
OR R0, R0
89+
OR R0, R0
90+
PNOP
91+
OR R0, R0
92+
OR R0, R0
93+
OR R0, R0
94+
OR R0, R0
95+
OR R0, R0
96+
OR R0, R0
97+
OR R0, R0
98+
OR R0, R0
99+
OR R0, R0
100+
OR R0, R0
101+
OR R0, R0
102+
OR R0, R0
103+
`
104+
105+
const x0pgm = `
106+
TEXT test(SB),0,$0-0
107+
OR R0, R0
108+
OR R0, R0
109+
OR R0, R0
110+
OR R0, R0
111+
PNOP
112+
OR R0, R0
113+
OR R0, R0
114+
OR R0, R0
115+
OR R0, R0
116+
OR R0, R0
117+
OR R0, R0
118+
OR R0, R0
119+
OR R0, R0
120+
OR R0, R0
121+
OR R0, R0
122+
OR R0, R0
123+
`
124+
const x64pgmA64 = `
125+
TEXT test(SB),0,$0-0
126+
OR R0, R0
127+
OR R0, R0
128+
OR R0, R0
129+
OR R0, R0
130+
OR R0, R0
131+
OR R0, R0
132+
OR R0, R0
133+
PNOP
134+
OR R0, R0
135+
OR R0, R0
136+
OR R0, R0
137+
OR R0, R0
138+
OR R0, R0
139+
OR R0, R0
140+
PNOP
141+
`
142+
143+
const x64pgmA32 = `
144+
TEXT test(SB),0,$0-0
145+
OR R0, R0
146+
OR R0, R0
147+
OR R0, R0
148+
PNOP
149+
OR R0, R0
150+
OR R0, R0
151+
OR R0, R0
152+
OR R0, R0
153+
OR R0, R0
154+
OR R0, R0
155+
OR R0, R0
156+
OR R0, R0
157+
OR R0, R0
158+
OR R0, R0
159+
PNOP
160+
`
161+
162+
// Test that nops are inserted when crossing 64B boundaries, and
163+
// alignment is adjusted to avoid crossing.
164+
func TestPfxAlign(t *testing.T) {
165+
testenv.MustHaveGoBuild(t)
166+
167+
dir, err := ioutil.TempDir("", "testpfxalign")
168+
if err != nil {
169+
t.Fatalf("could not create directory: %v", err)
170+
}
171+
defer os.RemoveAll(dir)
172+
173+
pgms := []struct {
174+
text []byte
175+
align string
176+
hasNop bool
177+
}{
178+
{[]byte(x0pgm), "align=0x0", false}, // No alignment or nop adjustments needed
179+
{[]byte(x16pgm), "align=0x20", false}, // Increased alignment needed
180+
{[]byte(x32pgm), "align=0x40", false}, // Worst case alignment needed
181+
{[]byte(x64pgm), "align=0x0", true}, // 0 aligned is default (16B) alignment
182+
{[]byte(x64pgmA64), "align=0x40", true}, // extra alignment + nop
183+
{[]byte(x64pgmA32), "align=0x20", true}, // extra alignment + nop
184+
}
185+
186+
for _, pgm := range pgms {
187+
tmpfile := filepath.Join(dir, "x.s")
188+
err = ioutil.WriteFile(tmpfile, pgm.text, 0644)
189+
if err != nil {
190+
t.Fatalf("can't write output: %v\n", err)
191+
}
192+
cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-S", "-o", filepath.Join(dir, "test.o"), tmpfile)
193+
cmd.Env = append(os.Environ(), "GOOS=linux", "GOARCH=ppc64le")
194+
out, err := cmd.CombinedOutput()
195+
if err != nil {
196+
t.Errorf("Failed to compile %v: %v\n", pgm, err)
197+
}
198+
if !strings.Contains(string(out), pgm.align) {
199+
t.Errorf(fmt.Sprintf("Fatal, misaligned text with prefixed instructions:\n%s\n", string(out)))
200+
}
201+
hasNop := strings.Contains(string(out), "00 00 00 60")
202+
if hasNop != pgm.hasNop {
203+
t.Errorf(fmt.Sprintf("Fatal, prefixed instruction is missing nop padding:\n%s\n", string(out)))
204+
}
205+
}
44206
}
45207

46208
// TestLarge generates a very large file to verify that large

0 commit comments

Comments
 (0)