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
{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},
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)
}
}
+ // 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
}
// 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])
AECIWX,
ACLRLSLWI,
AMTVSRDD,
+ APNOP,
obj.ANOP,
obj.ATEXT,
obj.AUNDEF,
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
"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
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