[go] cmd/internal/obj/riscv: add displacements to vector loads and stores

3 views
Skip to first unread message

Julian Zhu (Gerrit)

unread,
Oct 16, 2026, 5:30:04 AM (6 days ago) Oct 16
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Julian Zhu has uploaded the change for review

Commit message

cmd/internal/obj/riscv: add displacements to vector loads and stores

RVV loads and stores cannot encode byte displacements. Materialize the
complete address in REG_TMP, preserving stride, index, and mask operands.
Reject symbol addresses that require relocation.

Use two ADDIs for offsets that fit two signed 12-bit immediates. Cover
address boundaries, stack offsets, and invalid addresses in assembler tests.

Updates #81892
Change-Id: I0fcda1141a48f79e1d8abb20c159864eeaec5c3a

Change diff

diff --git a/src/cmd/asm/internal/asm/testdata/riscv64.s b/src/cmd/asm/internal/asm/testdata/riscv64.s
index 5269322..a2df128 100644
--- a/src/cmd/asm/internal/asm/testdata/riscv64.s
+++ b/src/cmd/asm/internal/asm/testdata/riscv64.s
@@ -1252,6 +1252,66 @@
VS4RV V4, (X11) // 27828562
VS8RV V8, (X11) // 278485e2

+ // The unit-stride forms address memory with a bare base register too.
+ VLE8V 8(X10), V3 // 930f850087810f02
+ VSE64V V3, 16(X11) // 938f0501a7f10f02
+ VLE64V 2048(X10), V3 // 930ff57f938f1f0087f10f02
+
+ // The whole-register forms address memory with a bare base register, so a
+ // displacement is added into a temporary first.
+ VL1RE8V 8(X10), V3 // 930f850087818f02
+ VL1RE8V 2047(X10), V3 // 930ff57f87818f02
+ VL1RE8V 2048(X10), V3 // 930ff57f938f1f0087818f02
+ VL1RE8V -2048(X10), V3 // 930f058087818f02
+ VL1RE8V -2049(X10), V3 // 930f0580938fffff87818f02
+ VL1RE8V 4096(X10), V3 // b71f0000b38faf0087818f02
+ VS1RV V3, 8(X11) // 938f8500a7818f02
+ VS1RV V3, 2048(X11) // 938ff57f938f1f00a7818f02
+
+ // Boundaries of the two-ADDI address expansion.
+ VL1RE8V 4094(X10), V3 // 930ff57f938fff7f87818f02
+ VL1RE8V 4095(X10), V3 // b71f00009b8fffffb38faf0087818f02
+ VS1RV V3, -4096(X10) // 930f0580938f0f80a7818f02
+ VS1RV V3, -4097(X10) // b7ffffff9b8fffffb38faf00a7818f02
+
+ // Strided and indexed forms add the displacement without changing the
+ // stride, index, segment count, or mask.
+ VLSE8V 8(X10), X11, V8 // 930f85000784bf0a
+ VLSE64V -2049(X10), X11, V0, V8 // 930f0580938fffff07f4bf08
+ VSSE8V V8, X11, 16(X12) // 930f06012784bf0a
+ VSSE64V V8, X11, V0, 2048(X12) // 930ff67f938f1f0027f4bf08
+ VLUXEI8V 64(X10), V2, V8 // 930f050407842f06
+ VLUXEI64V 2047(X10), V2, V0, V8 // 930ff57f07f42f04
+ VSUXEI8V V8, V2, -64(X12) // 930f06fc27842f06
+ VSUXEI64V V8, V2, V0, -2048(X12) // 930f068027f42f04
+ VLOXEI8V 4096(X10), V2, V8 // b71f0000b38faf0007842f0e
+ VLOXEI64V 2147483647(X10), V2, V0, V8 // b70f00809b8fffffb38faf0007f42f0c
+ VSOXEI8V V8, V2, -4096(X12) // 930f0680938f0f8027842f0e
+ VSOXEI64V V8, V2, V0, -2147483648(X12) // b70f0080b38fcf0027f42f0c
+ VLSSEG2E8V 8(X10), X11, V8 // 930f85000784bf2a
+ VLSSEG8E64V 2048(X10), X11, V0, V8 // 930ff57f938f1f0007f4bfe8
+ VSSSEG2E8V V8, X11, -8(X12) // 930f86ff2784bf2a
+ VSSSEG8E64V V8, X11, V0, -2049(X12) // 930f0680938fffff27f4bfe8
+ VLUXSEG2EI8V 16(X10), V2, V8 // 930f050107842f26
+ VLUXSEG8EI64V 2048(X10), V2, V0, V8 // 930ff57f938f1f0007f42fe4
+ VSUXSEG2EI8V V8, V2, -16(X12) // 930f06ff27842f26
+ VSUXSEG8EI64V V8, V2, V0, -2049(X12) // 930f0680938fffff27f42fe4
+ VLOXSEG2EI8V 32(X10), V2, V8 // 930f050207842f2e
+ VLOXSEG8EI64V 2048(X10), V2, V0, V8 // 930ff57f938f1f0007f42fec
+ VSOXSEG2EI8V V8, V2, -32(X12) // 930f06fe27842f2e
+ VSOXSEG8EI64V V8, V2, V0, -2049(X12) // 930f0680938fffff27f42fec
+
+ // The saved return address adds 8 bytes to AUTO offsets and 16 to PARAM
+ // offsets. Check the adjusted 12-bit boundary and zero displacement.
+ VLE8V 40(X2), V8 // VLE8V 40(SP), V8 // 930f810207840f02
+ VSE8V V8, 40(X2) // VSE8V V8, 40(SP) // 930f810227840f02
+ VL1RE8V slot+2039(SP), V8 // 930ff17f07848f02
+ VS1RV V8, slot+2040(SP) // 930ff17f938f1f0027848f02
+ VLSE8V arg+2032(FP), X11, V8 // 930ff17f938f1f000784bf0a
+ VSSE8V V8, X11, arg+2040(FP) // 930ff17f938f9f002784bf0a
+ VLUXEI8V slot-8(SP), V2, V8 // 07042106
+ VSUXEI8V V8, V2, slot-8(SP) // 27042106
+
// 31.11.1: Vector Single-Width Integer Add and Subtract
VADDVV V1, V2, V3 // d7812002
VADDVV V1, V2, V0, V3 // d7812000
diff --git a/src/cmd/asm/internal/asm/testdata/riscv64error.s b/src/cmd/asm/internal/asm/testdata/riscv64error.s
index 91f95fb..4bdb3ee 100644
--- a/src/cmd/asm/internal/asm/testdata/riscv64error.s
+++ b/src/cmd/asm/internal/asm/testdata/riscv64error.s
@@ -136,6 +136,24 @@
VNOTV V2, V0, V0 // ERROR "masked instruction cannot write its destination to V0, which holds the mask"
VFNEGV V2, V0, V0 // ERROR "masked instruction cannot write its destination to V0, which holds the mask"
VS1RV V3, V0, (X11) // ERROR "too many operands for instruction"
+ VL1RE8V errors(SB), V3 // ERROR "unsupported address for vector load or store"
+ VS1RV V3, errors(SB) // ERROR "unsupported address for vector load or store"
+ VLSE8V errors(SB), X11, V8 // ERROR "unsupported address for vector load or store"
+ VSSE8V V8, X11, errors(SB) // ERROR "unsupported address for vector load or store"
+ VLUXEI8V errors(SB), V2, V8 // ERROR "unsupported address for vector load or store"
+ VSUXEI8V V8, V2, errors(SB) // ERROR "unsupported address for vector load or store"
+ VLOXEI8V errors(SB), V2, V8 // ERROR "unsupported address for vector load or store"
+ VSOXEI8V V8, V2, errors(SB) // ERROR "unsupported address for vector load or store"
+ VLSSEG2E8V errors(SB), X11, V8 // ERROR "unsupported address for vector load or store"
+ VSSSEG2E8V V8, X11, errors(SB) // ERROR "unsupported address for vector load or store"
+ VLUXSEG2EI8V errors(SB), V2, V8 // ERROR "unsupported address for vector load or store"
+ VSUXSEG2EI8V V8, V2, errors(SB) // ERROR "unsupported address for vector load or store"
+ VLOXSEG2EI8V errors(SB), V2, V8 // ERROR "unsupported address for vector load or store"
+ VSOXSEG2EI8V V8, V2, errors(SB) // ERROR "unsupported address for vector load or store"
+ VLSE8V 2147483648(X10), X11, V8 // ERROR "constant 2147483648 too large"
+ VSSE8V V8, X11, -2147483649(X10) // ERROR "constant -2147483649 too large"
+ VLUXEI8V 2147483648(X10), V2, V8 // ERROR "constant 2147483648 too large"
+ VSOXEI8V V8, V2, -2147483649(X10) // ERROR "constant -2147483649 too large"
VADDVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
VADDVX X10, V2, V1, V3 // ERROR "invalid vector mask register"
VADDVI $15, V4, V1, V2 // ERROR "invalid vector mask register"
diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go
index 7b9e681..8184138 100644
--- a/src/cmd/internal/obj/riscv/obj.go
+++ b/src/cmd/internal/obj/riscv/obj.go
@@ -3936,6 +3936,57 @@
return []*instruction{insLUI, insADDIW, ins}
}

+// instructionsForVectorMem returns the machine instructions for a vector load
+// or store, adding any displacement in a to the base register stored in *reg.
+func instructionsForVectorMem(p *obj.Prog, ins *instruction, a *obj.Addr, reg *uint32) []*instruction {
+ rs := uint32(addrToReg(*a))
+ ins.imm, *reg = 0, rs
+
+ switch a.Name {
+ case obj.NAME_NONE, obj.NAME_AUTO, obj.NAME_PARAM:
+ default:
+ p.Ctxt.Diag("%v: unsupported address for vector load or store", p)
+ return []*instruction{ins}
+ }
+
+ if a.Offset == 0 {
+ return []*instruction{ins}
+ }
+
+ if off, base, ok := splitTwo12BitImmediate(a.Offset); ok {
+ *reg = REG_TMP
+ if base == 0 {
+ // ADDI $off, REG, TMP
+ insADDI := &instruction{as: AADDI, rd: REG_TMP, rs1: rs, imm: off}
+ return []*instruction{insADDI, ins}
+ }
+
+ // ADDI $base, REG, TMP
+ // ADDI $off, TMP, TMP
+ insADDI := &instruction{as: AADDI, rd: REG_TMP, rs1: rs, imm: base}
+ insADDI2 := &instruction{as: AADDI, rd: REG_TMP, rs1: REG_TMP, imm: off}
+ return []*instruction{insADDI, insADDI2, ins}
+ }
+
+ low, high, err := Split32BitImmediate(a.Offset)
+ if err != nil {
+ p.Ctxt.Diag("%v: constant %d too large", p, a.Offset)
+ return []*instruction{ins}
+ }
+
+ // LUI $high, TMP
+ // ADDIW $low, TMP, TMP
+ // ADD TMP, REG, TMP
+ *reg = REG_TMP
+ insLUI := &instruction{as: ALUI, rd: REG_TMP, imm: high}
+ insADDIW := &instruction{as: AADDIW, rd: REG_TMP, rs1: REG_TMP, imm: low}
+ insADD := &instruction{as: AADD, rd: REG_TMP, rs1: REG_TMP, rs2: rs}
+ if low == 0 {
+ return []*instruction{insLUI, insADD, ins}
+ }
+ return []*instruction{insLUI, insADDIW, insADD, ins}
+}
+
// instructionsForLoad returns the machine instructions for a load. The load
// instruction is specified by as and the base/source register is specified
// by rs, instead of the obj.Prog.
@@ -4878,6 +4929,11 @@
checkMaskedVd(p)
}
ins.rd, ins.rs1, ins.rs2 = uint32(p.To.Reg), uint32(p.From.Reg), obj.REG_NONE
+ if p.From.Type == obj.TYPE_MEM {
+ inss = instructionsForVectorMem(p, ins, &p.From, &ins.rs1)
+ } else {
+ inss = instructionsForVectorMem(p, ins, &p.To, &ins.rd)
+ }

case AVLSE8V, AVLSE16V, AVLSE32V, AVLSE64V,
AVLUXEI8V, AVLUXEI16V, AVLUXEI32V, AVLUXEI64V, AVLOXEI8V, AVLOXEI16V, AVLOXEI32V, AVLOXEI64V,
@@ -4903,6 +4959,7 @@
checkMaskedVd(p)
}
ins.rs1, ins.rs2, ins.rs3 = ins.rs2, ins.rs1, obj.REG_NONE
+ inss = instructionsForVectorMem(p, ins, &p.From, &ins.rs1)

case AVSSE8V, AVSSE16V, AVSSE32V, AVSSE64V,
AVSUXEI8V, AVSUXEI16V, AVSUXEI32V, AVSUXEI64V, AVSOXEI8V, AVSOXEI16V, AVSOXEI32V, AVSOXEI64V,
@@ -4926,6 +4983,7 @@
p.Ctxt.Diag("%v: invalid vector mask register", p)
}
ins.rd, ins.rs1, ins.rs2, ins.rs3 = ins.rs2, ins.rd, ins.rs1, obj.REG_NONE
+ inss = instructionsForVectorMem(p, ins, &p.To, &ins.rs1)

case AVL1RV, AVL1RE8V, AVL1RE16V, AVL1RE32V, AVL1RE64V, AVL2RV, AVL2RE8V, AVL2RE16V, AVL2RE32V, AVL2RE64V,
AVL4RV, AVL4RE8V, AVL4RE16V, AVL4RE32V, AVL4RE64V, AVL8RV, AVL8RE8V, AVL8RE16V, AVL8RE32V, AVL8RE64V:
@@ -4943,12 +5001,14 @@
p.Ctxt.Diag("%v: too many operands for instruction", p)
}
ins.rd, ins.rs1, ins.rs2 = uint32(p.To.Reg), uint32(p.From.Reg), obj.REG_NONE
+ inss = instructionsForVectorMem(p, ins, &p.From, &ins.rs1)

case AVS1RV, AVS2RV, AVS4RV, AVS8RV:
if ins.rs1 != obj.REG_NONE {
p.Ctxt.Diag("%v: too many operands for instruction", p)
}
ins.rd, ins.rs1, ins.rs2 = uint32(p.To.Reg), uint32(p.From.Reg), obj.REG_NONE
+ inss = instructionsForVectorMem(p, ins, &p.To, &ins.rd)

case AVADDVV, AVADDVX, AVSUBVV, AVSUBVX, AVRSUBVX, AVWADDUVV, AVWADDUVX, AVWSUBUVV, AVWSUBUVX,
AVWADDVV, AVWADDVX, AVWSUBVV, AVWSUBVX, AVWADDUWV, AVWADDUWX, AVWSUBUWV, AVWSUBUWX,

Change information

Files:
  • M src/cmd/asm/internal/asm/testdata/riscv64.s
  • M src/cmd/asm/internal/asm/testdata/riscv64error.s
  • M src/cmd/internal/obj/riscv/obj.go
Change size: M
Delta: 3 files changed, 138 insertions(+), 0 deletions(-)
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I0fcda1141a48f79e1d8abb20c159864eeaec5c3a
Gerrit-Change-Number: 849966
Gerrit-PatchSet: 1
Gerrit-Owner: Julian Zhu <jz53...@gmail.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Julian Zhu (Gerrit)

unread,
Oct 16, 2026, 5:31:53 AM (6 days ago) Oct 16
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Julian Zhu voted Commit-Queue+1

Commit-Queue+1
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I0fcda1141a48f79e1d8abb20c159864eeaec5c3a
Gerrit-Change-Number: 849966
Gerrit-PatchSet: 1
Gerrit-Owner: Julian Zhu <jz53...@gmail.com>
Gerrit-Reviewer: Julian Zhu <jz53...@gmail.com>
Gerrit-Comment-Date: Fri, 16 Oct 2026 09:31:40 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
unsatisfied_requirement
satisfied_requirement
open
diffy

Joel Sing (Gerrit)

unread,
Oct 18, 2026, 10:25:31 AM (4 days ago) Oct 18
to Julian Zhu, goph...@pubsubhelper.golang.org, golang...@luci-project-accounts.iam.gserviceaccount.com, Meng Zhuo, golang-co...@googlegroups.com
Attention needed from Julian Zhu and Meng Zhuo

Joel Sing added 1 comment

Patchset-level comments
File-level comment, Patchset 1 (Latest):
Joel Sing . unresolved

I'm not convinced that this is the right move - unlike scalar/floating point loads and stores, every single non-zero displacement requires the use of REG_TMP. This means that consecutive loads are going to use the same register, plus we create more unsafe points.

Is a good justification for supporting this (instead of having the assembly/compiler allocate a register with the correct address)?

Open in Gerrit

Related details

Attention is currently required from:
  • Julian Zhu
  • Meng Zhuo
Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I0fcda1141a48f79e1d8abb20c159864eeaec5c3a
    Gerrit-Change-Number: 849966
    Gerrit-PatchSet: 1
    Gerrit-Owner: Julian Zhu <jz53...@gmail.com>
    Gerrit-Reviewer: Joel Sing <jo...@sing.id.au>
    Gerrit-Reviewer: Julian Zhu <jz53...@gmail.com>
    Gerrit-Reviewer: Meng Zhuo <mengzh...@gmail.com>
    Gerrit-Attention: Julian Zhu <jz53...@gmail.com>
    Gerrit-Attention: Meng Zhuo <mengzh...@gmail.com>
    Gerrit-Comment-Date: Sun, 18 Oct 2026 14:25:02 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    satisfied_requirement
    open
    diffy

    Julian Zhu (Gerrit)

    unread,
    Oct 18, 2026, 11:26:07 AM (4 days ago) Oct 18
    to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
    Attention needed from Julian Zhu and Meng Zhuo

    Julian Zhu uploaded new patchset

    Julian Zhu <jz53...@gmail.com> uploaded patch set #2 to this change.
    Following approvals got outdated and were removed:
    Open in Gerrit

    Related details

    Attention is currently required from:
    • Julian Zhu
    • Meng Zhuo
    Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I0fcda1141a48f79e1d8abb20c159864eeaec5c3a
      Gerrit-Change-Number: 849966
      Gerrit-PatchSet: 2
      unsatisfied_requirement
      open
      diffy

      Julian Zhu (Gerrit)

      unread,
      Oct 18, 2026, 11:26:23 AM (4 days ago) Oct 18
      to goph...@pubsubhelper.golang.org, golang...@luci-project-accounts.iam.gserviceaccount.com, Joel Sing, Meng Zhuo, golang-co...@googlegroups.com
      Attention needed from Joel Sing and Meng Zhuo

      Julian Zhu voted and added 1 comment

      Votes added by Julian Zhu <jz53...@gmail.com>

      Commit-Queue+1

      1 comment

      Patchset-level comments

      I'm not convinced that this is the right move - unlike scalar/floating point loads and stores, every single non-zero displacement requires the use of REG_TMP. This means that consecutive loads are going to use the same register, plus we create more unsafe points.

      Is a good justification for supporting this (instead of having the assembly/compiler allocate a register with the correct address)?

      Julian Zhu

      Done

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Joel Sing
      • Meng Zhuo
      Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement is not satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: go
        Gerrit-Branch: master
        Gerrit-Change-Id: I0fcda1141a48f79e1d8abb20c159864eeaec5c3a
        Gerrit-Change-Number: 849966
        Gerrit-PatchSet: 2
        Gerrit-Owner: Julian Zhu <jz53...@gmail.com>
        Gerrit-Reviewer: Joel Sing <jo...@sing.id.au>
        Gerrit-Reviewer: Julian Zhu <jz53...@gmail.com>
        Gerrit-Reviewer: Meng Zhuo <mengzh...@gmail.com>
        Gerrit-Attention: Joel Sing <jo...@sing.id.au>
        Gerrit-Attention: Meng Zhuo <mengzh...@gmail.com>
        Gerrit-Comment-Date: Sun, 18 Oct 2026 15:26:12 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: Yes
        Comment-In-Reply-To: Joel Sing <jo...@sing.id.au>
        unsatisfied_requirement
        satisfied_requirement
        open
        diffy
        Reply all
        Reply to author
        Forward
        0 new messages