Skip to content

Commit

Permalink
cmd/internal/obj/arm64: fix VMOVQ instruction encoding error
Browse files Browse the repository at this point in the history
The VMOVQ instruction moves a 128-bit constant into a V register, as 128-bit
constant can't be loaded into a register directly, we split it into two 64-bit
constants and load it from constant pool. Currently we add the 128-bit constant
to literal pool by calling the 'addpool' function twice, this is not the right
way because it doesn't guarantee the two DWORD instructions are consecutive,
and the second call of addpool will overwrite the p.Pool field,resulting in a
wrong PC-relative offset value of the Prog.

This CL renames the flag LFROM3 to LFROM128, and adds a new function addpool128
to add a 128-bit constant to the literal pool.

Change-Id: I616f043c99a9a18a663f8768842cc980de2e6f79
Reviewed-on: https://go-review.googlesource.com/c/go/+/282334
Reviewed-by: eric fang <[email protected]>
Reviewed-by: Cherry Zhang <[email protected]>
Run-TryBot: eric fang <[email protected]>
Trust: eric fang <[email protected]>
  • Loading branch information
eric fang committed Jan 23, 2021
1 parent 66ee8b1 commit cd99385
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 8 deletions.
38 changes: 34 additions & 4 deletions src/cmd/internal/obj/arm64/asm7.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ func MOVCONST(d int64, s int, rt int) uint32 {
const (
// Optab.flag
LFROM = 1 << 0 // p.From uses constant pool
LFROM3 = 1 << 1 // p.From3 uses constant pool
LFROM128 = 1 << 1 // p.From3<<64+p.From forms a 128-bit constant in literal pool
LTO = 1 << 2 // p.To uses constant pool
NOTUSETMP = 1 << 3 // p expands to multiple instructions, but does NOT use REGTMP
)
Expand Down Expand Up @@ -419,7 +419,7 @@ var optab = []Optab{
{AMOVD, C_LACON, C_NONE, C_NONE, C_RSP, 34, 8, REGSP, LFROM, 0},

// Move a large constant to a vector register.
{AVMOVQ, C_VCON, C_NONE, C_VCON, C_VREG, 101, 4, 0, LFROM | LFROM3, 0},
{AVMOVQ, C_VCON, C_NONE, C_VCON, C_VREG, 101, 4, 0, LFROM128, 0},
{AVMOVD, C_VCON, C_NONE, C_NONE, C_VREG, 101, 4, 0, LFROM, 0},
{AVMOVS, C_LCON, C_NONE, C_NONE, C_VREG, 101, 4, 0, LFROM, 0},

Expand Down Expand Up @@ -995,8 +995,8 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
if o.flag&LFROM != 0 {
c.addpool(p, &p.From)
}
if o.flag&LFROM3 != 0 {
c.addpool(p, p.GetFrom3())
if o.flag&LFROM128 != 0 {
c.addpool128(p, &p.From, p.GetFrom3())
}
if o.flag&LTO != 0 {
c.addpool(p, &p.To)
Expand Down Expand Up @@ -1201,6 +1201,36 @@ func (c *ctxt7) flushpool(p *obj.Prog, skip int) {
}
}

// addpool128 adds a 128-bit constant to literal pool by two consecutive DWORD
// instructions, the 128-bit constant is formed by ah.Offset<<64+al.Offset.
func (c *ctxt7) addpool128(p *obj.Prog, al, ah *obj.Addr) {
lit := al.Offset
q := c.newprog()
q.As = ADWORD
q.To.Type = obj.TYPE_CONST
q.To.Offset = lit
q.Pc = int64(c.pool.size)

lit = ah.Offset
t := c.newprog()
t.As = ADWORD
t.To.Type = obj.TYPE_CONST
t.To.Offset = lit
t.Pc = int64(c.pool.size + 8)
q.Link = t

if c.blitrl == nil {
c.blitrl = q
c.pool.start = uint32(p.Pc)
} else {
c.elitrl.Link = q
}

c.elitrl = t
c.pool.size += 16
p.Pool = q
}

/*
* MOVD foo(SB), R is actually
* MOVD addr, REGTMP
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func TestLarge(t *testing.T) {

// assemble generated file
cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-S", "-o", filepath.Join(dir, "test.o"), tmpfile)
cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
cmd.Env = append(os.Environ(), "GOOS=linux")
out, err := cmd.CombinedOutput()
if err != nil {
t.Errorf("Assemble failed: %v, output: %s", err, out)
Expand All @@ -62,7 +62,7 @@ func TestLarge(t *testing.T) {

// build generated file
cmd = exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
cmd.Env = append(os.Environ(), "GOOS=linux")
out, err = cmd.CombinedOutput()
if err != nil {
t.Errorf("Build failed: %v, output: %s", err, out)
Expand Down Expand Up @@ -96,7 +96,7 @@ func TestNoRet(t *testing.T) {
t.Fatal(err)
}
cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
cmd.Env = append(os.Environ(), "GOOS=linux")
if out, err := cmd.CombinedOutput(); err != nil {
t.Errorf("%v\n%s", err, out)
}
Expand Down Expand Up @@ -134,7 +134,7 @@ func TestPCALIGN(t *testing.T) {
t.Fatal(err)
}
cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-S", "-o", tmpout, tmpfile)
cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
cmd.Env = append(os.Environ(), "GOOS=linux")
out, err := cmd.CombinedOutput()
if err != nil {
t.Errorf("The %s build failed: %v, output: %s", test.name, err, out)
Expand All @@ -150,3 +150,13 @@ func TestPCALIGN(t *testing.T) {
}
}
}

func testvmovq() (r1, r2 uint64)

// TestVMOVQ checks if the arm64 VMOVQ instruction is working properly.
func TestVMOVQ(t *testing.T) {
a, b := testvmovq()
if a != 0x7040201008040201 || b != 0x3040201008040201 {
t.Errorf("TestVMOVQ got: a=0x%x, b=0x%x, want: a=0x7040201008040201, b=0x3040201008040201", a, b)
}
}
14 changes: 14 additions & 0 deletions src/cmd/internal/obj/arm64/asm_arm64_test.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include "textflag.h"

// testvmovq() (r1, r2 uint64)
TEXT ·testvmovq(SB), NOSPLIT, $0-16
VMOVQ $0x7040201008040201, $0x3040201008040201, V1
VMOV V1.D[0], R0
VMOV V1.D[1], R1
MOVD R0, r1+0(FP)
MOVD R1, r2+8(FP)
RET

0 comments on commit cd99385

Please sign in to comment.