From 47b22b6548822d7b51ba3dca3c8c5fe669bfdd59 Mon Sep 17 00:00:00 2001 From: WANG Xuerui Date: Tue, 28 Mar 2023 19:30:04 +0800 Subject: [PATCH] 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 Reviewed-by: Keith Randall Run-TryBot: WANG Xuerui Auto-Submit: Ian Lance Taylor Reviewed-by: abner chenc TryBot-Result: Gopher Robot Reviewed-by: Cherry Mui --- src/cmd/internal/obj/loong64/asm.go | 45 ++++++++++++++++++++++++++--- src/cmd/link/link_test.go | 25 ++++++++++++---- 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/src/cmd/internal/obj/loong64/asm.go b/src/cmd/internal/obj/loong64/asm.go index 32b13d91c0..e23165a874 100644 --- a/src/cmd/internal/obj/loong64/asm.go +++ b/src/cmd/internal/obj/loong64/asm.go @@ -345,6 +345,7 @@ var optab = []Optab{ {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.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_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}, @@ -359,6 +360,15 @@ var optab = []Optab{ {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 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) m = int(o.size) 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) } - continue } pc += int64(m) @@ -443,10 +463,16 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { m = int(o.size) 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) } - continue } 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) { 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[:]) for i = 0; i < int32(o.size/4); i++ { c.ctxt.Arch.ByteOrder.PutUint32(bp, out[i]) @@ -1062,6 +1098,7 @@ func buildop(ctxt *obj.Link) { obj.ATEXT, obj.AUNDEF, obj.AFUNCDATA, + obj.APCALIGN, obj.APCDATA, obj.ADUFFZERO, obj.ADUFFCOPY: diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go index 72dbca5c63..c37d6e57bc 100644 --- a/src/cmd/link/link_test.go +++ b/src/cmd/link/link_test.go @@ -567,7 +567,8 @@ func main() { } ` -const testFuncAlignAsmSrc = ` +var testFuncAlignAsmSources = map[string]string{ + "arm64": ` #include "textflag.h" TEXT ·alignPc(SB),NOSPLIT, $0-0 @@ -578,13 +579,27 @@ TEXT ·alignPc(SB),NOSPLIT, $0-0 GLOBL ·alignPcFnAddr(SB),RODATA,$8 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 -// with a specific value on arm64. +// with a specific value on arm64 and loong64. func TestFuncAlign(t *testing.T) { - if runtime.GOARCH != "arm64" || runtime.GOOS != "linux" { - t.Skip("skipping on non-linux/arm64 platform") + testFuncAlignAsmSrc := testFuncAlignAsmSources[runtime.GOARCH] + if len(testFuncAlignAsmSrc) == 0 || runtime.GOOS != "linux" { + t.Skip("skipping on non-linux/{arm64,loong64} platform") } testenv.MustHaveGoBuild(t) -- 2.48.1