aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/internal
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/internal')
-rw-r--r--src/cmd/internal/archive/archive.go31
-rw-r--r--src/cmd/internal/browser/browser.go2
-rw-r--r--src/cmd/internal/diff/diff.go22
-rw-r--r--src/cmd/internal/dwarf/dwarf.go2
-rw-r--r--src/cmd/internal/goobj/funcinfo.go60
-rw-r--r--src/cmd/internal/goobj/mkbuiltin.go4
-rw-r--r--src/cmd/internal/goobj/objfile.go11
-rw-r--r--src/cmd/internal/moddeps/moddeps_test.go400
-rw-r--r--src/cmd/internal/obj/arm/obj5.go16
-rw-r--r--src/cmd/internal/obj/arm64/a.out.go2
-rw-r--r--src/cmd/internal/obj/arm64/asm7.go38
-rw-r--r--src/cmd/internal/obj/arm64/asm_arm64_test.go (renamed from src/cmd/internal/obj/arm64/asm_test.go)18
-rw-r--r--src/cmd/internal/obj/arm64/asm_arm64_test.s14
-rw-r--r--src/cmd/internal/obj/arm64/obj7.go285
-rw-r--r--src/cmd/internal/obj/link.go137
-rw-r--r--src/cmd/internal/obj/mips/obj0.go16
-rw-r--r--src/cmd/internal/obj/objfile.go12
-rw-r--r--src/cmd/internal/obj/plist.go16
-rw-r--r--src/cmd/internal/obj/ppc64/obj9.go16
-rw-r--r--src/cmd/internal/obj/riscv/obj.go22
-rw-r--r--src/cmd/internal/obj/s390x/condition_code.go2
-rw-r--r--src/cmd/internal/obj/s390x/objz.go16
-rw-r--r--src/cmd/internal/obj/s390x/rotate.go16
-rw-r--r--src/cmd/internal/obj/s390x/rotate_test.go2
-rw-r--r--src/cmd/internal/obj/textflag.go5
-rw-r--r--src/cmd/internal/obj/util.go2
-rw-r--r--src/cmd/internal/obj/x86/a.out.go1
-rw-r--r--src/cmd/internal/obj/x86/obj6.go58
-rw-r--r--src/cmd/internal/objabi/funcid.go124
-rw-r--r--src/cmd/internal/objabi/path.go2
-rw-r--r--src/cmd/internal/objabi/util.go2
-rw-r--r--src/cmd/internal/objfile/pe.go2
-rw-r--r--src/cmd/internal/objfile/xcoff.go4
-rw-r--r--src/cmd/internal/pkgpath/pkgpath.go2
34 files changed, 942 insertions, 420 deletions
diff --git a/src/cmd/internal/archive/archive.go b/src/cmd/internal/archive/archive.go
index c1661d7711..e9b25fe240 100644
--- a/src/cmd/internal/archive/archive.go
+++ b/src/cmd/internal/archive/archive.go
@@ -118,9 +118,9 @@ type objReader struct {
func (r *objReader) init(f *os.File) {
r.a = &Archive{f, nil}
- r.offset, _ = f.Seek(0, io.SeekCurrent)
- r.limit, _ = f.Seek(0, io.SeekEnd)
- f.Seek(r.offset, io.SeekStart)
+ r.offset, _ = f.Seek(0, os.SEEK_CUR)
+ r.limit, _ = f.Seek(0, os.SEEK_END)
+ f.Seek(r.offset, os.SEEK_SET)
r.b = bio.NewReader(f)
}
@@ -221,7 +221,7 @@ func (r *objReader) skip(n int64) {
r.readFull(r.tmp[:n])
} else {
// Seek, giving up buffered data.
- r.b.MustSeek(r.offset+n, io.SeekStart)
+ r.b.MustSeek(r.offset+n, os.SEEK_SET)
r.offset += n
}
}
@@ -426,7 +426,7 @@ func (r *objReader) parseObject(o *GoObj, size int64) error {
// AddEntry adds an entry to the end of a, with the content from r.
func (a *Archive) AddEntry(typ EntryType, name string, mtime int64, uid, gid int, mode os.FileMode, size int64, r io.Reader) {
- off, err := a.f.Seek(0, io.SeekEnd)
+ off, err := a.f.Seek(0, os.SEEK_END)
if err != nil {
log.Fatal(err)
}
@@ -464,3 +464,24 @@ func exactly16Bytes(s string) string {
s += sixteenSpaces[:16-len(s)]
return s
}
+
+// architecture-independent object file output
+const HeaderSize = 60
+
+func ReadHeader(b *bufio.Reader, name string) int {
+ var buf [HeaderSize]byte
+ if _, err := io.ReadFull(b, buf[:]); err != nil {
+ return -1
+ }
+ aname := strings.Trim(string(buf[0:16]), " ")
+ if !strings.HasPrefix(aname, name) {
+ return -1
+ }
+ asize := strings.Trim(string(buf[48:58]), " ")
+ i, _ := strconv.Atoi(asize)
+ return i
+}
+
+func FormatHeader(arhdr []byte, name string, size int64) {
+ copy(arhdr[:], fmt.Sprintf("%-16s%-12d%-6d%-6d%-8o%-10d`\n", name, 0, 0, 0, 0644, size))
+}
diff --git a/src/cmd/internal/browser/browser.go b/src/cmd/internal/browser/browser.go
index 6867c85d23..577d31789f 100644
--- a/src/cmd/internal/browser/browser.go
+++ b/src/cmd/internal/browser/browser.go
@@ -6,8 +6,8 @@
package browser
import (
+ exec "internal/execabs"
"os"
- "os/exec"
"runtime"
"time"
)
diff --git a/src/cmd/internal/diff/diff.go b/src/cmd/internal/diff/diff.go
index e9d2c23780..0ec2d7f8f9 100644
--- a/src/cmd/internal/diff/diff.go
+++ b/src/cmd/internal/diff/diff.go
@@ -7,9 +7,10 @@
package diff
import (
+ "bytes"
+ exec "internal/execabs"
"io/ioutil"
"os"
- "os/exec"
"runtime"
)
@@ -38,6 +39,25 @@ func Diff(prefix string, b1, b2 []byte) ([]byte, error) {
// Ignore that failure as long as we get output.
err = nil
}
+
+ // If we are on Windows and the diff is Cygwin diff,
+ // machines can get into a state where every Cygwin
+ // command works fine but prints a useless message like:
+ //
+ // Cygwin WARNING:
+ // Couldn't compute FAST_CWD pointer. This typically occurs if you're using
+ // an older Cygwin version on a newer Windows. Please update to the latest
+ // available Cygwin version from https://cygwin.com/. If the problem persists,
+ // please see https://cygwin.com/problems.html
+ //
+ // Skip over that message and just return the actual diff.
+ if len(data) > 0 && !bytes.HasPrefix(data, []byte("--- ")) {
+ i := bytes.Index(data, []byte("\n--- "))
+ if i >= 0 && i < 80*10 && bytes.Contains(data[:i], []byte("://cygwin.com/")) {
+ data = data[i+1:]
+ }
+ }
+
return data, err
}
diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go
index e1a70ef853..8de4096f06 100644
--- a/src/cmd/internal/dwarf/dwarf.go
+++ b/src/cmd/internal/dwarf/dwarf.go
@@ -12,7 +12,7 @@ import (
"cmd/internal/objabi"
"errors"
"fmt"
- "os/exec"
+ exec "internal/execabs"
"sort"
"strconv"
"strings"
diff --git a/src/cmd/internal/goobj/funcinfo.go b/src/cmd/internal/goobj/funcinfo.go
index 2cca8f6c4e..6d33a10a51 100644
--- a/src/cmd/internal/goobj/funcinfo.go
+++ b/src/cmd/internal/goobj/funcinfo.go
@@ -19,9 +19,10 @@ type CUFileIndex uint32
//
// TODO: make each pcdata a separate symbol?
type FuncInfo struct {
- Args uint32
- Locals uint32
- FuncID objabi.FuncID
+ Args uint32
+ Locals uint32
+ FuncID objabi.FuncID
+ FuncFlag objabi.FuncFlag
Pcsp SymRef
Pcfile SymRef
@@ -35,6 +36,9 @@ type FuncInfo struct {
}
func (a *FuncInfo) Write(w *bytes.Buffer) {
+ writeUint8 := func(x uint8) {
+ w.WriteByte(x)
+ }
var b [4]byte
writeUint32 := func(x uint32) {
binary.LittleEndian.PutUint32(b[:], x)
@@ -47,8 +51,10 @@ func (a *FuncInfo) Write(w *bytes.Buffer) {
writeUint32(a.Args)
writeUint32(a.Locals)
- writeUint32(uint32(a.FuncID))
-
+ writeUint8(uint8(a.FuncID))
+ writeUint8(uint8(a.FuncFlag))
+ writeUint8(0) // pad to uint32 boundary
+ writeUint8(0)
writeSymRef(a.Pcsp)
writeSymRef(a.Pcfile)
writeSymRef(a.Pcline)
@@ -72,46 +78,6 @@ func (a *FuncInfo) Write(w *bytes.Buffer) {
}
}
-func (a *FuncInfo) Read(b []byte) {
- readUint32 := func() uint32 {
- x := binary.LittleEndian.Uint32(b)
- b = b[4:]
- return x
- }
- readSymIdx := func() SymRef {
- return SymRef{readUint32(), readUint32()}
- }
-
- a.Args = readUint32()
- a.Locals = readUint32()
- a.FuncID = objabi.FuncID(readUint32())
-
- a.Pcsp = readSymIdx()
- a.Pcfile = readSymIdx()
- a.Pcline = readSymIdx()
- a.Pcinline = readSymIdx()
- a.Pcdata = make([]SymRef, readUint32())
- for i := range a.Pcdata {
- a.Pcdata[i] = readSymIdx()
- }
-
- funcdataofflen := readUint32()
- a.Funcdataoff = make([]uint32, funcdataofflen)
- for i := range a.Funcdataoff {
- a.Funcdataoff[i] = readUint32()
- }
- filelen := readUint32()
- a.File = make([]CUFileIndex, filelen)
- for i := range a.File {
- a.File[i] = CUFileIndex(readUint32())
- }
- inltreelen := readUint32()
- a.InlTree = make([]InlTreeNode, inltreelen)
- for i := range a.InlTree {
- b = a.InlTree[i].Read(b)
- }
-}
-
// FuncInfoLengths is a cache containing a roadmap of offsets and
// lengths for things within a serialized FuncInfo. Each length field
// stores the number of items (e.g. files, inltree nodes, etc), and the
@@ -159,7 +125,9 @@ func (*FuncInfo) ReadArgs(b []byte) uint32 { return binary.LittleEndian.Uint32(b
func (*FuncInfo) ReadLocals(b []byte) uint32 { return binary.LittleEndian.Uint32(b[4:]) }
-func (*FuncInfo) ReadFuncID(b []byte) uint32 { return binary.LittleEndian.Uint32(b[8:]) }
+func (*FuncInfo) ReadFuncID(b []byte) objabi.FuncID { return objabi.FuncID(b[8]) }
+
+func (*FuncInfo) ReadFuncFlag(b []byte) objabi.FuncFlag { return objabi.FuncFlag(b[9]) }
func (*FuncInfo) ReadPcsp(b []byte) SymRef {
return SymRef{binary.LittleEndian.Uint32(b[12:]), binary.LittleEndian.Uint32(b[16:])}
diff --git a/src/cmd/internal/goobj/mkbuiltin.go b/src/cmd/internal/goobj/mkbuiltin.go
index 07c3406681..22608e7e69 100644
--- a/src/cmd/internal/goobj/mkbuiltin.go
+++ b/src/cmd/internal/goobj/mkbuiltin.go
@@ -118,8 +118,8 @@ func mkbuiltin(w io.Writer) {
// addBasicTypes returns the symbol names for basic types that are
// defined in the runtime and referenced in other packages.
-// Needs to be kept in sync with reflect.go:dumpbasictypes() and
-// reflect.go:dtypesym() in the compiler.
+// Needs to be kept in sync with reflect.go:WriteBasicTypes() and
+// reflect.go:writeType() in the compiler.
func enumerateBasicTypes() []extra {
names := [...]string{
"int8", "uint8", "int16", "uint16",
diff --git a/src/cmd/internal/goobj/objfile.go b/src/cmd/internal/goobj/objfile.go
index 6e76bea111..d1b838f676 100644
--- a/src/cmd/internal/goobj/objfile.go
+++ b/src/cmd/internal/goobj/objfile.go
@@ -298,7 +298,6 @@ const (
SymFlagNoSplit
SymFlagReflectMethod
SymFlagGoType
- SymFlagTopFrame
)
// Sym.Flag2
@@ -332,7 +331,6 @@ func (s *Sym) Leaf() bool { return s.Flag()&SymFlagLeaf != 0 }
func (s *Sym) NoSplit() bool { return s.Flag()&SymFlagNoSplit != 0 }
func (s *Sym) ReflectMethod() bool { return s.Flag()&SymFlagReflectMethod != 0 }
func (s *Sym) IsGoType() bool { return s.Flag()&SymFlagGoType != 0 }
-func (s *Sym) TopFrame() bool { return s.Flag()&SymFlagTopFrame != 0 }
func (s *Sym) UsedInIface() bool { return s.Flag2()&SymFlagUsedInIface != 0 }
func (s *Sym) IsItab() bool { return s.Flag2()&SymFlagItab != 0 }
@@ -483,6 +481,11 @@ func (r *RefFlags) SetFlag2(x uint8) { r[9] = x }
func (r *RefFlags) Write(w *Writer) { w.Bytes(r[:]) }
+// Used to construct an artifically large array type when reading an
+// item from the object file relocs section or aux sym section (needs
+// to work on 32-bit as well as 64-bit). See issue 41621.
+const huge = (1<<31 - 1) / RelocSize
+
// Referenced symbol name.
//
// Serialized format:
@@ -792,7 +795,7 @@ func (r *Reader) Reloc(i uint32, j int) *Reloc {
func (r *Reader) Relocs(i uint32) []Reloc {
off := r.RelocOff(i, 0)
n := r.NReloc(i)
- return (*[1 << 20]Reloc)(unsafe.Pointer(&r.b[off]))[:n:n]
+ return (*[huge]Reloc)(unsafe.Pointer(&r.b[off]))[:n:n]
}
// NAux returns the number of aux symbols of the i-th symbol.
@@ -818,7 +821,7 @@ func (r *Reader) Aux(i uint32, j int) *Aux {
func (r *Reader) Auxs(i uint32) []Aux {
off := r.AuxOff(i, 0)
n := r.NAux(i)
- return (*[1 << 20]Aux)(unsafe.Pointer(&r.b[off]))[:n:n]
+ return (*[huge]Aux)(unsafe.Pointer(&r.b[off]))[:n:n]
}
// DataOff returns the offset of the i-th symbol's data.
diff --git a/src/cmd/internal/moddeps/moddeps_test.go b/src/cmd/internal/moddeps/moddeps_test.go
index 9ea21873c5..cba401c896 100644
--- a/src/cmd/internal/moddeps/moddeps_test.go
+++ b/src/cmd/internal/moddeps/moddeps_test.go
@@ -8,6 +8,7 @@ import (
"encoding/json"
"fmt"
"internal/testenv"
+ "io"
"io/fs"
"io/ioutil"
"os"
@@ -21,93 +22,34 @@ import (
"golang.org/x/mod/module"
)
-type gorootModule struct {
- Path string
- Dir string
- hasVendor bool
-}
-
-// findGorootModules returns the list of modules found in the GOROOT source tree.
-func findGorootModules(t *testing.T) []gorootModule {
- t.Helper()
- goBin := testenv.GoToolPath(t)
-
- goroot.once.Do(func() {
- goroot.err = filepath.WalkDir(runtime.GOROOT(), func(path string, info fs.DirEntry, err error) error {
- if err != nil {
- return err
- }
- if info.IsDir() && (info.Name() == "vendor" || info.Name() == "testdata") {
- return filepath.SkipDir
- }
- if path == filepath.Join(runtime.GOROOT(), "pkg") {
- // GOROOT/pkg contains generated artifacts, not source code.
- //
- // In https://golang.org/issue/37929 it was observed to somehow contain
- // a module cache, so it is important to skip. (That helps with the
- // running time of this test anyway.)
- return filepath.SkipDir
- }
- if info.IsDir() || info.Name() != "go.mod" {
- return nil
- }
- dir := filepath.Dir(path)
-
- // Use 'go list' to describe the module contained in this directory (but
- // not its dependencies).
- cmd := exec.Command(goBin, "list", "-json", "-m")
- cmd.Env = append(os.Environ(), "GO111MODULE=on")
- cmd.Dir = dir
- cmd.Stderr = new(strings.Builder)
- out, err := cmd.Output()
- if err != nil {
- return fmt.Errorf("'go list -json -m' in %s: %w\n%s", dir, err, cmd.Stderr)
- }
-
- var m gorootModule
- if err := json.Unmarshal(out, &m); err != nil {
- return fmt.Errorf("decoding 'go list -json -m' in %s: %w", dir, err)
- }
- if m.Path == "" || m.Dir == "" {
- return fmt.Errorf("'go list -json -m' in %s failed to populate Path and/or Dir", dir)
- }
- if _, err := os.Stat(filepath.Join(dir, "vendor")); err == nil {
- m.hasVendor = true
- }
- goroot.modules = append(goroot.modules, m)
- return nil
- })
- })
-
- if goroot.err != nil {
- t.Fatal(goroot.err)
- }
- return goroot.modules
-}
-
-// goroot caches the list of modules found in the GOROOT source tree.
-var goroot struct {
- once sync.Once
- modules []gorootModule
- err error
-}
-
-// TestAllDependenciesVendored ensures that all packages imported within GOROOT
-// are vendored in the corresponding GOROOT module.
+// TestAllDependencies ensures dependencies of all
+// modules in GOROOT are in a consistent state.
//
-// This property allows offline development within the Go project, and ensures
-// that all dependency changes are presented in the usual code review process.
+// In short mode, it does a limited quick check and stops there.
+// In long mode, it also makes a copy of the entire GOROOT tree
+// and requires network access to perform more thorough checks.
+// Keep this distinction in mind when adding new checks.
//
-// This test does NOT ensure that the vendored contents match the unmodified
-// contents of the corresponding dependency versions. Such as test would require
-// network access, and would currently either need to copy the entire GOROOT module
-// or explicitly invoke version control to check for changes.
-// (See golang.org/issue/36852 and golang.org/issue/27348.)
-func TestAllDependenciesVendored(t *testing.T) {
+// See issues 36852, 41409, and 43687.
+// (Also see golang.org/issue/27348.)
+func TestAllDependencies(t *testing.T) {
goBin := testenv.GoToolPath(t)
+ // Ensure that all packages imported within GOROOT
+ // are vendored in the corresponding GOROOT module.
+ //
+ // This property allows offline development within the Go project, and ensures
+ // that all dependency changes are presented in the usual code review process.
+ //
+ // As a quick first-order check, avoid network access and the need to copy the
+ // entire GOROOT tree or explicitly invoke version control to check for changes.
+ // Just check that packages are vendored. (In non-short mode, we go on to also
+ // copy the GOROOT tree and perform more rigorous consistency checks. Jump below
+ // for more details.)
for _, m := range findGorootModules(t) {
- t.Run(m.Path, func(t *testing.T) {
+ // This short test does NOT ensure that the vendored contents match
+ // the unmodified contents of the corresponding dependency versions.
+ t.Run(m.Path+"(quick)", func(t *testing.T) {
if m.hasVendor {
// Load all of the packages in the module to ensure that their
// dependencies are vendored. If any imported package is missing,
@@ -140,6 +82,226 @@ func TestAllDependenciesVendored(t *testing.T) {
}
})
}
+
+ // We now get to the slow, but more thorough part of the test.
+ // Only run it in long test mode.
+ if testing.Short() {
+ return
+ }
+
+ // Ensure that all modules within GOROOT are tidy, vendored, and bundled.
+ // Ensure that the vendored contents match the unmodified contents of the
+ // corresponding dependency versions.
+ //
+ // The non-short section of this test requires network access and the diff
+ // command.
+ //
+ // It makes a temporary copy of the entire GOROOT tree (where it can safely
+ // perform operations that may mutate the tree), executes the same module
+ // maintenance commands that we expect Go developers to run, and then
+ // diffs the potentially modified module copy with the real one in GOROOT.
+ // (We could try to rely on Git to do things differently, but that's not the
+ // path we've chosen at this time. This allows the test to run when the tree
+ // is not checked into Git.)
+
+ testenv.MustHaveExternalNetwork(t)
+ if haveDiff := func() bool {
+ diff, err := exec.Command("diff", "--recursive", "--unified", ".", ".").CombinedOutput()
+ if err != nil || len(diff) != 0 {
+ return false
+ }
+ diff, err = exec.Command("diff", "--recursive", "--unified", ".", "..").CombinedOutput()
+ if err == nil || len(diff) == 0 {
+ return false
+ }
+ return true
+ }(); !haveDiff {
+ // For now, the diff command is a mandatory dependency of this test.
+ // This test will primarily run on longtest builders, since few people
+ // would test the cmd/internal/moddeps package directly, and all.bash
+ // runs tests in short mode. It's fine to skip if diff is unavailable.
+ t.Skip("skipping because a diff command with support for --recursive and --unified flags is unavailable")
+ }
+
+ // Build the bundle binary at the golang.org/x/tools
+ // module version specified in GOROOT/src/cmd/go.mod.
+ bundleDir := t.TempDir()
+ r := runner{Dir: filepath.Join(runtime.GOROOT(), "src/cmd")}
+ r.run(t, goBin, "build", "-mod=readonly", "-o", bundleDir, "golang.org/x/tools/cmd/bundle")
+
+ var gorootCopyDir string
+ for _, m := range findGorootModules(t) {
+ // Create a test-wide GOROOT copy. It can be created once
+ // and reused between subtests whenever they don't fail.
+ //
+ // This is a relatively expensive operation, but it's a pre-requisite to
+ // be able to safely run commands like "go mod tidy", "go mod vendor", and
+ // "go generate" on the GOROOT tree content. Those commands may modify the
+ // tree, and we don't want to happen to the real tree as part of executing
+ // a test.
+ if gorootCopyDir == "" {
+ gorootCopyDir = makeGOROOTCopy(t)
+ }
+
+ t.Run(m.Path+"(thorough)", func(t *testing.T) {
+ defer func() {
+ if t.Failed() {
+ // The test failed, which means it's possible the GOROOT copy
+ // may have been modified. No choice but to reset it for next
+ // module test case. (This is slow, but it happens only during
+ // test failures.)
+ gorootCopyDir = ""
+ }
+ }()
+
+ rel, err := filepath.Rel(runtime.GOROOT(), m.Dir)
+ if err != nil {
+ t.Fatalf("filepath.Rel(%q, %q): %v", runtime.GOROOT(), m.Dir, err)
+ }
+ r := runner{
+ Dir: filepath.Join(gorootCopyDir, rel),
+ Env: append(os.Environ(),
+ // Set GOROOT.
+ "GOROOT="+gorootCopyDir,
+ // Explicitly clear PWD and GOROOT_FINAL so that GOROOT=gorootCopyDir is definitely used.
+ "PWD=",
+ "GOROOT_FINAL=",
+ // Add GOROOTcopy/bin and bundleDir to front of PATH.
+ "PATH="+filepath.Join(gorootCopyDir, "bin")+string(filepath.ListSeparator)+
+ bundleDir+string(filepath.ListSeparator)+os.Getenv("PATH"),
+ ),
+ }
+ goBinCopy := filepath.Join(gorootCopyDir, "bin", "go")
+ r.run(t, goBinCopy, "mod", "tidy") // See issue 43687.
+ r.run(t, goBinCopy, "mod", "verify") // Verify should be a no-op, but test it just in case.
+ r.run(t, goBinCopy, "mod", "vendor") // See issue 36852.
+ pkgs := packagePattern(m.Path)
+ r.run(t, goBinCopy, "generate", `-run=^//go:generate bundle `, pkgs) // See issue 41409.
+ advice := "$ cd " + m.Dir + "\n" +
+ "$ go mod tidy # to remove extraneous dependencies\n" +
+ "$ go mod vendor # to vendor dependecies\n" +
+ "$ go generate -run=bundle " + pkgs + " # to regenerate bundled packages\n"
+ if m.Path == "std" {
+ r.run(t, goBinCopy, "generate", "syscall", "internal/syscall/...") // See issue 43440.
+ advice += "$ go generate syscall internal/syscall/... # to regenerate syscall packages\n"
+ }
+ // TODO(golang.org/issue/43440): Check anything else influenced by dependency versions.
+
+ diff, err := exec.Command("diff", "--recursive", "--unified", r.Dir, m.Dir).CombinedOutput()
+ if err != nil || len(diff) != 0 {
+ t.Errorf(`Module %s in %s is not tidy (-want +got):
+
+%s
+To fix it, run:
+
+%s
+(If module %[1]s is definitely tidy, this could mean
+there's a problem in the go or bundle command.)`, m.Path, m.Dir, diff, advice)
+ }
+ })
+ }
+}
+
+// packagePattern returns a package pattern that matches all packages
+// in the module modulePath, and ideally as few others as possible.
+func packagePattern(modulePath string) string {
+ if modulePath == "std" {
+ return "std"
+ }
+ return modulePath + "/..."
+}
+
+// makeGOROOTCopy makes a temporary copy of the current GOROOT tree.
+// The goal is to allow the calling test t to safely mutate a GOROOT
+// copy without also modifying the original GOROOT.
+//
+// It copies the entire tree as is, with the exception of the GOROOT/.git
+// directory, which is skipped, and the GOROOT/{bin,pkg} directories,
+// which are symlinked. This is done for speed, since a GOROOT tree is
+// functional without being in a Git repository, and bin and pkg are
+// deemed safe to share for the purpose of the TestAllDependencies test.
+func makeGOROOTCopy(t *testing.T) string {
+ t.Helper()
+ gorootCopyDir := t.TempDir()
+ err := filepath.Walk(runtime.GOROOT(), func(src string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ if src == filepath.Join(runtime.GOROOT(), ".git") {
+ return filepath.SkipDir
+ }
+
+ rel, err := filepath.Rel(runtime.GOROOT(), src)
+ if err != nil {
+ return fmt.Errorf("filepath.Rel(%q, %q): %v", runtime.GOROOT(), src, err)
+ }
+ dst := filepath.Join(gorootCopyDir, rel)
+
+ switch src {
+ case filepath.Join(runtime.GOROOT(), "bin"),
+ filepath.Join(runtime.GOROOT(), "pkg"):
+ // If the OS supports symlinks, use them instead
+ // of copying the bin and pkg directories.
+ if err := os.Symlink(src, dst); err == nil {
+ return filepath.SkipDir
+ }
+ }
+
+ perm := info.Mode() & os.ModePerm
+ if info.Mode()&os.ModeSymlink != 0 {
+ info, err = os.Stat(src)
+ if err != nil {
+ return err
+ }
+ perm = info.Mode() & os.ModePerm
+ }
+
+ // If it's a directory, make a corresponding directory.
+ if info.IsDir() {
+ return os.MkdirAll(dst, perm|0200)
+ }
+
+ // Copy the file bytes.
+ // We can't create a symlink because the file may get modified;
+ // we need to ensure that only the temporary copy is affected.
+ s, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+ defer s.Close()
+ d, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
+ if err != nil {
+ return err
+ }
+ _, err = io.Copy(d, s)
+ if err != nil {
+ d.Close()
+ return err
+ }
+ return d.Close()
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ return gorootCopyDir
+}
+
+type runner struct {
+ Dir string
+ Env []string
+}
+
+// run runs the command and requires that it succeeds.
+func (r runner) run(t *testing.T, args ...string) {
+ t.Helper()
+ cmd := exec.Command(args[0], args[1:]...)
+ cmd.Dir = r.Dir
+ cmd.Env = r.Env
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Logf("> %s\n", strings.Join(args, " "))
+ t.Fatalf("command failed: %s\n%s", err, out)
+ }
}
// TestDependencyVersionsConsistent verifies that each module in GOROOT that
@@ -159,8 +321,7 @@ func TestDependencyVersionsConsistent(t *testing.T) {
seen := map[string]map[requirement][]gorootModule{} // module path → requirement → set of modules with that requirement
for _, m := range findGorootModules(t) {
if !m.hasVendor {
- // TestAllDependenciesVendored will ensure that the module has no
- // dependencies.
+ // TestAllDependencies will ensure that the module has no dependencies.
continue
}
@@ -233,3 +394,74 @@ func TestDependencyVersionsConsistent(t *testing.T) {
}
}
}
+
+type gorootModule struct {
+ Path string
+ Dir string
+ hasVendor bool
+}
+
+// findGorootModules returns the list of modules found in the GOROOT source tree.
+func findGorootModules(t *testing.T) []gorootModule {
+ t.Helper()
+ goBin := testenv.GoToolPath(t)
+
+ goroot.once.Do(func() {
+ goroot.err = filepath.WalkDir(runtime.GOROOT(), func(path string, info fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if info.IsDir() && (info.Name() == "vendor" || info.Name() == "testdata") {
+ return filepath.SkipDir
+ }
+ if path == filepath.Join(runtime.GOROOT(), "pkg") {
+ // GOROOT/pkg contains generated artifacts, not source code.
+ //
+ // In https://golang.org/issue/37929 it was observed to somehow contain
+ // a module cache, so it is important to skip. (That helps with the
+ // running time of this test anyway.)
+ return filepath.SkipDir
+ }
+ if info.IsDir() || info.Name() != "go.mod" {
+ return nil
+ }
+ dir := filepath.Dir(path)
+
+ // Use 'go list' to describe the module contained in this directory (but
+ // not its dependencies).
+ cmd := exec.Command(goBin, "list", "-json", "-m")
+ cmd.Env = append(os.Environ(), "GO111MODULE=on")
+ cmd.Dir = dir
+ cmd.Stderr = new(strings.Builder)
+ out, err := cmd.Output()
+ if err != nil {
+ return fmt.Errorf("'go list -json -m' in %s: %w\n%s", dir, err, cmd.Stderr)
+ }
+
+ var m gorootModule
+ if err := json.Unmarshal(out, &m); err != nil {
+ return fmt.Errorf("decoding 'go list -json -m' in %s: %w", dir, err)
+ }
+ if m.Path == "" || m.Dir == "" {
+ return fmt.Errorf("'go list -json -m' in %s failed to populate Path and/or Dir", dir)
+ }
+ if _, err := os.Stat(filepath.Join(dir, "vendor")); err == nil {
+ m.hasVendor = true
+ }
+ goroot.modules = append(goroot.modules, m)
+ return nil
+ })
+ })
+
+ if goroot.err != nil {
+ t.Fatal(goroot.err)
+ }
+ return goroot.modules
+}
+
+// goroot caches the list of modules found in the GOROOT source tree.
+var goroot struct {
+ once sync.Once
+ modules []gorootModule
+ err error
+}
diff --git a/src/cmd/internal/obj/arm/obj5.go b/src/cmd/internal/obj/arm/obj5.go
index 29d3a5867d..7de04302d9 100644
--- a/src/cmd/internal/obj/arm/obj5.go
+++ b/src/cmd/internal/obj/arm/obj5.go
@@ -34,6 +34,7 @@ import (
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/sys"
+ "log"
)
var progedit_tlsfallback *obj.LSym
@@ -613,6 +614,21 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p.From.Reg = REGSP
}
}
+
+ if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
+ f := c.cursym.Func()
+ if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
+ c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
+ if ctxt.Debugvlog || !ctxt.IsAsm {
+ ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
+ if !ctxt.IsAsm {
+ ctxt.Diag("invalid auto-SPWRITE in non-assembly")
+ ctxt.DiagFlush()
+ log.Fatalf("bad SPWRITE")
+ }
+ }
+ }
+ }
}
}
diff --git a/src/cmd/internal/obj/arm64/a.out.go b/src/cmd/internal/obj/arm64/a.out.go
index 1d1bea505c..7ab9c1475f 100644
--- a/src/cmd/internal/obj/arm64/a.out.go
+++ b/src/cmd/internal/obj/arm64/a.out.go
@@ -239,7 +239,7 @@ const (
REGCTXT = REG_R26 // environment for closures
REGTMP = REG_R27 // reserved for liblink
REGG = REG_R28 // G
- REGFP = REG_R29 // frame pointer, unused in the Go toolchain
+ REGFP = REG_R29 // frame pointer
REGLINK = REG_R30
// ARM64 uses R31 as both stack pointer and zero register,
diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go
index 1a359f1921..70072cfba4 100644
--- a/src/cmd/internal/obj/arm64/asm7.go
+++ b/src/cmd/internal/obj/arm64/asm7.go
@@ -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
)
@@ -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},
@@ -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)
@@ -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
diff --git a/src/cmd/internal/obj/arm64/asm_test.go b/src/cmd/internal/obj/arm64/asm_arm64_test.go
index 9efdb0217f..c6a00f5b94 100644
--- a/src/cmd/internal/obj/arm64/asm_test.go
+++ b/src/cmd/internal/obj/arm64/asm_arm64_test.go
@@ -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)
@@ -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)
@@ -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)
}
@@ -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)
@@ -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)
+ }
+}
diff --git a/src/cmd/internal/obj/arm64/asm_arm64_test.s b/src/cmd/internal/obj/arm64/asm_arm64_test.s
new file mode 100644
index 0000000000..9d337a4fd1
--- /dev/null
+++ b/src/cmd/internal/obj/arm64/asm_arm64_test.s
@@ -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
diff --git a/src/cmd/internal/obj/arm64/obj7.go b/src/cmd/internal/obj/arm64/obj7.go
index 0baf51973a..8f7648e5d5 100644
--- a/src/cmd/internal/obj/arm64/obj7.go
+++ b/src/cmd/internal/obj/arm64/obj7.go
@@ -35,6 +35,7 @@ import (
"cmd/internal/objabi"
"cmd/internal/src"
"cmd/internal/sys"
+ "log"
"math"
)
@@ -621,25 +622,24 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd)
- if objabi.Framepointer_enabled {
- q1 = obj.Appendp(q1, c.newprog)
- q1.Pos = p.Pos
- q1.As = AMOVD
- q1.From.Type = obj.TYPE_REG
- q1.From.Reg = REGFP
- q1.To.Type = obj.TYPE_MEM
- q1.To.Reg = REGSP
- q1.To.Offset = -8
+ // Frame pointer.
+ q1 = obj.Appendp(q1, c.newprog)
+ q1.Pos = p.Pos
+ q1.As = AMOVD
+ q1.From.Type = obj.TYPE_REG
+ q1.From.Reg = REGFP
+ q1.To.Type = obj.TYPE_MEM
+ q1.To.Reg = REGSP
+ q1.To.Offset = -8
- q1 = obj.Appendp(q1, c.newprog)
- q1.Pos = p.Pos
- q1.As = ASUB
- q1.From.Type = obj.TYPE_CONST
- q1.From.Offset = 8
- q1.Reg = REGSP
- q1.To.Type = obj.TYPE_REG
- q1.To.Reg = REGFP
- }
+ q1 = obj.Appendp(q1, c.newprog)
+ q1.Pos = p.Pos
+ q1.As = ASUB
+ q1.From.Type = obj.TYPE_CONST
+ q1.From.Offset = 8
+ q1.Reg = REGSP
+ q1.To.Type = obj.TYPE_REG
+ q1.To.Reg = REGFP
if c.cursym.Func().Text.From.Sym.Wrapper() {
// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
@@ -764,28 +764,26 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p.To.Reg = REGSP
p.Spadj = -c.autosize
- if objabi.Framepointer_enabled {
- p = obj.Appendp(p, c.newprog)
- p.As = ASUB
- p.From.Type = obj.TYPE_CONST
- p.From.Offset = 8
- p.Reg = REGSP
- p.To.Type = obj.TYPE_REG
- p.To.Reg = REGFP
- }
+ // Frame pointer.
+ p = obj.Appendp(p, c.newprog)
+ p.As = ASUB
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = 8
+ p.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGFP
}
} else {
/* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
- if objabi.Framepointer_enabled {
- p.As = AMOVD
- p.From.Type = obj.TYPE_MEM
- p.From.Reg = REGSP
- p.From.Offset = -8
- p.To.Type = obj.TYPE_REG
- p.To.Reg = REGFP
- p = obj.Appendp(p, c.newprog)
- }
+ // Frame pointer.
+ p.As = AMOVD
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = REGSP
+ p.From.Offset = -8
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGFP
+ p = obj.Appendp(p, c.newprog)
aoffset := c.autosize
@@ -820,6 +818,28 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
}
}
+ // If enabled, this code emits 'MOV PC, R27' before every 'MOV LR, PC',
+ // so that if you are debugging a low-level crash where PC and LR are zero,
+ // you can look at R27 to see what jumped to the zero.
+ // This is useful when bringing up Go on a new system.
+ // (There is similar code in ../ppc64/obj9.go:/if.false.)
+ const debugRETZERO = false
+ if debugRETZERO {
+ if p.As != obj.ARET {
+ q = newprog()
+ q.Pos = p.Pos
+ q.Link = p.Link
+ p.Link = q
+ p = q
+ }
+ p.As = AADR
+ p.From.Type = obj.TYPE_BRANCH
+ p.From.Offset = 0
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGTMP
+
+ }
+
if p.As != obj.ARET {
q = newprog()
q.Pos = p.Pos
@@ -865,109 +885,120 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
}
case obj.ADUFFCOPY:
- if objabi.Framepointer_enabled {
- // ADR ret_addr, R27
- // STP (FP, R27), -24(SP)
- // SUB 24, SP, FP
- // DUFFCOPY
- // ret_addr:
- // SUB 8, SP, FP
+ // ADR ret_addr, R27
+ // STP (FP, R27), -24(SP)
+ // SUB 24, SP, FP
+ // DUFFCOPY
+ // ret_addr:
+ // SUB 8, SP, FP
- q1 := p
- // copy DUFFCOPY from q1 to q4
- q4 := obj.Appendp(p, c.newprog)
- q4.Pos = p.Pos
- q4.As = obj.ADUFFCOPY
- q4.To = p.To
+ q1 := p
+ // copy DUFFCOPY from q1 to q4
+ q4 := obj.Appendp(p, c.newprog)
+ q4.Pos = p.Pos
+ q4.As = obj.ADUFFCOPY
+ q4.To = p.To
- q1.As = AADR
- q1.From.Type = obj.TYPE_BRANCH
- q1.To.Type = obj.TYPE_REG
- q1.To.Reg = REG_R27
+ q1.As = AADR
+ q1.From.Type = obj.TYPE_BRANCH
+ q1.To.Type = obj.TYPE_REG
+ q1.To.Reg = REG_R27
- q2 := obj.Appendp(q1, c.newprog)
- q2.Pos = p.Pos
- q2.As = ASTP
- q2.From.Type = obj.TYPE_REGREG
- q2.From.Reg = REGFP
- q2.From.Offset = int64(REG_R27)
- q2.To.Type = obj.TYPE_MEM
- q2.To.Reg = REGSP
- q2.To.Offset = -24
+ q2 := obj.Appendp(q1, c.newprog)
+ q2.Pos = p.Pos
+ q2.As = ASTP
+ q2.From.Type = obj.TYPE_REGREG
+ q2.From.Reg = REGFP
+ q2.From.Offset = int64(REG_R27)
+ q2.To.Type = obj.TYPE_MEM
+ q2.To.Reg = REGSP
+ q2.To.Offset = -24
- // maintaine FP for DUFFCOPY
- q3 := obj.Appendp(q2, c.newprog)
- q3.Pos = p.Pos
- q3.As = ASUB
- q3.From.Type = obj.TYPE_CONST
- q3.From.Offset = 24
- q3.Reg = REGSP
- q3.To.Type = obj.TYPE_REG
- q3.To.Reg = REGFP
+ // maintain FP for DUFFCOPY
+ q3 := obj.Appendp(q2, c.newprog)
+ q3.Pos = p.Pos
+ q3.As = ASUB
+ q3.From.Type = obj.TYPE_CONST
+ q3.From.Offset = 24
+ q3.Reg = REGSP
+ q3.To.Type = obj.TYPE_REG
+ q3.To.Reg = REGFP
- q5 := obj.Appendp(q4, c.newprog)
- q5.Pos = p.Pos
- q5.As = ASUB
- q5.From.Type = obj.TYPE_CONST
- q5.From.Offset = 8
- q5.Reg = REGSP
- q5.To.Type = obj.TYPE_REG
- q5.To.Reg = REGFP
- q1.From.SetTarget(q5)
- p = q5
- }
+ q5 := obj.Appendp(q4, c.newprog)
+ q5.Pos = p.Pos
+ q5.As = ASUB
+ q5.From.Type = obj.TYPE_CONST
+ q5.From.Offset = 8
+ q5.Reg = REGSP
+ q5.To.Type = obj.TYPE_REG
+ q5.To.Reg = REGFP
+ q1.From.SetTarget(q5)
+ p = q5
case obj.ADUFFZERO:
- if objabi.Framepointer_enabled {
- // ADR ret_addr, R27
- // STP (FP, R27), -24(SP)
- // SUB 24, SP, FP
- // DUFFZERO
- // ret_addr:
- // SUB 8, SP, FP
+ // ADR ret_addr, R27
+ // STP (FP, R27), -24(SP)
+ // SUB 24, SP, FP
+ // DUFFZERO
+ // ret_addr:
+ // SUB 8, SP, FP
- q1 := p
- // copy DUFFZERO from q1 to q4
- q4 := obj.Appendp(p, c.newprog)
- q4.Pos = p.Pos
- q4.As = obj.ADUFFZERO
- q4.To = p.To
+ q1 := p
+ // copy DUFFZERO from q1 to q4
+ q4 := obj.Appendp(p, c.newprog)
+ q4.Pos = p.Pos
+ q4.As = obj.ADUFFZERO
+ q4.To = p.To
- q1.As = AADR
- q1.From.Type = obj.TYPE_BRANCH
- q1.To.Type = obj.TYPE_REG
- q1.To.Reg = REG_R27
+ q1.As = AADR
+ q1.From.Type = obj.TYPE_BRANCH
+ q1.To.Type = obj.TYPE_REG
+ q1.To.Reg = REG_R27
- q2 := obj.Appendp(q1, c.newprog)
- q2.Pos = p.Pos
- q2.As = ASTP
- q2.From.Type = obj.TYPE_REGREG
- q2.From.Reg = REGFP
- q2.From.Offset = int64(REG_R27)
- q2.To.Type = obj.TYPE_MEM
- q2.To.Reg = REGSP
- q2.To.Offset = -24
+ q2 := obj.Appendp(q1, c.newprog)
+ q2.Pos = p.Pos
+ q2.As = ASTP
+ q2.From.Type = obj.TYPE_REGREG
+ q2.From.Reg = REGFP
+ q2.From.Offset = int64(REG_R27)
+ q2.To.Type = obj.TYPE_MEM
+ q2.To.Reg = REGSP
+ q2.To.Offset = -24
- // maintaine FP for DUFFZERO
- q3 := obj.Appendp(q2, c.newprog)
- q3.Pos = p.Pos
- q3.As = ASUB
- q3.From.Type = obj.TYPE_CONST
- q3.From.Offset = 24
- q3.Reg = REGSP
- q3.To.Type = obj.TYPE_REG
- q3.To.Reg = REGFP
+ // maintain FP for DUFFZERO
+ q3 := obj.Appendp(q2, c.newprog)
+ q3.Pos = p.Pos
+ q3.As = ASUB
+ q3.From.Type = obj.TYPE_CONST
+ q3.From.Offset = 24
+ q3.Reg = REGSP
+ q3.To.Type = obj.TYPE_REG
+ q3.To.Reg = REGFP
- q5 := obj.Appendp(q4, c.newprog)
- q5.Pos = p.Pos
- q5.As = ASUB
- q5.From.Type = obj.TYPE_CONST
- q5.From.Offset = 8
- q5.Reg = REGSP
- q5.To.Type = obj.TYPE_REG
- q5.To.Reg = REGFP
- q1.From.SetTarget(q5)
- p = q5
+ q5 := obj.Appendp(q4, c.newprog)
+ q5.Pos = p.Pos
+ q5.As = ASUB
+ q5.From.Type = obj.TYPE_CONST
+ q5.From.Offset = 8
+ q5.Reg = REGSP
+ q5.To.Type = obj.TYPE_REG
+ q5.To.Reg = REGFP
+ q1.From.SetTarget(q5)
+ p = q5
+ }
+
+ if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
+ f := c.cursym.Func()
+ if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
+ c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
+ if ctxt.Debugvlog || !ctxt.IsAsm {
+ ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
+ if !ctxt.IsAsm {
+ ctxt.Diag("invalid auto-SPWRITE in non-assembly")
+ ctxt.DiagFlush()
+ log.Fatalf("bad SPWRITE")
+ }
+ }
}
}
}
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index 8c8ff587ff..a48db3bdc8 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -39,6 +39,7 @@ import (
"cmd/internal/sys"
"fmt"
"sync"
+ "sync/atomic"
)
// An Addr is an argument to an instruction.
@@ -250,6 +251,12 @@ func (a *Addr) SetTarget(t *Prog) {
a.Val = t
}
+func (a *Addr) SetConst(v int64) {
+ a.Sym = nil
+ a.Type = TYPE_CONST
+ a.Offset = v
+}
+
// Prog describes a single machine instruction.
//
// The general instruction form is:
@@ -447,6 +454,7 @@ type FuncInfo struct {
Locals int32
Align int32
FuncID objabi.FuncID
+ FuncFlag objabi.FuncFlag
Text *Prog
Autot map[*LSym]struct{}
Pcln Pcln
@@ -612,10 +620,6 @@ const (
// target of an inline during compilation
AttrWasInlined
- // TopFrame means that this function is an entry point and unwinders should not
- // keep unwinding beyond this frame.
- AttrTopFrame
-
// Indexed indicates this symbol has been assigned with an index (when using the
// new object file format).
AttrIndexed
@@ -629,6 +633,10 @@ const (
// ContentAddressable indicates this is a content-addressable symbol.
AttrContentAddressable
+ // ABI wrapper is set for compiler-generated text symbols that
+ // convert between ABI0 and ABIInternal calling conventions.
+ AttrABIWrapper
+
// attrABIBase is the value at which the ABI is encoded in
// Attribute. This must be last; all bits after this are
// assumed to be an ABI value.
@@ -637,36 +645,51 @@ const (
attrABIBase
)
-func (a Attribute) DuplicateOK() bool { return a&AttrDuplicateOK != 0 }
-func (a Attribute) MakeTypelink() bool { return a&AttrMakeTypelink != 0 }
-func (a Attribute) CFunc() bool { return a&AttrCFunc != 0 }
-func (a Attribute) NoSplit() bool { return a&AttrNoSplit != 0 }
-func (a Attribute) Leaf() bool { return a&AttrLeaf != 0 }
-func (a Attribute) OnList() bool { return a&AttrOnList != 0 }
-func (a Attribute) ReflectMethod() bool { return a&AttrReflectMethod != 0 }
-func (a Attribute) Local() bool { return a&AttrLocal != 0 }
-func (a Attribute) Wrapper() bool { return a&AttrWrapper != 0 }
-func (a Attribute) NeedCtxt() bool { return a&AttrNeedCtxt != 0 }
-func (a Attribute) NoFrame() bool { return a&AttrNoFrame != 0 }
-func (a Attribute) Static() bool { return a&AttrStatic != 0 }
-func (a Attribute) WasInlined() bool { return a&AttrWasInlined != 0 }
-func (a Attribute) TopFrame() bool { return a&AttrTopFrame != 0 }
-func (a Attribute) Indexed() bool { return a&AttrIndexed != 0 }
-func (a Attribute) UsedInIface() bool { return a&AttrUsedInIface != 0 }
-func (a Attribute) ContentAddressable() bool { return a&AttrContentAddressable != 0 }
+func (a *Attribute) load() Attribute { return Attribute(atomic.LoadUint32((*uint32)(a))) }
+
+func (a *Attribute) DuplicateOK() bool { return a.load()&AttrDuplicateOK != 0 }
+func (a *Attribute) MakeTypelink() bool { return a.load()&AttrMakeTypelink != 0 }
+func (a *Attribute) CFunc() bool { return a.load()&AttrCFunc != 0 }
+func (a *Attribute) NoSplit() bool { return a.load()&AttrNoSplit != 0 }
+func (a *Attribute) Leaf() bool { return a.load()&AttrLeaf != 0 }
+func (a *Attribute) OnList() bool { return a.load()&AttrOnList != 0 }
+func (a *Attribute) ReflectMethod() bool { return a.load()&AttrReflectMethod != 0 }
+func (a *Attribute) Local() bool { return a.load()&AttrLocal != 0 }
+func (a *Attribute) Wrapper() bool { return a.load()&AttrWrapper != 0 }
+func (a *Attribute) NeedCtxt() bool { return a.load()&AttrNeedCtxt != 0 }
+func (a *Attribute) NoFrame() bool { return a.load()&AttrNoFrame != 0 }
+func (a *Attribute) Static() bool { return a.load()&AttrStatic != 0 }
+func (a *Attribute) WasInlined() bool { return a.load()&AttrWasInlined != 0 }
+func (a *Attribute) Indexed() bool { return a.load()&AttrIndexed != 0 }
+func (a *Attribute) UsedInIface() bool { return a.load()&AttrUsedInIface != 0 }
+func (a *Attribute) ContentAddressable() bool { return a.load()&AttrContentAddressable != 0 }
+func (a *Attribute) ABIWrapper() bool { return a.load()&AttrABIWrapper != 0 }
func (a *Attribute) Set(flag Attribute, value bool) {
- if value {
- *a |= flag
- } else {
- *a &^= flag
+ for {
+ v0 := a.load()
+ v := v0
+ if value {
+ v |= flag
+ } else {
+ v &^= flag
+ }
+ if atomic.CompareAndSwapUint32((*uint32)(a), uint32(v0), uint32(v)) {
+ break
+ }
}
}
-func (a Attribute) ABI() ABI { return ABI(a / attrABIBase) }
+func (a *Attribute) ABI() ABI { return ABI(a.load() / attrABIBase) }
func (a *Attribute) SetABI(abi ABI) {
const mask = 1 // Only one ABI bit for now.
- *a = (*a &^ (mask * attrABIBase)) | Attribute(abi)*attrABIBase
+ for {
+ v0 := a.load()
+ v := (v0 &^ (mask * attrABIBase)) | Attribute(abi)*attrABIBase
+ if atomic.CompareAndSwapUint32((*uint32)(a), uint32(v0), uint32(v)) {
+ break
+ }
+ }
}
var textAttrStrings = [...]struct {
@@ -686,13 +709,13 @@ var textAttrStrings = [...]struct {
{bit: AttrNoFrame, s: "NOFRAME"},
{bit: AttrStatic, s: "STATIC"},
{bit: AttrWasInlined, s: ""},
- {bit: AttrTopFrame, s: "TOPFRAME"},
{bit: AttrIndexed, s: ""},
{bit: AttrContentAddressable, s: ""},
+ {bit: AttrABIWrapper, s: "ABIWRAPPER"},
}
-// TextAttrString formats a for printing in as part of a TEXT prog.
-func (a Attribute) TextAttrString() string {
+// String formats a for printing in as part of a TEXT prog.
+func (a Attribute) String() string {
var s string
for _, x := range textAttrStrings {
if a&x.bit != 0 {
@@ -718,13 +741,25 @@ func (a Attribute) TextAttrString() string {
return s
}
+// TextAttrString formats the symbol attributes for printing in as part of a TEXT prog.
+func (s *LSym) TextAttrString() string {
+ attr := s.Attribute.String()
+ if s.Func().FuncFlag&objabi.FuncFlag_TOPFRAME != 0 {
+ if attr != "" {
+ attr += "|"
+ }
+ attr += "TOPFRAME"
+ }
+ return attr
+}
+
func (s *LSym) String() string {
return s.Name
}
// The compiler needs *LSym to be assignable to cmd/compile/internal/ssa.Sym.
-func (s *LSym) CanBeAnSSASym() {
-}
+func (*LSym) CanBeAnSSASym() {}
+func (*LSym) CanBeAnSSAAux() {}
type Pcln struct {
// Aux symbols for pcln
@@ -754,6 +789,17 @@ type Auto struct {
Gotype *LSym
}
+// RegArg provides spill/fill information for a register-resident argument
+// to a function. These need spilling/filling in the safepoint/stackgrowth case.
+// At the time of fill/spill, the offset must be adjusted by the architecture-dependent
+// adjustment to hardware SP that occurs in a call instruction. E.g., for AMD64,
+// at Offset+8 because the return address was pushed.
+type RegArg struct {
+ Addr Addr
+ Reg int16
+ Spill, Unspill As
+}
+
// Link holds the context for writing object code from a compiler
// to be linker input or for reading that input into the linker.
type Link struct {
@@ -784,6 +830,7 @@ type Link struct {
DebugInfo func(fn *LSym, info *LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls) // if non-nil, curfn is a *gc.Node
GenAbstractFunc func(fn *LSym)
Errors int
+ RegArgs []RegArg
InParallel bool // parallel backend phase in effect
UseBASEntries bool // use Base Address Selection Entries in location lists and PC ranges
@@ -832,6 +879,32 @@ func (ctxt *Link) Logf(format string, args ...interface{}) {
ctxt.Bso.Flush()
}
+func (ctxt *Link) SpillRegisterArgs(last *Prog, pa ProgAlloc) *Prog {
+ // Spill register args.
+ for _, ra := range ctxt.RegArgs {
+ spill := Appendp(last, pa)
+ spill.As = ra.Spill
+ spill.From.Type = TYPE_REG
+ spill.From.Reg = ra.Reg
+ spill.To = ra.Addr
+ last = spill
+ }
+ return last
+}
+
+func (ctxt *Link) UnspillRegisterArgs(last *Prog, pa ProgAlloc) *Prog {
+ // Unspill any spilled register args
+ for _, ra := range ctxt.RegArgs {
+ unspill := Appendp(last, pa)
+ unspill.As = ra.Unspill
+ unspill.From = ra.Addr
+ unspill.To.Type = TYPE_REG
+ unspill.To.Reg = ra.Reg
+ last = unspill
+ }
+ return last
+}
+
// The smallest possible offset from the hardware stack pointer to a local
// variable on the stack. Architectures that use a link register save its value
// on the stack in the function prologue and so always have a pointer between
diff --git a/src/cmd/internal/obj/mips/obj0.go b/src/cmd/internal/obj/mips/obj0.go
index 135a8df3aa..91bba90d41 100644
--- a/src/cmd/internal/obj/mips/obj0.go
+++ b/src/cmd/internal/obj/mips/obj0.go
@@ -35,6 +35,7 @@ import (
"cmd/internal/sys"
"encoding/binary"
"fmt"
+ "log"
"math"
)
@@ -536,6 +537,21 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p.From.Reg = REGSP
}
}
+
+ if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
+ f := c.cursym.Func()
+ if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
+ c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
+ if ctxt.Debugvlog || !ctxt.IsAsm {
+ ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
+ if !ctxt.IsAsm {
+ ctxt.Diag("invalid auto-SPWRITE in non-assembly")
+ ctxt.DiagFlush()
+ log.Fatalf("bad SPWRITE")
+ }
+ }
+ }
+ }
}
if c.ctxt.Arch.Family == sys.MIPS {
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index bb58b4f0c2..85f0570e5d 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -330,9 +330,6 @@ func (w *writer) Sym(s *LSym) {
if s.ReflectMethod() {
flag |= goobj.SymFlagReflectMethod
}
- if s.TopFrame() {
- flag |= goobj.SymFlagTopFrame
- }
if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' && s.Type == objabi.SRODATA {
flag |= goobj.SymFlagGoType
}
@@ -673,9 +670,10 @@ func genFuncInfoSyms(ctxt *Link) {
continue
}
o := goobj.FuncInfo{
- Args: uint32(fn.Args),
- Locals: uint32(fn.Locals),
- FuncID: objabi.FuncID(fn.FuncID),
+ Args: uint32(fn.Args),
+ Locals: uint32(fn.Locals),
+ FuncID: fn.FuncID,
+ FuncFlag: fn.FuncFlag,
}
pc := &fn.Pcln
o.Pcsp = makeSymRef(preparePcSym(pc.Pcsp))
@@ -788,7 +786,7 @@ func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) {
if s.NoSplit() {
fmt.Fprintf(ctxt.Bso, "nosplit ")
}
- if s.TopFrame() {
+ if s.Func() != nil && s.Func().FuncFlag&objabi.FuncFlag_TOPFRAME != 0 {
fmt.Fprintf(ctxt.Bso, "topframe ")
}
fmt.Fprintf(ctxt.Bso, "size=%d", s.Size)
diff --git a/src/cmd/internal/obj/plist.go b/src/cmd/internal/obj/plist.go
index 2b096996f7..177083261c 100644
--- a/src/cmd/internal/obj/plist.go
+++ b/src/cmd/internal/obj/plist.go
@@ -80,6 +80,11 @@ func Flushplist(ctxt *Link, plist *Plist, newprog ProgAlloc, myimportpath string
if !strings.HasPrefix(s.Name, "\"\".") {
continue
}
+ if s.ABIWrapper() {
+ // Don't create an args_stackmap symbol reference for an ABI
+ // wrapper function
+ continue
+ }
found := false
for p := s.Func().Text; p != nil; p = p.Link {
if p.As == AFUNCDATA && p.From.Type == TYPE_CONST && p.From.Offset == objabi.FUNCDATA_ArgsPointerMaps {
@@ -129,14 +134,15 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) {
}
name := strings.Replace(s.Name, "\"\"", ctxt.Pkgpath, -1)
s.Func().FuncID = objabi.GetFuncID(name, flag&WRAPPER != 0)
+ s.Func().FuncFlag = toFuncFlag(flag)
s.Set(AttrOnList, true)
s.Set(AttrDuplicateOK, flag&DUPOK != 0)
s.Set(AttrNoSplit, flag&NOSPLIT != 0)
s.Set(AttrReflectMethod, flag&REFLECTMETHOD != 0)
s.Set(AttrWrapper, flag&WRAPPER != 0)
+ s.Set(AttrABIWrapper, flag&ABIWRAPPER != 0)
s.Set(AttrNeedCtxt, flag&NEEDCTXT != 0)
s.Set(AttrNoFrame, flag&NOFRAME != 0)
- s.Set(AttrTopFrame, flag&TOPFRAME != 0)
s.Type = objabi.STEXT
ctxt.Text = append(ctxt.Text, s)
@@ -144,6 +150,14 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) {
ctxt.dwarfSym(s)
}
+func toFuncFlag(flag int) objabi.FuncFlag {
+ var out objabi.FuncFlag
+ if flag&TOPFRAME != 0 {
+ out |= objabi.FuncFlag_TOPFRAME
+ }
+ return out
+}
+
func (ctxt *Link) Globl(s *LSym, size int64, flag int) {
if s.OnList() {
ctxt.Diag("symbol %s listed multiple times", s.Name)
diff --git a/src/cmd/internal/obj/ppc64/obj9.go b/src/cmd/internal/obj/ppc64/obj9.go
index fddf552156..a77be29cf0 100644
--- a/src/cmd/internal/obj/ppc64/obj9.go
+++ b/src/cmd/internal/obj/ppc64/obj9.go
@@ -34,6 +34,7 @@ import (
"cmd/internal/objabi"
"cmd/internal/src"
"cmd/internal/sys"
+ "log"
)
func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
@@ -984,6 +985,21 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p.From.Reg = REGSP
}
}
+
+ if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 && p.As != ACMPU {
+ f := c.cursym.Func()
+ if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
+ c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
+ if ctxt.Debugvlog || !ctxt.IsAsm {
+ ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
+ if !ctxt.IsAsm {
+ ctxt.Diag("invalid auto-SPWRITE in non-assembly")
+ ctxt.DiagFlush()
+ log.Fatalf("bad SPWRITE")
+ }
+ }
+ }
+ }
}
}
diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go
index 9257a6453a..d104f1cfa5 100644
--- a/src/cmd/internal/obj/riscv/obj.go
+++ b/src/cmd/internal/obj/riscv/obj.go
@@ -25,6 +25,7 @@ import (
"cmd/internal/objabi"
"cmd/internal/sys"
"fmt"
+ "log"
)
func buildop(ctxt *obj.Link) {}
@@ -119,7 +120,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
switch p.To.Name {
case obj.NAME_NONE:
p.As = AJALR
- case obj.NAME_EXTERN:
+ case obj.NAME_EXTERN, obj.NAME_STATIC:
// Handled in preprocess.
default:
ctxt.Diag("unsupported name %d for %v", p.To.Name, p)
@@ -267,7 +268,7 @@ func rewriteMOV(ctxt *obj.Link, newprog obj.ProgAlloc, p *obj.Prog) {
p.As = movToStore(p.As)
p.To.Reg = addrToReg(p.To)
- case obj.NAME_EXTERN:
+ case obj.NAME_EXTERN, obj.NAME_STATIC:
// AUIPC $off_hi, TMP
// S $off_lo, TMP, R
as := p.As
@@ -666,7 +667,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
switch p.To.Type {
case obj.TYPE_MEM:
switch p.To.Name {
- case obj.NAME_EXTERN:
+ case obj.NAME_EXTERN, obj.NAME_STATIC:
// JMP to symbol.
jalrToSym(ctxt, p, newprog, REG_ZERO)
}
@@ -716,6 +717,21 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p.Spadj = int32(-p.From.Offset)
}
}
+
+ if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
+ f := cursym.Func()
+ if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
+ f.FuncFlag |= objabi.FuncFlag_SPWRITE
+ if ctxt.Debugvlog || !ctxt.IsAsm {
+ ctxt.Logf("auto-SPWRITE: %s %v\n", cursym.Name, p)
+ if !ctxt.IsAsm {
+ ctxt.Diag("invalid auto-SPWRITE in non-assembly")
+ ctxt.DiagFlush()
+ log.Fatalf("bad SPWRITE")
+ }
+ }
+ }
+ }
}
// Rewrite MOV pseudo-instructions. This cannot be done in
diff --git a/src/cmd/internal/obj/s390x/condition_code.go b/src/cmd/internal/obj/s390x/condition_code.go
index 764fc5bc6a..f498fd6f77 100644
--- a/src/cmd/internal/obj/s390x/condition_code.go
+++ b/src/cmd/internal/obj/s390x/condition_code.go
@@ -124,3 +124,5 @@ func (c CCMask) String() string {
// invalid
return fmt.Sprintf("Invalid (%#x)", c)
}
+
+func (CCMask) CanBeAnSSAAux() {}
diff --git a/src/cmd/internal/obj/s390x/objz.go b/src/cmd/internal/obj/s390x/objz.go
index 970cf827d6..a02c4fc17f 100644
--- a/src/cmd/internal/obj/s390x/objz.go
+++ b/src/cmd/internal/obj/s390x/objz.go
@@ -33,6 +33,7 @@ import (
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/sys"
+ "log"
"math"
)
@@ -545,6 +546,21 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p.From.Reg = REGSP
}
}
+
+ if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
+ f := c.cursym.Func()
+ if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
+ c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
+ if ctxt.Debugvlog || !ctxt.IsAsm {
+ ctxt.Logf("auto-SPWRITE: %s\n", c.cursym.Name)
+ if !ctxt.IsAsm {
+ ctxt.Diag("invalid auto-SPWRITE in non-assembly")
+ ctxt.DiagFlush()
+ log.Fatalf("bad SPWRITE")
+ }
+ }
+ }
+ }
}
if wasSplit {
c.stacksplitPost(pLast, pPre, pPreempt, autosize) // emit post part of split check
diff --git a/src/cmd/internal/obj/s390x/rotate.go b/src/cmd/internal/obj/s390x/rotate.go
index 7dbc45e648..5407c8df11 100644
--- a/src/cmd/internal/obj/s390x/rotate.go
+++ b/src/cmd/internal/obj/s390x/rotate.go
@@ -28,9 +28,9 @@ import (
// input left by. Note that this rotation is performed
// before the masked region is used.
type RotateParams struct {
- Start int8 // big-endian start bit index [0..63]
- End int8 // big-endian end bit index [0..63]
- Amount int8 // amount to rotate left
+ Start uint8 // big-endian start bit index [0..63]
+ End uint8 // big-endian end bit index [0..63]
+ Amount uint8 // amount to rotate left
}
// NewRotateParams creates a set of parameters representing a
@@ -39,7 +39,7 @@ type RotateParams struct {
//
// The start and end indexes and the rotation amount must all
// be in the range 0-63 inclusive or this function will panic.
-func NewRotateParams(start, end, amount int8) RotateParams {
+func NewRotateParams(start, end, amount uint8) RotateParams {
if start&^63 != 0 {
panic("start out of bounds")
}
@@ -58,7 +58,7 @@ func NewRotateParams(start, end, amount int8) RotateParams {
// RotateLeft generates a new set of parameters with the rotation amount
// increased by the given value. The selected bits are left unchanged.
-func (r RotateParams) RotateLeft(amount int8) RotateParams {
+func (r RotateParams) RotateLeft(amount uint8) RotateParams {
r.Amount += amount
r.Amount &= 63
return r
@@ -100,8 +100,8 @@ func (r RotateParams) OutMerge(mask uint64) *RotateParams {
}
// update start and end positions (rotation amount remains the same)
- r.Start = int8(o+z) & 63
- r.End = (r.Start + int8(l) - 1) & 63
+ r.Start = uint8(o+z) & 63
+ r.End = (r.Start + uint8(l) - 1) & 63
return &r
}
@@ -113,3 +113,5 @@ func (r RotateParams) OutMerge(mask uint64) *RotateParams {
func (r RotateParams) InMerge(mask uint64) *RotateParams {
return r.OutMerge(bits.RotateLeft64(mask, int(r.Amount)))
}
+
+func (RotateParams) CanBeAnSSAAux() {}
diff --git a/src/cmd/internal/obj/s390x/rotate_test.go b/src/cmd/internal/obj/s390x/rotate_test.go
index fa5b5bdecd..88421b1b83 100644
--- a/src/cmd/internal/obj/s390x/rotate_test.go
+++ b/src/cmd/internal/obj/s390x/rotate_test.go
@@ -10,7 +10,7 @@ import (
func TestRotateParamsMask(t *testing.T) {
tests := []struct {
- start, end, amount int8
+ start, end, amount uint8
inMask, outMask uint64
}{
// start before end, no rotation
diff --git a/src/cmd/internal/obj/textflag.go b/src/cmd/internal/obj/textflag.go
index d2cec734b1..2f55793285 100644
--- a/src/cmd/internal/obj/textflag.go
+++ b/src/cmd/internal/obj/textflag.go
@@ -33,7 +33,7 @@ const (
// This function uses its incoming context register.
NEEDCTXT = 64
- // When passed to ggloblsym, causes Local to be set to true on the LSym it creates.
+ // When passed to objw.Global, causes Local to be set to true on the LSym it creates.
LOCAL = 128
// Allocate a word of thread local storage and store the offset from the
@@ -51,4 +51,7 @@ const (
// Function is the top of the call stack. Call stack unwinders should stop
// at this function.
TOPFRAME = 2048
+
+ // Function is an ABI wrapper.
+ ABIWRAPPER = 4096
)
diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go
index b9bacb7a22..1c34b4e833 100644
--- a/src/cmd/internal/obj/util.go
+++ b/src/cmd/internal/obj/util.go
@@ -187,7 +187,7 @@ func (p *Prog) WriteInstructionString(w io.Writer) {
// In short, print one of these two:
// TEXT foo(SB), DUPOK|NOSPLIT, $0
// TEXT foo(SB), $0
- s := p.From.Sym.Attribute.TextAttrString()
+ s := p.From.Sym.TextAttrString()
if s != "" {
fmt.Fprintf(w, "%s%s", sep, s)
sep = ", "
diff --git a/src/cmd/internal/obj/x86/a.out.go b/src/cmd/internal/obj/x86/a.out.go
index 30c1a6a445..3be4b59da4 100644
--- a/src/cmd/internal/obj/x86/a.out.go
+++ b/src/cmd/internal/obj/x86/a.out.go
@@ -263,6 +263,7 @@ const (
FREGRET = REG_X0
REGSP = REG_SP
REGCTXT = REG_DX
+ REGG = REG_R14 // g register in ABIInternal
REGEXT = REG_R15 // compiler allocates external registers R15 down
FREGMIN = REG_X0 + 5 // first register variable
FREGEXT = REG_X0 + 15 // first external register
diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go
index 184fb4308b..bc3a3b4bbe 100644
--- a/src/cmd/internal/obj/x86/obj6.go
+++ b/src/cmd/internal/obj/x86/obj6.go
@@ -35,6 +35,7 @@ import (
"cmd/internal/objabi"
"cmd/internal/src"
"cmd/internal/sys"
+ "log"
"math"
"strings"
)
@@ -637,13 +638,19 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
}
}
- if !p.From.Sym.NoSplit() || p.From.Sym.Wrapper() {
- p = obj.Appendp(p, newprog)
- p = load_g_cx(ctxt, p, newprog) // load g into CX
+ var regg int16
+ if !p.From.Sym.NoSplit() || (p.From.Sym.Wrapper() && !p.From.Sym.ABIWrapper()) {
+ if ctxt.Arch.Family == sys.AMD64 && objabi.Regabi_enabled != 0 && cursym.ABI() == obj.ABIInternal {
+ regg = REGG // use the g register directly in ABIInternal
+ } else {
+ p = obj.Appendp(p, newprog)
+ p = load_g_cx(ctxt, p, newprog) // load g into CX
+ regg = REG_CX
+ }
}
if !cursym.Func().Text.From.Sym.NoSplit() {
- p = stacksplit(ctxt, cursym, p, newprog, autoffset, int32(textarg)) // emit split check
+ p = stacksplit(ctxt, cursym, p, newprog, autoffset, int32(textarg), regg) // emit split check
}
// Delve debugger would like the next instruction to be noted as the end of the function prologue.
@@ -690,12 +697,12 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p.To.Reg = REG_BP
}
- if cursym.Func().Text.From.Sym.Wrapper() {
+ if cursym.Func().Text.From.Sym.Wrapper() && !cursym.Func().Text.From.Sym.ABIWrapper() {
// if g._panic != nil && g._panic.argp == FP {
// g._panic.argp = bottom-of-frame
// }
//
- // MOVQ g_panic(CX), BX
+ // MOVQ g_panic(g), BX
// TESTQ BX, BX
// JNE checkargp
// end:
@@ -718,7 +725,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p = obj.Appendp(p, newprog)
p.As = AMOVQ
p.From.Type = obj.TYPE_MEM
- p.From.Reg = REG_CX
+ p.From.Reg = regg
p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // g_panic
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_BX
@@ -833,6 +840,20 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
switch p.As {
default:
+ if p.To.Type == obj.TYPE_REG && p.To.Reg == REG_SP && p.As != ACMPL && p.As != ACMPQ {
+ f := cursym.Func()
+ if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
+ f.FuncFlag |= objabi.FuncFlag_SPWRITE
+ if ctxt.Debugvlog || !ctxt.IsAsm {
+ ctxt.Logf("auto-SPWRITE: %s %v\n", cursym.Name, p)
+ if !ctxt.IsAsm {
+ ctxt.Diag("invalid auto-SPWRITE in non-assembly")
+ ctxt.DiagFlush()
+ log.Fatalf("bad SPWRITE")
+ }
+ }
+ }
+ }
continue
case APUSHL, APUSHFL:
@@ -969,9 +990,9 @@ func load_g_cx(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) *obj.Prog {
// Append code to p to check for stack split.
// Appends to (does not overwrite) p.
-// Assumes g is in CX.
+// Assumes g is in rg.
// Returns last new instruction.
-func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgAlloc, framesize int32, textarg int32) *obj.Prog {
+func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgAlloc, framesize int32, textarg int32, rg int16) *obj.Prog {
cmp := ACMPQ
lea := ALEAQ
mov := AMOVQ
@@ -993,7 +1014,8 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
p.As = cmp
p.From.Type = obj.TYPE_REG
p.From.Reg = REG_SP
- indir_cx(ctxt, &p.To)
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = rg
p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
if cursym.CFunc() {
p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
@@ -1021,7 +1043,8 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
p.As = cmp
p.From.Type = obj.TYPE_REG
p.From.Reg = REG_AX
- indir_cx(ctxt, &p.To)
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = rg
p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
if cursym.CFunc() {
p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
@@ -1047,7 +1070,8 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
p = obj.Appendp(p, newprog)
p.As = mov
- indir_cx(ctxt, &p.From)
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = rg
p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
if cursym.CFunc() {
p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
@@ -1114,7 +1138,8 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
spfix.Spadj = -framesize
pcdata := ctxt.EmitEntryStackMap(cursym, spfix, newprog)
- pcdata = ctxt.StartUnsafePoint(pcdata, newprog)
+ spill := ctxt.StartUnsafePoint(pcdata, newprog)
+ pcdata = ctxt.SpillRegisterArgs(spill, newprog)
call := obj.Appendp(pcdata, newprog)
call.Pos = cursym.Func().Text.Pos
@@ -1139,7 +1164,8 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
progedit(ctxt, callend.Link, newprog)
}
- pcdata = ctxt.EndUnsafePoint(callend, newprog, -1)
+ pcdata = ctxt.UnspillRegisterArgs(callend, newprog)
+ pcdata = ctxt.EndUnsafePoint(pcdata, newprog, -1)
jmp := obj.Appendp(pcdata, newprog)
jmp.As = obj.AJMP
@@ -1147,9 +1173,9 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
jmp.To.SetTarget(cursym.Func().Text.Link)
jmp.Spadj = +framesize
- jls.To.SetTarget(call)
+ jls.To.SetTarget(spill)
if q1 != nil {
- q1.To.SetTarget(call)
+ q1.To.SetTarget(spill)
}
return end
diff --git a/src/cmd/internal/objabi/funcid.go b/src/cmd/internal/objabi/funcid.go
index 1d098ee172..6e188e31bb 100644
--- a/src/cmd/internal/objabi/funcid.go
+++ b/src/cmd/internal/objabi/funcid.go
@@ -4,97 +4,89 @@
package objabi
+import "strings"
+
+// A FuncFlag records bits about a function, passed to the runtime.
+type FuncFlag uint8
+
+// Note: This list must match the list in runtime/symtab.go.
+const (
+ FuncFlag_TOPFRAME = 1 << iota
+ FuncFlag_SPWRITE
+)
+
// A FuncID identifies particular functions that need to be treated
// specially by the runtime.
// Note that in some situations involving plugins, there may be multiple
// copies of a particular special runtime function.
-// Note: this list must match the list in runtime/symtab.go.
type FuncID uint8
+// Note: this list must match the list in runtime/symtab.go.
const (
FuncID_normal FuncID = iota // not a special function
- FuncID_runtime_main
+ FuncID_asmcgocall
+ FuncID_asyncPreempt
+ FuncID_cgocallback
+ FuncID_debugCallV1
+ FuncID_externalthreadhandler
+ FuncID_gcBgMarkWorker
FuncID_goexit
+ FuncID_gogo
+ FuncID_gopanic
+ FuncID_handleAsyncEvent
FuncID_jmpdefer
FuncID_mcall
FuncID_morestack
FuncID_mstart
+ FuncID_panicwrap
FuncID_rt0_go
- FuncID_asmcgocall
- FuncID_sigpanic
FuncID_runfinq
- FuncID_gcBgMarkWorker
- FuncID_systemstack_switch
+ FuncID_runtime_main
+ FuncID_sigpanic
FuncID_systemstack
- FuncID_cgocallback
- FuncID_gogo
- FuncID_externalthreadhandler
- FuncID_debugCallV1
- FuncID_gopanic
- FuncID_panicwrap
- FuncID_handleAsyncEvent
- FuncID_asyncPreempt
+ FuncID_systemstack_switch
FuncID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.)
)
+var funcIDs = map[string]FuncID{
+ "asmcgocall": FuncID_asmcgocall,
+ "asyncPreempt": FuncID_asyncPreempt,
+ "cgocallback": FuncID_cgocallback,
+ "debugCallV1": FuncID_debugCallV1,
+ "externalthreadhandler": FuncID_externalthreadhandler,
+ "gcBgMarkWorker": FuncID_gcBgMarkWorker,
+ "go": FuncID_rt0_go,
+ "goexit": FuncID_goexit,
+ "gogo": FuncID_gogo,
+ "gopanic": FuncID_gopanic,
+ "handleAsyncEvent": FuncID_handleAsyncEvent,
+ "jmpdefer": FuncID_jmpdefer,
+ "main": FuncID_runtime_main,
+ "mcall": FuncID_mcall,
+ "morestack": FuncID_morestack,
+ "mstart": FuncID_mstart,
+ "panicwrap": FuncID_panicwrap,
+ "runfinq": FuncID_runfinq,
+ "sigpanic": FuncID_sigpanic,
+ "switch": FuncID_systemstack_switch,
+ "systemstack": FuncID_systemstack,
+
+ // Don't show in call stack but otherwise not special.
+ "deferreturn": FuncID_wrapper,
+ "runOpenDeferFrame": FuncID_wrapper,
+ "reflectcallSave": FuncID_wrapper,
+}
+
// Get the function ID for the named function in the named file.
// The function should be package-qualified.
func GetFuncID(name string, isWrapper bool) FuncID {
if isWrapper {
return FuncID_wrapper
}
- switch name {
- case "runtime.main":
- return FuncID_runtime_main
- case "runtime.goexit":
- return FuncID_goexit
- case "runtime.jmpdefer":
- return FuncID_jmpdefer
- case "runtime.mcall":
- return FuncID_mcall
- case "runtime.morestack":
- return FuncID_morestack
- case "runtime.mstart":
- return FuncID_mstart
- case "runtime.rt0_go":
- return FuncID_rt0_go
- case "runtime.asmcgocall":
- return FuncID_asmcgocall
- case "runtime.sigpanic":
- return FuncID_sigpanic
- case "runtime.runfinq":
- return FuncID_runfinq
- case "runtime.gcBgMarkWorker":
- return FuncID_gcBgMarkWorker
- case "runtime.systemstack_switch":
- return FuncID_systemstack_switch
- case "runtime.systemstack":
- return FuncID_systemstack
- case "runtime.cgocallback":
- return FuncID_cgocallback
- case "runtime.gogo":
- return FuncID_gogo
- case "runtime.externalthreadhandler":
- return FuncID_externalthreadhandler
- case "runtime.debugCallV1":
- return FuncID_debugCallV1
- case "runtime.gopanic":
- return FuncID_gopanic
- case "runtime.panicwrap":
- return FuncID_panicwrap
- case "runtime.handleAsyncEvent":
- return FuncID_handleAsyncEvent
- case "runtime.asyncPreempt":
- return FuncID_asyncPreempt
- case "runtime.deferreturn":
- // Don't show in the call stack (used when invoking defer functions)
- return FuncID_wrapper
- case "runtime.runOpenDeferFrame":
- // Don't show in the call stack (used when invoking defer functions)
- return FuncID_wrapper
- case "runtime.reflectcallSave":
- // Don't show in the call stack (used when invoking defer functions)
- return FuncID_wrapper
+ if strings.HasPrefix(name, "runtime.") {
+ if id, ok := funcIDs[name[len("runtime."):]]; ok {
+ return id
+ }
}
return FuncID_normal
}
diff --git a/src/cmd/internal/objabi/path.go b/src/cmd/internal/objabi/path.go
index fd1c9981c6..1a0784cf7f 100644
--- a/src/cmd/internal/objabi/path.go
+++ b/src/cmd/internal/objabi/path.go
@@ -56,6 +56,8 @@ func IsRuntimePackagePath(pkgpath string) bool {
rval = true
case "syscall":
rval = true
+ case "crypto/x509/internal/macos": // libc function wrappers need to be ABIInternal
+ rval = true
default:
rval = strings.HasPrefix(pkgpath, "runtime/internal")
}
diff --git a/src/cmd/internal/objabi/util.go b/src/cmd/internal/objabi/util.go
index a73ab479a1..1f99f8ed5d 100644
--- a/src/cmd/internal/objabi/util.go
+++ b/src/cmd/internal/objabi/util.go
@@ -137,7 +137,7 @@ func init() {
}
// Note: must agree with runtime.framepointer_enabled.
-var Framepointer_enabled = GOARCH == "amd64" || GOARCH == "arm64" && (GOOS == "linux" || GOOS == "darwin" || GOOS == "ios")
+var Framepointer_enabled = GOARCH == "amd64" || GOARCH == "arm64"
func addexp(s string) {
// Could do general integer parsing here, but the runtime copy doesn't yet.
diff --git a/src/cmd/internal/objfile/pe.go b/src/cmd/internal/objfile/pe.go
index b20cda9a44..9088866fcf 100644
--- a/src/cmd/internal/objfile/pe.go
+++ b/src/cmd/internal/objfile/pe.go
@@ -189,6 +189,8 @@ func (f *peFile) goarch() string {
return "amd64"
case pe.IMAGE_FILE_MACHINE_ARMNT:
return "arm"
+ case pe.IMAGE_FILE_MACHINE_ARM64:
+ return "arm64"
default:
return ""
}
diff --git a/src/cmd/internal/objfile/xcoff.go b/src/cmd/internal/objfile/xcoff.go
index d438c80226..d6df4db8f0 100644
--- a/src/cmd/internal/objfile/xcoff.go
+++ b/src/cmd/internal/objfile/xcoff.go
@@ -94,9 +94,7 @@ func (f *xcoffFile) pcln() (textStart uint64, symtab, pclntab []byte, err error)
if pclntab, err = loadXCOFFTable(f.xcoff, "runtime.pclntab", "runtime.epclntab"); err != nil {
return 0, nil, nil, err
}
- if symtab, err = loadXCOFFTable(f.xcoff, "runtime.symtab", "runtime.esymtab"); err != nil {
- return 0, nil, nil, err
- }
+ symtab, _ = loadXCOFFTable(f.xcoff, "runtime.symtab", "runtime.esymtab") // ignore error, this symbol is not useful anyway
return textStart, symtab, pclntab, nil
}
diff --git a/src/cmd/internal/pkgpath/pkgpath.go b/src/cmd/internal/pkgpath/pkgpath.go
index 40a040a81a..72e3bdb631 100644
--- a/src/cmd/internal/pkgpath/pkgpath.go
+++ b/src/cmd/internal/pkgpath/pkgpath.go
@@ -10,9 +10,9 @@ import (
"bytes"
"errors"
"fmt"
+ exec "internal/execabs"
"io/ioutil"
"os"
- "os/exec"
"strings"
)