cmd/link, cmd/internal/obj/loong64: support the PCALIGN directive

Allow writing `PCALIGN $imm` where imm is a power-of-2 between 8 and
2048 (inclusive), for ensuring that the following instruction is
placed at an imm-byte boundary relative to the beginning of the
function. If the PC is not sufficiently aligned, NOOPs will be
inserted to make it so, otherwise the directive will do nothing.

This could be useful for both asm performance hand-tuning, and future
scenarios where a certain bigger alignment might be required.

Change-Id: Iad6244669a3d5adea88eceb0dc7be1af4f0d4fc9
Reviewed-on: https://go-review.googlesource.com/c/go/+/479815
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
Run-TryBot: WANG Xuerui <git@xen0n.name>
Auto-Submit: Ian Lance Taylor <iant@golang.org>
Reviewed-by: abner chenc <chenguoqi@loongson.cn>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
This commit is contained in:
WANG Xuerui 2023-03-28 19:30:04 +08:00 committed by Gopher Robot
parent 10c079a0ad
commit 47b22b6548
2 changed files with 61 additions and 9 deletions

View file

@ -345,6 +345,7 @@ var optab = []Optab{
{ARDTIMED, C_NONE, C_NONE, C_NONE, C_REG, C_REG, 62, 4, 0, 0}, {ARDTIMED, C_NONE, C_NONE, C_NONE, C_REG, C_REG, 62, 4, 0, 0},
{obj.AUNDEF, C_NONE, C_NONE, C_NONE, C_NONE, C_NONE, 49, 4, 0, 0}, {obj.AUNDEF, C_NONE, C_NONE, C_NONE, C_NONE, C_NONE, 49, 4, 0, 0},
{obj.APCALIGN, C_SCON, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0},
{obj.APCDATA, C_LCON, C_NONE, C_NONE, C_LCON, C_NONE, 0, 0, 0, 0}, {obj.APCDATA, C_LCON, C_NONE, C_NONE, C_LCON, C_NONE, 0, 0, 0, 0},
{obj.APCDATA, C_DCON, C_NONE, C_NONE, C_DCON, C_NONE, 0, 0, 0, 0}, {obj.APCDATA, C_DCON, C_NONE, C_NONE, C_DCON, C_NONE, 0, 0, 0, 0},
{obj.AFUNCDATA, C_SCON, C_NONE, C_NONE, C_ADDR, C_NONE, 0, 0, 0, 0}, {obj.AFUNCDATA, C_SCON, C_NONE, C_NONE, C_ADDR, C_NONE, 0, 0, 0, 0},
@ -359,6 +360,15 @@ var optab = []Optab{
{obj.AXXX, C_NONE, C_NONE, C_NONE, C_NONE, C_NONE, 0, 4, 0, 0}, {obj.AXXX, C_NONE, C_NONE, C_NONE, C_NONE, C_NONE, 0, 4, 0, 0},
} }
// pcAlignPadLength returns the number of bytes required to align pc to alignedValue,
// reporting an error if alignedValue is not a power of two or is out of range.
func pcAlignPadLength(ctxt *obj.Link, pc int64, alignedValue int64) int {
if !((alignedValue&(alignedValue-1) == 0) && 8 <= alignedValue && alignedValue <= 2048) {
ctxt.Diag("alignment value of an instruction must be a power of two and in the range [8, 2048], got %d\n", alignedValue)
}
return int(-pc & (alignedValue - 1))
}
var oprange [ALAST & obj.AMask][]Optab var oprange [ALAST & obj.AMask][]Optab
var xcmp [C_NCLASS][C_NCLASS]bool var xcmp [C_NCLASS][C_NCLASS]bool
@ -390,10 +400,20 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
o = c.oplook(p) o = c.oplook(p)
m = int(o.size) m = int(o.size)
if m == 0 { if m == 0 {
if p.As != obj.ANOP && p.As != obj.AFUNCDATA && p.As != obj.APCDATA { switch p.As {
case obj.APCALIGN:
alignedValue := p.From.Offset
m = pcAlignPadLength(ctxt, pc, alignedValue)
// Update the current text symbol alignment value.
if int32(alignedValue) > cursym.Func().Align {
cursym.Func().Align = int32(alignedValue)
}
break
case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
continue
default:
c.ctxt.Diag("zero-width instruction\n%v", p) c.ctxt.Diag("zero-width instruction\n%v", p)
} }
continue
} }
pc += int64(m) pc += int64(m)
@ -443,10 +463,16 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
m = int(o.size) m = int(o.size)
if m == 0 { if m == 0 {
if p.As != obj.ANOP && p.As != obj.AFUNCDATA && p.As != obj.APCDATA { switch p.As {
case obj.APCALIGN:
alignedValue := p.From.Offset
m = pcAlignPadLength(ctxt, pc, alignedValue)
break
case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
continue
default:
c.ctxt.Diag("zero-width instruction\n%v", p) c.ctxt.Diag("zero-width instruction\n%v", p)
} }
continue
} }
pc += int64(m) pc += int64(m)
@ -470,6 +496,16 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
if int(o.size) > 4*len(out) { if int(o.size) > 4*len(out) {
log.Fatalf("out array in span0 is too small, need at least %d for %v", o.size/4, p) log.Fatalf("out array in span0 is too small, need at least %d for %v", o.size/4, p)
} }
if p.As == obj.APCALIGN {
alignedValue := p.From.Offset
v := pcAlignPadLength(c.ctxt, p.Pc, alignedValue)
for i = 0; i < int32(v/4); i++ {
// emit ANOOP instruction by the padding size
c.ctxt.Arch.ByteOrder.PutUint32(bp, c.oprrr(ANOOP))
bp = bp[4:]
}
continue
}
c.asmout(p, o, out[:]) c.asmout(p, o, out[:])
for i = 0; i < int32(o.size/4); i++ { for i = 0; i < int32(o.size/4); i++ {
c.ctxt.Arch.ByteOrder.PutUint32(bp, out[i]) c.ctxt.Arch.ByteOrder.PutUint32(bp, out[i])
@ -1062,6 +1098,7 @@ func buildop(ctxt *obj.Link) {
obj.ATEXT, obj.ATEXT,
obj.AUNDEF, obj.AUNDEF,
obj.AFUNCDATA, obj.AFUNCDATA,
obj.APCALIGN,
obj.APCDATA, obj.APCDATA,
obj.ADUFFZERO, obj.ADUFFZERO,
obj.ADUFFCOPY: obj.ADUFFCOPY:

View file

@ -567,7 +567,8 @@ func main() {
} }
` `
const testFuncAlignAsmSrc = ` var testFuncAlignAsmSources = map[string]string{
"arm64": `
#include "textflag.h" #include "textflag.h"
TEXT ·alignPc(SB),NOSPLIT, $0-0 TEXT ·alignPc(SB),NOSPLIT, $0-0
@ -578,13 +579,27 @@ TEXT ·alignPc(SB),NOSPLIT, $0-0
GLOBL ·alignPcFnAddr(SB),RODATA,$8 GLOBL ·alignPcFnAddr(SB),RODATA,$8
DATA ·alignPcFnAddr(SB)/8,$·alignPc(SB) DATA ·alignPcFnAddr(SB)/8,$·alignPc(SB)
` `,
"loong64": `
#include "textflag.h"
TEXT ·alignPc(SB),NOSPLIT, $0-0
MOVV $2, R4
PCALIGN $512
MOVV $3, R5
RET
GLOBL ·alignPcFnAddr(SB),RODATA,$8
DATA ·alignPcFnAddr(SB)/8,$·alignPc(SB)
`,
}
// TestFuncAlign verifies that the address of a function can be aligned // TestFuncAlign verifies that the address of a function can be aligned
// with a specific value on arm64. // with a specific value on arm64 and loong64.
func TestFuncAlign(t *testing.T) { func TestFuncAlign(t *testing.T) {
if runtime.GOARCH != "arm64" || runtime.GOOS != "linux" { testFuncAlignAsmSrc := testFuncAlignAsmSources[runtime.GOARCH]
t.Skip("skipping on non-linux/arm64 platform") if len(testFuncAlignAsmSrc) == 0 || runtime.GOOS != "linux" {
t.Skip("skipping on non-linux/{arm64,loong64} platform")
} }
testenv.MustHaveGoBuild(t) testenv.MustHaveGoBuild(t)