diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go index 782b6d4aff1051..745dda716bf47a 100644 --- a/src/cmd/internal/obj/x86/asm6.go +++ b/src/cmd/internal/obj/x86/asm6.go @@ -2036,6 +2036,25 @@ type nopPad struct { n int32 // Size of the pad } +// padding bytes to add to align code as requested. +func addpad(pc, a int64, ctxt *obj.Link, cursym *obj.LSym) int { + switch a { + case 8, 16, 32, 64: + // By default function alignment is 16. If an alignment > 16 is + // requested then the function alignment must also be promoted. + // The function alignment is not promoted on AIX at this time. + if cursym.Func().Align < int32(a) { + cursym.Func().Align = int32(a) + } + if pc&(a-1) != 0 { + return int(a - (pc & (a - 1))) + } + default: + ctxt.Diag("Unexpected alignment: %d for PCALIGN directive\n", a) + } + return 0 +} + func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { if ctxt.Retpoline && ctxt.Arch.Family == sys.I386 { ctxt.Diag("-spectre=ret not supported on 386") @@ -2119,6 +2138,24 @@ func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { c0 := c c = pjc.padJump(ctxt, s, p, c) + if p.As == obj.APCALIGN { + aln := p.From.Offset + v := addpad(int64(c), aln, ctxt, s) + if v > 0 { + s.Grow(int64(c) + int64(v)) + fillnop(s.P[c:], int(v)) + } + + // Update the current text symbol alignment value. + if int32(v) > s.Func().Align { + s.Func().Align = int32(v) + } + + c += int32(v) + pPrev = p + continue + } + if maxLoopPad > 0 && p.Back&branchLoopHead != 0 && c&(loopAlign-1) != 0 { // pad with NOPs v := -c & (loopAlign - 1) diff --git a/src/cmd/internal/obj/x86/asm_test.go b/src/cmd/internal/obj/x86/asm_test.go index 36c8fce675b7da..1bbc83afc4eaa8 100644 --- a/src/cmd/internal/obj/x86/asm_test.go +++ b/src/cmd/internal/obj/x86/asm_test.go @@ -7,6 +7,10 @@ package x86 import ( "cmd/internal/obj" "cmd/internal/objabi" + "internal/testenv" + "os" + "path/filepath" + "regexp" "testing" ) @@ -289,3 +293,52 @@ func TestRegIndex(t *testing.T) { } } } + +// TestPCALIGN verifies the correctness of the PCALIGN by checking if the +// code can be aligned to the alignment value. +func TestPCALIGN(t *testing.T) { + testenv.MustHaveGoBuild(t) + dir, err := os.MkdirTemp("", "testpcalign") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + tmpfile := filepath.Join(dir, "test.s") + tmpout := filepath.Join(dir, "test.o") + + code1 := []byte("TEXT ·foo(SB),$0-0\nMOVQ $0, AX\nPCALIGN $8\nMOVQ $1, BX\nRET\n") + code2 := []byte("TEXT ·foo(SB),$0-0\nMOVQ $0, AX\nPCALIGN $16\nMOVQ $2, CX\nRET\n") + // If the output contains this pattern, the pc-offsite of "MOVQ $1, AX" is 8 bytes aligned. + out1 := `0x0008\s00008\s\(.*\)\tMOVQ\t\$1,\sBX` + // If the output contains this pattern, the pc-offsite of "MOVQ $2, CX" is 16 bytes aligned. + out2 := `0x0010\s00016\s\(.*\)\tMOVQ\t\$2,\sCX` + var testCases = []struct { + name string + code []byte + out string + }{ + {"8-byte alignment", code1, out1}, + {"16-byte alignment", code2, out2}, + } + + for _, test := range testCases { + if err := os.WriteFile(tmpfile, test.code, 0644); err != nil { + t.Fatal(err) + } + cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", tmpout, tmpfile) + cmd.Env = append(os.Environ(), "GOARCH=amd64", "GOOS=linux") + out, err := cmd.CombinedOutput() + if err != nil { + t.Errorf("The %s build failed: %v, output: %s", test.name, err, out) + continue + } + + matched, err := regexp.MatchString(test.out, string(out)) + if err != nil { + t.Fatal(err) + } + if !matched { + t.Errorf("The %s testing failed!\ninput: %s\noutput: %s\n", test.name, test.code, out) + } + } +}