diff options
Diffstat (limited to 'src/cmd/internal')
62 files changed, 3020 insertions, 957 deletions
diff --git a/src/cmd/internal/archive/archive_test.go b/src/cmd/internal/archive/archive_test.go index 1468a58210..cb4eb842b4 100644 --- a/src/cmd/internal/archive/archive_test.go +++ b/src/cmd/internal/archive/archive_test.go @@ -243,7 +243,7 @@ func TestParseCGOArchive(t *testing.T) { c1 := "c1" c2 := "c2" switch runtime.GOOS { - case "darwin": + case "darwin", "ios": c1 = "_" + c1 c2 = "_" + c2 case "windows": @@ -275,7 +275,7 @@ func TestParseCGOArchive(t *testing.T) { obj := io.NewSectionReader(f, e.Offset, e.Size) switch runtime.GOOS { - case "darwin": + case "darwin", "ios": mf, err := macho.NewFile(obj) if err != nil { t.Fatal(err) diff --git a/src/cmd/internal/buildid/buildid.go b/src/cmd/internal/buildid/buildid.go index ac238d70ea..1e8855d3ac 100644 --- a/src/cmd/internal/buildid/buildid.go +++ b/src/cmd/internal/buildid/buildid.go @@ -10,18 +10,15 @@ import ( "fmt" "internal/xcoff" "io" + "io/fs" "os" "strconv" "strings" ) var ( - errBuildIDToolchain = fmt.Errorf("build ID only supported in gc toolchain") errBuildIDMalformed = fmt.Errorf("malformed object file") - errBuildIDUnknown = fmt.Errorf("lost build ID") -) -var ( bangArch = []byte("!<arch>") pkgdef = []byte("__.PKGDEF") goobject = []byte("go object ") @@ -109,7 +106,7 @@ func ReadFile(name string) (id string, err error) { // in cmd/go/internal/work/exec.go. func readGccgoArchive(name string, f *os.File) (string, error) { bad := func() (string, error) { - return "", &os.PathError{Op: "parse", Path: name, Err: errBuildIDMalformed} + return "", &fs.PathError{Op: "parse", Path: name, Err: errBuildIDMalformed} } off := int64(8) @@ -167,7 +164,7 @@ func readGccgoArchive(name string, f *os.File) (string, error) { // in cmd/go/internal/work/exec.go. func readGccgoBigArchive(name string, f *os.File) (string, error) { bad := func() (string, error) { - return "", &os.PathError{Op: "parse", Path: name, Err: errBuildIDMalformed} + return "", &fs.PathError{Op: "parse", Path: name, Err: errBuildIDMalformed} } // Read fixed-length header. @@ -309,13 +306,38 @@ func readRaw(name string, data []byte) (id string, err error) { j := bytes.Index(data[i+len(goBuildPrefix):], goBuildEnd) if j < 0 { - return "", &os.PathError{Op: "parse", Path: name, Err: errBuildIDMalformed} + return "", &fs.PathError{Op: "parse", Path: name, Err: errBuildIDMalformed} } quoted := data[i+len(goBuildPrefix)-1 : i+len(goBuildPrefix)+j+1] id, err = strconv.Unquote(string(quoted)) if err != nil { - return "", &os.PathError{Op: "parse", Path: name, Err: errBuildIDMalformed} + return "", &fs.PathError{Op: "parse", Path: name, Err: errBuildIDMalformed} } return id, nil } + +// HashToString converts the hash h to a string to be recorded +// in package archives and binaries as part of the build ID. +// We use the first 120 bits of the hash (5 chunks of 24 bits each) and encode +// it in base64, resulting in a 20-byte string. Because this is only used for +// detecting the need to rebuild installed files (not for lookups +// in the object file cache), 120 bits are sufficient to drive the +// probability of a false "do not need to rebuild" decision to effectively zero. +// We embed two different hashes in archives and four in binaries, +// so cutting to 20 bytes is a significant savings when build IDs are displayed. +// (20*4+3 = 83 bytes compared to 64*4+3 = 259 bytes for the +// more straightforward option of printing the entire h in base64). +func HashToString(h [32]byte) string { + const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" + const chunks = 5 + var dst [chunks * 4]byte + for i := 0; i < chunks; i++ { + v := uint32(h[3*i])<<16 | uint32(h[3*i+1])<<8 | uint32(h[3*i+2]) + dst[4*i+0] = b64[(v>>18)&0x3F] + dst[4*i+1] = b64[(v>>12)&0x3F] + dst[4*i+2] = b64[(v>>6)&0x3F] + dst[4*i+3] = b64[v&0x3F] + } + return string(dst[:]) +} diff --git a/src/cmd/internal/buildid/buildid_test.go b/src/cmd/internal/buildid/buildid_test.go index 904c2c6f37..e832f9987e 100644 --- a/src/cmd/internal/buildid/buildid_test.go +++ b/src/cmd/internal/buildid/buildid_test.go @@ -11,6 +11,7 @@ import ( "io/ioutil" "os" "reflect" + "strings" "testing" ) @@ -146,3 +147,33 @@ func TestFindAndHash(t *testing.T) { } } } + +func TestExcludedReader(t *testing.T) { + const s = "0123456789abcdefghijklmn" + tests := []struct { + start, end int64 // excluded range + results []string // expected results of reads + }{ + {12, 15, []string{"0123456789", "ab\x00\x00\x00fghij", "klmn"}}, // within one read + {8, 21, []string{"01234567\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x00lmn"}}, // across multiple reads + {10, 20, []string{"0123456789", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", "klmn"}}, // a whole read + {0, 5, []string{"\x00\x00\x00\x00\x0056789", "abcdefghij", "klmn"}}, // start + {12, 24, []string{"0123456789", "ab\x00\x00\x00\x00\x00\x00\x00\x00", "\x00\x00\x00\x00"}}, // end + } + p := make([]byte, 10) + for _, test := range tests { + r := &excludedReader{strings.NewReader(s), 0, test.start, test.end} + for _, res := range test.results { + n, err := r.Read(p) + if err != nil { + t.Errorf("read failed: %v", err) + } + if n != len(res) { + t.Errorf("unexpected number of bytes read: want %d, got %d", len(res), n) + } + if string(p[:n]) != res { + t.Errorf("unexpected bytes: want %q, got %q", res, p[:n]) + } + } + } +} diff --git a/src/cmd/internal/buildid/note.go b/src/cmd/internal/buildid/note.go index 2d26ea9961..f5b6fc565f 100644 --- a/src/cmd/internal/buildid/note.go +++ b/src/cmd/internal/buildid/note.go @@ -11,6 +11,7 @@ import ( "encoding/binary" "fmt" "io" + "io/fs" "os" ) @@ -96,7 +97,7 @@ func readELF(name string, f *os.File, data []byte) (buildid string, err error) { ef, err := elf.NewFile(bytes.NewReader(data)) if err != nil { - return "", &os.PathError{Path: name, Op: "parse", Err: err} + return "", &fs.PathError{Path: name, Op: "parse", Err: err} } var gnu string for _, p := range ef.Progs { @@ -181,13 +182,13 @@ func readMacho(name string, f *os.File, data []byte) (buildid string, err error) mf, err := macho.NewFile(f) if err != nil { - return "", &os.PathError{Path: name, Op: "parse", Err: err} + return "", &fs.PathError{Path: name, Op: "parse", Err: err} } sect := mf.Section("__text") if sect == nil { // Every binary has a __text section. Something is wrong. - return "", &os.PathError{Path: name, Op: "parse", Err: fmt.Errorf("cannot find __text section")} + return "", &fs.PathError{Path: name, Op: "parse", Err: fmt.Errorf("cannot find __text section")} } // It should be in the first few bytes, but read a lot just in case, diff --git a/src/cmd/internal/buildid/rewrite.go b/src/cmd/internal/buildid/rewrite.go index 5be54552a6..a7928959c4 100644 --- a/src/cmd/internal/buildid/rewrite.go +++ b/src/cmd/internal/buildid/rewrite.go @@ -6,7 +6,9 @@ package buildid import ( "bytes" + "cmd/internal/codesign" "crypto/sha256" + "debug/macho" "fmt" "io" ) @@ -26,6 +28,11 @@ func FindAndHash(r io.Reader, id string, bufSize int) (matches []int64, hash [32 zeros := make([]byte, len(id)) idBytes := []byte(id) + // For Mach-O files, we want to exclude the code signature. + // The code signature contains hashes of the whole file (except the signature + // itself), including the buildid. So the buildid cannot contain the signature. + r = excludeMachoCodeSignature(r) + // The strategy is to read the file through buf, looking for id, // but we need to worry about what happens if id is broken up // and returned in parts by two different reads. @@ -87,5 +94,69 @@ func Rewrite(w io.WriterAt, pos []int64, id string) error { return err } } + + // Update Mach-O code signature, if any. + if f, cmd, ok := findMachoCodeSignature(w); ok { + if codesign.Size(int64(cmd.Dataoff), "a.out") == int64(cmd.Datasize) { + // Update the signature if the size matches, so we don't need to + // fix up headers. Binaries generated by the Go linker should have + // the expected size. Otherwise skip. + text := f.Segment("__TEXT") + cs := make([]byte, cmd.Datasize) + codesign.Sign(cs, w.(io.Reader), "a.out", int64(cmd.Dataoff), int64(text.Offset), int64(text.Filesz), f.Type == macho.TypeExec) + if _, err := w.WriteAt(cs, int64(cmd.Dataoff)); err != nil { + return err + } + } + } + return nil } + +func excludeMachoCodeSignature(r io.Reader) io.Reader { + _, cmd, ok := findMachoCodeSignature(r) + if !ok { + return r + } + return &excludedReader{r, 0, int64(cmd.Dataoff), int64(cmd.Dataoff + cmd.Datasize)} +} + +// excludedReader wraps an io.Reader. Reading from it returns the bytes from +// the underlying reader, except that when the byte offset is within the +// range between start and end, it returns zero bytes. +type excludedReader struct { + r io.Reader + off int64 // current offset + start, end int64 // the range to be excluded (read as zero) +} + +func (r *excludedReader) Read(p []byte) (int, error) { + n, err := r.r.Read(p) + if n > 0 && r.off+int64(n) > r.start && r.off < r.end { + cstart := r.start - r.off + if cstart < 0 { + cstart = 0 + } + cend := r.end - r.off + if cend > int64(n) { + cend = int64(n) + } + zeros := make([]byte, cend-cstart) + copy(p[cstart:cend], zeros) + } + r.off += int64(n) + return n, err +} + +func findMachoCodeSignature(r interface{}) (*macho.File, codesign.CodeSigCmd, bool) { + ra, ok := r.(io.ReaderAt) + if !ok { + return nil, codesign.CodeSigCmd{}, false + } + f, err := macho.NewFile(ra) + if err != nil { + return nil, codesign.CodeSigCmd{}, false + } + cmd, ok := codesign.FindCodeSigCmd(f) + return f, cmd, ok +} diff --git a/src/cmd/internal/codesign/codesign.go b/src/cmd/internal/codesign/codesign.go new file mode 100644 index 0000000000..0517a10640 --- /dev/null +++ b/src/cmd/internal/codesign/codesign.go @@ -0,0 +1,268 @@ +// Copyright 2020 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. + +// Package codesign provides basic functionalities for +// ad-hoc code signing of Mach-O files. +// +// This is not a general tool for code-signing. It is made +// specifically for the Go toolchain. It uses the same +// ad-hoc signing algorithm as the Darwin linker. +package codesign + +import ( + "crypto/sha256" + "debug/macho" + "encoding/binary" + "io" +) + +// Code signature layout. +// +// The code signature is a block of bytes that contains +// a SuperBlob, which contains one or more Blobs. For ad-hoc +// signing, a single CodeDirectory Blob suffices. +// +// A SuperBlob starts with its header (the binary representation +// of the SuperBlob struct), followed by a list of (in our case, +// one) Blobs (offset and size). A CodeDirectory Blob starts +// with its head (the binary representation of CodeDirectory struct), +// followed by the identifier (as a C string) and the hashes, at +// the corresponding offsets. +// +// The signature data must be included in the __LINKEDIT segment. +// In the Mach-O file header, an LC_CODE_SIGNATURE load command +// points to the data. + +const ( + pageSizeBits = 12 + pageSize = 1 << pageSizeBits +) + +const LC_CODE_SIGNATURE = 0x1d + +// Constants and struct layouts are from +// https://opensource.apple.com/source/xnu/xnu-4903.270.47/osfmk/kern/cs_blobs.h + +const ( + CSMAGIC_REQUIREMENT = 0xfade0c00 // single Requirement blob + CSMAGIC_REQUIREMENTS = 0xfade0c01 // Requirements vector (internal requirements) + CSMAGIC_CODEDIRECTORY = 0xfade0c02 // CodeDirectory blob + CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0 // embedded form of signature data + CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1 // multi-arch collection of embedded signatures + + CSSLOT_CODEDIRECTORY = 0 // slot index for CodeDirectory +) + +const ( + CS_HASHTYPE_SHA1 = 1 + CS_HASHTYPE_SHA256 = 2 + CS_HASHTYPE_SHA256_TRUNCATED = 3 + CS_HASHTYPE_SHA384 = 4 +) + +const ( + CS_EXECSEG_MAIN_BINARY = 0x1 // executable segment denotes main binary + CS_EXECSEG_ALLOW_UNSIGNED = 0x10 // allow unsigned pages (for debugging) + CS_EXECSEG_DEBUGGER = 0x20 // main binary is debugger + CS_EXECSEG_JIT = 0x40 // JIT enabled + CS_EXECSEG_SKIP_LV = 0x80 // skip library validation + CS_EXECSEG_CAN_LOAD_CDHASH = 0x100 // can bless cdhash for execution + CS_EXECSEG_CAN_EXEC_CDHASH = 0x200 // can execute blessed cdhash +) + +type Blob struct { + typ uint32 // type of entry + offset uint32 // offset of entry + // data follows +} + +func (b *Blob) put(out []byte) []byte { + out = put32be(out, b.typ) + out = put32be(out, b.offset) + return out +} + +const blobSize = 2 * 4 + +type SuperBlob struct { + magic uint32 // magic number + length uint32 // total length of SuperBlob + count uint32 // number of index entries following + // blobs []Blob +} + +func (s *SuperBlob) put(out []byte) []byte { + out = put32be(out, s.magic) + out = put32be(out, s.length) + out = put32be(out, s.count) + return out +} + +const superBlobSize = 3 * 4 + +type CodeDirectory struct { + magic uint32 // magic number (CSMAGIC_CODEDIRECTORY) + length uint32 // total length of CodeDirectory blob + version uint32 // compatibility version + flags uint32 // setup and mode flags + hashOffset uint32 // offset of hash slot element at index zero + identOffset uint32 // offset of identifier string + nSpecialSlots uint32 // number of special hash slots + nCodeSlots uint32 // number of ordinary (code) hash slots + codeLimit uint32 // limit to main image signature range + hashSize uint8 // size of each hash in bytes + hashType uint8 // type of hash (cdHashType* constants) + _pad1 uint8 // unused (must be zero) + pageSize uint8 // log2(page size in bytes); 0 => infinite + _pad2 uint32 // unused (must be zero) + scatterOffset uint32 + teamOffset uint32 + _pad3 uint32 + codeLimit64 uint64 + execSegBase uint64 + execSegLimit uint64 + execSegFlags uint64 + // data follows +} + +func (c *CodeDirectory) put(out []byte) []byte { + out = put32be(out, c.magic) + out = put32be(out, c.length) + out = put32be(out, c.version) + out = put32be(out, c.flags) + out = put32be(out, c.hashOffset) + out = put32be(out, c.identOffset) + out = put32be(out, c.nSpecialSlots) + out = put32be(out, c.nCodeSlots) + out = put32be(out, c.codeLimit) + out = put8(out, c.hashSize) + out = put8(out, c.hashType) + out = put8(out, c._pad1) + out = put8(out, c.pageSize) + out = put32be(out, c._pad2) + out = put32be(out, c.scatterOffset) + out = put32be(out, c.teamOffset) + out = put32be(out, c._pad3) + out = put64be(out, c.codeLimit64) + out = put64be(out, c.execSegBase) + out = put64be(out, c.execSegLimit) + out = put64be(out, c.execSegFlags) + return out +} + +const codeDirectorySize = 13*4 + 4 + 4*8 + +// CodeSigCmd is Mach-O LC_CODE_SIGNATURE load command. +type CodeSigCmd struct { + Cmd uint32 // LC_CODE_SIGNATURE + Cmdsize uint32 // sizeof this command (16) + Dataoff uint32 // file offset of data in __LINKEDIT segment + Datasize uint32 // file size of data in __LINKEDIT segment +} + +func FindCodeSigCmd(f *macho.File) (CodeSigCmd, bool) { + get32 := f.ByteOrder.Uint32 + for _, l := range f.Loads { + data := l.Raw() + cmd := get32(data) + if cmd == LC_CODE_SIGNATURE { + return CodeSigCmd{ + cmd, + get32(data[4:]), + get32(data[8:]), + get32(data[12:]), + }, true + } + } + return CodeSigCmd{}, false +} + +func put32be(b []byte, x uint32) []byte { binary.BigEndian.PutUint32(b, x); return b[4:] } +func put64be(b []byte, x uint64) []byte { binary.BigEndian.PutUint64(b, x); return b[8:] } +func put8(b []byte, x uint8) []byte { b[0] = x; return b[1:] } +func puts(b, s []byte) []byte { n := copy(b, s); return b[n:] } + +// Size computes the size of the code signature. +// id is the identifier used for signing (a field in CodeDirectory blob, which +// has no significance in ad-hoc signing). +func Size(codeSize int64, id string) int64 { + nhashes := (codeSize + pageSize - 1) / pageSize + idOff := int64(codeDirectorySize) + hashOff := idOff + int64(len(id)+1) + cdirSz := hashOff + nhashes*sha256.Size + return int64(superBlobSize+blobSize) + cdirSz +} + +// Sign generates an ad-hoc code signature and writes it to out. +// out must have length at least Size(codeSize, id). +// data is the file content without the signature, of size codeSize. +// textOff and textSize is the file offset and size of the text segment. +// isMain is true if this is a main executable. +// id is the identifier used for signing (a field in CodeDirectory blob, which +// has no significance in ad-hoc signing). +func Sign(out []byte, data io.Reader, id string, codeSize, textOff, textSize int64, isMain bool) { + nhashes := (codeSize + pageSize - 1) / pageSize + idOff := int64(codeDirectorySize) + hashOff := idOff + int64(len(id)+1) + sz := Size(codeSize, id) + + // emit blob headers + sb := SuperBlob{ + magic: CSMAGIC_EMBEDDED_SIGNATURE, + length: uint32(sz), + count: 1, + } + blob := Blob{ + typ: CSSLOT_CODEDIRECTORY, + offset: superBlobSize + blobSize, + } + cdir := CodeDirectory{ + magic: CSMAGIC_CODEDIRECTORY, + length: uint32(sz) - (superBlobSize + blobSize), + version: 0x20400, + flags: 0x20002, // adhoc | linkerSigned + hashOffset: uint32(hashOff), + identOffset: uint32(idOff), + nCodeSlots: uint32(nhashes), + codeLimit: uint32(codeSize), + hashSize: sha256.Size, + hashType: CS_HASHTYPE_SHA256, + pageSize: uint8(pageSizeBits), + execSegBase: uint64(textOff), + execSegLimit: uint64(textSize), + } + if isMain { + cdir.execSegFlags = CS_EXECSEG_MAIN_BINARY + } + + outp := out + outp = sb.put(outp) + outp = blob.put(outp) + outp = cdir.put(outp) + + // emit the identifier + outp = puts(outp, []byte(id+"\000")) + + // emit hashes + var buf [pageSize]byte + h := sha256.New() + p := 0 + for p < int(codeSize) { + n, err := io.ReadFull(data, buf[:]) + if err == io.EOF { + break + } + if err != nil && err != io.ErrUnexpectedEOF { + panic(err) + } + if p+n > int(codeSize) { + n = int(codeSize) - p + } + p += n + h.Reset() + h.Write(buf[:n]) + b := h.Sum(nil) + outp = puts(outp, b[:]) + } +} diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go index 6ab83639ac..e1a70ef853 100644 --- a/src/cmd/internal/dwarf/dwarf.go +++ b/src/cmd/internal/dwarf/dwarf.go @@ -101,26 +101,26 @@ func EnableLogging(doit bool) { logDwarf = doit } -// UnifyRanges merges the list of ranges of c into the list of ranges of s -func (s *Scope) UnifyRanges(c *Scope) { - out := make([]Range, 0, len(s.Ranges)+len(c.Ranges)) - +// MergeRanges creates a new range list by merging the ranges from +// its two arguments, then returns the new list. +func MergeRanges(in1, in2 []Range) []Range { + out := make([]Range, 0, len(in1)+len(in2)) i, j := 0, 0 for { var cur Range - if i < len(s.Ranges) && j < len(c.Ranges) { - if s.Ranges[i].Start < c.Ranges[j].Start { - cur = s.Ranges[i] + if i < len(in2) && j < len(in1) { + if in2[i].Start < in1[j].Start { + cur = in2[i] i++ } else { - cur = c.Ranges[j] + cur = in1[j] j++ } - } else if i < len(s.Ranges) { - cur = s.Ranges[i] + } else if i < len(in2) { + cur = in2[i] i++ - } else if j < len(c.Ranges) { - cur = c.Ranges[j] + } else if j < len(in1) { + cur = in1[j] j++ } else { break @@ -133,7 +133,12 @@ func (s *Scope) UnifyRanges(c *Scope) { } } - s.Ranges = out + return out +} + +// UnifyRanges merges the ranges from 'c' into the list of ranges for 's'. +func (s *Scope) UnifyRanges(c *Scope) { + s.Ranges = MergeRanges(s.Ranges, c.Ranges) } // AppendRange adds r to s, if r is non-empty. @@ -378,7 +383,7 @@ func expandPseudoForm(form uint8) uint8 { return form } expandedForm := DW_FORM_udata - if objabi.GOOS == "darwin" { + if objabi.GOOS == "darwin" || objabi.GOOS == "ios" { expandedForm = DW_FORM_data4 } return uint8(expandedForm) diff --git a/src/cmd/internal/goobj/funcinfo.go b/src/cmd/internal/goobj/funcinfo.go index e0e6068b4b..2cca8f6c4e 100644 --- a/src/cmd/internal/goobj/funcinfo.go +++ b/src/cmd/internal/goobj/funcinfo.go @@ -23,12 +23,11 @@ type FuncInfo struct { Locals uint32 FuncID objabi.FuncID - Pcsp uint32 - Pcfile uint32 - Pcline uint32 - Pcinline uint32 - Pcdata []uint32 - PcdataEnd uint32 + Pcsp SymRef + Pcfile SymRef + Pcline SymRef + Pcinline SymRef + Pcdata []SymRef Funcdataoff []uint32 File []CUFileIndex @@ -41,20 +40,24 @@ func (a *FuncInfo) Write(w *bytes.Buffer) { binary.LittleEndian.PutUint32(b[:], x) w.Write(b[:]) } + writeSymRef := func(s SymRef) { + writeUint32(s.PkgIdx) + writeUint32(s.SymIdx) + } writeUint32(a.Args) writeUint32(a.Locals) writeUint32(uint32(a.FuncID)) - writeUint32(a.Pcsp) - writeUint32(a.Pcfile) - writeUint32(a.Pcline) - writeUint32(a.Pcinline) + writeSymRef(a.Pcsp) + writeSymRef(a.Pcfile) + writeSymRef(a.Pcline) + writeSymRef(a.Pcinline) writeUint32(uint32(len(a.Pcdata))) - for _, x := range a.Pcdata { - writeUint32(x) + for _, sym := range a.Pcdata { + writeSymRef(sym) } - writeUint32(a.PcdataEnd) + writeUint32(uint32(len(a.Funcdataoff))) for _, x := range a.Funcdataoff { writeUint32(x) @@ -75,21 +78,23 @@ func (a *FuncInfo) Read(b []byte) { 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 = readUint32() - a.Pcfile = readUint32() - a.Pcline = readUint32() - a.Pcinline = readUint32() - pcdatalen := readUint32() - a.Pcdata = make([]uint32, pcdatalen) + 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] = readUint32() + a.Pcdata[i] = readSymIdx() } - a.PcdataEnd = readUint32() + funcdataofflen := readUint32() a.Funcdataoff = make([]uint32, funcdataofflen) for i := range a.Funcdataoff { @@ -127,11 +132,13 @@ type FuncInfoLengths struct { func (*FuncInfo) ReadFuncInfoLengths(b []byte) FuncInfoLengths { var result FuncInfoLengths - const numpcdataOff = 28 + // Offset to the number of pcdata values. This value is determined by counting + // the number of bytes until we write pcdata to the file. + const numpcdataOff = 44 result.NumPcdata = binary.LittleEndian.Uint32(b[numpcdataOff:]) result.PcdataOff = numpcdataOff + 4 - numfuncdataoffOff := result.PcdataOff + 4*(result.NumPcdata+1) + numfuncdataoffOff := result.PcdataOff + 8*result.NumPcdata result.NumFuncdataoff = binary.LittleEndian.Uint32(b[numfuncdataoffOff:]) result.FuncdataoffOff = numfuncdataoffOff + 4 @@ -154,29 +161,28 @@ func (*FuncInfo) ReadLocals(b []byte) uint32 { return binary.LittleEndian.Uint32 func (*FuncInfo) ReadFuncID(b []byte) uint32 { return binary.LittleEndian.Uint32(b[8:]) } -// return start and end offsets. -func (*FuncInfo) ReadPcsp(b []byte) (uint32, uint32) { - return binary.LittleEndian.Uint32(b[12:]), binary.LittleEndian.Uint32(b[16:]) +func (*FuncInfo) ReadPcsp(b []byte) SymRef { + return SymRef{binary.LittleEndian.Uint32(b[12:]), binary.LittleEndian.Uint32(b[16:])} } -// return start and end offsets. -func (*FuncInfo) ReadPcfile(b []byte) (uint32, uint32) { - return binary.LittleEndian.Uint32(b[16:]), binary.LittleEndian.Uint32(b[20:]) +func (*FuncInfo) ReadPcfile(b []byte) SymRef { + return SymRef{binary.LittleEndian.Uint32(b[20:]), binary.LittleEndian.Uint32(b[24:])} } -// return start and end offsets. -func (*FuncInfo) ReadPcline(b []byte) (uint32, uint32) { - return binary.LittleEndian.Uint32(b[20:]), binary.LittleEndian.Uint32(b[24:]) +func (*FuncInfo) ReadPcline(b []byte) SymRef { + return SymRef{binary.LittleEndian.Uint32(b[28:]), binary.LittleEndian.Uint32(b[32:])} } -// return start and end offsets. -func (*FuncInfo) ReadPcinline(b []byte, pcdataoffset uint32) (uint32, uint32) { - return binary.LittleEndian.Uint32(b[24:]), binary.LittleEndian.Uint32(b[pcdataoffset:]) +func (*FuncInfo) ReadPcinline(b []byte) SymRef { + return SymRef{binary.LittleEndian.Uint32(b[36:]), binary.LittleEndian.Uint32(b[40:])} } -// return start and end offsets. -func (*FuncInfo) ReadPcdata(b []byte, pcdataoffset uint32, k uint32) (uint32, uint32) { - return binary.LittleEndian.Uint32(b[pcdataoffset+4*k:]), binary.LittleEndian.Uint32(b[pcdataoffset+4+4*k:]) +func (*FuncInfo) ReadPcdata(b []byte) []SymRef { + syms := make([]SymRef, binary.LittleEndian.Uint32(b[44:])) + for i := range syms { + syms[i] = SymRef{binary.LittleEndian.Uint32(b[48+i*8:]), binary.LittleEndian.Uint32(b[52+i*8:])} + } + return syms } func (*FuncInfo) ReadFuncdataoff(b []byte, funcdataofffoff uint32, k uint32) int64 { diff --git a/src/cmd/internal/goobj/objfile.go b/src/cmd/internal/goobj/objfile.go index 8ec7c481d6..6e76bea111 100644 --- a/src/cmd/internal/goobj/objfile.go +++ b/src/cmd/internal/goobj/objfile.go @@ -433,8 +433,11 @@ const ( AuxDwarfLoc AuxDwarfRanges AuxDwarfLines - - // TODO: more. Pcdata? + AuxPcsp + AuxPcfile + AuxPcline + AuxPcinline + AuxPcdata ) func (a *Aux) Type() uint8 { return a[0] } @@ -839,11 +842,6 @@ func (r *Reader) Data(i uint32) []byte { return r.BytesAt(base+off, int(end-off)) } -// AuxDataBase returns the base offset of the aux data block. -func (r *Reader) PcdataBase() uint32 { - return r.h.Offsets[BlkPcdata] -} - // NRefName returns the number of referenced symbol names. func (r *Reader) NRefName() int { return int(r.h.Offsets[BlkRefName+1]-r.h.Offsets[BlkRefName]) / RefNameSize diff --git a/src/cmd/internal/moddeps/moddeps_test.go b/src/cmd/internal/moddeps/moddeps_test.go index 2e6167322e..9ea21873c5 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/fs" "io/ioutil" "os" "os/exec" @@ -32,7 +33,7 @@ func findGorootModules(t *testing.T) []gorootModule { goBin := testenv.GoToolPath(t) goroot.once.Do(func() { - goroot.err = filepath.Walk(runtime.GOROOT(), func(path string, info os.FileInfo, err error) error { + goroot.err = filepath.WalkDir(runtime.GOROOT(), func(path string, info fs.DirEntry, err error) error { if err != nil { return err } diff --git a/src/cmd/internal/obj/arm/asm5.go b/src/cmd/internal/obj/arm/asm5.go index 269a4223d5..ebb98b4859 100644 --- a/src/cmd/internal/obj/arm/asm5.go +++ b/src/cmd/internal/obj/arm/asm5.go @@ -390,7 +390,7 @@ func span5(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { var p *obj.Prog var op *obj.Prog - p = cursym.Func.Text + p = cursym.Func().Text if p == nil || p.Link == nil { // handle external functions and ELF section symbols return } @@ -482,8 +482,8 @@ func span5(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { bflag = 0 pc = 0 times++ - c.cursym.Func.Text.Pc = 0 // force re-layout the code. - for p = c.cursym.Func.Text; p != nil; p = p.Link { + c.cursym.Func().Text.Pc = 0 // force re-layout the code. + for p = c.cursym.Func().Text; p != nil; p = p.Link { o = c.oplook(p) if int64(pc) > p.Pc { p.Pc = int64(pc) @@ -558,7 +558,7 @@ func span5(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { * perhaps we'd be able to parallelize the span loop above. */ - p = c.cursym.Func.Text + p = c.cursym.Func().Text c.autosize = p.To.Offset + 4 c.cursym.Grow(c.cursym.Size) diff --git a/src/cmd/internal/obj/arm/obj5.go b/src/cmd/internal/obj/arm/obj5.go index 4d9187b530..29d3a5867d 100644 --- a/src/cmd/internal/obj/arm/obj5.go +++ b/src/cmd/internal/obj/arm/obj5.go @@ -249,13 +249,13 @@ const ( func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { autosize := int32(0) - if cursym.Func.Text == nil || cursym.Func.Text.Link == nil { + if cursym.Func().Text == nil || cursym.Func().Text.Link == nil { return } c := ctxt5{ctxt: ctxt, cursym: cursym, newprog: newprog} - p := c.cursym.Func.Text + p := c.cursym.Func().Text autoffset := int32(p.To.Offset) if autoffset == -4 { // Historical way to mark NOFRAME. @@ -271,30 +271,30 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } } - cursym.Func.Locals = autoffset - cursym.Func.Args = p.To.Val.(int32) + cursym.Func().Locals = autoffset + cursym.Func().Args = p.To.Val.(int32) /* * find leaf subroutines */ - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { switch p.As { case obj.ATEXT: p.Mark |= LEAF case ADIV, ADIVU, AMOD, AMODU: - cursym.Func.Text.Mark &^= LEAF + cursym.Func().Text.Mark &^= LEAF case ABL, ABX, obj.ADUFFZERO, obj.ADUFFCOPY: - cursym.Func.Text.Mark &^= LEAF + cursym.Func().Text.Mark &^= LEAF } } var q2 *obj.Prog - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { o := p.As switch o { case obj.ATEXT: @@ -311,20 +311,20 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { autosize += 4 } - if autosize == 0 && cursym.Func.Text.Mark&LEAF == 0 { + if autosize == 0 && cursym.Func().Text.Mark&LEAF == 0 { // A very few functions that do not return to their caller // are not identified as leaves but still have no frame. if ctxt.Debugvlog { ctxt.Logf("save suppressed in: %s\n", cursym.Name) } - cursym.Func.Text.Mark |= LEAF + cursym.Func().Text.Mark |= LEAF } // FP offsets need an updated p.To.Offset. p.To.Offset = int64(autosize) - 4 - if cursym.Func.Text.Mark&LEAF != 0 { + if cursym.Func().Text.Mark&LEAF != 0 { cursym.Set(obj.AttrLeaf, true) if p.From.Sym.NoFrame() { break @@ -347,7 +347,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p.To.Reg = REGSP p.Spadj = autosize - if cursym.Func.Text.From.Sym.Wrapper() { + if cursym.Func().Text.From.Sym.Wrapper() { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOVW g_panic(g), R1 @@ -460,7 +460,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { case obj.ARET: nocache(p) - if cursym.Func.Text.Mark&LEAF != 0 { + if cursym.Func().Text.Mark&LEAF != 0 { if autosize == 0 { p.As = AB p.From = obj.Addr{} @@ -487,6 +487,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // If there are instructions following // this ARET, they come from a branch // with the same stackframe, so no spadj. + if p.To.Sym != nil { // retjmp p.To.Reg = REGLINK q2 = obj.Appendp(p, newprog) @@ -494,6 +495,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q2.To.Type = obj.TYPE_BRANCH q2.To.Sym = p.To.Sym p.To.Sym = nil + p.To.Name = obj.NAME_NONE p = q2 } @@ -508,7 +510,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } case ADIV, ADIVU, AMOD, AMODU: - if cursym.Func.Text.From.Sym.NoSplit() { + if cursym.Func().Text.From.Sym.NoSplit() { ctxt.Diag("cannot divide in NOSPLIT function") } const debugdivmod = false @@ -720,7 +722,7 @@ func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1) var last *obj.Prog - for last = c.cursym.Func.Text; last.Link != nil; last = last.Link { + for last = c.cursym.Func().Text; last.Link != nil; last = last.Link { } // Now we are at the end of the function, but logically @@ -751,7 +753,7 @@ func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { switch { case c.cursym.CFunc(): morestack = "runtime.morestackc" - case !c.cursym.Func.Text.From.Sym.NeedCtxt(): + case !c.cursym.Func().Text.From.Sym.NeedCtxt(): morestack = "runtime.morestack_noctxt" } call.To.Sym = c.ctxt.Lookup(morestack) @@ -762,7 +764,7 @@ func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { b := obj.Appendp(pcdata, c.newprog) b.As = obj.AJMP b.To.Type = obj.TYPE_BRANCH - b.To.SetTarget(c.cursym.Func.Text.Link) + b.To.SetTarget(c.cursym.Func().Text.Link) b.Spadj = +framesize return end diff --git a/src/cmd/internal/obj/arm64/a.out.go b/src/cmd/internal/obj/arm64/a.out.go index 03e0278a33..1d1bea505c 100644 --- a/src/cmd/internal/obj/arm64/a.out.go +++ b/src/cmd/internal/obj/arm64/a.out.go @@ -410,35 +410,42 @@ const ( C_FCON // floating-point constant C_VCONADDR // 64-bit memory address - C_AACON // ADDCON offset in auto constant $a(FP) - C_LACON // 32-bit offset in auto constant $a(FP) - C_AECON // ADDCON offset in extern constant $e(SB) + C_AACON // ADDCON offset in auto constant $a(FP) + C_AACON2 // 24-bit offset in auto constant $a(FP) + C_LACON // 32-bit offset in auto constant $a(FP) + C_AECON // ADDCON offset in extern constant $e(SB) // TODO(aram): only one branch class should be enough C_SBRA // for TYPE_BRANCH C_LBRA - C_ZAUTO // 0(RSP) - C_NSAUTO_8 // -256 <= x < 0, 0 mod 8 - C_NSAUTO_4 // -256 <= x < 0, 0 mod 4 - C_NSAUTO // -256 <= x < 0 - C_NPAUTO // -512 <= x < 0, 0 mod 8 - C_NAUTO4K // -4095 <= x < 0 - C_PSAUTO_8 // 0 to 255, 0 mod 8 - C_PSAUTO_4 // 0 to 255, 0 mod 4 - C_PSAUTO // 0 to 255 - C_PPAUTO // 0 to 504, 0 mod 8 - C_UAUTO4K_8 // 0 to 4095, 0 mod 8 - C_UAUTO4K_4 // 0 to 4095, 0 mod 4 - C_UAUTO4K_2 // 0 to 4095, 0 mod 2 - C_UAUTO4K // 0 to 4095 - C_UAUTO8K_8 // 0 to 8190, 0 mod 8 - C_UAUTO8K_4 // 0 to 8190, 0 mod 4 - C_UAUTO8K // 0 to 8190, 0 mod 2 - C_UAUTO16K_8 // 0 to 16380, 0 mod 8 - C_UAUTO16K // 0 to 16380, 0 mod 4 - C_UAUTO32K // 0 to 32760, 0 mod 8 - C_LAUTO // any other 32-bit constant + C_ZAUTO // 0(RSP) + C_NSAUTO_8 // -256 <= x < 0, 0 mod 8 + C_NSAUTO_4 // -256 <= x < 0, 0 mod 4 + C_NSAUTO // -256 <= x < 0 + C_NPAUTO // -512 <= x < 0, 0 mod 8 + C_NAUTO4K // -4095 <= x < 0 + C_PSAUTO_8 // 0 to 255, 0 mod 8 + C_PSAUTO_4 // 0 to 255, 0 mod 4 + C_PSAUTO // 0 to 255 + C_PPAUTO_16 // 0 to 504, 0 mod 16 + C_PPAUTO // 0 to 504, 0 mod 8 + C_UAUTO4K_16 // 0 to 4095, 0 mod 16 + C_UAUTO4K_8 // 0 to 4095, 0 mod 8 + C_UAUTO4K_4 // 0 to 4095, 0 mod 4 + C_UAUTO4K_2 // 0 to 4095, 0 mod 2 + C_UAUTO4K // 0 to 4095 + C_UAUTO8K_16 // 0 to 8190, 0 mod 16 + C_UAUTO8K_8 // 0 to 8190, 0 mod 8 + C_UAUTO8K_4 // 0 to 8190, 0 mod 4 + C_UAUTO8K // 0 to 8190, 0 mod 2 + C_PSAUTO + C_UAUTO16K_16 // 0 to 16380, 0 mod 16 + C_UAUTO16K_8 // 0 to 16380, 0 mod 8 + C_UAUTO16K // 0 to 16380, 0 mod 4 + C_PSAUTO + C_UAUTO32K_16 // 0 to 32760, 0 mod 16 + C_PSAUTO + C_UAUTO32K // 0 to 32760, 0 mod 8 + C_PSAUTO + C_UAUTO64K // 0 to 65520, 0 mod 16 + C_PSAUTO + C_LAUTO // any other 32-bit constant C_SEXT1 // 0 to 4095, direct C_SEXT2 // 0 to 8190 @@ -456,17 +463,23 @@ const ( C_PSOREG_8 C_PSOREG_4 C_PSOREG + C_PPOREG_16 C_PPOREG + C_UOREG4K_16 C_UOREG4K_8 C_UOREG4K_4 C_UOREG4K_2 C_UOREG4K + C_UOREG8K_16 C_UOREG8K_8 C_UOREG8K_4 C_UOREG8K + C_UOREG16K_16 C_UOREG16K_8 C_UOREG16K + C_UOREG32K_16 C_UOREG32K + C_UOREG64K C_LOREG C_ADDR // TODO(aram): explain difference from C_VCONADDR @@ -604,22 +617,6 @@ const ( ALDADDLD ALDADDLH ALDADDLW - ALDANDAB - ALDANDAD - ALDANDAH - ALDANDAW - ALDANDALB - ALDANDALD - ALDANDALH - ALDANDALW - ALDANDB - ALDANDD - ALDANDH - ALDANDW - ALDANDLB - ALDANDLD - ALDANDLH - ALDANDLW ALDAR ALDARB ALDARH @@ -630,6 +627,22 @@ const ( ALDAXRB ALDAXRH ALDAXRW + ALDCLRAB + ALDCLRAD + ALDCLRAH + ALDCLRAW + ALDCLRALB + ALDCLRALD + ALDCLRALH + ALDCLRALW + ALDCLRB + ALDCLRD + ALDCLRH + ALDCLRW + ALDCLRLB + ALDCLRLD + ALDCLRLH + ALDCLRLW ALDEORAB ALDEORAD ALDEORAH @@ -830,6 +843,20 @@ const ( ASWPLW ASWPLH ASWPLB + ACASD + ACASW + ACASH + ACASB + ACASAD + ACASAW + ACASLD + ACASLW + ACASALD + ACASALW + ACASALH + ACASALB + ACASPD + ACASPW ABEQ ABNE ABCS @@ -872,8 +899,12 @@ const ( AFDIVS AFLDPD AFLDPS + AFMOVQ AFMOVD AFMOVS + AVMOVQ + AVMOVD + AVMOVS AFMULD AFMULS AFNEGD @@ -953,9 +984,12 @@ const ( AVADD AVADDP AVAND + AVBIF + AVBCAX AVCMEQ AVCNT AVEOR + AVEOR3 AVMOV AVLD1 AVLD2 @@ -984,12 +1018,27 @@ const ( AVPMULL2 AVEXT AVRBIT + AVRAX1 AVUSHR + AVUSHLL + AVUSHLL2 + AVUXTL + AVUXTL2 + AVUZP1 + AVUZP2 AVSHL AVSRI + AVSLI + AVBSL + AVBIT AVTBL + AVXAR AVZIP1 AVZIP2 + AVCMTST + AVUADDW2 + AVUADDW + AVUSRA ALAST AB = obj.AJMP ABL = obj.ACALL diff --git a/src/cmd/internal/obj/arm64/anames.go b/src/cmd/internal/obj/arm64/anames.go index 65ecd007ea..a98f8c7ed5 100644 --- a/src/cmd/internal/obj/arm64/anames.go +++ b/src/cmd/internal/obj/arm64/anames.go @@ -111,22 +111,6 @@ var Anames = []string{ "LDADDLD", "LDADDLH", "LDADDLW", - "LDANDAB", - "LDANDAD", - "LDANDAH", - "LDANDAW", - "LDANDALB", - "LDANDALD", - "LDANDALH", - "LDANDALW", - "LDANDB", - "LDANDD", - "LDANDH", - "LDANDW", - "LDANDLB", - "LDANDLD", - "LDANDLH", - "LDANDLW", "LDAR", "LDARB", "LDARH", @@ -137,6 +121,22 @@ var Anames = []string{ "LDAXRB", "LDAXRH", "LDAXRW", + "LDCLRAB", + "LDCLRAD", + "LDCLRAH", + "LDCLRAW", + "LDCLRALB", + "LDCLRALD", + "LDCLRALH", + "LDCLRALW", + "LDCLRB", + "LDCLRD", + "LDCLRH", + "LDCLRW", + "LDCLRLB", + "LDCLRLD", + "LDCLRLH", + "LDCLRLW", "LDEORAB", "LDEORAD", "LDEORAH", @@ -337,6 +337,20 @@ var Anames = []string{ "SWPLW", "SWPLH", "SWPLB", + "CASD", + "CASW", + "CASH", + "CASB", + "CASAD", + "CASAW", + "CASLD", + "CASLW", + "CASALD", + "CASALW", + "CASALH", + "CASALB", + "CASPD", + "CASPW", "BEQ", "BNE", "BCS", @@ -379,8 +393,12 @@ var Anames = []string{ "FDIVS", "FLDPD", "FLDPS", + "FMOVQ", "FMOVD", "FMOVS", + "VMOVQ", + "VMOVD", + "VMOVS", "FMULD", "FMULS", "FNEGD", @@ -460,9 +478,12 @@ var Anames = []string{ "VADD", "VADDP", "VAND", + "VBIF", + "VBCAX", "VCMEQ", "VCNT", "VEOR", + "VEOR3", "VMOV", "VLD1", "VLD2", @@ -491,11 +512,26 @@ var Anames = []string{ "VPMULL2", "VEXT", "VRBIT", + "VRAX1", "VUSHR", + "VUSHLL", + "VUSHLL2", + "VUXTL", + "VUXTL2", + "VUZP1", + "VUZP2", "VSHL", "VSRI", + "VSLI", + "VBSL", + "VBIT", "VTBL", + "VXAR", "VZIP1", "VZIP2", + "VCMTST", + "VUADDW2", + "VUADDW", + "VUSRA", "LAST", } diff --git a/src/cmd/internal/obj/arm64/anames7.go b/src/cmd/internal/obj/arm64/anames7.go index e1703fc4ab..f7e99517ce 100644 --- a/src/cmd/internal/obj/arm64/anames7.go +++ b/src/cmd/internal/obj/arm64/anames7.go @@ -36,6 +36,7 @@ var cnames7 = []string{ "FCON", "VCONADDR", "AACON", + "AACON2", "LACON", "AECON", "SBRA", @@ -50,16 +51,21 @@ var cnames7 = []string{ "PSAUTO_4", "PSAUTO", "PPAUTO", + "UAUTO4K_16", "UAUTO4K_8", "UAUTO4K_4", "UAUTO4K_2", "UAUTO4K", + "UAUTO8K_16", "UAUTO8K_8", "UAUTO8K_4", "UAUTO8K", + "UAUTO16K_16", "UAUTO16K_8", "UAUTO16K", + "UAUTO32K_8", "UAUTO32K", + "UAUTO64K", "LAUTO", "SEXT1", "SEXT2", @@ -77,16 +83,21 @@ var cnames7 = []string{ "PSOREG_4", "PSOREG", "PPOREG", + "UOREG4K_16", "UOREG4K_8", "UOREG4K_4", "UOREG4K_2", "UOREG4K", + "UOREG8K_16", "UOREG8K_8", "UOREG8K_4", "UOREG8K", + "UOREG16K_16", "UOREG16K_8", "UOREG16K", + "UOREG32K_16", "UOREG32K", + "UOREG64K", "LOREG", "ADDR", "GOTADDR", diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go index 0b90e31392..1a359f1921 100644 --- a/src/cmd/internal/obj/arm64/asm7.go +++ b/src/cmd/internal/obj/arm64/asm7.go @@ -80,12 +80,17 @@ type Optab struct { } func IsAtomicInstruction(as obj.As) bool { - _, ok := atomicInstructions[as] - return ok + if _, ok := atomicLDADD[as]; ok { + return true + } + if _, ok := atomicSWP[as]; ok { + return true + } + return false } // known field values of an instruction. -var atomicInstructions = map[obj.As]uint32{ +var atomicLDADD = map[obj.As]uint32{ ALDADDAD: 3<<30 | 0x1c5<<21 | 0x00<<10, ALDADDAW: 2<<30 | 0x1c5<<21 | 0x00<<10, ALDADDAH: 1<<30 | 0x1c5<<21 | 0x00<<10, @@ -102,22 +107,22 @@ var atomicInstructions = map[obj.As]uint32{ ALDADDLW: 2<<30 | 0x1c3<<21 | 0x00<<10, ALDADDLH: 1<<30 | 0x1c3<<21 | 0x00<<10, ALDADDLB: 0<<30 | 0x1c3<<21 | 0x00<<10, - ALDANDAD: 3<<30 | 0x1c5<<21 | 0x04<<10, - ALDANDAW: 2<<30 | 0x1c5<<21 | 0x04<<10, - ALDANDAH: 1<<30 | 0x1c5<<21 | 0x04<<10, - ALDANDAB: 0<<30 | 0x1c5<<21 | 0x04<<10, - ALDANDALD: 3<<30 | 0x1c7<<21 | 0x04<<10, - ALDANDALW: 2<<30 | 0x1c7<<21 | 0x04<<10, - ALDANDALH: 1<<30 | 0x1c7<<21 | 0x04<<10, - ALDANDALB: 0<<30 | 0x1c7<<21 | 0x04<<10, - ALDANDD: 3<<30 | 0x1c1<<21 | 0x04<<10, - ALDANDW: 2<<30 | 0x1c1<<21 | 0x04<<10, - ALDANDH: 1<<30 | 0x1c1<<21 | 0x04<<10, - ALDANDB: 0<<30 | 0x1c1<<21 | 0x04<<10, - ALDANDLD: 3<<30 | 0x1c3<<21 | 0x04<<10, - ALDANDLW: 2<<30 | 0x1c3<<21 | 0x04<<10, - ALDANDLH: 1<<30 | 0x1c3<<21 | 0x04<<10, - ALDANDLB: 0<<30 | 0x1c3<<21 | 0x04<<10, + ALDCLRAD: 3<<30 | 0x1c5<<21 | 0x04<<10, + ALDCLRAW: 2<<30 | 0x1c5<<21 | 0x04<<10, + ALDCLRAH: 1<<30 | 0x1c5<<21 | 0x04<<10, + ALDCLRAB: 0<<30 | 0x1c5<<21 | 0x04<<10, + ALDCLRALD: 3<<30 | 0x1c7<<21 | 0x04<<10, + ALDCLRALW: 2<<30 | 0x1c7<<21 | 0x04<<10, + ALDCLRALH: 1<<30 | 0x1c7<<21 | 0x04<<10, + ALDCLRALB: 0<<30 | 0x1c7<<21 | 0x04<<10, + ALDCLRD: 3<<30 | 0x1c1<<21 | 0x04<<10, + ALDCLRW: 2<<30 | 0x1c1<<21 | 0x04<<10, + ALDCLRH: 1<<30 | 0x1c1<<21 | 0x04<<10, + ALDCLRB: 0<<30 | 0x1c1<<21 | 0x04<<10, + ALDCLRLD: 3<<30 | 0x1c3<<21 | 0x04<<10, + ALDCLRLW: 2<<30 | 0x1c3<<21 | 0x04<<10, + ALDCLRLH: 1<<30 | 0x1c3<<21 | 0x04<<10, + ALDCLRLB: 0<<30 | 0x1c3<<21 | 0x04<<10, ALDEORAD: 3<<30 | 0x1c5<<21 | 0x08<<10, ALDEORAW: 2<<30 | 0x1c5<<21 | 0x08<<10, ALDEORAH: 1<<30 | 0x1c5<<21 | 0x08<<10, @@ -150,22 +155,41 @@ var atomicInstructions = map[obj.As]uint32{ ALDORLW: 2<<30 | 0x1c3<<21 | 0x0c<<10, ALDORLH: 1<<30 | 0x1c3<<21 | 0x0c<<10, ALDORLB: 0<<30 | 0x1c3<<21 | 0x0c<<10, - ASWPAD: 3<<30 | 0x1c5<<21 | 0x20<<10, - ASWPAW: 2<<30 | 0x1c5<<21 | 0x20<<10, - ASWPAH: 1<<30 | 0x1c5<<21 | 0x20<<10, - ASWPAB: 0<<30 | 0x1c5<<21 | 0x20<<10, - ASWPALD: 3<<30 | 0x1c7<<21 | 0x20<<10, - ASWPALW: 2<<30 | 0x1c7<<21 | 0x20<<10, - ASWPALH: 1<<30 | 0x1c7<<21 | 0x20<<10, - ASWPALB: 0<<30 | 0x1c7<<21 | 0x20<<10, - ASWPD: 3<<30 | 0x1c1<<21 | 0x20<<10, - ASWPW: 2<<30 | 0x1c1<<21 | 0x20<<10, - ASWPH: 1<<30 | 0x1c1<<21 | 0x20<<10, - ASWPB: 0<<30 | 0x1c1<<21 | 0x20<<10, - ASWPLD: 3<<30 | 0x1c3<<21 | 0x20<<10, - ASWPLW: 2<<30 | 0x1c3<<21 | 0x20<<10, - ASWPLH: 1<<30 | 0x1c3<<21 | 0x20<<10, - ASWPLB: 0<<30 | 0x1c3<<21 | 0x20<<10, +} + +var atomicSWP = map[obj.As]uint32{ + ASWPAD: 3<<30 | 0x1c5<<21 | 0x20<<10, + ASWPAW: 2<<30 | 0x1c5<<21 | 0x20<<10, + ASWPAH: 1<<30 | 0x1c5<<21 | 0x20<<10, + ASWPAB: 0<<30 | 0x1c5<<21 | 0x20<<10, + ASWPALD: 3<<30 | 0x1c7<<21 | 0x20<<10, + ASWPALW: 2<<30 | 0x1c7<<21 | 0x20<<10, + ASWPALH: 1<<30 | 0x1c7<<21 | 0x20<<10, + ASWPALB: 0<<30 | 0x1c7<<21 | 0x20<<10, + ASWPD: 3<<30 | 0x1c1<<21 | 0x20<<10, + ASWPW: 2<<30 | 0x1c1<<21 | 0x20<<10, + ASWPH: 1<<30 | 0x1c1<<21 | 0x20<<10, + ASWPB: 0<<30 | 0x1c1<<21 | 0x20<<10, + ASWPLD: 3<<30 | 0x1c3<<21 | 0x20<<10, + ASWPLW: 2<<30 | 0x1c3<<21 | 0x20<<10, + ASWPLH: 1<<30 | 0x1c3<<21 | 0x20<<10, + ASWPLB: 0<<30 | 0x1c3<<21 | 0x20<<10, + ACASD: 3<<30 | 0x45<<21 | 0x1f<<10, + ACASW: 2<<30 | 0x45<<21 | 0x1f<<10, + ACASH: 1<<30 | 0x45<<21 | 0x1f<<10, + ACASB: 0<<30 | 0x45<<21 | 0x1f<<10, + ACASAD: 3<<30 | 0x47<<21 | 0x1f<<10, + ACASAW: 2<<30 | 0x47<<21 | 0x1f<<10, + ACASLD: 3<<30 | 0x45<<21 | 0x3f<<10, + ACASLW: 2<<30 | 0x45<<21 | 0x3f<<10, + ACASALD: 3<<30 | 0x47<<21 | 0x3f<<10, + ACASALW: 2<<30 | 0x47<<21 | 0x3f<<10, + ACASALH: 1<<30 | 0x47<<21 | 0x3f<<10, + ACASALB: 0<<30 | 0x47<<21 | 0x3f<<10, +} +var atomicCASP = map[obj.As]uint32{ + ACASPD: 1<<30 | 0x41<<21 | 0x1f<<10, + ACASPW: 0<<30 | 0x41<<21 | 0x1f<<10, } var oprange [ALAST & obj.AMask][]Optab @@ -205,12 +229,8 @@ func SYSHINT(x uint32) uint32 { return SYSOP(0, 0, 3, 2, 0, x, 0x1F) } -func LDSTR12U(sz uint32, v uint32, opc uint32) uint32 { - return sz<<30 | 7<<27 | v<<26 | 1<<24 | opc<<22 -} - -func LDSTR9S(sz uint32, v uint32, opc uint32) uint32 { - return sz<<30 | 7<<27 | v<<26 | 0<<24 | opc<<22 +func LDSTR(sz uint32, v uint32, opc uint32) uint32 { + return sz<<30 | 7<<27 | v<<26 | opc<<22 } func LD2STR(o uint32) uint32 { @@ -260,8 +280,9 @@ func MOVCONST(d int64, s int, rt int) uint32 { const ( // Optab.flag LFROM = 1 << 0 // p.From uses constant pool - LTO = 1 << 1 // p.To uses constant pool - NOTUSETMP = 1 << 2 // p expands to multiple instructions, but does NOT use REGTMP + LFROM3 = 1 << 1 // p.From3 uses constant pool + LTO = 1 << 2 // p.To uses constant pool + NOTUSETMP = 1 << 3 // p expands to multiple instructions, but does NOT use REGTMP ) var optab = []Optab{ @@ -391,7 +412,16 @@ var optab = []Optab{ {AMOVD, C_VCON, C_NONE, C_NONE, C_REG, 12, 16, 0, NOTUSETMP, 0}, {AMOVK, C_VCON, C_NONE, C_NONE, C_REG, 33, 4, 0, 0, 0}, - {AMOVD, C_AACON, C_NONE, C_NONE, C_REG, 4, 4, REGFROM, 0, 0}, + {AMOVD, C_AACON, C_NONE, C_NONE, C_RSP, 4, 4, REGFROM, 0, 0}, + {AMOVD, C_AACON2, C_NONE, C_NONE, C_RSP, 4, 8, REGFROM, 0, 0}, + + /* load long effective stack address (load int32 offset and add) */ + {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}, + {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}, /* jump operations */ {AB, C_NONE, C_NONE, C_NONE, C_SBRA, 5, 4, 0, 0, 0}, @@ -403,12 +433,14 @@ var optab = []Optab{ {obj.ARET, C_NONE, C_NONE, C_NONE, C_REG, 6, 4, 0, 0, 0}, {obj.ARET, C_NONE, C_NONE, C_NONE, C_ZOREG, 6, 4, 0, 0, 0}, {ABEQ, C_NONE, C_NONE, C_NONE, C_SBRA, 7, 4, 0, 0, 0}, - {AADRP, C_SBRA, C_NONE, C_NONE, C_REG, 60, 4, 0, 0, 0}, - {AADR, C_SBRA, C_NONE, C_NONE, C_REG, 61, 4, 0, 0, 0}, {ACBZ, C_REG, C_NONE, C_NONE, C_SBRA, 39, 4, 0, 0, 0}, {ATBZ, C_VCON, C_REG, C_NONE, C_SBRA, 40, 4, 0, 0, 0}, {AERET, C_NONE, C_NONE, C_NONE, C_NONE, 41, 4, 0, 0, 0}, + // get a PC-relative address + {AADRP, C_SBRA, C_NONE, C_NONE, C_REG, 60, 4, 0, 0, 0}, + {AADR, C_SBRA, C_NONE, C_NONE, C_REG, 61, 4, 0, 0, 0}, + {ACLREX, C_NONE, C_NONE, C_NONE, C_VCON, 38, 4, 0, 0, 0}, {ACLREX, C_NONE, C_NONE, C_NONE, C_NONE, 38, 4, 0, 0, 0}, {ABFM, C_VCON, C_REG, C_VCON, C_REG, 42, 4, 0, 0, 0}, @@ -473,6 +505,9 @@ var optab = []Optab{ {AVTBL, C_ARNG, C_NONE, C_LIST, C_ARNG, 100, 4, 0, 0, 0}, {AVUSHR, C_VCON, C_ARNG, C_NONE, C_ARNG, 95, 4, 0, 0, 0}, {AVZIP1, C_ARNG, C_ARNG, C_NONE, C_ARNG, 72, 4, 0, 0, 0}, + {AVUSHLL, C_VCON, C_ARNG, C_NONE, C_ARNG, 102, 4, 0, 0, 0}, + {AVUXTL, C_ARNG, C_NONE, C_NONE, C_ARNG, 102, 4, 0, 0, 0}, + {AVUADDW, C_ARNG, C_ARNG, C_NONE, C_ARNG, 105, 4, 0, 0, 0}, /* conditional operations */ {ACSEL, C_COND, C_REG, C_REG, C_REG, 18, 4, 0, 0, 0}, @@ -499,6 +534,8 @@ var optab = []Optab{ {AFMOVS, C_FREG, C_NONE, C_NONE, C_UOREG16K, 20, 4, 0, 0, 0}, {AFMOVD, C_FREG, C_NONE, C_NONE, C_UAUTO32K, 20, 4, REGSP, 0, 0}, {AFMOVD, C_FREG, C_NONE, C_NONE, C_UOREG32K, 20, 4, 0, 0, 0}, + {AFMOVQ, C_FREG, C_NONE, C_NONE, C_UAUTO64K, 20, 4, REGSP, 0, 0}, + {AFMOVQ, C_FREG, C_NONE, C_NONE, C_UOREG64K, 20, 4, 0, 0, 0}, /* unscaled 9-bit signed displacement store */ {AMOVB, C_REG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, @@ -516,6 +553,8 @@ var optab = []Optab{ {AFMOVS, C_FREG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, {AFMOVD, C_FREG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, {AFMOVD, C_FREG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, + {AFMOVQ, C_FREG, C_NONE, C_NONE, C_NSAUTO, 20, 4, REGSP, 0, 0}, + {AFMOVQ, C_FREG, C_NONE, C_NONE, C_NSOREG, 20, 4, 0, 0, 0}, /* scaled 12-bit unsigned displacement load */ {AMOVB, C_UAUTO4K, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, @@ -533,6 +572,8 @@ var optab = []Optab{ {AFMOVS, C_UOREG16K, C_NONE, C_NONE, C_FREG, 21, 4, 0, 0, 0}, {AFMOVD, C_UAUTO32K, C_NONE, C_NONE, C_FREG, 21, 4, REGSP, 0, 0}, {AFMOVD, C_UOREG32K, C_NONE, C_NONE, C_FREG, 21, 4, 0, 0, 0}, + {AFMOVQ, C_UAUTO64K, C_NONE, C_NONE, C_FREG, 21, 4, REGSP, 0, 0}, + {AFMOVQ, C_UOREG64K, C_NONE, C_NONE, C_FREG, 21, 4, 0, 0, 0}, /* unscaled 9-bit signed displacement load */ {AMOVB, C_NSAUTO, C_NONE, C_NONE, C_REG, 21, 4, REGSP, 0, 0}, @@ -550,6 +591,8 @@ var optab = []Optab{ {AFMOVS, C_NSOREG, C_NONE, C_NONE, C_FREG, 21, 4, 0, 0, 0}, {AFMOVD, C_NSAUTO, C_NONE, C_NONE, C_FREG, 21, 4, REGSP, 0, 0}, {AFMOVD, C_NSOREG, C_NONE, C_NONE, C_FREG, 21, 4, 0, 0, 0}, + {AFMOVQ, C_NSAUTO, C_NONE, C_NONE, C_FREG, 21, 4, REGSP, 0, 0}, + {AFMOVQ, C_NSOREG, C_NONE, C_NONE, C_FREG, 21, 4, 0, 0, 0}, /* long displacement store */ {AMOVB, C_REG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, @@ -567,6 +610,8 @@ var optab = []Optab{ {AFMOVS, C_FREG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, {AFMOVD, C_FREG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, {AFMOVD, C_FREG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, + {AFMOVQ, C_FREG, C_NONE, C_NONE, C_LAUTO, 30, 8, REGSP, LTO, 0}, + {AFMOVQ, C_FREG, C_NONE, C_NONE, C_LOREG, 30, 8, 0, LTO, 0}, /* long displacement load */ {AMOVB, C_LAUTO, C_NONE, C_NONE, C_REG, 31, 8, REGSP, LFROM, 0}, @@ -584,9 +629,8 @@ var optab = []Optab{ {AFMOVS, C_LOREG, C_NONE, C_NONE, C_FREG, 31, 8, 0, LFROM, 0}, {AFMOVD, C_LAUTO, C_NONE, C_NONE, C_FREG, 31, 8, REGSP, LFROM, 0}, {AFMOVD, C_LOREG, C_NONE, C_NONE, C_FREG, 31, 8, 0, LFROM, 0}, - - /* load long effective stack address (load int32 offset and add) */ - {AMOVD, C_LACON, C_NONE, C_NONE, C_REG, 34, 8, REGSP, LFROM, 0}, + {AFMOVQ, C_LAUTO, C_NONE, C_NONE, C_FREG, 31, 8, REGSP, LFROM, 0}, + {AFMOVQ, C_LOREG, C_NONE, C_NONE, C_FREG, 31, 8, 0, LFROM, 0}, /* pre/post-indexed load (unscaled, signed 9-bit offset) */ {AMOVD, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPOST}, @@ -596,6 +640,7 @@ var optab = []Optab{ {AMOVBU, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPOST}, {AFMOVS, C_LOREG, C_NONE, C_NONE, C_FREG, 22, 4, 0, 0, C_XPOST}, {AFMOVD, C_LOREG, C_NONE, C_NONE, C_FREG, 22, 4, 0, 0, C_XPOST}, + {AFMOVQ, C_LOREG, C_NONE, C_NONE, C_FREG, 22, 4, 0, 0, C_XPOST}, {AMOVD, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPRE}, {AMOVW, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPRE}, @@ -604,6 +649,7 @@ var optab = []Optab{ {AMOVBU, C_LOREG, C_NONE, C_NONE, C_REG, 22, 4, 0, 0, C_XPRE}, {AFMOVS, C_LOREG, C_NONE, C_NONE, C_FREG, 22, 4, 0, 0, C_XPRE}, {AFMOVD, C_LOREG, C_NONE, C_NONE, C_FREG, 22, 4, 0, 0, C_XPRE}, + {AFMOVQ, C_LOREG, C_NONE, C_NONE, C_FREG, 22, 4, 0, 0, C_XPRE}, /* pre/post-indexed store (unscaled, signed 9-bit offset) */ {AMOVD, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, @@ -613,6 +659,7 @@ var optab = []Optab{ {AMOVBU, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, {AFMOVS, C_FREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, {AFMOVD, C_FREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, + {AFMOVQ, C_FREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPOST}, {AMOVD, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, {AMOVW, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, @@ -621,6 +668,7 @@ var optab = []Optab{ {AMOVBU, C_REG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, {AFMOVS, C_FREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, {AFMOVD, C_FREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, + {AFMOVQ, C_FREG, C_NONE, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE}, /* load with shifted or extended register offset */ {AMOVD, C_ROFF, C_NONE, C_NONE, C_REG, 98, 4, 0, 0, 0}, @@ -770,8 +818,10 @@ var optab = []Optab{ {ASTPW, C_PAIR, C_NONE, C_NONE, C_LOREG, 77, 12, 0, LTO, C_XPOST}, {ASTPW, C_PAIR, C_NONE, C_NONE, C_ADDR, 87, 12, 0, 0, 0}, - {ASWPD, C_REG, C_NONE, C_NONE, C_ZOREG, 47, 4, 0, 0, 0}, // RegTo2=C_REG - {ASWPD, C_REG, C_NONE, C_NONE, C_ZAUTO, 47, 4, REGSP, 0, 0}, // RegTo2=C_REG + {ASWPD, C_REG, C_NONE, C_NONE, C_ZOREG, 47, 4, 0, 0, 0}, // RegTo2=C_REG + {ASWPD, C_REG, C_NONE, C_NONE, C_ZAUTO, 47, 4, REGSP, 0, 0}, // RegTo2=C_REG + {ACASPD, C_PAIR, C_NONE, C_NONE, C_ZOREG, 106, 4, 0, 0, 0}, // RegTo2=C_REGREG + {ACASPD, C_PAIR, C_NONE, C_NONE, C_ZAUTO, 106, 4, REGSP, 0, 0}, // RegTo2=C_REGREG {ALDAR, C_ZOREG, C_NONE, C_NONE, C_REG, 58, 4, 0, 0, 0}, {ALDXR, C_ZOREG, C_NONE, C_NONE, C_REG, 58, 4, 0, 0, 0}, {ALDAXR, C_ZOREG, C_NONE, C_NONE, C_REG, 58, 4, 0, 0, 0}, @@ -832,6 +882,8 @@ var optab = []Optab{ {ASHA256H, C_ARNG, C_VREG, C_NONE, C_VREG, 1, 4, 0, 0, 0}, {AVREV32, C_ARNG, C_NONE, C_NONE, C_ARNG, 83, 4, 0, 0, 0}, {AVPMULL, C_ARNG, C_ARNG, C_NONE, C_ARNG, 93, 4, 0, 0, 0}, + {AVEOR3, C_ARNG, C_ARNG, C_ARNG, C_ARNG, 103, 4, 0, 0, 0}, + {AVXAR, C_VCON, C_ARNG, C_ARNG, C_ARNG, 104, 4, 0, 0, 0}, {obj.AUNDEF, C_NONE, C_NONE, C_NONE, C_NONE, 90, 4, 0, 0, 0}, {obj.APCDATA, C_VCON, C_NONE, C_NONE, C_VCON, 0, 0, 0, 0, 0}, @@ -900,7 +952,7 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { ctxt.Retpoline = false // don't keep printing } - p := cursym.Func.Text + p := cursym.Func().Text if p == nil || p.Link == nil { // handle external functions and ELF section symbols return } @@ -930,8 +982,8 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { alignedValue := p.From.Offset m = pcAlignPadLength(pc, alignedValue, ctxt) // Update the current text symbol alignment value. - if int32(alignedValue) > cursym.Func.Align { - cursym.Func.Align = int32(alignedValue) + if int32(alignedValue) > cursym.Func().Align { + cursym.Func().Align = int32(alignedValue) } break case obj.ANOP, obj.AFUNCDATA, obj.APCDATA: @@ -940,13 +992,14 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { c.ctxt.Diag("zero-width instruction\n%v", p) } } - switch o.flag & (LFROM | LTO) { - case LFROM: + if o.flag&LFROM != 0 { c.addpool(p, &p.From) - - case LTO: + } + if o.flag&LFROM3 != 0 { + c.addpool(p, p.GetFrom3()) + } + if o.flag<O != 0 { c.addpool(p, &p.To) - break } if p.As == AB || p.As == obj.ARET || p.As == AERET { /* TODO: other unconditional operations */ @@ -969,7 +1022,7 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { for bflag != 0 { bflag = 0 pc = 0 - for p = c.cursym.Func.Text.Link; p != nil; p = p.Link { + for p = c.cursym.Func().Text.Link; p != nil; p = p.Link { if p.As == ADWORD && (pc&7) != 0 { pc += 4 } @@ -1033,7 +1086,7 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { psz := int32(0) var i int var out [6]uint32 - for p := c.cursym.Func.Text.Link; p != nil; p = p.Link { + for p := c.cursym.Func().Text.Link; p != nil; p = p.Link { c.pc = p.Pc o = c.oplook(p) @@ -1074,7 +1127,7 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // We use REGTMP as a scratch register during call injection, // so instruction sequences that use REGTMP are unsafe to // preempt asynchronously. - obj.MarkUnsafePoints(c.ctxt, c.cursym.Func.Text, c.newprog, c.isUnsafePoint, c.isRestartable) + obj.MarkUnsafePoints(c.ctxt, c.cursym.Func().Text, c.newprog, c.isUnsafePoint, c.isRestartable) } // isUnsafePoint returns whether p is an unsafe point. @@ -1164,8 +1217,8 @@ func (c *ctxt7) addpool(p *obj.Prog, a *obj.Addr) { sz := 4 if a.Type == obj.TYPE_CONST { - if lit != int64(int32(lit)) && uint64(lit) != uint64(uint32(lit)) { - // out of range -0x80000000 ~ 0xffffffff, must store 64-bit + if (lit != int64(int32(lit)) && uint64(lit) != uint64(uint32(lit))) || p.As == AVMOVQ || p.As == AVMOVD { + // out of range -0x80000000 ~ 0xffffffff or VMOVQ or VMOVD operand, must store 64-bit. t.As = ADWORD sz = 8 } // else store 32-bit @@ -1197,37 +1250,49 @@ func (c *ctxt7) addpool(p *obj.Prog, a *obj.Addr) { C_PSAUTO, C_PSAUTO_8, C_PSAUTO_4, + C_PPAUTO_16, C_PPAUTO, + C_UAUTO4K_16, C_UAUTO4K_8, C_UAUTO4K_4, C_UAUTO4K_2, C_UAUTO4K, + C_UAUTO8K_16, C_UAUTO8K_8, C_UAUTO8K_4, C_UAUTO8K, + C_UAUTO16K_16, C_UAUTO16K_8, C_UAUTO16K, + C_UAUTO32K_16, C_UAUTO32K, + C_UAUTO64K, C_NSAUTO_8, C_NSAUTO_4, C_NSAUTO, C_NPAUTO, C_NAUTO4K, C_LAUTO, - C_PPOREG, C_PSOREG, - C_PSOREG_4, C_PSOREG_8, + C_PSOREG_4, + C_PPOREG_16, + C_PPOREG, + C_UOREG4K_16, C_UOREG4K_8, C_UOREG4K_4, C_UOREG4K_2, C_UOREG4K, + C_UOREG8K_16, C_UOREG8K_8, C_UOREG8K_4, C_UOREG8K, + C_UOREG16K_16, C_UOREG16K_8, C_UOREG16K, + C_UOREG32K_16, C_UOREG32K, + C_UOREG64K, C_NSOREG_8, C_NSOREG_4, C_NSOREG, @@ -1352,6 +1417,10 @@ func isaddcon(v int64) bool { return v <= 0xFFF } +func isaddcon2(v int64) bool { + return 0 <= v && v <= 0xFFFFFF +} + // isbitcon reports whether a constant can be encoded into a logical instruction. // bitcon has a binary form of repetition of a bit sequence of length 2, 4, 8, 16, 32, or 64, // which itself is a rotate (w.r.t. the length of the unit) of a sequence of ones. @@ -1518,10 +1587,18 @@ func autoclass(l int64) int { } return C_PSAUTO } - if l <= 504 && l&7 == 0 { - return C_PPAUTO + if l <= 504 { + if l&15 == 0 { + return C_PPAUTO_16 + } + if l&7 == 0 { + return C_PPAUTO + } } if l <= 4095 { + if l&15 == 0 { + return C_UAUTO4K_16 + } if l&7 == 0 { return C_UAUTO4K_8 } @@ -1534,6 +1611,9 @@ func autoclass(l int64) int { return C_UAUTO4K } if l <= 8190 { + if l&15 == 0 { + return C_UAUTO8K_16 + } if l&7 == 0 { return C_UAUTO8K_8 } @@ -1545,6 +1625,9 @@ func autoclass(l int64) int { } } if l <= 16380 { + if l&15 == 0 { + return C_UAUTO16K_16 + } if l&7 == 0 { return C_UAUTO16K_8 } @@ -1552,8 +1635,16 @@ func autoclass(l int64) int { return C_UAUTO16K } } - if l <= 32760 && (l&7) == 0 { - return C_UAUTO32K + if l <= 32760 { + if l&15 == 0 { + return C_UAUTO32K_16 + } + if l&7 == 0 { + return C_UAUTO32K + } + } + if l <= 65520 && (l&15) == 0 { + return C_UAUTO64K } return C_LAUTO } @@ -1581,6 +1672,8 @@ func (c *ctxt7) offsetshift(p *obj.Prog, v int64, cls int) int64 { s = 2 case C_UAUTO32K, C_UOREG32K: s = 3 + case C_UAUTO64K, C_UOREG64K: + s = 4 default: c.ctxt.Diag("bad class: %v\n%v", DRconv(cls), p) } @@ -1880,10 +1973,14 @@ func (c *ctxt7) aclass(a *obj.Addr) int { default: return C_GOK } - - if isaddcon(c.instoffset) { + cf := c.instoffset + if isaddcon(cf) || isaddcon(-cf) { return C_AACON } + if isaddcon2(cf) { + return C_AACON2 + } + return C_LACON case obj.TYPE_BRANCH: @@ -1940,7 +2037,7 @@ func (c *ctxt7) oplook(p *obj.Prog) *Optab { a1-- a3 := C_NONE + 1 - if p.GetFrom3() != nil { + if p.GetFrom3() != nil && p.RestArgs[0].Pos == 0 { a3 = int(p.GetFrom3().Class) if a3 == 0 { a3 = c.aclass(p.GetFrom3()) + 1 @@ -2037,7 +2134,7 @@ func cmp(a int, b int) bool { return cmp(C_LCON, b) case C_LACON: - if b == C_AACON { + if b == C_AACON || b == C_AACON2 { return true } @@ -2108,46 +2205,66 @@ func cmp(a int, b int) bool { case C_PPAUTO: switch b { - case C_ZAUTO, C_PSAUTO_8: + case C_ZAUTO, C_PSAUTO_8, C_PPAUTO_16: return true } case C_UAUTO4K: switch b { case C_ZAUTO, C_PSAUTO, C_PSAUTO_4, C_PSAUTO_8, - C_PPAUTO, C_UAUTO4K_2, C_UAUTO4K_4, C_UAUTO4K_8: + C_PPAUTO, C_PPAUTO_16, + C_UAUTO4K_2, C_UAUTO4K_4, C_UAUTO4K_8, C_UAUTO4K_16: return true } case C_UAUTO8K: switch b { - case C_ZAUTO, C_PSAUTO, C_PSAUTO_4, C_PSAUTO_8, C_PPAUTO, - C_UAUTO4K_2, C_UAUTO4K_4, C_UAUTO4K_8, C_UAUTO8K_4, C_UAUTO8K_8: + case C_ZAUTO, C_PSAUTO, C_PSAUTO_4, C_PSAUTO_8, + C_PPAUTO, C_PPAUTO_16, + C_UAUTO4K_2, C_UAUTO4K_4, C_UAUTO4K_8, C_UAUTO4K_16, + C_UAUTO8K_4, C_UAUTO8K_8, C_UAUTO8K_16: return true } case C_UAUTO16K: switch b { - case C_ZAUTO, C_PSAUTO, C_PSAUTO_4, C_PSAUTO_8, C_PPAUTO, - C_UAUTO4K_4, C_UAUTO4K_8, C_UAUTO8K_4, C_UAUTO8K_8, C_UAUTO16K_8: + case C_ZAUTO, C_PSAUTO, C_PSAUTO_4, C_PSAUTO_8, + C_PPAUTO, C_PPAUTO_16, + C_UAUTO4K_4, C_UAUTO4K_8, C_UAUTO4K_16, + C_UAUTO8K_4, C_UAUTO8K_8, C_UAUTO8K_16, + C_UAUTO16K_8, C_UAUTO16K_16: return true } case C_UAUTO32K: switch b { case C_ZAUTO, C_PSAUTO, C_PSAUTO_4, C_PSAUTO_8, - C_PPAUTO, C_UAUTO4K_8, C_UAUTO8K_8, C_UAUTO16K_8: + C_PPAUTO, C_PPAUTO_16, + C_UAUTO4K_8, C_UAUTO4K_16, + C_UAUTO8K_8, C_UAUTO8K_16, + C_UAUTO16K_8, C_UAUTO16K_16, + C_UAUTO32K_16: + return true + } + + case C_UAUTO64K: + switch b { + case C_ZAUTO, C_PSAUTO, C_PSAUTO_4, C_PSAUTO_8, + C_PPAUTO_16, C_UAUTO4K_16, C_UAUTO8K_16, C_UAUTO16K_16, + C_UAUTO32K_16: return true } case C_LAUTO: switch b { - case C_ZAUTO, C_NSAUTO, C_NSAUTO_4, C_NSAUTO_8, C_NPAUTO, - C_NAUTO4K, C_PSAUTO, C_PSAUTO_4, C_PSAUTO_8, C_PPAUTO, - C_UAUTO4K, C_UAUTO4K_2, C_UAUTO4K_4, C_UAUTO4K_8, - C_UAUTO8K, C_UAUTO8K_4, C_UAUTO8K_8, - C_UAUTO16K, C_UAUTO16K_8, - C_UAUTO32K: + case C_ZAUTO, C_NSAUTO, C_NSAUTO_4, C_NSAUTO_8, C_NPAUTO, C_NAUTO4K, + C_PSAUTO, C_PSAUTO_4, C_PSAUTO_8, + C_PPAUTO, C_PPAUTO_16, + C_UAUTO4K, C_UAUTO4K_2, C_UAUTO4K_4, C_UAUTO4K_8, C_UAUTO4K_16, + C_UAUTO8K, C_UAUTO8K_4, C_UAUTO8K_8, C_UAUTO8K_16, + C_UAUTO16K, C_UAUTO16K_8, C_UAUTO16K_16, + C_UAUTO32K, C_UAUTO32K_16, + C_UAUTO64K: return true } @@ -2174,6 +2291,11 @@ func cmp(a int, b int) bool { return true } + case C_PSOREG_8: + if b == C_ZOREG { + return true + } + case C_PSOREG_4: switch b { case C_ZOREG, C_PSOREG_8: @@ -2188,48 +2310,66 @@ func cmp(a int, b int) bool { case C_PPOREG: switch b { - case C_ZOREG, C_PSOREG_8: + case C_ZOREG, C_PSOREG_8, C_PPOREG_16: return true } case C_UOREG4K: switch b { - case C_ZOREG, C_PSOREG_4, C_PSOREG_8, C_PSOREG, - C_PPOREG, C_UOREG4K_2, C_UOREG4K_4, C_UOREG4K_8: + case C_ZOREG, C_PSOREG, C_PSOREG_4, C_PSOREG_8, + C_PPOREG, C_PPOREG_16, + C_UOREG4K_2, C_UOREG4K_4, C_UOREG4K_8, C_UOREG4K_16: return true } case C_UOREG8K: switch b { - case C_ZOREG, C_PSOREG_4, C_PSOREG_8, C_PSOREG, - C_PPOREG, C_UOREG4K_2, C_UOREG4K_4, C_UOREG4K_8, - C_UOREG8K_4, C_UOREG8K_8: + case C_ZOREG, C_PSOREG, C_PSOREG_4, C_PSOREG_8, + C_PPOREG, C_PPOREG_16, + C_UOREG4K_2, C_UOREG4K_4, C_UOREG4K_8, C_UOREG4K_16, + C_UOREG8K_4, C_UOREG8K_8, C_UOREG8K_16: return true } case C_UOREG16K: switch b { - case C_ZOREG, C_PSOREG_4, C_PSOREG_8, C_PSOREG, - C_PPOREG, C_UOREG4K_4, C_UOREG4K_8, C_UOREG8K_4, - C_UOREG8K_8, C_UOREG16K_8: + case C_ZOREG, C_PSOREG, C_PSOREG_4, C_PSOREG_8, + C_PPOREG, C_PPOREG_16, + C_UOREG4K_4, C_UOREG4K_8, C_UOREG4K_16, + C_UOREG8K_4, C_UOREG8K_8, C_UOREG8K_16, + C_UOREG16K_8, C_UOREG16K_16: return true } case C_UOREG32K: switch b { - case C_ZOREG, C_PSOREG_4, C_PSOREG_8, C_PSOREG, - C_PPOREG, C_UOREG4K_8, C_UOREG8K_8, C_UOREG16K_8: + case C_ZOREG, C_PSOREG, C_PSOREG_4, C_PSOREG_8, + C_PPOREG, C_PPOREG_16, + C_UOREG4K_8, C_UOREG4K_16, + C_UOREG8K_8, C_UOREG8K_16, + C_UOREG16K_8, C_UOREG16K_16, + C_UOREG32K_16: + return true + } + + case C_UOREG64K: + switch b { + case C_ZOREG, C_PSOREG, C_PSOREG_4, C_PSOREG_8, + C_PPOREG_16, C_UOREG4K_16, C_UOREG8K_16, C_UOREG16K_16, + C_UOREG32K_16: return true } case C_LOREG: switch b { - case C_ZOREG, C_NSOREG, C_NSOREG_4, C_NSOREG_8, C_NPOREG, - C_NOREG4K, C_PSOREG_4, C_PSOREG_8, C_PSOREG, C_PPOREG, - C_UOREG4K, C_UOREG4K_2, C_UOREG4K_4, C_UOREG4K_8, - C_UOREG8K, C_UOREG8K_4, C_UOREG8K_8, - C_UOREG16K, C_UOREG16K_8, - C_UOREG32K: + case C_ZOREG, C_NSOREG, C_NSOREG_4, C_NSOREG_8, C_NPOREG, C_NOREG4K, + C_PSOREG, C_PSOREG_4, C_PSOREG_8, + C_PPOREG, C_PPOREG_16, + C_UOREG4K, C_UOREG4K_2, C_UOREG4K_4, C_UOREG4K_8, C_UOREG4K_16, + C_UOREG8K, C_UOREG8K_4, C_UOREG8K_8, C_UOREG8K_16, + C_UOREG16K, C_UOREG16K_8, C_UOREG16K_16, + C_UOREG32K, C_UOREG32K_16, + C_UOREG64K: return true } @@ -2382,10 +2522,18 @@ func buildop(ctxt *obj.Link) { oprangeset(AMOVZW, t) case ASWPD: - for i := range atomicInstructions { + for i := range atomicLDADD { + oprangeset(i, t) + } + for i := range atomicSWP { + if i == ASWPD { + continue + } oprangeset(i, t) } + case ACASPD: + oprangeset(ACASPW, t) case ABEQ: oprangeset(ABNE, t) oprangeset(ABCS, t) @@ -2657,7 +2805,8 @@ func buildop(ctxt *obj.Link) { case AFCSELD: oprangeset(AFCSELS, t) - case AFMOVS, AFMOVD: + case AFMOVQ, AFMOVD, AFMOVS, + AVMOVQ, AVMOVD, AVMOVS: break case AFCVTZSD: @@ -2740,9 +2889,16 @@ func buildop(ctxt *obj.Link) { oprangeset(AVCMEQ, t) oprangeset(AVORR, t) oprangeset(AVEOR, t) + oprangeset(AVBSL, t) + oprangeset(AVBIT, t) + oprangeset(AVCMTST, t) + oprangeset(AVUZP1, t) + oprangeset(AVUZP2, t) + oprangeset(AVBIF, t) case AVADD: oprangeset(AVSUB, t) + oprangeset(AVRAX1, t) case AAESD: oprangeset(AAESE, t) @@ -2777,6 +2933,8 @@ func buildop(ctxt *obj.Link) { case AVUSHR: oprangeset(AVSHL, t) oprangeset(AVSRI, t) + oprangeset(AVSLI, t) + oprangeset(AVUSRA, t) case AVREV32: oprangeset(AVCNT, t) @@ -2787,6 +2945,12 @@ func buildop(ctxt *obj.Link) { case AVZIP1: oprangeset(AVZIP2, t) + case AVUXTL: + oprangeset(AVUXTL2, t) + + case AVUSHLL: + oprangeset(AVUSHLL2, t) + case AVLD1R: oprangeset(AVLD2, t) oprangeset(AVLD2R, t) @@ -2795,6 +2959,12 @@ func buildop(ctxt *obj.Link) { oprangeset(AVLD4, t) oprangeset(AVLD4R, t) + case AVEOR3: + oprangeset(AVBCAX, t) + + case AVUADDW: + oprangeset(AVUADDW2, t) + case ASHA1H, AVCNT, AVMOV, @@ -2807,7 +2977,8 @@ func buildop(ctxt *obj.Link) { AVDUP, AVMOVI, APRFM, - AVEXT: + AVEXT, + AVXAR: break case obj.ANOP, @@ -3041,11 +3212,10 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { } o1 |= (uint32(r&31) << 5) | uint32(rt&31) - case 4: /* mov $addcon, R; mov $recon, R; mov $racon, R */ - o1 = c.opirr(p, p.As) - + case 4: /* mov $addcon, R; mov $recon, R; mov $racon, R; mov $addcon2, R */ rt := int(p.To.Reg) r := int(o.param) + if r == 0 { r = REGZERO } else if r == REGFROM { @@ -3054,13 +3224,23 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { if r == 0 { r = REGSP } + v := int32(c.regoff(&p.From)) - if (v & 0xFFF000) != 0 { - v >>= 12 - o1 |= 1 << 22 /* shift, by 12 */ + var op int32 + if v < 0 { + v = -v + op = int32(c.opirr(p, ASUB)) + } else { + op = int32(c.opirr(p, AADD)) } - o1 |= ((uint32(v) & 0xFFF) << 10) | (uint32(r&31) << 5) | uint32(rt&31) + if int(o.size) == 8 { + o1 = c.oaddi(p, op, v&0xfff000, r, REGTMP) + o2 = c.oaddi(p, op, v&0x000fff, REGTMP, rt) + break + } + + o1 = c.oaddi(p, op, v, r, rt) case 5: /* b s; bl s */ o1 = c.opbra(p, p.As) @@ -3079,12 +3259,13 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { case 6: /* b ,O(R); bl ,O(R) */ o1 = c.opbrr(p, p.As) - o1 |= uint32(p.To.Reg&31) << 5 - rel := obj.Addrel(c.cursym) - rel.Off = int32(c.pc) - rel.Siz = 0 - rel.Type = objabi.R_CALLIND + if p.As == obj.ACALL { + rel := obj.Addrel(c.cursym) + rel.Off = int32(c.pc) + rel.Siz = 0 + rel.Type = objabi.R_CALLIND + } case 7: /* beq s */ o1 = c.opbra(p, p.As) @@ -3343,10 +3524,10 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { r = int(o.param) } if v < 0 || v%sz != 0 { /* unscaled 9-bit signed */ - o1 = c.olsr9s(p, int32(c.opstr9(p, p.As)), v, r, int(p.From.Reg)) + o1 = c.olsr9s(p, int32(c.opstr(p, p.As)), v, r, int(p.From.Reg)) } else { v = int32(c.offsetshift(p, int64(v), int(o.a4))) - o1 = c.olsr12u(p, int32(c.opstr12(p, p.As)), v, r, int(p.From.Reg)) + o1 = c.olsr12u(p, int32(c.opstr(p, p.As)), v, r, int(p.From.Reg)) } case 21: /* movT O(R),R -> ldrT */ @@ -3358,11 +3539,11 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { r = int(o.param) } if v < 0 || v%sz != 0 { /* unscaled 9-bit signed */ - o1 = c.olsr9s(p, int32(c.opldr9(p, p.As)), v, r, int(p.To.Reg)) + o1 = c.olsr9s(p, int32(c.opldr(p, p.As)), v, r, int(p.To.Reg)) } else { v = int32(c.offsetshift(p, int64(v), int(o.a1))) //print("offset=%lld v=%ld a1=%d\n", instoffset, v, o->a1); - o1 = c.olsr12u(p, int32(c.opldr12(p, p.As)), v, r, int(p.To.Reg)) + o1 = c.olsr12u(p, int32(c.opldr(p, p.As)), v, r, int(p.To.Reg)) } case 22: /* movT (R)O!,R; movT O(R)!, R -> ldrT */ @@ -3375,7 +3556,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { if v < -256 || v > 255 { c.ctxt.Diag("offset out of range [-255,254]: %v", p) } - o1 = c.opldrpp(p, p.As) + o1 = c.opldr(p, p.As) if o.scond == C_XPOST { o1 |= 1 << 10 } else { @@ -3393,7 +3574,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { if v < -256 || v > 255 { c.ctxt.Diag("offset out of range [-255,254]: %v", p) } - o1 = LD2STR(c.opldrpp(p, p.As)) + o1 = c.opstr(p, p.As) if o.scond == C_XPOST { o1 |= 1 << 10 } else { @@ -3545,7 +3726,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { } o1 = c.oaddi(p, int32(c.opirr(p, AADD)), hi, r, REGTMP) - o2 = c.olsr12u(p, int32(c.opstr12(p, p.As)), ((v-hi)>>uint(s))&0xFFF, REGTMP, int(p.From.Reg)) + o2 = c.olsr12u(p, int32(c.opstr(p, p.As)), ((v-hi)>>uint(s))&0xFFF, REGTMP, int(p.From.Reg)) break storeusepool: @@ -3589,7 +3770,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { } o1 = c.oaddi(p, int32(c.opirr(p, AADD)), hi, r, REGTMP) - o2 = c.olsr12u(p, int32(c.opldr12(p, p.As)), ((v-hi)>>uint(s))&0xFFF, REGTMP, int(p.To.Reg)) + o2 = c.olsr12u(p, int32(c.opldr(p, p.As)), ((v-hi)>>uint(s))&0xFFF, REGTMP, int(p.To.Reg)) break loadusepool: @@ -3847,17 +4028,27 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { o1 |= uint32(p.From.Reg&31) << 5 o1 |= uint32(p.To.Reg & 31) - case 47: /* SWPx/LDADDx/LDANDx/LDEORx/LDORx Rs, (Rb), Rt */ + case 47: // SWPx/LDADDx/LDCLRx/LDEORx/LDORx/CASx Rs, (Rb), Rt rs := p.From.Reg rt := p.RegTo2 rb := p.To.Reg - fields := atomicInstructions[p.As] - // rt can't be sp. rt can't be r31 when field A is 0, A bit is the 23rd bit. - if rt == REG_RSP || (rt == REGZERO && (fields&(1<<23) == 0)) { + // rt can't be sp. + if rt == REG_RSP { c.ctxt.Diag("illegal destination register: %v\n", p) } - o1 |= fields | uint32(rs&31)<<16 | uint32(rb&31)<<5 | uint32(rt&31) + if enc, ok := atomicLDADD[p.As]; ok { + // for LDADDx-like instructions, rt can't be r31 when field.enc A is 0, A bit is the 23rd bit. + if (rt == REGZERO) && (enc&(1<<23) == 0) { + c.ctxt.Diag("illegal destination register: %v\n", p) + } + o1 |= enc + } else if enc, ok := atomicSWP[p.As]; ok { + o1 |= enc + } else { + c.ctxt.Diag("invalid atomic instructions: %v\n", p) + } + o1 |= uint32(rs&31)<<16 | uint32(rb&31)<<5 | uint32(rt&31) case 48: /* ADD $C_ADDCON2, Rm, Rd */ // NOTE: this case does not use REGTMP. If it ever does, @@ -4078,7 +4269,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { rel.Sym = p.To.Sym rel.Add = p.To.Offset rel.Type = objabi.R_ADDRARM64 - o3 = c.olsr12u(p, int32(c.opstr12(p, p.As)), 0, REGTMP, int(p.From.Reg)) + o3 = c.olsr12u(p, int32(c.opstr(p, p.As)), 0, REGTMP, int(p.From.Reg)) case 65: /* movT addr,R -> adrp + add + movT (REGTMP), R */ o1 = ADR(1, 0, REGTMP) @@ -4089,7 +4280,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { rel.Sym = p.From.Sym rel.Add = p.From.Offset rel.Type = objabi.R_ADDRARM64 - o3 = c.olsr12u(p, int32(c.opldr12(p, p.As)), 0, REGTMP, int(p.To.Reg)) + o3 = c.olsr12u(p, int32(c.opldr(p, p.As)), 0, REGTMP, int(p.To.Reg)) case 66: /* ldp O(R)!, (r1, r2); ldp (R)O!, (r1, r2) */ v := int32(c.regoff(&p.From)) @@ -4142,7 +4333,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { case 70: /* IE model movd $tlsvar, reg -> adrp REGTMP, 0; ldr reg, [REGTMP, #0] + relocs */ o1 = ADR(1, 0, REGTMP) - o2 = c.olsr12u(p, int32(c.opldr12(p, AMOVD)), 0, REGTMP, int(p.To.Reg)) + o2 = c.olsr12u(p, int32(c.opldr(p, AMOVD)), 0, REGTMP, int(p.To.Reg)) rel := obj.Addrel(c.cursym) rel.Off = int32(c.pc) rel.Siz = 8 @@ -4155,7 +4346,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { case 71: /* movd sym@GOT, reg -> adrp REGTMP, #0; ldr reg, [REGTMP, #0] + relocs */ o1 = ADR(1, 0, REGTMP) - o2 = c.olsr12u(p, int32(c.opldr12(p, AMOVD)), 0, REGTMP, int(p.To.Reg)) + o2 = c.olsr12u(p, int32(c.opldr(p, AMOVD)), 0, REGTMP, int(p.To.Reg)) rel := obj.Addrel(c.cursym) rel.Off = int32(c.pc) rel.Siz = 8 @@ -4163,7 +4354,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { rel.Add = 0 rel.Type = objabi.R_ARM64_GOTPCREL - case 72: /* vaddp/vand/vcmeq/vorr/vadd/veor/vfmla/vfmls Vm.<T>, Vn.<T>, Vd.<T> */ + case 72: /* vaddp/vand/vcmeq/vorr/vadd/veor/vfmla/vfmls/vbit/vbsl/vcmtst/vsub/vbif/vuzip1/vuzip2/vrax1 Vm.<T>, Vn.<T>, Vd.<T> */ af := int((p.From.Reg >> 5) & 15) af3 := int((p.Reg >> 5) & 15) at := int((p.To.Reg >> 5) & 15) @@ -4204,22 +4395,35 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { c.ctxt.Diag("invalid arrangement: %v", p) } - if (p.As == AVORR || p.As == AVAND || p.As == AVEOR) && - (af != ARNG_16B && af != ARNG_8B) { - c.ctxt.Diag("invalid arrangement: %v", p) - } else if (p.As == AVFMLA || p.As == AVFMLS) && - (af != ARNG_2D && af != ARNG_2S && af != ARNG_4S) { - c.ctxt.Diag("invalid arrangement: %v", p) - } else if p.As == AVORR { - size = 2 - } else if p.As == AVAND || p.As == AVEOR { + switch p.As { + case AVORR, AVAND, AVEOR, AVBIT, AVBSL, AVBIF: + if af != ARNG_16B && af != ARNG_8B { + c.ctxt.Diag("invalid arrangement: %v", p) + } + case AVFMLA, AVFMLS: + if af != ARNG_2D && af != ARNG_2S && af != ARNG_4S { + c.ctxt.Diag("invalid arrangement: %v", p) + } + } + switch p.As { + case AVAND, AVEOR: size = 0 - } else if p.As == AVFMLA || p.As == AVFMLS { + case AVBSL: + size = 1 + case AVORR, AVBIT, AVBIF: + size = 2 + case AVFMLA, AVFMLS: if af == ARNG_2D { size = 1 } else { size = 0 } + case AVRAX1: + if af != ARNG_2D { + c.ctxt.Diag("invalid arrangement: %v", p) + } + size = 0 + Q = 0 } o1 |= (uint32(Q&1) << 30) | (uint32(size&3) << 22) | (uint32(rf&31) << 16) | (uint32(r&31) << 5) | uint32(rt&31) @@ -4703,7 +4907,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { } } - o1 = c.opldrpp(p, p.As) + o1 = c.opirr(p, p.As) o1 |= (uint32(r&31) << 5) | (uint32((imm>>3)&0xfff) << 10) | (uint32(v & 31)) case 92: /* vmov Vn.<T>[index], Vd.<T>[index] */ @@ -4747,47 +4951,31 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { } o1 |= (uint32(imm5&0x1f) << 16) | (uint32(imm4&0xf) << 11) | (uint32(rf&31) << 5) | uint32(rt&31) - case 93: /* vpmull{2} Vm.<T>, Vn.<T>, Vd */ - af := int((p.From.Reg >> 5) & 15) - at := int((p.To.Reg >> 5) & 15) - a := int((p.Reg >> 5) & 15) + case 93: /* vpmull{2} Vm.<Tb>, Vn.<Tb>, Vd.<Ta> */ + af := uint8((p.From.Reg >> 5) & 15) + at := uint8((p.To.Reg >> 5) & 15) + a := uint8((p.Reg >> 5) & 15) + if af != a { + c.ctxt.Diag("invalid arrangement: %v", p) + } var Q, size uint32 - if p.As == AVPMULL { - Q = 0 - } else { + if p.As == AVPMULL2 { Q = 1 } - - var fArng int - switch at { - case ARNG_8H: - if Q == 0 { - fArng = ARNG_8B - } else { - fArng = ARNG_16B - } + switch pack(Q, at, af) { + case pack(0, ARNG_8H, ARNG_8B), pack(1, ARNG_8H, ARNG_16B): size = 0 - case ARNG_1Q: - if Q == 0 { - fArng = ARNG_1D - } else { - fArng = ARNG_2D - } + case pack(0, ARNG_1Q, ARNG_1D), pack(1, ARNG_1Q, ARNG_2D): size = 3 default: - c.ctxt.Diag("invalid arrangement on Vd.<T>: %v", p) - } - - if af != a || af != fArng { - c.ctxt.Diag("invalid arrangement: %v", p) + c.ctxt.Diag("operand mismatch: %v\n", p) } o1 = c.oprrr(p, p.As) rf := int((p.From.Reg) & 31) rt := int((p.To.Reg) & 31) r := int((p.Reg) & 31) - o1 |= ((Q & 1) << 30) | ((size & 3) << 22) | (uint32(rf&31) << 16) | (uint32(r&31) << 5) | uint32(rt&31) case 94: /* vext $imm4, Vm.<T>, Vn.<T>, Vd.<T> */ @@ -4825,7 +5013,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { o1 |= ((Q & 1) << 30) | (uint32(r&31) << 16) | (uint32(index&15) << 11) | (uint32(rf&31) << 5) | uint32(rt&31) - case 95: /* vushr $shift, Vn.<T>, Vd.<T> */ + case 95: /* vushr/vshl/vsri/vsli/vusra $shift, Vn.<T>, Vd.<T> */ at := int((p.To.Reg >> 5) & 15) af := int((p.Reg >> 5) & 15) shift := int(p.From.Offset) @@ -4862,14 +5050,13 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { } imm := 0 - switch p.As { - case AVUSHR, AVSRI: + case AVUSHR, AVSRI, AVUSRA: imm = esize*2 - shift if imm < esize || imm > imax { c.ctxt.Diag("shift out of range: %v", p) } - case AVSHL: + case AVSHL, AVSLI: imm = esize + shift if imm > imax { c.ctxt.Diag("shift out of range: %v", p) @@ -4882,7 +5069,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { rt := int((p.To.Reg) & 31) rf := int((p.Reg) & 31) - o1 |= ((Q & 1) << 30) | (uint32(imm&127) << 16) | (uint32(rf&31) << 5) | uint32(rt&31) + o1 |= ((Q & 1) << 30) | (uint32(imm&0x7f) << 16) | (uint32(rf&31) << 5) | uint32(rt&31) case 96: /* vst1 Vt1.<T>[index], offset(Rn) */ af := int((p.From.Reg >> 5) & 15) @@ -5096,6 +5283,150 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { o1 = q<<30 | 0xe<<24 | len<<13 o1 |= (uint32(rf&31) << 16) | uint32(offset&31)<<5 | uint32(rt&31) + case 101: // VMOVQ $vcon1, $vcon2, Vd or VMOVD|VMOVS $vcon, Vd -> FMOVQ/FMOVD/FMOVS pool(PC), Vd: load from constant pool. + o1 = c.omovlit(p.As, p, &p.From, int(p.To.Reg)) + + case 102: /* vushll, vushll2, vuxtl, vuxtl2 */ + o1 = c.opirr(p, p.As) + rf := p.Reg + af := uint8((p.Reg >> 5) & 15) + at := uint8((p.To.Reg >> 5) & 15) + shift := int(p.From.Offset) + if p.As == AVUXTL || p.As == AVUXTL2 { + rf = p.From.Reg + af = uint8((p.From.Reg >> 5) & 15) + shift = 0 + } + + Q := (o1 >> 30) & 1 + var immh, width uint8 + switch pack(Q, af, at) { + case pack(0, ARNG_8B, ARNG_8H): + immh, width = 1, 8 + case pack(1, ARNG_16B, ARNG_8H): + immh, width = 1, 8 + case pack(0, ARNG_4H, ARNG_4S): + immh, width = 2, 16 + case pack(1, ARNG_8H, ARNG_4S): + immh, width = 2, 16 + case pack(0, ARNG_2S, ARNG_2D): + immh, width = 4, 32 + case pack(1, ARNG_4S, ARNG_2D): + immh, width = 4, 32 + default: + c.ctxt.Diag("operand mismatch: %v\n", p) + } + if !(0 <= shift && shift <= int(width-1)) { + c.ctxt.Diag("shift amount out of range: %v\n", p) + } + o1 |= uint32(immh)<<19 | uint32(shift)<<16 | uint32(rf&31)<<5 | uint32(p.To.Reg&31) + + case 103: /* VEOR3/VBCAX Va.B16, Vm.B16, Vn.B16, Vd.B16 */ + ta := (p.From.Reg >> 5) & 15 + tm := (p.Reg >> 5) & 15 + td := (p.To.Reg >> 5) & 15 + tn := ((p.GetFrom3().Reg) >> 5) & 15 + + if ta != tm || ta != tn || ta != td || ta != ARNG_16B { + c.ctxt.Diag("invalid arrangement: %v", p) + break + } + + o1 = c.oprrr(p, p.As) + ra := int(p.From.Reg) + rm := int(p.Reg) + rn := int(p.GetFrom3().Reg) + rd := int(p.To.Reg) + o1 |= uint32(rm&31)<<16 | uint32(ra&31)<<10 | uint32(rn&31)<<5 | uint32(rd)&31 + + case 104: /* vxar $imm4, Vm.<T>, Vn.<T>, Vd.<T> */ + af := ((p.GetFrom3().Reg) >> 5) & 15 + at := (p.To.Reg >> 5) & 15 + a := (p.Reg >> 5) & 15 + index := int(p.From.Offset) + + if af != a || af != at { + c.ctxt.Diag("invalid arrangement: %v", p) + break + } + + if af != ARNG_2D { + c.ctxt.Diag("invalid arrangement, should be D2: %v", p) + break + } + + if index < 0 || index > 63 { + c.ctxt.Diag("illegal offset: %v", p) + } + + o1 = c.opirr(p, p.As) + rf := (p.GetFrom3().Reg) & 31 + rt := (p.To.Reg) & 31 + r := (p.Reg) & 31 + + o1 |= (uint32(r&31) << 16) | (uint32(index&63) << 10) | (uint32(rf&31) << 5) | uint32(rt&31) + + case 105: /* vuaddw{2} Vm.<Tb>, Vn.<Ta>, Vd.<Ta> */ + af := uint8((p.From.Reg >> 5) & 15) + at := uint8((p.To.Reg >> 5) & 15) + a := uint8((p.Reg >> 5) & 15) + if at != a { + c.ctxt.Diag("invalid arrangement: %v", p) + break + } + + var Q, size uint32 + if p.As == AVUADDW2 { + Q = 1 + } + switch pack(Q, at, af) { + case pack(0, ARNG_8H, ARNG_8B), pack(1, ARNG_8H, ARNG_16B): + size = 0 + case pack(0, ARNG_4S, ARNG_4H), pack(1, ARNG_4S, ARNG_8H): + size = 1 + case pack(0, ARNG_2D, ARNG_2S), pack(1, ARNG_2D, ARNG_4S): + size = 2 + default: + c.ctxt.Diag("operand mismatch: %v\n", p) + } + + o1 = c.oprrr(p, p.As) + rf := int((p.From.Reg) & 31) + rt := int((p.To.Reg) & 31) + r := int((p.Reg) & 31) + o1 |= ((Q & 1) << 30) | ((size & 3) << 22) | (uint32(rf&31) << 16) | (uint32(r&31) << 5) | uint32(rt&31) + + case 106: // CASPx (Rs, Rs+1), (Rb), (Rt, Rt+1) + rs := p.From.Reg + rt := p.GetTo2().Reg + rb := p.To.Reg + rs1 := int16(p.From.Offset) + rt1 := int16(p.GetTo2().Offset) + + enc, ok := atomicCASP[p.As] + if !ok { + c.ctxt.Diag("invalid CASP-like atomic instructions: %v\n", p) + } + // for CASPx-like instructions, Rs<0> != 1 && Rt<0> != 1 + switch { + case rs&1 != 0: + c.ctxt.Diag("source register pair must start from even register: %v\n", p) + break + case rt&1 != 0: + c.ctxt.Diag("destination register pair must start from even register: %v\n", p) + break + case rs != rs1-1: + c.ctxt.Diag("source register pair must be contiguous: %v\n", p) + break + case rt != rt1-1: + c.ctxt.Diag("destination register pair must be contiguous: %v\n", p) + break + } + // rt can't be sp. + if rt == REG_RSP { + c.ctxt.Diag("illegal destination register: %v\n", p) + } + o1 |= enc | uint32(rs&31)<<16 | uint32(rb&31)<<5 | uint32(rt&31) } out[0] = o1 out[1] = o2 @@ -5662,12 +5993,18 @@ func (c *ctxt7) oprrr(p *obj.Prog, a obj.As) uint32 { case AVADD: return 7<<25 | 1<<21 | 1<<15 | 1<<10 + case AVSUB: + return 0x17<<25 | 1<<21 | 1<<15 | 1<<10 + case AVADDP: return 7<<25 | 1<<21 | 1<<15 | 15<<10 case AVAND: return 7<<25 | 1<<21 | 7<<10 + case AVBCAX: + return 0xCE<<24 | 1<<21 + case AVCMEQ: return 1<<29 | 0x71<<21 | 0x23<<10 @@ -5683,12 +6020,18 @@ func (c *ctxt7) oprrr(p *obj.Prog, a obj.As) uint32 { case AVEOR: return 1<<29 | 0x71<<21 | 7<<10 + case AVEOR3: + return 0xCE << 24 + case AVORR: return 7<<25 | 5<<21 | 7<<10 case AVREV16: return 3<<26 | 2<<24 | 1<<21 | 3<<11 + case AVRAX1: + return 0xCE<<24 | 3<<21 | 1<<15 | 3<<10 + case AVREV32: return 11<<26 | 2<<24 | 1<<21 | 1<<11 @@ -5724,6 +6067,27 @@ func (c *ctxt7) oprrr(p *obj.Prog, a obj.As) uint32 { case AVLD2R, AVLD4R: return 0xD<<24 | 3<<21 + + case AVBIF: + return 1<<29 | 7<<25 | 7<<21 | 7<<10 + + case AVBIT: + return 1<<29 | 0x75<<21 | 7<<10 + + case AVBSL: + return 1<<29 | 0x73<<21 | 7<<10 + + case AVCMTST: + return 0xE<<24 | 1<<21 | 0x23<<10 + + case AVUZP1: + return 7<<25 | 3<<11 + + case AVUZP2: + return 7<<25 | 1<<14 | 3<<11 + + case AVUADDW, AVUADDW2: + return 0x17<<25 | 1<<21 | 1<<12 } c.ctxt.Diag("%v: bad rrr %d %v", p, a, a) @@ -5922,6 +6286,24 @@ func (c *ctxt7) opirr(p *obj.Prog, a obj.As) uint32 { case AVSRI: return 0x5E<<23 | 17<<10 + + case AVSLI: + return 0x5E<<23 | 21<<10 + + case AVUSHLL, AVUXTL: + return 1<<29 | 15<<24 | 0x29<<10 + + case AVUSHLL2, AVUXTL2: + return 3<<29 | 15<<24 | 0x29<<10 + + case AVXAR: + return 0xCE<<24 | 1<<23 + + case AVUSRA: + return 1<<29 | 15<<24 | 5<<10 + + case APRFM: + return 0xf9<<24 | 2<<22 } c.ctxt.Diag("%v: bad irr %v", p, a) @@ -6316,7 +6698,7 @@ func (c *ctxt7) opstore(p *obj.Prog, a obj.As) uint32 { } /* - * load/store register (unsigned immediate) C3.3.13 + * load/store register (scaled 12-bit unsigned immediate) C3.3.13 * these produce 64-bit values (when there's an option) */ func (c *ctxt7) olsr12u(p *obj.Prog, o int32, v int32, b int, r int) uint32 { @@ -6326,49 +6708,12 @@ func (c *ctxt7) olsr12u(p *obj.Prog, o int32, v int32, b int, r int) uint32 { o |= (v & 0xFFF) << 10 o |= int32(b&31) << 5 o |= int32(r & 31) + o |= 1 << 24 return uint32(o) } -func (c *ctxt7) opldr12(p *obj.Prog, a obj.As) uint32 { - switch a { - case AMOVD: - return LDSTR12U(3, 0, 1) /* imm12<<10 | Rn<<5 | Rt */ - - case AMOVW: - return LDSTR12U(2, 0, 2) - - case AMOVWU: - return LDSTR12U(2, 0, 1) - - case AMOVH: - return LDSTR12U(1, 0, 2) - - case AMOVHU: - return LDSTR12U(1, 0, 1) - - case AMOVB: - return LDSTR12U(0, 0, 2) - - case AMOVBU: - return LDSTR12U(0, 0, 1) - - case AFMOVS: - return LDSTR12U(2, 1, 1) - - case AFMOVD: - return LDSTR12U(3, 1, 1) - } - - c.ctxt.Diag("bad opldr12 %v\n%v", a, p) - return 0 -} - -func (c *ctxt7) opstr12(p *obj.Prog, a obj.As) uint32 { - return LD2STR(c.opldr12(p, a)) -} - /* - * load/store register (unscaled immediate) C3.3.12 + * load/store register (unscaled 9-bit signed immediate) C3.3.12 */ func (c *ctxt7) olsr9s(p *obj.Prog, o int32, v int32, b int, r int) uint32 { if v < -256 || v > 255 { @@ -6380,76 +6725,58 @@ func (c *ctxt7) olsr9s(p *obj.Prog, o int32, v int32, b int, r int) uint32 { return uint32(o) } -func (c *ctxt7) opldr9(p *obj.Prog, a obj.As) uint32 { - switch a { - case AMOVD: - return LDSTR9S(3, 0, 1) /* simm9<<12 | Rn<<5 | Rt */ - - case AMOVW: - return LDSTR9S(2, 0, 2) - - case AMOVWU: - return LDSTR9S(2, 0, 1) - - case AMOVH: - return LDSTR9S(1, 0, 2) - - case AMOVHU: - return LDSTR9S(1, 0, 1) - - case AMOVB: - return LDSTR9S(0, 0, 2) - - case AMOVBU: - return LDSTR9S(0, 0, 1) - - case AFMOVS: - return LDSTR9S(2, 1, 1) - - case AFMOVD: - return LDSTR9S(3, 1, 1) +// store(immediate) +// scaled 12-bit unsigned immediate offset. +// unscaled 9-bit signed immediate offset. +// pre/post-indexed store. +// and the 12-bit and 9-bit are distinguished in olsr12u and oslr9s. +func (c *ctxt7) opstr(p *obj.Prog, a obj.As) uint32 { + enc := c.opldr(p, a) + switch p.As { + case AFMOVQ: + enc = enc &^ (1 << 22) + default: + enc = LD2STR(enc) } - - c.ctxt.Diag("bad opldr9 %v\n%v", a, p) - return 0 -} - -func (c *ctxt7) opstr9(p *obj.Prog, a obj.As) uint32 { - return LD2STR(c.opldr9(p, a)) + return enc } -func (c *ctxt7) opldrpp(p *obj.Prog, a obj.As) uint32 { +// load(immediate) +// scaled 12-bit unsigned immediate offset. +// unscaled 9-bit signed immediate offset. +// pre/post-indexed load. +// and the 12-bit and 9-bit are distinguished in olsr12u and oslr9s. +func (c *ctxt7) opldr(p *obj.Prog, a obj.As) uint32 { switch a { case AMOVD: - return 3<<30 | 7<<27 | 0<<26 | 0<<24 | 1<<22 /* simm9<<12 | Rn<<5 | Rt */ + return LDSTR(3, 0, 1) /* simm9<<12 | Rn<<5 | Rt */ case AMOVW: - return 2<<30 | 7<<27 | 0<<26 | 0<<24 | 2<<22 + return LDSTR(2, 0, 2) case AMOVWU: - return 2<<30 | 7<<27 | 0<<26 | 0<<24 | 1<<22 + return LDSTR(2, 0, 1) case AMOVH: - return 1<<30 | 7<<27 | 0<<26 | 0<<24 | 2<<22 + return LDSTR(1, 0, 2) case AMOVHU: - return 1<<30 | 7<<27 | 0<<26 | 0<<24 | 1<<22 + return LDSTR(1, 0, 1) case AMOVB: - return 0<<30 | 7<<27 | 0<<26 | 0<<24 | 2<<22 + return LDSTR(0, 0, 2) case AMOVBU: - return 0<<30 | 7<<27 | 0<<26 | 0<<24 | 1<<22 + return LDSTR(0, 0, 1) case AFMOVS: - return 2<<30 | 7<<27 | 1<<26 | 0<<24 | 1<<22 + return LDSTR(2, 1, 1) case AFMOVD: - return 3<<30 | 7<<27 | 1<<26 | 0<<24 | 1<<22 - - case APRFM: - return 0xf9<<24 | 2<<22 + return LDSTR(3, 1, 1) + case AFMOVQ: + return LDSTR(0, 1, 3) } c.ctxt.Diag("bad opldr %v\n%v", a, p) @@ -6558,14 +6885,18 @@ func (c *ctxt7) omovlit(as obj.As, p *obj.Prog, a *obj.Addr, dr int) uint32 { } else { fp, w := 0, 0 switch as { - case AFMOVS: + case AFMOVS, AVMOVS: fp = 1 w = 0 /* 32-bit SIMD/FP */ - case AFMOVD: + case AFMOVD, AVMOVD: fp = 1 w = 1 /* 64-bit SIMD/FP */ + case AVMOVQ: + fp = 1 + w = 2 /* 128-bit SIMD/FP */ + case AMOVD: if p.Pool.As == ADWORD { w = 1 /* 64-bit */ @@ -6938,10 +7269,13 @@ func (c *ctxt7) maskOpvldvst(p *obj.Prog, o1 uint32) uint32 { */ func movesize(a obj.As) int { switch a { - case AMOVD: + case AFMOVQ: + return 4 + + case AMOVD, AFMOVD: return 3 - case AMOVW, AMOVWU: + case AMOVW, AMOVWU, AFMOVS: return 2 case AMOVH, AMOVHU: @@ -6950,12 +7284,6 @@ func movesize(a obj.As) int { case AMOVB, AMOVBU: return 0 - case AFMOVS: - return 2 - - case AFMOVD: - return 3 - default: return -1 } @@ -7020,3 +7348,8 @@ func (c *ctxt7) encRegShiftOrExt(a *obj.Addr, r int16) uint32 { return 0 } + +// pack returns the encoding of the "Q" field and two arrangement specifiers. +func pack(q uint32, arngA, arngB uint8) uint32 { + return uint32(q)<<16 | uint32(arngA)<<8 | uint32(arngB) +} diff --git a/src/cmd/internal/obj/arm64/doc.go b/src/cmd/internal/obj/arm64/doc.go index 7515217544..efd4577f56 100644 --- a/src/cmd/internal/obj/arm64/doc.go +++ b/src/cmd/internal/obj/arm64/doc.go @@ -86,6 +86,16 @@ In the following example, PCALIGN at the entry of the function Add will align it MOVD $1, R1 RET +7. Move large constants to vector registers. + +Go asm uses VMOVQ/VMOVD/VMOVS to move 128-bit, 64-bit and 32-bit constants into vector registers, respectively. +And for a 128-bit interger, it take two 64-bit operands, for the high and low parts separately. + + Examples: + VMOVS $0x11223344, V0 + VMOVD $0x1122334455667788, V1 + VMOVQ $0x1122334455667788, $8877665544332211, V2 // V2=0x11223344556677888877665544332211 + Special Cases. (1) umov is written as VMOV. diff --git a/src/cmd/internal/obj/arm64/obj7.go b/src/cmd/internal/obj/arm64/obj7.go index 56da854f16..0baf51973a 100644 --- a/src/cmd/internal/obj/arm64/obj7.go +++ b/src/cmd/internal/obj/arm64/obj7.go @@ -166,7 +166,7 @@ func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1) var last *obj.Prog - for last = c.cursym.Func.Text; last.Link != nil; last = last.Link { + for last = c.cursym.Func().Text; last.Link != nil; last = last.Link { } // Now we are at the end of the function, but logically @@ -209,7 +209,7 @@ func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { switch { case c.cursym.CFunc(): morestack = "runtime.morestackc" - case !c.cursym.Func.Text.From.Sym.NeedCtxt(): + case !c.cursym.Func().Text.From.Sym.NeedCtxt(): morestack = "runtime.morestack_noctxt" } call.To.Sym = c.ctxt.Lookup(morestack) @@ -220,7 +220,7 @@ func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { jmp := obj.Appendp(pcdata, c.newprog) jmp.As = AB jmp.To.Type = obj.TYPE_BRANCH - jmp.To.SetTarget(c.cursym.Func.Text.Link) + jmp.To.SetTarget(c.cursym.Func().Text.Link) jmp.Spadj = +framesize return end @@ -441,13 +441,13 @@ func (c *ctxt7) rewriteToUseGot(p *obj.Prog) { } func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { - if cursym.Func.Text == nil || cursym.Func.Text.Link == nil { + if cursym.Func().Text == nil || cursym.Func().Text.Link == nil { return } c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym} - p := c.cursym.Func.Text + p := c.cursym.Func().Text textstksiz := p.To.Offset if textstksiz == -8 { // Historical way to mark NOFRAME. @@ -463,13 +463,13 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } } - c.cursym.Func.Args = p.To.Val.(int32) - c.cursym.Func.Locals = int32(textstksiz) + c.cursym.Func().Args = p.To.Val.(int32) + c.cursym.Func().Locals = int32(textstksiz) /* * find leaf subroutines */ - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { switch p.As { case obj.ATEXT: p.Mark |= LEAF @@ -477,18 +477,18 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { case ABL, obj.ADUFFZERO, obj.ADUFFCOPY: - c.cursym.Func.Text.Mark &^= LEAF + c.cursym.Func().Text.Mark &^= LEAF } } var q *obj.Prog var q1 *obj.Prog var retjmp *obj.LSym - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { o := p.As switch o { case obj.ATEXT: - c.cursym.Func.Text = p + c.cursym.Func().Text = p c.autosize = int32(textstksiz) if p.Mark&LEAF != 0 && c.autosize == 0 { @@ -514,7 +514,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8) } c.autosize += extrasize - c.cursym.Func.Locals += extrasize + c.cursym.Func().Locals += extrasize // low 32 bits for autosize // high 32 bits for extrasize @@ -524,14 +524,14 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p.To.Offset = 0 } - if c.autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 { + if c.autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 { if c.ctxt.Debugvlog { - c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func.Text.From.Sym.Name) + c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func().Text.From.Sym.Name) } - c.cursym.Func.Text.Mark |= LEAF + c.cursym.Func().Text.Mark |= LEAF } - if cursym.Func.Text.Mark&LEAF != 0 { + if cursym.Func().Text.Mark&LEAF != 0 { cursym.Set(obj.AttrLeaf, true) if p.From.Sym.NoFrame() { break @@ -589,7 +589,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q1.To.Reg = REGSP q1.Spadj = c.autosize - if c.ctxt.Headtype == objabi.Hdarwin { + if objabi.GOOS == "ios" { // iOS does not support SA_ONSTACK. We will run the signal handler // on the G stack. If we write below SP, it may be clobbered by // the signal handler. So we save LR after decrementing SP. @@ -641,7 +641,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q1.To.Reg = REGFP } - if c.cursym.Func.Text.From.Sym.Wrapper() { + if c.cursym.Func().Text.From.Sym.Wrapper() { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOV g_panic(g), R1 @@ -755,7 +755,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { retjmp = p.To.Sym p.To = obj.Addr{} - if c.cursym.Func.Text.Mark&LEAF != 0 { + if c.cursym.Func().Text.Mark&LEAF != 0 { if c.autosize != 0 { p.As = AADD p.From.Type = obj.TYPE_CONST diff --git a/src/cmd/internal/obj/dwarf.go b/src/cmd/internal/obj/dwarf.go index 9abb31b558..87c62e2981 100644 --- a/src/cmd/internal/obj/dwarf.go +++ b/src/cmd/internal/obj/dwarf.go @@ -46,12 +46,12 @@ func (ctxt *Link) generateDebugLinesSymbol(s, lines *LSym) { // we expect at the start of a new sequence. stmt := true line := int64(1) - pc := s.Func.Text.Pc + pc := s.Func().Text.Pc var lastpc int64 // last PC written to line table, not last PC in func name := "" prologue, wrotePrologue := false, false // Walk the progs, generating the DWARF table. - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { prologue = prologue || (p.Pos.Xlogue() == src.PosPrologueEnd) // If we're not at a real instruction, keep looping! if p.Pos.Line() == 0 || (p.Link != nil && p.Link.Pc == p.Pc) { @@ -103,8 +103,9 @@ func (ctxt *Link) generateDebugLinesSymbol(s, lines *LSym) { // text address before the end sequence op. If this isn't done, // GDB will assign a line number of zero the last row in the line // table, which we don't want. - lastlen := uint64(s.Size - (lastpc - s.Func.Text.Pc)) - putpclcdelta(ctxt, dctxt, lines, lastlen, 0) + lastlen := uint64(s.Size - (lastpc - s.Func().Text.Pc)) + dctxt.AddUint8(lines, dwarf.DW_LNS_advance_pc) + dwarf.Uleb128put(dctxt, lines, int64(lastlen)) dctxt.AddUint8(lines, 0) // start extended opcode dwarf.Uleb128put(dctxt, lines, 1) dctxt.AddUint8(lines, dwarf.DW_LNE_end_sequence) @@ -301,26 +302,27 @@ func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfLocSym, dwarfRangesSym, if s.Type != objabi.STEXT { ctxt.Diag("dwarfSym of non-TEXT %v", s) } - if s.Func.dwarfInfoSym == nil { - s.Func.dwarfInfoSym = &LSym{ + fn := s.Func() + if fn.dwarfInfoSym == nil { + fn.dwarfInfoSym = &LSym{ Type: objabi.SDWARFFCN, } if ctxt.Flag_locationlists { - s.Func.dwarfLocSym = &LSym{ + fn.dwarfLocSym = &LSym{ Type: objabi.SDWARFLOC, } } - s.Func.dwarfRangesSym = &LSym{ + fn.dwarfRangesSym = &LSym{ Type: objabi.SDWARFRANGE, } - s.Func.dwarfDebugLinesSym = &LSym{ + fn.dwarfDebugLinesSym = &LSym{ Type: objabi.SDWARFLINES, } if s.WasInlined() { - s.Func.dwarfAbsFnSym = ctxt.DwFixups.AbsFuncDwarfSym(s) + fn.dwarfAbsFnSym = ctxt.DwFixups.AbsFuncDwarfSym(s) } } - return s.Func.dwarfInfoSym, s.Func.dwarfLocSym, s.Func.dwarfRangesSym, s.Func.dwarfAbsFnSym, s.Func.dwarfDebugLinesSym + return fn.dwarfInfoSym, fn.dwarfLocSym, fn.dwarfRangesSym, fn.dwarfAbsFnSym, fn.dwarfDebugLinesSym } func (s *LSym) Length(dwarfContext interface{}) int64 { @@ -331,7 +333,7 @@ func (s *LSym) Length(dwarfContext interface{}) int64 { // first instruction (prog) of the specified function. This will // presumably be the file in which the function is defined. func (ctxt *Link) fileSymbol(fn *LSym) *LSym { - p := fn.Func.Text + p := fn.Func().Text if p != nil { f, _ := linkgetlineFromPos(ctxt, p.Pos) fsym := ctxt.Lookup(f) @@ -405,8 +407,8 @@ func (ctxt *Link) DwarfAbstractFunc(curfn interface{}, s *LSym, myimportpath str if absfn.Size != 0 { ctxt.Diag("internal error: DwarfAbstractFunc double process %v", s) } - if s.Func == nil { - s.Func = new(FuncInfo) + if s.Func() == nil { + s.NewFuncInfo() } scopes, _ := ctxt.DebugInfo(s, absfn, curfn) dwctxt := dwCtxt{ctxt} @@ -527,8 +529,8 @@ func (ft *DwarfFixupTable) SetPrecursorFunc(s *LSym, fn interface{}) { // wrapper generation as opposed to the main inlining phase) it's // possible that we didn't cache the abstract function sym for the // text symbol -- do so now if needed. See issue 38068. - if s.Func != nil && s.Func.dwarfAbsFnSym == nil { - s.Func.dwarfAbsFnSym = absfn + if fn := s.Func(); fn != nil && fn.dwarfAbsFnSym == nil { + fn.dwarfAbsFnSym = absfn } ft.precursor[s] = fnState{precursor: fn, absfn: absfn} diff --git a/src/cmd/internal/obj/ld.go b/src/cmd/internal/obj/ld.go index 4ba52c7785..5d6c000dc6 100644 --- a/src/cmd/internal/obj/ld.go +++ b/src/cmd/internal/obj/ld.go @@ -59,7 +59,7 @@ func mkfwd(sym *LSym) { } i := 0 - for p := sym.Func.Text; p != nil && p.Link != nil; p = p.Link { + for p := sym.Func().Text; p != nil && p.Link != nil; p = p.Link { i-- if i < 0 { i = LOG - 1 diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index 1d4217b5f5..8c8ff587ff 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -282,29 +282,42 @@ func (a *Addr) SetTarget(t *Prog) { // The other fields not yet mentioned are for use by the back ends and should // be left zeroed by creators of Prog lists. type Prog struct { - Ctxt *Link // linker context - Link *Prog // next Prog in linked list - From Addr // first source operand - RestArgs []Addr // can pack any operands that not fit into {Prog.From, Prog.To} - To Addr // destination operand (second is RegTo2 below) - Pool *Prog // constant pool entry, for arm,arm64 back ends - Forwd *Prog // for x86 back end - Rel *Prog // for x86, arm back ends - Pc int64 // for back ends or assembler: virtual or actual program counter, depending on phase - Pos src.XPos // source position of this instruction - Spadj int32 // effect of instruction on stack pointer (increment or decrement amount) - As As // assembler opcode - Reg int16 // 2nd source operand - RegTo2 int16 // 2nd destination operand - Mark uint16 // bitmask of arch-specific items - Optab uint16 // arch-specific opcode index - Scond uint8 // bits that describe instruction suffixes (e.g. ARM conditions) - Back uint8 // for x86 back end: backwards branch state - Ft uint8 // for x86 back end: type index of Prog.From - Tt uint8 // for x86 back end: type index of Prog.To - Isize uint8 // for x86 back end: size of the instruction in bytes + Ctxt *Link // linker context + Link *Prog // next Prog in linked list + From Addr // first source operand + RestArgs []AddrPos // can pack any operands that not fit into {Prog.From, Prog.To} + To Addr // destination operand (second is RegTo2 below) + Pool *Prog // constant pool entry, for arm,arm64 back ends + Forwd *Prog // for x86 back end + Rel *Prog // for x86, arm back ends + Pc int64 // for back ends or assembler: virtual or actual program counter, depending on phase + Pos src.XPos // source position of this instruction + Spadj int32 // effect of instruction on stack pointer (increment or decrement amount) + As As // assembler opcode + Reg int16 // 2nd source operand + RegTo2 int16 // 2nd destination operand + Mark uint16 // bitmask of arch-specific items + Optab uint16 // arch-specific opcode index + Scond uint8 // bits that describe instruction suffixes (e.g. ARM conditions) + Back uint8 // for x86 back end: backwards branch state + Ft uint8 // for x86 back end: type index of Prog.From + Tt uint8 // for x86 back end: type index of Prog.To + Isize uint8 // for x86 back end: size of the instruction in bytes } +// Pos indicates whether the oprand is the source or the destination. +type AddrPos struct { + Addr + Pos OperandPos +} + +type OperandPos int8 + +const ( + Source OperandPos = iota + Destination +) + // From3Type returns p.GetFrom3().Type, or TYPE_NONE when // p.GetFrom3() returns nil. // @@ -329,15 +342,36 @@ func (p *Prog) GetFrom3() *Addr { if p.RestArgs == nil { return nil } - return &p.RestArgs[0] + return &p.RestArgs[0].Addr } -// SetFrom3 assigns []Addr{a} to p.RestArgs. +// SetFrom3 assigns []Args{{a, 0}} to p.RestArgs. // In pair with Prog.GetFrom3 it can help in emulation of Prog.From3. // // Deprecated: for the same reasons as Prog.GetFrom3. func (p *Prog) SetFrom3(a Addr) { - p.RestArgs = []Addr{a} + p.RestArgs = []AddrPos{{a, Source}} +} + +// SetTo2 assings []Args{{a, 1}} to p.RestArgs when the second destination +// operand does not fit into prog.RegTo2. +func (p *Prog) SetTo2(a Addr) { + p.RestArgs = []AddrPos{{a, Destination}} +} + +// GetTo2 returns the second destination operand. +func (p *Prog) GetTo2() *Addr { + if p.RestArgs == nil { + return nil + } + return &p.RestArgs[0].Addr +} + +// SetRestArgs assigns more than one source operands to p.RestArgs. +func (p *Prog) SetRestArgs(args []Addr) { + for i := range args { + p.RestArgs = append(p.RestArgs, AddrPos{args[i], Source}) + } } // An As denotes an assembler opcode. @@ -395,17 +429,16 @@ type LSym struct { Type objabi.SymKind Attribute - RefIdx int // Index of this symbol in the symbol reference list. Size int64 Gotype *LSym P []byte R []Reloc - Func *FuncInfo + Extra *interface{} // *FuncInfo or *FileInfo, if present Pkg string PkgIdx int32 - SymIdx int32 // TODO: replace RefIdx + SymIdx int32 } // A FuncInfo contains extra fields for STEXT symbols. @@ -427,13 +460,59 @@ type FuncInfo struct { GCArgs *LSym GCLocals *LSym - GCRegs *LSym // Only if !go115ReduceLiveness StackObjects *LSym OpenCodedDeferInfo *LSym FuncInfoSym *LSym } +// NewFuncInfo allocates and returns a FuncInfo for LSym. +func (s *LSym) NewFuncInfo() *FuncInfo { + if s.Extra != nil { + panic(fmt.Sprintf("invalid use of LSym - NewFuncInfo with Extra of type %T", *s.Extra)) + } + f := new(FuncInfo) + s.Extra = new(interface{}) + *s.Extra = f + return f +} + +// Func returns the *FuncInfo associated with s, or else nil. +func (s *LSym) Func() *FuncInfo { + if s.Extra == nil { + return nil + } + f, _ := (*s.Extra).(*FuncInfo) + return f +} + +// A FileInfo contains extra fields for SDATA symbols backed by files. +// (If LSym.Extra is a *FileInfo, LSym.P == nil.) +type FileInfo struct { + Name string // name of file to read into object file + Size int64 // length of file +} + +// NewFileInfo allocates and returns a FileInfo for LSym. +func (s *LSym) NewFileInfo() *FileInfo { + if s.Extra != nil { + panic(fmt.Sprintf("invalid use of LSym - NewFileInfo with Extra of type %T", *s.Extra)) + } + f := new(FileInfo) + s.Extra = new(interface{}) + *s.Extra = f + return f +} + +// File returns the *FileInfo associated with s, or else nil. +func (s *LSym) File() *FileInfo { + if s.Extra == nil { + return nil + } + f, _ := (*s.Extra).(*FileInfo) + return f +} + type InlMark struct { // When unwinding from an instruction in an inlined body, mark // where we should unwind to. @@ -482,6 +561,20 @@ const ( ABICount ) +// ParseABI converts from a string representation in 'abistr' to the +// corresponding ABI value. Second return value is TRUE if the +// abi string is recognized, FALSE otherwise. +func ParseABI(abistr string) (ABI, bool) { + switch abistr { + default: + return ABI0, false + case "ABI0": + return ABI0, true + case "ABIInternal": + return ABIInternal, true + } +} + // Attribute is a set of symbol attributes. type Attribute uint32 @@ -634,11 +727,12 @@ func (s *LSym) CanBeAnSSASym() { } type Pcln struct { - Pcsp Pcdata - Pcfile Pcdata - Pcline Pcdata - Pcinline Pcdata - Pcdata []Pcdata + // Aux symbols for pcln + Pcsp *LSym + Pcfile *LSym + Pcline *LSym + Pcinline *LSym + Pcdata []*LSym Funcdata []*LSym Funcdataoff []int64 UsedFiles map[goobj.CUFileIndex]struct{} // file indices used while generating pcfile @@ -660,10 +754,6 @@ type Auto struct { Gotype *LSym } -type Pcdata struct { - P []byte -} - // 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 { diff --git a/src/cmd/internal/obj/mips/asm0.go b/src/cmd/internal/obj/mips/asm0.go index 6107974745..fd29f9fa21 100644 --- a/src/cmd/internal/obj/mips/asm0.go +++ b/src/cmd/internal/obj/mips/asm0.go @@ -410,7 +410,7 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { ctxt.Retpoline = false // don't keep printing } - p := cursym.Func.Text + p := cursym.Func().Text if p == nil || p.Link == nil { // handle external functions and ELF section symbols return } @@ -455,7 +455,7 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { for bflag != 0 { bflag = 0 pc = 0 - for p = c.cursym.Func.Text.Link; p != nil; p = p.Link { + for p = c.cursym.Func().Text.Link; p != nil; p = p.Link { p.Pc = pc o = c.oplook(p) @@ -512,7 +512,7 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { bp := c.cursym.P var i int32 var out [4]uint32 - for p := c.cursym.Func.Text.Link; p != nil; p = p.Link { + for p := c.cursym.Func().Text.Link; p != nil; p = p.Link { c.pc = p.Pc o = c.oplook(p) if int(o.size) > 4*len(out) { @@ -529,7 +529,7 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // We use REGTMP as a scratch register during call injection, // so instruction sequences that use REGTMP are unsafe to // preempt asynchronously. - obj.MarkUnsafePoints(c.ctxt, c.cursym.Func.Text, c.newprog, c.isUnsafePoint, c.isRestartable) + obj.MarkUnsafePoints(c.ctxt, c.cursym.Func().Text, c.newprog, c.isUnsafePoint, c.isRestartable) } // isUnsafePoint returns whether p is an unsafe point. @@ -1302,7 +1302,7 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { } o1 = OP_JMP(c.opirr(p.As), uint32(v)) if p.To.Sym == nil { - p.To.Sym = c.cursym.Func.Text.From.Sym + p.To.Sym = c.cursym.Func().Text.From.Sym p.To.Offset = p.To.Target().Pc } rel := obj.Addrel(c.cursym) diff --git a/src/cmd/internal/obj/mips/obj0.go b/src/cmd/internal/obj/mips/obj0.go index f19facc00c..135a8df3aa 100644 --- a/src/cmd/internal/obj/mips/obj0.go +++ b/src/cmd/internal/obj/mips/obj0.go @@ -133,11 +133,11 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // a switch for enabling/disabling instruction scheduling nosched := true - if c.cursym.Func.Text == nil || c.cursym.Func.Text.Link == nil { + if c.cursym.Func().Text == nil || c.cursym.Func().Text.Link == nil { return } - p := c.cursym.Func.Text + p := c.cursym.Func().Text textstksiz := p.To.Offset if textstksiz == -ctxt.FixedFrameSize() { // Historical way to mark NOFRAME. @@ -153,8 +153,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } } - c.cursym.Func.Args = p.To.Val.(int32) - c.cursym.Func.Locals = int32(textstksiz) + c.cursym.Func().Args = p.To.Val.(int32) + c.cursym.Func().Locals = int32(textstksiz) /* * find leaf subroutines @@ -162,7 +162,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { * expand BECOME pseudo */ - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { switch p.As { /* too hard, just leave alone */ case obj.ATEXT: @@ -203,7 +203,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { AJAL, obj.ADUFFZERO, obj.ADUFFCOPY: - c.cursym.Func.Text.Mark &^= LEAF + c.cursym.Func().Text.Mark &^= LEAF fallthrough case AJMP, @@ -267,7 +267,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { autosize := int32(0) var p1 *obj.Prog var p2 *obj.Prog - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { o := p.As switch o { case obj.ATEXT: @@ -288,19 +288,19 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { autosize += 4 } - if autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 { - if c.cursym.Func.Text.From.Sym.NoSplit() { + if autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 { + if c.cursym.Func().Text.From.Sym.NoSplit() { if ctxt.Debugvlog { ctxt.Logf("save suppressed in: %s\n", c.cursym.Name) } - c.cursym.Func.Text.Mark |= LEAF + c.cursym.Func().Text.Mark |= LEAF } } p.To.Offset = int64(autosize) - ctxt.FixedFrameSize() - if c.cursym.Func.Text.Mark&LEAF != 0 { + if c.cursym.Func().Text.Mark&LEAF != 0 { c.cursym.Set(obj.AttrLeaf, true) if p.From.Sym.NoFrame() { break @@ -344,7 +344,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q = c.ctxt.EndUnsafePoint(q, c.newprog, -1) } - if c.cursym.Func.Text.From.Sym.Wrapper() && c.cursym.Func.Text.Mark&LEAF == 0 { + if c.cursym.Func().Text.From.Sym.Wrapper() && c.cursym.Func().Text.Mark&LEAF == 0 { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOV g_panic(g), R1 @@ -438,7 +438,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction p.To.Sym = nil - if c.cursym.Func.Text.Mark&LEAF != 0 { + if c.cursym.Func().Text.Mark&LEAF != 0 { if autosize == 0 { p.As = AJMP p.From = obj.Addr{} @@ -540,7 +540,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { if c.ctxt.Arch.Family == sys.MIPS { // rewrite MOVD into two MOVF in 32-bit mode to avoid unaligned memory access - for p = c.cursym.Func.Text; p != nil; p = p1 { + for p = c.cursym.Func().Text; p != nil; p = p1 { p1 = p.Link if p.As != AMOVD { @@ -580,7 +580,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { if nosched { // if we don't do instruction scheduling, simply add // NOP after each branch instruction. - for p = c.cursym.Func.Text; p != nil; p = p.Link { + for p = c.cursym.Func().Text; p != nil; p = p.Link { if p.Mark&BRANCH != 0 { c.addnop(p) } @@ -589,10 +589,10 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } // instruction scheduling - q = nil // p - 1 - q1 = c.cursym.Func.Text // top of block - o := 0 // count of instructions - for p = c.cursym.Func.Text; p != nil; p = p1 { + q = nil // p - 1 + q1 = c.cursym.Func().Text // top of block + o := 0 // count of instructions + for p = c.cursym.Func().Text; p != nil; p = p1 { p1 = p.Link o++ if p.Mark&NOSCHED != 0 { @@ -791,7 +791,7 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p.To.Type = obj.TYPE_BRANCH if c.cursym.CFunc() { p.To.Sym = c.ctxt.Lookup("runtime.morestackc") - } else if !c.cursym.Func.Text.From.Sym.NeedCtxt() { + } else if !c.cursym.Func().Text.From.Sym.NeedCtxt() { p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt") } else { p.To.Sym = c.ctxt.Lookup("runtime.morestack") @@ -805,7 +805,7 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p.As = AJMP p.To.Type = obj.TYPE_BRANCH - p.To.SetTarget(c.cursym.Func.Text.Link) + p.To.SetTarget(c.cursym.Func().Text.Link) p.Mark |= BRANCH // placeholder for q1's jump target diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index 7bc4f4992e..bb58b4f0c2 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -16,6 +16,8 @@ import ( "encoding/binary" "fmt" "io" + "log" + "os" "path/filepath" "sort" "strings" @@ -147,14 +149,20 @@ func WriteObjFile(ctxt *Link, b *bio.Writer) { // Data indexes h.Offsets[goobj.BlkDataIdx] = w.Offset() - dataOff := uint32(0) + dataOff := int64(0) for _, list := range lists { for _, s := range list { - w.Uint32(dataOff) - dataOff += uint32(len(s.P)) + w.Uint32(uint32(dataOff)) + dataOff += int64(len(s.P)) + if file := s.File(); file != nil { + dataOff += int64(file.Size) + } } } - w.Uint32(dataOff) + if int64(uint32(dataOff)) != dataOff { + log.Fatalf("data too large") + } + w.Uint32(uint32(dataOff)) // Relocs h.Offsets[goobj.BlkReloc] = w.Offset() @@ -179,14 +187,21 @@ func WriteObjFile(ctxt *Link, b *bio.Writer) { for _, list := range lists { for _, s := range list { w.Bytes(s.P) + if file := s.File(); file != nil { + w.writeFile(ctxt, file) + } } } // Pcdata h.Offsets[goobj.BlkPcdata] = w.Offset() for _, s := range ctxt.Text { // iteration order must match genFuncInfoSyms - if s.Func != nil { - pc := &s.Func.Pcln + // Because of the phase order, it's possible that we try to write an invalid + // object file, and the Pcln variables haven't been filled in. As such, we + // need to check that Pcsp exists, and assume the other pcln variables exist + // as well. Tests like test/fixedbugs/issue22200.go demonstrate this issue. + if fn := s.Func(); fn != nil && fn.Pcln.Pcsp != nil { + pc := &fn.Pcln w.Bytes(pc.Pcsp.P) w.Bytes(pc.Pcfile.P) w.Bytes(pc.Pcline.P) @@ -214,6 +229,7 @@ func WriteObjFile(ctxt *Link, b *bio.Writer) { type writer struct { *goobj.Writer + filebuf []byte ctxt *Link pkgpath string // the package import path (escaped), "" if unknown pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx @@ -228,6 +244,35 @@ func (w *writer) init() { } } +func (w *writer) writeFile(ctxt *Link, file *FileInfo) { + f, err := os.Open(file.Name) + if err != nil { + ctxt.Diag("%v", err) + return + } + defer f.Close() + if w.filebuf == nil { + w.filebuf = make([]byte, 1024) + } + buf := w.filebuf + written := int64(0) + for { + n, err := f.Read(buf) + w.Bytes(buf[:n]) + written += int64(n) + if err == io.EOF { + break + } + if err != nil { + ctxt.Diag("%v", err) + return + } + } + if written != file.Size { + ctxt.Diag("copy %s: unexpected length %d != %d", file.Name, written, file.Size) + } +} + func (w *writer) StringTable() { w.AddString("") for _, p := range w.ctxt.Imports { @@ -257,6 +302,10 @@ func (w *writer) StringTable() { } } +// cutoff is the maximum data section size permitted by the linker +// (see issue #9862). +const cutoff = int64(2e9) // 2 GB (or so; looks better in errors than 2^31) + func (w *writer) Sym(s *LSym) { abi := uint16(s.ABI()) if s.Static() { @@ -299,8 +348,8 @@ func (w *writer) Sym(s *LSym) { name = filepath.ToSlash(name) } var align uint32 - if s.Func != nil { - align = uint32(s.Func.Align) + if fn := s.Func(); fn != nil { + align = uint32(fn.Align) } if s.ContentAddressable() { // We generally assume data symbols are natually aligned, @@ -321,6 +370,9 @@ func (w *writer) Sym(s *LSym) { // don't bother setting align to 1. } } + if s.Size > cutoff { + w.ctxt.Diag("%s: symbol too large (%d bytes > %d bytes)", s.Name, s.Size, cutoff) + } var o goobj.Sym o.SetName(name, w.Writer) o.SetABI(abi) @@ -372,10 +424,29 @@ func contentHash64(s *LSym) goobj.Hash64Type { // hashed symbols. func (w *writer) contentHash(s *LSym) goobj.HashType { h := sha1.New() + var tmp [14]byte + + // Include the size of the symbol in the hash. + // This preserves the length of symbols, preventing the following two symbols + // from hashing the same: + // + // [2]int{1,2} ≠ [10]int{1,2,0,0,0...} + // + // In this case, if the smaller symbol is alive, the larger is not kept unless + // needed. + binary.LittleEndian.PutUint64(tmp[:8], uint64(s.Size)) + h.Write(tmp[:8]) + + // Don't dedup type symbols with others, as they are in a different + // section. + if strings.HasPrefix(s.Name, "type.") { + h.Write([]byte{'T'}) + } else { + h.Write([]byte{0}) + } // The compiler trims trailing zeros _sometimes_. We just do // it always. h.Write(bytes.TrimRight(s.P, "\x00")) - var tmp [14]byte for i := range s.R { r := &s.R[i] binary.LittleEndian.PutUint32(tmp[:4], uint32(r.Off)) @@ -447,25 +518,41 @@ func (w *writer) Aux(s *LSym) { if s.Gotype != nil { w.aux1(goobj.AuxGotype, s.Gotype) } - if s.Func != nil { - w.aux1(goobj.AuxFuncInfo, s.Func.FuncInfoSym) + if fn := s.Func(); fn != nil { + w.aux1(goobj.AuxFuncInfo, fn.FuncInfoSym) - for _, d := range s.Func.Pcln.Funcdata { + for _, d := range fn.Pcln.Funcdata { w.aux1(goobj.AuxFuncdata, d) } - if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 { - w.aux1(goobj.AuxDwarfInfo, s.Func.dwarfInfoSym) + if fn.dwarfInfoSym != nil && fn.dwarfInfoSym.Size != 0 { + w.aux1(goobj.AuxDwarfInfo, fn.dwarfInfoSym) } - if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 { - w.aux1(goobj.AuxDwarfLoc, s.Func.dwarfLocSym) + if fn.dwarfLocSym != nil && fn.dwarfLocSym.Size != 0 { + w.aux1(goobj.AuxDwarfLoc, fn.dwarfLocSym) } - if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 { - w.aux1(goobj.AuxDwarfRanges, s.Func.dwarfRangesSym) + if fn.dwarfRangesSym != nil && fn.dwarfRangesSym.Size != 0 { + w.aux1(goobj.AuxDwarfRanges, fn.dwarfRangesSym) } - if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 { - w.aux1(goobj.AuxDwarfLines, s.Func.dwarfDebugLinesSym) + if fn.dwarfDebugLinesSym != nil && fn.dwarfDebugLinesSym.Size != 0 { + w.aux1(goobj.AuxDwarfLines, fn.dwarfDebugLinesSym) } + if fn.Pcln.Pcsp != nil && fn.Pcln.Pcsp.Size != 0 { + w.aux1(goobj.AuxPcsp, fn.Pcln.Pcsp) + } + if fn.Pcln.Pcfile != nil && fn.Pcln.Pcfile.Size != 0 { + w.aux1(goobj.AuxPcfile, fn.Pcln.Pcfile) + } + if fn.Pcln.Pcline != nil && fn.Pcln.Pcline.Size != 0 { + w.aux1(goobj.AuxPcline, fn.Pcln.Pcline) + } + if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 { + w.aux1(goobj.AuxPcinline, fn.Pcln.Pcinline) + } + for _, pcSym := range fn.Pcln.Pcdata { + w.aux1(goobj.AuxPcdata, pcSym) + } + } } @@ -532,21 +619,34 @@ func nAuxSym(s *LSym) int { if s.Gotype != nil { n++ } - if s.Func != nil { + if fn := s.Func(); fn != nil { // FuncInfo is an aux symbol, each Funcdata is an aux symbol - n += 1 + len(s.Func.Pcln.Funcdata) - if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 { + n += 1 + len(fn.Pcln.Funcdata) + if fn.dwarfInfoSym != nil && fn.dwarfInfoSym.Size != 0 { + n++ + } + if fn.dwarfLocSym != nil && fn.dwarfLocSym.Size != 0 { + n++ + } + if fn.dwarfRangesSym != nil && fn.dwarfRangesSym.Size != 0 { n++ } - if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 { + if fn.dwarfDebugLinesSym != nil && fn.dwarfDebugLinesSym.Size != 0 { n++ } - if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 { + if fn.Pcln.Pcsp != nil && fn.Pcln.Pcsp.Size != 0 { n++ } - if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 { + if fn.Pcln.Pcfile != nil && fn.Pcln.Pcfile.Size != 0 { n++ } + if fn.Pcln.Pcline != nil && fn.Pcln.Pcline.Size != 0 { + n++ + } + if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 { + n++ + } + n += len(fn.Pcln.Pcdata) } return n } @@ -554,33 +654,38 @@ func nAuxSym(s *LSym) int { // generate symbols for FuncInfo. func genFuncInfoSyms(ctxt *Link) { infosyms := make([]*LSym, 0, len(ctxt.Text)) - var pcdataoff uint32 + hashedsyms := make([]*LSym, 0, 4*len(ctxt.Text)) + preparePcSym := func(s *LSym) *LSym { + if s == nil { + return s + } + s.PkgIdx = goobj.PkgIdxHashed + s.SymIdx = int32(len(hashedsyms) + len(ctxt.hasheddefs)) + s.Set(AttrIndexed, true) + hashedsyms = append(hashedsyms, s) + return s + } var b bytes.Buffer symidx := int32(len(ctxt.defs)) for _, s := range ctxt.Text { - if s.Func == nil { + fn := s.Func() + if fn == nil { continue } o := goobj.FuncInfo{ - Args: uint32(s.Func.Args), - Locals: uint32(s.Func.Locals), - FuncID: objabi.FuncID(s.Func.FuncID), + Args: uint32(fn.Args), + Locals: uint32(fn.Locals), + FuncID: objabi.FuncID(fn.FuncID), } - pc := &s.Func.Pcln - o.Pcsp = pcdataoff - pcdataoff += uint32(len(pc.Pcsp.P)) - o.Pcfile = pcdataoff - pcdataoff += uint32(len(pc.Pcfile.P)) - o.Pcline = pcdataoff - pcdataoff += uint32(len(pc.Pcline.P)) - o.Pcinline = pcdataoff - pcdataoff += uint32(len(pc.Pcinline.P)) - o.Pcdata = make([]uint32, len(pc.Pcdata)) - for i, pcd := range pc.Pcdata { - o.Pcdata[i] = pcdataoff - pcdataoff += uint32(len(pcd.P)) + pc := &fn.Pcln + o.Pcsp = makeSymRef(preparePcSym(pc.Pcsp)) + o.Pcfile = makeSymRef(preparePcSym(pc.Pcfile)) + o.Pcline = makeSymRef(preparePcSym(pc.Pcline)) + o.Pcinline = makeSymRef(preparePcSym(pc.Pcinline)) + o.Pcdata = make([]goobj.SymRef, len(pc.Pcdata)) + for i, pcSym := range pc.Pcdata { + o.Pcdata[i] = makeSymRef(preparePcSym(pcSym)) } - o.PcdataEnd = pcdataoff o.Funcdataoff = make([]uint32, len(pc.Funcdataoff)) for i, x := range pc.Funcdataoff { o.Funcdataoff[i] = uint32(x) @@ -614,10 +719,10 @@ func genFuncInfoSyms(ctxt *Link) { isym.Set(AttrIndexed, true) symidx++ infosyms = append(infosyms, isym) - s.Func.FuncInfoSym = isym + fn.FuncInfoSym = isym b.Reset() - dwsyms := []*LSym{s.Func.dwarfRangesSym, s.Func.dwarfLocSym, s.Func.dwarfDebugLinesSym, s.Func.dwarfInfoSym} + dwsyms := []*LSym{fn.dwarfRangesSym, fn.dwarfLocSym, fn.dwarfDebugLinesSym, fn.dwarfInfoSym} for _, s := range dwsyms { if s == nil || s.Size == 0 { continue @@ -630,9 +735,9 @@ func genFuncInfoSyms(ctxt *Link) { } } ctxt.defs = append(ctxt.defs, infosyms...) + ctxt.hasheddefs = append(ctxt.hasheddefs, hashedsyms...) } -// debugDumpAux is a dumper for selected aux symbols. func writeAuxSymDebug(ctxt *Link, par *LSym, aux *LSym) { // Most aux symbols (ex: funcdata) are not interesting-- // pick out just the DWARF ones for now. @@ -663,7 +768,11 @@ func (ctxt *Link) writeSymDebug(s *LSym) { } func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) { - fmt.Fprintf(ctxt.Bso, "%s ", name) + ver := "" + if ctxt.Debugasm > 1 { + ver = fmt.Sprintf("<%d>", s.ABI()) + } + fmt.Fprintf(ctxt.Bso, "%s%s ", name, ver) if s.Type != 0 { fmt.Fprintf(ctxt.Bso, "%v ", s.Type) } @@ -684,14 +793,15 @@ func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) { } fmt.Fprintf(ctxt.Bso, "size=%d", s.Size) if s.Type == objabi.STEXT { - fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x", uint64(s.Func.Args), uint64(s.Func.Locals), uint64(s.Func.FuncID)) + fn := s.Func() + fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x", uint64(fn.Args), uint64(fn.Locals), uint64(fn.FuncID)) if s.Leaf() { fmt.Fprintf(ctxt.Bso, " leaf") } } fmt.Fprintf(ctxt.Bso, "\n") if s.Type == objabi.STEXT { - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { fmt.Fprintf(ctxt.Bso, "\t%#04x ", uint(int(p.Pc))) if ctxt.Debugasm > 1 { io.WriteString(ctxt.Bso, p.String()) @@ -726,15 +836,19 @@ func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) { sort.Sort(relocByOff(s.R)) // generate stable output for _, r := range s.R { name := "" + ver := "" if r.Sym != nil { name = r.Sym.Name + if ctxt.Debugasm > 1 { + ver = fmt.Sprintf("<%d>", r.Sym.ABI()) + } } else if r.Type == objabi.R_TLS_LE { name = "TLS" } if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) { - fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%x\n", int(r.Off), r.Siz, r.Type, name, uint64(r.Add)) + fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s%s+%x\n", int(r.Off), r.Siz, r.Type, name, ver, uint64(r.Add)) } else { - fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, name, r.Add) + fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s%s+%d\n", int(r.Off), r.Siz, r.Type, name, ver, r.Add) } } } diff --git a/src/cmd/internal/obj/objfile_test.go b/src/cmd/internal/obj/objfile_test.go index 155701fa4e..146627b62b 100644 --- a/src/cmd/internal/obj/objfile_test.go +++ b/src/cmd/internal/obj/objfile_test.go @@ -5,9 +5,16 @@ package obj import ( + "bytes" "cmd/internal/goobj" "cmd/internal/sys" + "internal/testenv" + "io/ioutil" + "os" + "os/exec" + "path/filepath" "testing" + "unsafe" ) var dummyArch = LinkArch{Arch: sys.ArchAMD64} @@ -85,3 +92,32 @@ func TestContentHash(t *testing.T) { } } } + +func TestSymbolTooLarge(t *testing.T) { // Issue 42054 + testenv.MustHaveGoBuild(t) + if unsafe.Sizeof(uintptr(0)) < 8 { + t.Skip("skip on 32-bit architectures") + } + + tmpdir, err := ioutil.TempDir("", "TestSymbolTooLarge") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + src := filepath.Join(tmpdir, "p.go") + err = ioutil.WriteFile(src, []byte("package p; var x [1<<32]byte"), 0666) + if err != nil { + t.Fatalf("failed to write source file: %v\n", err) + } + obj := filepath.Join(tmpdir, "p.o") + cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", obj, src) + out, err := cmd.CombinedOutput() + if err == nil { + t.Fatalf("did not fail\noutput: %s", out) + } + const want = "symbol too large" + if !bytes.Contains(out, []byte(want)) { + t.Errorf("unexpected error message: want: %q, got: %s", want, out) + } +} diff --git a/src/cmd/internal/obj/pass.go b/src/cmd/internal/obj/pass.go index 09d520b4e9..01657dd4f6 100644 --- a/src/cmd/internal/obj/pass.go +++ b/src/cmd/internal/obj/pass.go @@ -118,7 +118,7 @@ func checkaddr(ctxt *Link, p *Prog, a *Addr) { } func linkpatch(ctxt *Link, sym *LSym, newprog ProgAlloc) { - for p := sym.Func.Text; p != nil; p = p.Link { + for p := sym.Func().Text; p != nil; p = p.Link { checkaddr(ctxt, p, &p.From) if p.GetFrom3() != nil { checkaddr(ctxt, p, p.GetFrom3()) @@ -138,7 +138,7 @@ func linkpatch(ctxt *Link, sym *LSym, newprog ProgAlloc) { if p.To.Sym != nil { continue } - q := sym.Func.Text + q := sym.Func().Text for q != nil && p.To.Offset != q.Pc { if q.Forwd != nil && p.To.Offset >= q.Forwd.Pc { q = q.Forwd @@ -164,7 +164,7 @@ func linkpatch(ctxt *Link, sym *LSym, newprog ProgAlloc) { } // Collapse series of jumps to jumps. - for p := sym.Func.Text; p != nil; p = p.Link { + for p := sym.Func().Text; p != nil; p = p.Link { if p.To.Target() == nil { continue } diff --git a/src/cmd/internal/obj/pcln.go b/src/cmd/internal/obj/pcln.go index bffeda041d..67c4f9a62b 100644 --- a/src/cmd/internal/obj/pcln.go +++ b/src/cmd/internal/obj/pcln.go @@ -6,6 +6,7 @@ package obj import ( "cmd/internal/goobj" + "cmd/internal/objabi" "encoding/binary" "log" ) @@ -14,16 +15,19 @@ import ( // returned by valfunc parameterized by arg. The invocation of valfunc to update the // current value is, for each p, // -// val = valfunc(func, val, p, 0, arg); -// record val as value at p->pc; -// val = valfunc(func, val, p, 1, arg); +// sym = valfunc(func, p, 0, arg); +// record sym.P as value at p->pc; +// sym = valfunc(func, p, 1, arg); // // where func is the function, val is the current value, p is the instruction being // considered, and arg can be used to further parameterize valfunc. -func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*Link, *LSym, int32, *Prog, int32, interface{}) int32, arg interface{}) { +func funcpctab(ctxt *Link, func_ *LSym, desc string, valfunc func(*Link, *LSym, int32, *Prog, int32, interface{}) int32, arg interface{}) *LSym { dbg := desc == ctxt.Debugpcln - - dst.P = dst.P[:0] + dst := []byte{} + sym := &LSym{ + Type: objabi.SRODATA, + Attribute: AttrContentAddressable, + } if dbg { ctxt.Logf("funcpctab %s [valfunc=%s]\n", func_.Name, desc) @@ -31,19 +35,21 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(* val := int32(-1) oldval := val - if func_.Func.Text == nil { - return + fn := func_.Func() + if fn.Text == nil { + // Return the emtpy symbol we've built so far. + return sym } - pc := func_.Func.Text.Pc + pc := fn.Text.Pc if dbg { - ctxt.Logf("%6x %6d %v\n", uint64(pc), val, func_.Func.Text) + ctxt.Logf("%6x %6d %v\n", uint64(pc), val, fn.Text) } buf := make([]byte, binary.MaxVarintLen32) started := false - for p := func_.Func.Text; p != nil; p = p.Link { + for p := fn.Text; p != nil; p = p.Link { // Update val. If it's not changing, keep going. val = valfunc(ctxt, func_, val, p, 0, arg) @@ -88,13 +94,13 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(* if started { pcdelta := (p.Pc - pc) / int64(ctxt.Arch.MinLC) n := binary.PutUvarint(buf, uint64(pcdelta)) - dst.P = append(dst.P, buf[:n]...) + dst = append(dst, buf[:n]...) pc = p.Pc } delta := val - oldval n := binary.PutVarint(buf, int64(delta)) - dst.P = append(dst.P, buf[:n]...) + dst = append(dst, buf[:n]...) oldval = val started = true val = valfunc(ctxt, func_, val, p, 1, arg) @@ -102,25 +108,29 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(* if started { if dbg { - ctxt.Logf("%6x done\n", uint64(func_.Func.Text.Pc+func_.Size)) + ctxt.Logf("%6x done\n", uint64(fn.Text.Pc+func_.Size)) } v := (func_.Size - pc) / int64(ctxt.Arch.MinLC) if v < 0 { ctxt.Diag("negative pc offset: %v", v) } n := binary.PutUvarint(buf, uint64(v)) - dst.P = append(dst.P, buf[:n]...) + dst = append(dst, buf[:n]...) // add terminating varint-encoded 0, which is just 0 - dst.P = append(dst.P, 0) + dst = append(dst, 0) } if dbg { - ctxt.Logf("wrote %d bytes to %p\n", len(dst.P), dst) - for _, p := range dst.P { + ctxt.Logf("wrote %d bytes to %p\n", len(dst), dst) + for _, p := range dst { ctxt.Logf(" %02x", p) } ctxt.Logf("\n") } + + sym.Size = int64(len(dst)) + sym.P = dst + return sym } // pctofileline computes either the file number (arg == 0) @@ -248,12 +258,12 @@ func pctopcdata(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg i } func linkpcln(ctxt *Link, cursym *LSym) { - pcln := &cursym.Func.Pcln + pcln := &cursym.Func().Pcln pcln.UsedFiles = make(map[goobj.CUFileIndex]struct{}) npcdata := 0 nfuncdata := 0 - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { // Find the highest ID of any used PCDATA table. This ignores PCDATA table // that consist entirely of "-1", since that's the assumed default value. // From.Offset is table ID @@ -268,23 +278,23 @@ func linkpcln(ctxt *Link, cursym *LSym) { } } - pcln.Pcdata = make([]Pcdata, npcdata) - pcln.Pcdata = pcln.Pcdata[:npcdata] + pcln.Pcdata = make([]*LSym, npcdata) pcln.Funcdata = make([]*LSym, nfuncdata) pcln.Funcdataoff = make([]int64, nfuncdata) pcln.Funcdataoff = pcln.Funcdataoff[:nfuncdata] - funcpctab(ctxt, &pcln.Pcsp, cursym, "pctospadj", pctospadj, nil) - funcpctab(ctxt, &pcln.Pcfile, cursym, "pctofile", pctofileline, pcln) - funcpctab(ctxt, &pcln.Pcline, cursym, "pctoline", pctofileline, nil) + pcln.Pcsp = funcpctab(ctxt, cursym, "pctospadj", pctospadj, nil) + pcln.Pcfile = funcpctab(ctxt, cursym, "pctofile", pctofileline, pcln) + pcln.Pcline = funcpctab(ctxt, cursym, "pctoline", pctofileline, nil) // Check that all the Progs used as inline markers are still reachable. // See issue #40473. - inlMarkProgs := make(map[*Prog]struct{}, len(cursym.Func.InlMarks)) - for _, inlMark := range cursym.Func.InlMarks { + fn := cursym.Func() + inlMarkProgs := make(map[*Prog]struct{}, len(fn.InlMarks)) + for _, inlMark := range fn.InlMarks { inlMarkProgs[inlMark.p] = struct{}{} } - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := fn.Text; p != nil; p = p.Link { if _, ok := inlMarkProgs[p]; ok { delete(inlMarkProgs, p) } @@ -294,8 +304,8 @@ func linkpcln(ctxt *Link, cursym *LSym) { } pcinlineState := new(pcinlineState) - funcpctab(ctxt, &pcln.Pcinline, cursym, "pctoinline", pcinlineState.pctoinline, nil) - for _, inlMark := range cursym.Func.InlMarks { + pcln.Pcinline = funcpctab(ctxt, cursym, "pctoinline", pcinlineState.pctoinline, nil) + for _, inlMark := range fn.InlMarks { pcinlineState.setParentPC(ctxt, int(inlMark.id), int32(inlMark.p.Pc)) } pcln.InlTree = pcinlineState.localTree @@ -308,7 +318,7 @@ func linkpcln(ctxt *Link, cursym *LSym) { // tabulate which pc and func data we have. havepc := make([]uint32, (npcdata+31)/32) havefunc := make([]uint32, (nfuncdata+31)/32) - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := fn.Text; p != nil; p = p.Link { if p.As == AFUNCDATA { if (havefunc[p.From.Offset/32]>>uint64(p.From.Offset%32))&1 != 0 { ctxt.Diag("multiple definitions for FUNCDATA $%d", p.From.Offset) @@ -324,14 +334,19 @@ func linkpcln(ctxt *Link, cursym *LSym) { // pcdata. for i := 0; i < npcdata; i++ { if (havepc[i/32]>>uint(i%32))&1 == 0 { - continue + // use an empty symbol. + pcln.Pcdata[i] = &LSym{ + Type: objabi.SRODATA, + Attribute: AttrContentAddressable, + } + } else { + pcln.Pcdata[i] = funcpctab(ctxt, cursym, "pctopcdata", pctopcdata, interface{}(uint32(i))) } - funcpctab(ctxt, &pcln.Pcdata[i], cursym, "pctopcdata", pctopcdata, interface{}(uint32(i))) } // funcdata if nfuncdata > 0 { - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := fn.Text; p != nil; p = p.Link { if p.As != AFUNCDATA { continue } diff --git a/src/cmd/internal/obj/plist.go b/src/cmd/internal/obj/plist.go index 6e33f29959..2b096996f7 100644 --- a/src/cmd/internal/obj/plist.go +++ b/src/cmd/internal/obj/plist.go @@ -81,7 +81,7 @@ func Flushplist(ctxt *Link, plist *Plist, newprog ProgAlloc, myimportpath string continue } found := false - for p := s.Func.Text; p != nil; p = p.Link { + 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 { found = true break @@ -89,7 +89,7 @@ func Flushplist(ctxt *Link, plist *Plist, newprog ProgAlloc, myimportpath string } if !found { - p := Appendp(s.Func.Text, newprog) + p := Appendp(s.Func().Text, newprog) p.As = AFUNCDATA p.From.Type = TYPE_CONST p.From.Offset = objabi.FUNCDATA_ArgsPointerMaps @@ -120,15 +120,15 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) { // func _() { } return } - if s.Func != nil { + if s.Func() != nil { ctxt.Diag("InitTextSym double init for %s", s.Name) } - s.Func = new(FuncInfo) + s.NewFuncInfo() if s.OnList() { ctxt.Diag("symbol %s listed multiple times", s.Name) } name := strings.Replace(s.Name, "\"\"", ctxt.Pkgpath, -1) - s.Func.FuncID = objabi.GetFuncID(name, flag&WRAPPER != 0) + s.Func().FuncID = objabi.GetFuncID(name, flag&WRAPPER != 0) s.Set(AttrOnList, true) s.Set(AttrDuplicateOK, flag&DUPOK != 0) s.Set(AttrNoSplit, flag&NOSPLIT != 0) @@ -178,14 +178,14 @@ func (ctxt *Link) Globl(s *LSym, size int64, flag int) { // Prog generated. func (ctxt *Link) EmitEntryLiveness(s *LSym, p *Prog, newprog ProgAlloc) *Prog { pcdata := ctxt.EmitEntryStackMap(s, p, newprog) - pcdata = ctxt.EmitEntryRegMap(s, pcdata, newprog) + pcdata = ctxt.EmitEntryUnsafePoint(s, pcdata, newprog) return pcdata } // Similar to EmitEntryLiveness, but just emit stack map. func (ctxt *Link) EmitEntryStackMap(s *LSym, p *Prog, newprog ProgAlloc) *Prog { pcdata := Appendp(p, newprog) - pcdata.Pos = s.Func.Text.Pos + pcdata.Pos = s.Func().Text.Pos pcdata.As = APCDATA pcdata.From.Type = TYPE_CONST pcdata.From.Offset = objabi.PCDATA_StackMapIndex @@ -195,13 +195,13 @@ func (ctxt *Link) EmitEntryStackMap(s *LSym, p *Prog, newprog ProgAlloc) *Prog { return pcdata } -// Similar to EmitEntryLiveness, but just emit register map. -func (ctxt *Link) EmitEntryRegMap(s *LSym, p *Prog, newprog ProgAlloc) *Prog { +// Similar to EmitEntryLiveness, but just emit unsafe point map. +func (ctxt *Link) EmitEntryUnsafePoint(s *LSym, p *Prog, newprog ProgAlloc) *Prog { pcdata := Appendp(p, newprog) - pcdata.Pos = s.Func.Text.Pos + pcdata.Pos = s.Func().Text.Pos pcdata.As = APCDATA pcdata.From.Type = TYPE_CONST - pcdata.From.Offset = objabi.PCDATA_RegMapIndex + pcdata.From.Offset = objabi.PCDATA_UnsafePoint pcdata.To.Type = TYPE_CONST pcdata.To.Offset = -1 @@ -216,9 +216,9 @@ func (ctxt *Link) StartUnsafePoint(p *Prog, newprog ProgAlloc) *Prog { pcdata := Appendp(p, newprog) pcdata.As = APCDATA pcdata.From.Type = TYPE_CONST - pcdata.From.Offset = objabi.PCDATA_RegMapIndex + pcdata.From.Offset = objabi.PCDATA_UnsafePoint pcdata.To.Type = TYPE_CONST - pcdata.To.Offset = objabi.PCDATA_RegMapUnsafe + pcdata.To.Offset = objabi.PCDATA_UnsafePointUnsafe return pcdata } @@ -231,7 +231,7 @@ func (ctxt *Link) EndUnsafePoint(p *Prog, newprog ProgAlloc, oldval int64) *Prog pcdata := Appendp(p, newprog) pcdata.As = APCDATA pcdata.From.Type = TYPE_CONST - pcdata.From.Offset = objabi.PCDATA_RegMapIndex + pcdata.From.Offset = objabi.PCDATA_UnsafePoint pcdata.To.Type = TYPE_CONST pcdata.To.Offset = oldval @@ -257,11 +257,11 @@ func MarkUnsafePoints(ctxt *Link, p0 *Prog, newprog ProgAlloc, isUnsafePoint, is prevPcdata := int64(-1) // entry PC data value prevRestart := int64(0) for p := prev.Link; p != nil; p, prev = p.Link, p { - if p.As == APCDATA && p.From.Offset == objabi.PCDATA_RegMapIndex { + if p.As == APCDATA && p.From.Offset == objabi.PCDATA_UnsafePoint { prevPcdata = p.To.Offset continue } - if prevPcdata == objabi.PCDATA_RegMapUnsafe { + if prevPcdata == objabi.PCDATA_UnsafePointUnsafe { continue // already unsafe } if isUnsafePoint(p) { @@ -288,7 +288,7 @@ func MarkUnsafePoints(ctxt *Link, p0 *Prog, newprog ProgAlloc, isUnsafePoint, is q := Appendp(prev, newprog) q.As = APCDATA q.From.Type = TYPE_CONST - q.From.Offset = objabi.PCDATA_RegMapIndex + q.From.Offset = objabi.PCDATA_UnsafePoint q.To.Type = TYPE_CONST q.To.Offset = val q.Pc = p.Pc @@ -305,7 +305,7 @@ func MarkUnsafePoints(ctxt *Link, p0 *Prog, newprog ProgAlloc, isUnsafePoint, is p = Appendp(p, newprog) p.As = APCDATA p.From.Type = TYPE_CONST - p.From.Offset = objabi.PCDATA_RegMapIndex + p.From.Offset = objabi.PCDATA_UnsafePoint p.To.Type = TYPE_CONST p.To.Offset = prevPcdata p.Pc = p.Link.Pc diff --git a/src/cmd/internal/obj/ppc64/a.out.go b/src/cmd/internal/obj/ppc64/a.out.go index 8b32692778..4c97302f83 100644 --- a/src/cmd/internal/obj/ppc64/a.out.go +++ b/src/cmd/internal/obj/ppc64/a.out.go @@ -575,6 +575,7 @@ const ( ARLWMICC ARLWNM ARLWNMCC + ACLRLSLWI ASLW ASLWCC ASRW @@ -716,6 +717,9 @@ const ( ARLDCLCC ARLDICL ARLDICLCC + ARLDIC + ARLDICCC + ACLRLSLDI AROTL AROTLW ASLBIA @@ -729,6 +733,8 @@ const ( ASRAD ASRADCC ASRDCC + AEXTSWSLI + AEXTSWSLICC ASTDCCC ATD diff --git a/src/cmd/internal/obj/ppc64/anames.go b/src/cmd/internal/obj/ppc64/anames.go index 287011877c..fca4b3e355 100644 --- a/src/cmd/internal/obj/ppc64/anames.go +++ b/src/cmd/internal/obj/ppc64/anames.go @@ -180,6 +180,7 @@ var Anames = []string{ "RLWMICC", "RLWNM", "RLWNMCC", + "CLRLSLWI", "SLW", "SLWCC", "SRW", @@ -312,6 +313,9 @@ var Anames = []string{ "RLDCLCC", "RLDICL", "RLDICLCC", + "RLDIC", + "RLDICCC", + "CLRLSLDI", "ROTL", "ROTLW", "SLBIA", @@ -325,6 +329,8 @@ var Anames = []string{ "SRAD", "SRADCC", "SRDCC", + "EXTSWSLI", + "EXTSWSLICC", "STDCCC", "TD", "DWORD", diff --git a/src/cmd/internal/obj/ppc64/asm9.go b/src/cmd/internal/obj/ppc64/asm9.go index 98b453de6c..41e263b2c0 100644 --- a/src/cmd/internal/obj/ppc64/asm9.go +++ b/src/cmd/internal/obj/ppc64/asm9.go @@ -160,6 +160,8 @@ var optab = []Optab{ {ASLD, C_REG, C_REG, C_NONE, C_REG, 6, 4, 0}, {ASLD, C_SCON, C_REG, C_NONE, C_REG, 25, 4, 0}, {ASLD, C_SCON, C_NONE, C_NONE, C_REG, 25, 4, 0}, + {AEXTSWSLI, C_SCON, C_NONE, C_NONE, C_REG, 25, 4, 0}, + {AEXTSWSLI, C_SCON, C_REG, C_NONE, C_REG, 25, 4, 0}, {ASLW, C_SCON, C_REG, C_NONE, C_REG, 57, 4, 0}, {ASLW, C_SCON, C_NONE, C_NONE, C_REG, 57, 4, 0}, {ASRAW, C_REG, C_NONE, C_NONE, C_REG, 6, 4, 0}, @@ -172,6 +174,7 @@ var optab = []Optab{ {ASRAD, C_SCON, C_NONE, C_NONE, C_REG, 56, 4, 0}, {ARLWMI, C_SCON, C_REG, C_LCON, C_REG, 62, 4, 0}, {ARLWMI, C_REG, C_REG, C_LCON, C_REG, 63, 4, 0}, + {ACLRLSLWI, C_SCON, C_REG, C_LCON, C_REG, 62, 4, 0}, {ARLDMI, C_SCON, C_REG, C_LCON, C_REG, 30, 4, 0}, {ARLDC, C_SCON, C_REG, C_LCON, C_REG, 29, 4, 0}, {ARLDCL, C_SCON, C_REG, C_LCON, C_REG, 29, 4, 0}, @@ -331,6 +334,7 @@ var optab = []Optab{ {ABC, C_SCON, C_REG, C_NONE, C_SBRA, 16, 4, 0}, {ABC, C_SCON, C_REG, C_NONE, C_LBRA, 17, 4, 0}, {ABR, C_NONE, C_NONE, C_NONE, C_LR, 18, 4, 0}, + {ABR, C_NONE, C_NONE, C_SCON, C_LR, 18, 4, 0}, {ABR, C_NONE, C_NONE, C_NONE, C_CTR, 18, 4, 0}, {ABR, C_REG, C_NONE, C_NONE, C_CTR, 18, 4, 0}, {ABR, C_NONE, C_NONE, C_NONE, C_ZOREG, 15, 8, 0}, @@ -661,8 +665,8 @@ func addpad(pc, a int64, ctxt *obj.Link, cursym *obj.LSym) int { // the function alignment is not changed which might // result in 16 byte alignment but that is still fine. // TODO: alignment on AIX - if ctxt.Headtype != objabi.Haix && cursym.Func.Align < 32 { - cursym.Func.Align = 32 + if ctxt.Headtype != objabi.Haix && cursym.Func().Align < 32 { + cursym.Func().Align = 32 } default: ctxt.Diag("Unexpected alignment: %d for PCALIGN directive\n", a) @@ -671,7 +675,7 @@ func addpad(pc, a int64, ctxt *obj.Link, cursym *obj.LSym) int { } func span9(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { - p := cursym.Func.Text + p := cursym.Func().Text if p == nil || p.Link == nil { // handle external functions and ELF section symbols return } @@ -720,7 +724,7 @@ func span9(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { for bflag != 0 { bflag = 0 pc = 0 - for p = c.cursym.Func.Text.Link; p != nil; p = p.Link { + for p = c.cursym.Func().Text.Link; p != nil; p = p.Link { p.Pc = pc o = c.oplook(p) @@ -782,7 +786,7 @@ func span9(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { bp := c.cursym.P var i int32 var out [6]uint32 - for p := c.cursym.Func.Text.Link; p != nil; p = p.Link { + for p := c.cursym.Func().Text.Link; p != nil; p = p.Link { c.pc = p.Pc o = c.oplook(p) if int(o.size) > 4*len(out) { @@ -1277,6 +1281,9 @@ func buildop(ctxt *obj.Link) { case AREMD: opset(AREMDU, r0) + case AMULLW: + opset(AMULLD, r0) + case ADIVW: /* op Rb[,Ra],Rd */ opset(AMULHW, r0) @@ -1310,7 +1317,6 @@ func buildop(ctxt *obj.Link) { opset(AMULHDCC, r0) opset(AMULHDU, r0) opset(AMULHDUCC, r0) - opset(AMULLD, r0) opset(AMULLDCC, r0) opset(AMULLDVCC, r0) opset(AMULLDV, r0) @@ -1877,6 +1883,9 @@ func buildop(ctxt *obj.Link) { case ASRAW: /* sraw Rb,Rs,Ra; srawi sh,Rs,Ra */ opset(ASRAWCC, r0) + case AEXTSWSLI: + opset(AEXTSWSLICC, r0) + case ASRAD: /* sraw Rb,Rs,Ra; srawi sh,Rs,Ra */ opset(ASRADCC, r0) @@ -1922,6 +1931,9 @@ func buildop(ctxt *obj.Link) { opset(ARLDICLCC, r0) opset(ARLDICR, r0) opset(ARLDICRCC, r0) + opset(ARLDIC, r0) + opset(ARLDICCC, r0) + opset(ACLRLSLDI, r0) case AFMOVD: opset(AFMOVDCC, r0) @@ -1987,7 +1999,6 @@ func buildop(ctxt *obj.Link) { AMOVB, /* macro: move byte with sign extension */ AMOVBU, /* macro: move byte with sign extension & update */ AMOVFL, - AMULLW, /* op $s[,r2],r3; op r1[,r2],r3; no cc/v */ ASUBC, /* op r1,$s,r3; op r1[,r2],r3 */ ASTSW, @@ -2000,6 +2011,7 @@ func buildop(ctxt *obj.Link) { AADDEX, ACMPEQB, AECIWX, + ACLRLSLWI, obj.ANOP, obj.ATEXT, obj.AUNDEF, @@ -2149,7 +2161,7 @@ func AOP_DQ(op uint32, d uint32, a uint32, b uint32) uint32 { /* Z23-form, 3-register operands + CY field */ func AOP_Z23I(op uint32, d uint32, a uint32, b uint32, c uint32) uint32 { - return op | (d&31)<<21 | (a&31)<<16 | (b&31)<<11 | (c&3)<<7 + return op | (d&31)<<21 | (a&31)<<16 | (b&31)<<11 | (c&3)<<9 } /* X-form, 3-register operands + EH field */ @@ -2185,49 +2197,54 @@ func AOP_RLDIC(op uint32, a uint32, s uint32, sh uint32, m uint32) uint32 { return op | (s&31)<<21 | (a&31)<<16 | (sh&31)<<11 | ((sh&32)>>5)<<1 | (m&31)<<6 | ((m&32)>>5)<<5 } +func AOP_EXTSWSLI(op uint32, a uint32, s uint32, sh uint32) uint32 { + return op | (a&31)<<21 | (s&31)<<16 | (sh&31)<<11 | ((sh&32)>>5)<<1 +} + func AOP_ISEL(op uint32, t uint32, a uint32, b uint32, bc uint32) uint32 { return op | (t&31)<<21 | (a&31)<<16 | (b&31)<<11 | (bc&0x1F)<<6 } const ( /* each rhs is OPVCC(_, _, _, _) */ - OP_ADD = 31<<26 | 266<<1 | 0<<10 | 0 - OP_ADDI = 14<<26 | 0<<1 | 0<<10 | 0 - OP_ADDIS = 15<<26 | 0<<1 | 0<<10 | 0 - OP_ANDI = 28<<26 | 0<<1 | 0<<10 | 0 - OP_EXTSB = 31<<26 | 954<<1 | 0<<10 | 0 - OP_EXTSH = 31<<26 | 922<<1 | 0<<10 | 0 - OP_EXTSW = 31<<26 | 986<<1 | 0<<10 | 0 - OP_ISEL = 31<<26 | 15<<1 | 0<<10 | 0 - OP_MCRF = 19<<26 | 0<<1 | 0<<10 | 0 - OP_MCRFS = 63<<26 | 64<<1 | 0<<10 | 0 - OP_MCRXR = 31<<26 | 512<<1 | 0<<10 | 0 - OP_MFCR = 31<<26 | 19<<1 | 0<<10 | 0 - OP_MFFS = 63<<26 | 583<<1 | 0<<10 | 0 - OP_MFMSR = 31<<26 | 83<<1 | 0<<10 | 0 - OP_MFSPR = 31<<26 | 339<<1 | 0<<10 | 0 - OP_MFSR = 31<<26 | 595<<1 | 0<<10 | 0 - OP_MFSRIN = 31<<26 | 659<<1 | 0<<10 | 0 - OP_MTCRF = 31<<26 | 144<<1 | 0<<10 | 0 - OP_MTFSF = 63<<26 | 711<<1 | 0<<10 | 0 - OP_MTFSFI = 63<<26 | 134<<1 | 0<<10 | 0 - OP_MTMSR = 31<<26 | 146<<1 | 0<<10 | 0 - OP_MTMSRD = 31<<26 | 178<<1 | 0<<10 | 0 - OP_MTSPR = 31<<26 | 467<<1 | 0<<10 | 0 - OP_MTSR = 31<<26 | 210<<1 | 0<<10 | 0 - OP_MTSRIN = 31<<26 | 242<<1 | 0<<10 | 0 - OP_MULLW = 31<<26 | 235<<1 | 0<<10 | 0 - OP_MULLD = 31<<26 | 233<<1 | 0<<10 | 0 - OP_OR = 31<<26 | 444<<1 | 0<<10 | 0 - OP_ORI = 24<<26 | 0<<1 | 0<<10 | 0 - OP_ORIS = 25<<26 | 0<<1 | 0<<10 | 0 - OP_RLWINM = 21<<26 | 0<<1 | 0<<10 | 0 - OP_RLWNM = 23<<26 | 0<<1 | 0<<10 | 0 - OP_SUBF = 31<<26 | 40<<1 | 0<<10 | 0 - OP_RLDIC = 30<<26 | 4<<1 | 0<<10 | 0 - OP_RLDICR = 30<<26 | 2<<1 | 0<<10 | 0 - OP_RLDICL = 30<<26 | 0<<1 | 0<<10 | 0 - OP_RLDCL = 30<<26 | 8<<1 | 0<<10 | 0 + OP_ADD = 31<<26 | 266<<1 | 0<<10 | 0 + OP_ADDI = 14<<26 | 0<<1 | 0<<10 | 0 + OP_ADDIS = 15<<26 | 0<<1 | 0<<10 | 0 + OP_ANDI = 28<<26 | 0<<1 | 0<<10 | 0 + OP_EXTSB = 31<<26 | 954<<1 | 0<<10 | 0 + OP_EXTSH = 31<<26 | 922<<1 | 0<<10 | 0 + OP_EXTSW = 31<<26 | 986<<1 | 0<<10 | 0 + OP_ISEL = 31<<26 | 15<<1 | 0<<10 | 0 + OP_MCRF = 19<<26 | 0<<1 | 0<<10 | 0 + OP_MCRFS = 63<<26 | 64<<1 | 0<<10 | 0 + OP_MCRXR = 31<<26 | 512<<1 | 0<<10 | 0 + OP_MFCR = 31<<26 | 19<<1 | 0<<10 | 0 + OP_MFFS = 63<<26 | 583<<1 | 0<<10 | 0 + OP_MFMSR = 31<<26 | 83<<1 | 0<<10 | 0 + OP_MFSPR = 31<<26 | 339<<1 | 0<<10 | 0 + OP_MFSR = 31<<26 | 595<<1 | 0<<10 | 0 + OP_MFSRIN = 31<<26 | 659<<1 | 0<<10 | 0 + OP_MTCRF = 31<<26 | 144<<1 | 0<<10 | 0 + OP_MTFSF = 63<<26 | 711<<1 | 0<<10 | 0 + OP_MTFSFI = 63<<26 | 134<<1 | 0<<10 | 0 + OP_MTMSR = 31<<26 | 146<<1 | 0<<10 | 0 + OP_MTMSRD = 31<<26 | 178<<1 | 0<<10 | 0 + OP_MTSPR = 31<<26 | 467<<1 | 0<<10 | 0 + OP_MTSR = 31<<26 | 210<<1 | 0<<10 | 0 + OP_MTSRIN = 31<<26 | 242<<1 | 0<<10 | 0 + OP_MULLW = 31<<26 | 235<<1 | 0<<10 | 0 + OP_MULLD = 31<<26 | 233<<1 | 0<<10 | 0 + OP_OR = 31<<26 | 444<<1 | 0<<10 | 0 + OP_ORI = 24<<26 | 0<<1 | 0<<10 | 0 + OP_ORIS = 25<<26 | 0<<1 | 0<<10 | 0 + OP_RLWINM = 21<<26 | 0<<1 | 0<<10 | 0 + OP_RLWNM = 23<<26 | 0<<1 | 0<<10 | 0 + OP_SUBF = 31<<26 | 40<<1 | 0<<10 | 0 + OP_RLDIC = 30<<26 | 4<<1 | 0<<10 | 0 + OP_RLDICR = 30<<26 | 2<<1 | 0<<10 | 0 + OP_RLDICL = 30<<26 | 0<<1 | 0<<10 | 0 + OP_RLDCL = 30<<26 | 8<<1 | 0<<10 | 0 + OP_EXTSWSLI = 31<<26 | 445<<2 ) func oclass(a *obj.Addr) int { @@ -2734,13 +2751,31 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) { case ARLDICR, ARLDICRCC: me := int(d) sh := c.regoff(&p.From) + if me < 0 || me > 63 || sh > 63 { + c.ctxt.Diag("Invalid me or sh for RLDICR: %x %x\n%v", int(d), sh, p) + } o1 = AOP_RLDIC(c.oprrr(p.As), uint32(p.To.Reg), uint32(r), uint32(sh), uint32(me)) - case ARLDICL, ARLDICLCC: + case ARLDICL, ARLDICLCC, ARLDIC, ARLDICCC: mb := int(d) sh := c.regoff(&p.From) + if mb < 0 || mb > 63 || sh > 63 { + c.ctxt.Diag("Invalid mb or sh for RLDIC, RLDICL: %x %x\n%v", mb, sh, p) + } o1 = AOP_RLDIC(c.oprrr(p.As), uint32(p.To.Reg), uint32(r), uint32(sh), uint32(mb)) + case ACLRLSLDI: + // This is an extended mnemonic defined in the ISA section C.8.1 + // clrlsldi ra,rs,b,n --> rldic ra,rs,n,b-n + // It maps onto RLDIC so is directly generated here based on the operands from + // the clrlsldi. + n := int32(d) + b := c.regoff(&p.From) + if n > b || b > 63 { + c.ctxt.Diag("Invalid n or b for CLRLSLDI: %x %x\n%v", n, b, p) + } + o1 = AOP_RLDIC(OP_RLDIC, uint32(p.To.Reg), uint32(r), uint32(n), uint32(b)-uint32(n)) + default: c.ctxt.Diag("unexpected op in rldc case\n%v", p) a = 0 @@ -2810,6 +2845,7 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) { case 18: /* br/bl (lr/ctr); bc/bcl bo,bi,(lr/ctr) */ var v int32 + var bh uint32 = 0 if p.As == ABC || p.As == ABCL { v = c.regoff(&p.From) & 31 } else { @@ -2831,6 +2867,15 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) { v = 0 } + // Insert optional branch hint for bclr[l]/bcctr[l] + if p.From3Type() != obj.TYPE_NONE { + bh = uint32(p.GetFrom3().Offset) + if bh == 2 || bh > 3 { + log.Fatalf("BH must be 0,1,3 for %v", p) + } + o1 |= bh << 11 + } + if p.As == ABL || p.As == ABCL { o1 |= 1 } @@ -2943,14 +2988,21 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) { case AROTL: a = int(0) op = OP_RLDICL + case AEXTSWSLI: + a = int(v) default: c.ctxt.Diag("unexpected op in sldi case\n%v", p) a = 0 o1 = 0 } - o1 = AOP_RLDIC(op, uint32(p.To.Reg), uint32(r), uint32(v), uint32(a)) - if p.As == ASLDCC || p.As == ASRDCC { + if p.As == AEXTSWSLI || p.As == AEXTSWSLICC { + o1 = AOP_EXTSWSLI(OP_EXTSWSLI, uint32(r), uint32(p.To.Reg), uint32(v)) + + } else { + o1 = AOP_RLDIC(op, uint32(p.To.Reg), uint32(r), uint32(v), uint32(a)) + } + if p.As == ASLDCC || p.As == ASRDCC || p.As == AEXTSWSLICC { o1 |= 1 // Set the condition code bit } @@ -3354,17 +3406,28 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) { case 62: /* rlwmi $sh,s,$mask,a */ v := c.regoff(&p.From) + switch p.As { + case ACLRLSLWI: + n := c.regoff(p.GetFrom3()) + // This is an extended mnemonic described in the ISA C.8.2 + // clrlslwi ra,rs,b,n -> rlwinm ra,rs,n,b-n,31-n + // It maps onto rlwinm which is directly generated here. + if n > v || v >= 32 { + c.ctxt.Diag("Invalid n or b for CLRLSLWI: %x %x\n%v", v, n, p) + } - var mask [2]uint8 - c.maskgen(p, mask[:], uint32(c.regoff(p.GetFrom3()))) - o1 = AOP_RRR(c.opirr(p.As), uint32(p.Reg), uint32(p.To.Reg), uint32(v)) - o1 |= (uint32(mask[0])&31)<<6 | (uint32(mask[1])&31)<<1 + o1 = OP_RLW(OP_RLWINM, uint32(p.To.Reg), uint32(p.Reg), uint32(n), uint32(v-n), uint32(31-n)) + default: + var mask [2]uint8 + c.maskgen(p, mask[:], uint32(c.regoff(p.GetFrom3()))) + o1 = AOP_RRR(c.opirr(p.As), uint32(p.Reg), uint32(p.To.Reg), uint32(v)) + o1 |= (uint32(mask[0])&31)<<6 | (uint32(mask[1])&31)<<1 + } case 63: /* rlwmi b,s,$mask,a */ var mask [2]uint8 c.maskgen(p, mask[:], uint32(c.regoff(p.GetFrom3()))) - - o1 = AOP_RRR(c.opirr(p.As), uint32(p.Reg), uint32(p.To.Reg), uint32(p.From.Reg)) + o1 = AOP_RRR(c.oprrr(p.As), uint32(p.Reg), uint32(p.To.Reg), uint32(p.From.Reg)) o1 |= (uint32(mask[0])&31)<<6 | (uint32(mask[1])&31)<<1 case 64: /* mtfsf fr[, $m] {,fpcsr} */ @@ -4277,6 +4340,11 @@ func (c *ctxt9) oprrr(a obj.As) uint32 { case ARLDICRCC: return OPVCC(30, 0, 0, 1) | 2<<1 // rldicr. + case ARLDIC: + return OPVCC(30, 0, 0, 0) | 4<<1 // rldic + case ARLDICCC: + return OPVCC(30, 0, 0, 1) | 4<<1 // rldic. + case ASYSCALL: return OPVCC(17, 1, 0, 0) @@ -4298,6 +4366,11 @@ func (c *ctxt9) oprrr(a obj.As) uint32 { case ASRADCC: return OPVCC(31, 794, 0, 1) + case AEXTSWSLI: + return OPVCC(31, 445, 0, 0) + case AEXTSWSLICC: + return OPVCC(31, 445, 0, 1) + case ASRW: return OPVCC(31, 536, 0, 0) case ASRWCC: @@ -4915,8 +4988,8 @@ func (c *ctxt9) opirr(a obj.As) uint32 { case ADARN: return OPVCC(31, 755, 0, 0) /* darn - v3.00 */ - case AMULLW: - return OPVCC(7, 0, 0, 0) + case AMULLW, AMULLD: + return OPVCC(7, 0, 0, 0) /* mulli works with MULLW or MULLD */ case AOR: return OPVCC(24, 0, 0, 0) @@ -4961,6 +5034,10 @@ func (c *ctxt9) opirr(a obj.As) uint32 { return OPVCC(31, (413 << 1), 0, 0) case ASRADCC: return OPVCC(31, (413 << 1), 0, 1) + case AEXTSWSLI: + return OPVCC(31, 445, 0, 0) + case AEXTSWSLICC: + return OPVCC(31, 445, 0, 1) case ASTSW: return OPVCC(31, 725, 0, 0) diff --git a/src/cmd/internal/obj/ppc64/obj9.go b/src/cmd/internal/obj/ppc64/obj9.go index c012762a18..fddf552156 100644 --- a/src/cmd/internal/obj/ppc64/obj9.go +++ b/src/cmd/internal/obj/ppc64/obj9.go @@ -32,6 +32,7 @@ package ppc64 import ( "cmd/internal/obj" "cmd/internal/objabi" + "cmd/internal/src" "cmd/internal/sys" ) @@ -402,13 +403,13 @@ func (c *ctxt9) rewriteToUseGot(p *obj.Prog) { func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // TODO(minux): add morestack short-cuts with small fixed frame-size. - if cursym.Func.Text == nil || cursym.Func.Text.Link == nil { + if cursym.Func().Text == nil || cursym.Func().Text.Link == nil { return } c := ctxt9{ctxt: ctxt, cursym: cursym, newprog: newprog} - p := c.cursym.Func.Text + p := c.cursym.Func().Text textstksiz := p.To.Offset if textstksiz == -8 { // Compatibility hack. @@ -424,8 +425,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } } - c.cursym.Func.Args = p.To.Val.(int32) - c.cursym.Func.Locals = int32(textstksiz) + c.cursym.Func().Args = p.To.Val.(int32) + c.cursym.Func().Locals = int32(textstksiz) /* * find leaf subroutines @@ -435,7 +436,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { var q *obj.Prog var q1 *obj.Prog - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { switch p.As { /* too hard, just leave alone */ case obj.ATEXT: @@ -541,7 +542,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { ABCL, obj.ADUFFZERO, obj.ADUFFCOPY: - c.cursym.Func.Text.Mark &^= LEAF + c.cursym.Func().Text.Mark &^= LEAF fallthrough case ABC, @@ -598,7 +599,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { autosize := int32(0) var p1 *obj.Prog var p2 *obj.Prog - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { o := p.As switch o { case obj.ATEXT: @@ -664,7 +665,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { rel.Type = objabi.R_ADDRPOWER_PCREL } - if !c.cursym.Func.Text.From.Sym.NoSplit() { + if !c.cursym.Func().Text.From.Sym.NoSplit() { q = c.stacksplit(q, autosize) // emit split check } @@ -672,6 +673,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // save the link register and update the stack, since that code is // called directly from C/C++ and can't clobber REGTMP (R31). if autosize != 0 && c.cursym.Name != "runtime.racecallbackthunk" { + var prologueEnd *obj.Prog // Save the link register and update the SP. MOVDU is used unless // the frame size is too large. The link register must be saved // even for non-empty leaf functions so that traceback works. @@ -685,6 +687,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q.To.Type = obj.TYPE_REG q.To.Reg = REGTMP + prologueEnd = q + q = obj.Appendp(q, c.newprog) q.As = AMOVDU q.Pos = p.Pos @@ -720,6 +724,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q.To.Offset = int64(-autosize) q.To.Reg = REGSP + prologueEnd = q + q = obj.Appendp(q, c.newprog) q.As = AADD q.Pos = p.Pos @@ -730,16 +736,16 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q.Spadj = +autosize q = c.ctxt.EndUnsafePoint(q, c.newprog, -1) - } - } else if c.cursym.Func.Text.Mark&LEAF == 0 { + prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd) + } else if c.cursym.Func().Text.Mark&LEAF == 0 { // A very few functions that do not return to their caller // (e.g. gogo) are not identified as leaves but still have // no frame. - c.cursym.Func.Text.Mark |= LEAF + c.cursym.Func().Text.Mark |= LEAF } - if c.cursym.Func.Text.Mark&LEAF != 0 { + if c.cursym.Func().Text.Mark&LEAF != 0 { c.cursym.Set(obj.AttrLeaf, true) break } @@ -755,7 +761,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q.To.Offset = 24 } - if c.cursym.Func.Text.From.Sym.Wrapper() { + if c.cursym.Func().Text.From.Sym.Wrapper() { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOVD g_panic(g), R3 @@ -853,7 +859,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { retTarget := p.To.Sym - if c.cursym.Func.Text.Mark&LEAF != 0 { + if c.cursym.Func().Text.Mark&LEAF != 0 { if autosize == 0 || c.cursym.Name == "runtime.racecallbackthunk" { p.As = ABR p.From = obj.Addr{} @@ -1161,7 +1167,7 @@ func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { var morestacksym *obj.LSym if c.cursym.CFunc() { morestacksym = c.ctxt.Lookup("runtime.morestackc") - } else if !c.cursym.Func.Text.From.Sym.NeedCtxt() { + } else if !c.cursym.Func().Text.From.Sym.NeedCtxt() { morestacksym = c.ctxt.Lookup("runtime.morestack_noctxt") } else { morestacksym = c.ctxt.Lookup("runtime.morestack") diff --git a/src/cmd/internal/obj/riscv/cpu.go b/src/cmd/internal/obj/riscv/cpu.go index 482f9e0b6d..b1324b62a0 100644 --- a/src/cmd/internal/obj/riscv/cpu.go +++ b/src/cmd/internal/obj/riscv/cpu.go @@ -109,7 +109,7 @@ const ( REG_RA = REG_X1 // aka REG_LR REG_SP = REG_X2 REG_GP = REG_X3 // aka REG_SB - REG_TP = REG_X4 // aka REG_G + REG_TP = REG_X4 REG_T0 = REG_X5 REG_T1 = REG_X6 REG_T2 = REG_X7 @@ -132,17 +132,17 @@ const ( REG_S8 = REG_X24 REG_S9 = REG_X25 REG_S10 = REG_X26 - REG_S11 = REG_X27 + REG_S11 = REG_X27 // aka REG_G REG_T3 = REG_X28 REG_T4 = REG_X29 REG_T5 = REG_X30 REG_T6 = REG_X31 // aka REG_TMP // Go runtime register names. - REG_G = REG_TP // G pointer. - REG_CTXT = REG_S4 // Context for closures. - REG_LR = REG_RA // Link register. - REG_TMP = REG_T6 // Reserved for assembler use. + REG_G = REG_S11 // G pointer. + REG_CTXT = REG_S4 // Context for closures. + REG_LR = REG_RA // Link register. + REG_TMP = REG_T6 // Reserved for assembler use. // ABI names for floating point registers. REG_FT0 = REG_F0 diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go index 77d383b290..9257a6453a 100644 --- a/src/cmd/internal/obj/riscv/obj.go +++ b/src/cmd/internal/obj/riscv/obj.go @@ -33,7 +33,7 @@ func buildop(ctxt *obj.Link) {} // lr is the link register to use for the JALR. // p must be a CALL, JMP or RET. func jalrToSym(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc, lr int16) *obj.Prog { - if p.As != obj.ACALL && p.As != obj.AJMP && p.As != obj.ARET { + if p.As != obj.ACALL && p.As != obj.AJMP && p.As != obj.ARET && p.As != obj.ADUFFZERO && p.As != obj.ADUFFCOPY { ctxt.Diag("unexpected Prog in jalrToSym: %v", p) return p } @@ -48,7 +48,7 @@ func jalrToSym(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc, lr int16) *ob p.As = AAUIPC p.Mark |= NEED_PCREL_ITYPE_RELOC - p.RestArgs = []obj.Addr{obj.Addr{Type: obj.TYPE_CONST, Offset: to.Offset, Sym: to.Sym}} + p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: to.Offset, Sym: to.Sym}) p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0} p.Reg = 0 p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP} @@ -58,30 +58,14 @@ func jalrToSym(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc, lr int16) *ob p.As = AJALR p.From.Type = obj.TYPE_REG p.From.Reg = lr - p.From.Sym = to.Sym p.Reg = 0 p.To.Type = obj.TYPE_REG p.To.Reg = REG_TMP - lowerJALR(p) + p.To.Sym = to.Sym return p } -// lowerJALR normalizes a JALR instruction. -func lowerJALR(p *obj.Prog) { - if p.As != AJALR { - panic("lowerJALR: not a JALR") - } - - // JALR gets parsed like JAL - the linkage pointer goes in From, - // and the target is in To. However, we need to assemble it as an - // I-type instruction, so place the linkage pointer in To, the - // target register in Reg, and the offset in From. - p.Reg = p.To.Reg - p.From, p.To = p.To, p.From - p.From.Type, p.From.Reg = obj.TYPE_CONST, obj.REG_NONE -} - // progedit is called individually for each *obj.Prog. It normalizes instruction // formats and eliminates as many pseudo-instructions as possible. func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { @@ -125,7 +109,6 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { switch p.As { case obj.AJMP: // Turn JMP into JAL ZERO or JALR ZERO. - // p.From is actually an _output_ for this instruction. p.From.Type = obj.TYPE_REG p.From.Reg = REG_ZERO @@ -136,7 +119,6 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { switch p.To.Name { case obj.NAME_NONE: p.As = AJALR - lowerJALR(p) case obj.NAME_EXTERN: // Handled in preprocess. default: @@ -154,14 +136,10 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { p.As = AJALR p.From.Type = obj.TYPE_REG p.From.Reg = REG_LR - lowerJALR(p) default: ctxt.Diag("unknown destination type %+v in CALL: %v", p.To.Type, p) } - case AJALR: - lowerJALR(p) - case obj.AUNDEF: p.As = AEBREAK @@ -256,7 +234,7 @@ func rewriteMOV(ctxt *obj.Link, newprog obj.ProgAlloc, p *obj.Prog) { p.As = AAUIPC p.Mark |= NEED_PCREL_ITYPE_RELOC - p.RestArgs = []obj.Addr{obj.Addr{Type: obj.TYPE_CONST, Offset: p.From.Offset, Sym: p.From.Sym}} + p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: p.From.Offset, Sym: p.From.Sym}) p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0} p.Reg = 0 p.To = obj.Addr{Type: obj.TYPE_REG, Reg: to.Reg} @@ -274,19 +252,7 @@ func rewriteMOV(ctxt *obj.Link, newprog obj.ProgAlloc, p *obj.Prog) { switch p.To.Type { case obj.TYPE_REG: switch p.As { - case AMOV: // MOV Ra, Rb -> ADDI $0, Ra, Rb - p.As = AADDI - p.Reg = p.From.Reg - p.From = obj.Addr{Type: obj.TYPE_CONST} - - case AMOVF: // MOVF Ra, Rb -> FSGNJS Ra, Ra, Rb - p.As = AFSGNJS - p.Reg = p.From.Reg - - case AMOVD: // MOVD Ra, Rb -> FSGNJD Ra, Ra, Rb - p.As = AFSGNJD - p.Reg = p.From.Reg - + case AMOV, AMOVB, AMOVH, AMOVW, AMOVBU, AMOVHU, AMOVWU, AMOVF, AMOVD: default: ctxt.Diag("unsupported register-register move at %v", p) } @@ -309,7 +275,7 @@ func rewriteMOV(ctxt *obj.Link, newprog obj.ProgAlloc, p *obj.Prog) { p.As = AAUIPC p.Mark |= NEED_PCREL_STYPE_RELOC - p.RestArgs = []obj.Addr{obj.Addr{Type: obj.TYPE_CONST, Offset: p.To.Offset, Sym: p.To.Sym}} + p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: p.To.Offset, Sym: p.To.Sym}) p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0} p.Reg = 0 p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP} @@ -374,7 +340,7 @@ func rewriteMOV(ctxt *obj.Link, newprog obj.ProgAlloc, p *obj.Prog) { p.As = AAUIPC p.Mark |= NEED_PCREL_ITYPE_RELOC - p.RestArgs = []obj.Addr{obj.Addr{Type: obj.TYPE_CONST, Offset: p.From.Offset, Sym: p.From.Sym}} + p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: p.From.Offset, Sym: p.From.Sym}) p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0} p.Reg = 0 p.To = to @@ -449,12 +415,12 @@ func InvertBranch(as obj.As) obj.As { // instruction. Must be called after progedit. func containsCall(sym *obj.LSym) bool { // CALLs are CALL or JAL(R) with link register LR. - for p := sym.Func.Text; p != nil; p = p.Link { + for p := sym.Func().Text; p != nil; p = p.Link { switch p.As { - case obj.ACALL: + case obj.ACALL, obj.ADUFFZERO, obj.ADUFFCOPY: return true case AJAL, AJALR: - if p.To.Type == obj.TYPE_REG && p.To.Reg == REG_LR { + if p.From.Type == obj.TYPE_REG && p.From.Reg == REG_LR { return true } } @@ -521,12 +487,12 @@ func stackOffset(a *obj.Addr, stacksize int64) { // concrete, real RISC-V instructions or directive pseudo-ops like TEXT, // PCDATA, and FUNCDATA. func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { - if cursym.Func.Text == nil || cursym.Func.Text.Link == nil { + if cursym.Func().Text == nil || cursym.Func().Text.Link == nil { return } // Generate the prologue. - text := cursym.Func.Text + text := cursym.Func().Text if text.As != obj.ATEXT { ctxt.Diag("preprocess: found symbol that does not start with TEXT directive") return @@ -560,12 +526,12 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { stacksize += ctxt.FixedFrameSize() } - cursym.Func.Args = text.To.Val.(int32) - cursym.Func.Locals = int32(stacksize) + cursym.Func().Args = text.To.Val.(int32) + cursym.Func().Locals = int32(stacksize) prologue := text - if !cursym.Func.Text.From.Sym.NoSplit() { + if !cursym.Func().Text.From.Sym.NoSplit() { prologue = stacksplit(ctxt, prologue, cursym, newprog, stacksize) // emit split check } @@ -589,7 +555,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { prologue = ctxt.EndUnsafePoint(prologue, newprog, -1) } - if cursym.Func.Text.From.Sym.Wrapper() { + if cursym.Func().Text.From.Sym.Wrapper() { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOV g_panic(g), X11 @@ -669,13 +635,13 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } // Update stack-based offsets. - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { stackOffset(&p.From, stacksize) stackOffset(&p.To, stacksize) } // Additional instruction rewriting. - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { switch p.As { case obj.AGETCALLERPC: if cursym.Leaf() { @@ -690,7 +656,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p.From.Reg = REG_SP } - case obj.ACALL: + case obj.ACALL, obj.ADUFFZERO, obj.ADUFFCOPY: switch p.To.Type { case obj.TYPE_MEM: jalrToSym(ctxt, p, newprog, REG_LR) @@ -731,11 +697,9 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p = jalrToSym(ctxt, p, newprog, REG_ZERO) } else { p.As = AJALR - p.From.Type = obj.TYPE_CONST - p.From.Offset = 0 - p.Reg = REG_LR - p.To.Type = obj.TYPE_REG - p.To.Reg = REG_ZERO + p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO} + p.Reg = 0 + p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR} } // "Add back" the stack removed in the previous instruction. @@ -757,7 +721,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // Rewrite MOV pseudo-instructions. This cannot be done in // progedit, as SP offsets need to be applied before we split // up some of the Addrs. - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { switch p.As { case AMOV, AMOVB, AMOVH, AMOVW, AMOVBU, AMOVHU, AMOVWU, AMOVF, AMOVD: rewriteMOV(ctxt, newprog, p) @@ -765,7 +729,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } // Split immediates larger than 12-bits. - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { switch p.As { // <opi> $imm, REG, TO case AADDI, AANDI, AORI, AXORI: @@ -882,9 +846,9 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // a fixed point will be reached). No attempt to handle functions > 2GiB. for { rescan := false - setPCs(cursym.Func.Text, 0) + setPCs(cursym.Func().Text, 0) - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { switch p.As { case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ: if p.To.Type != obj.TYPE_BRANCH { @@ -917,9 +881,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // it is reserved by SSA. jmp := obj.Appendp(p, newprog) jmp.As = AJALR - jmp.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0} - jmp.To = p.From - jmp.Reg = REG_TMP + jmp.From = p.From + jmp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP} // p.From is not generally valid, however will be // fixed up in the next loop. @@ -942,7 +905,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // Now that there are no long branches, resolve branch and jump targets. // At this point, instruction rewriting which changes the number of // instructions will break everything--don't do it! - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { switch p.As { case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ, AJAL: switch p.To.Type { @@ -965,7 +928,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } // Validate all instructions - this provides nice error messages. - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { for _, ins := range instructionsForProg(p) { ins.validate(ctxt) } @@ -1093,7 +1056,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgA p.To.Type = obj.TYPE_BRANCH if cursym.CFunc() { p.To.Sym = ctxt.Lookup("runtime.morestackc") - } else if !cursym.Func.Text.From.Sym.NeedCtxt() { + } else if !cursym.Func().Text.From.Sym.NeedCtxt() { p.To.Sym = ctxt.Lookup("runtime.morestack_noctxt") } else { p.To.Sym = ctxt.Lookup("runtime.morestack") @@ -1108,7 +1071,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgA p.As = AJAL p.To = obj.Addr{Type: obj.TYPE_BRANCH} p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO} - p.To.SetTarget(cursym.Func.Text.Link) + p.To.SetTarget(cursym.Func().Text.Link) // placeholder for to_done's jump target p = obj.Appendp(p, newprog) @@ -1733,6 +1696,8 @@ var encodings = [ALAST & obj.AMask]encoding{ obj.APCDATA: pseudoOpEncoding, obj.ATEXT: pseudoOpEncoding, obj.ANOP: pseudoOpEncoding, + obj.ADUFFZERO: pseudoOpEncoding, + obj.ADUFFCOPY: pseudoOpEncoding, } // encodingForAs returns the encoding for an obj.As. @@ -1801,8 +1766,8 @@ func instructionsForProg(p *obj.Prog) []*instruction { inss := []*instruction{ins} switch ins.as { - case AJAL: - ins.rd, ins.rs2 = uint32(p.From.Reg), obj.REG_NONE + case AJAL, AJALR: + ins.rd, ins.rs1, ins.rs2 = uint32(p.From.Reg), uint32(p.To.Reg), obj.REG_NONE ins.imm = p.To.Offset case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ: @@ -1812,15 +1777,15 @@ func instructionsForProg(p *obj.Prog) []*instruction { case ABGEZ: ins.as, ins.rs1, ins.rs2 = ABGE, REG_ZERO, uint32(p.From.Reg) case ABGT: - ins.as, ins.rs1, ins.rs2 = ABLT, uint32(p.Reg), uint32(p.From.Reg) + ins.as, ins.rs1, ins.rs2 = ABLT, uint32(p.From.Reg), uint32(p.Reg) case ABGTU: - ins.as, ins.rs1, ins.rs2 = ABLTU, uint32(p.Reg), uint32(p.From.Reg) + ins.as, ins.rs1, ins.rs2 = ABLTU, uint32(p.From.Reg), uint32(p.Reg) case ABGTZ: ins.as, ins.rs1, ins.rs2 = ABLT, uint32(p.From.Reg), REG_ZERO case ABLE: - ins.as, ins.rs1, ins.rs2 = ABGE, uint32(p.Reg), uint32(p.From.Reg) + ins.as, ins.rs1, ins.rs2 = ABGE, uint32(p.From.Reg), uint32(p.Reg) case ABLEU: - ins.as, ins.rs1, ins.rs2 = ABGEU, uint32(p.Reg), uint32(p.From.Reg) + ins.as, ins.rs1, ins.rs2 = ABGEU, uint32(p.From.Reg), uint32(p.Reg) case ABLEZ: ins.as, ins.rs1, ins.rs2 = ABGE, uint32(p.From.Reg), REG_ZERO case ABLTZ: @@ -1830,6 +1795,44 @@ func instructionsForProg(p *obj.Prog) []*instruction { } ins.imm = p.To.Offset + case AMOV, AMOVB, AMOVH, AMOVW, AMOVBU, AMOVHU, AMOVWU, AMOVF, AMOVD: + // Handle register to register moves. + if p.From.Type != obj.TYPE_REG || p.To.Type != obj.TYPE_REG { + break + } + switch p.As { + case AMOV: // MOV Ra, Rb -> ADDI $0, Ra, Rb + ins.as, ins.rs1, ins.rs2, ins.imm = AADDI, uint32(p.From.Reg), obj.REG_NONE, 0 + case AMOVW: // MOVW Ra, Rb -> ADDIW $0, Ra, Rb + ins.as, ins.rs1, ins.rs2, ins.imm = AADDIW, uint32(p.From.Reg), obj.REG_NONE, 0 + case AMOVBU: // MOVBU Ra, Rb -> ANDI $255, Ra, Rb + ins.as, ins.rs1, ins.rs2, ins.imm = AANDI, uint32(p.From.Reg), obj.REG_NONE, 255 + case AMOVF: // MOVF Ra, Rb -> FSGNJS Ra, Ra, Rb + ins.as, ins.rs1 = AFSGNJS, uint32(p.From.Reg) + case AMOVD: // MOVD Ra, Rb -> FSGNJD Ra, Ra, Rb + ins.as, ins.rs1 = AFSGNJD, uint32(p.From.Reg) + case AMOVB, AMOVH: + // Use SLLI/SRAI to extend. + ins.as, ins.rs1, ins.rs2 = ASLLI, uint32(p.From.Reg), obj.REG_NONE + if p.As == AMOVB { + ins.imm = 56 + } else if p.As == AMOVH { + ins.imm = 48 + } + ins2 := &instruction{as: ASRAI, rd: ins.rd, rs1: ins.rd, imm: ins.imm} + inss = append(inss, ins2) + case AMOVHU, AMOVWU: + // Use SLLI/SRLI to extend. + ins.as, ins.rs1, ins.rs2 = ASLLI, uint32(p.From.Reg), obj.REG_NONE + if p.As == AMOVHU { + ins.imm = 48 + } else if p.As == AMOVWU { + ins.imm = 32 + } + ins2 := &instruction{as: ASRLI, rd: ins.rd, rs1: ins.rd, imm: ins.imm} + inss = append(inss, ins2) + } + case ALW, ALWU, ALH, ALHU, ALB, ALBU, ALD, AFLW, AFLD: if p.From.Type != obj.TYPE_MEM { p.Ctxt.Diag("%v requires memory for source", p) @@ -1884,13 +1887,13 @@ func instructionsForProg(p *obj.Prog) []*instruction { } else { ins.as = AFEQD } - ins = &instruction{ + ins2 := &instruction{ as: AXORI, // [bit] xor 1 = not [bit] rd: ins.rd, rs1: ins.rd, imm: 1, } - inss = append(inss, ins) + inss = append(inss, ins2) case AFSQRTS, AFSQRTD: // These instructions expect a zero (i.e. float register 0) @@ -1951,7 +1954,7 @@ func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } var symcode []uint32 - for p := cursym.Func.Text; p != nil; p = p.Link { + for p := cursym.Func().Text; p != nil; p = p.Link { switch p.As { case AJALR: if p.To.Sym != nil { @@ -1983,6 +1986,13 @@ func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { ctxt.Diag("AUIPC needing PC-relative reloc missing symbol") break } + if addr.Sym.Type == objabi.STLSBSS { + if rt == objabi.R_RISCV_PCREL_ITYPE { + rt = objabi.R_RISCV_TLS_IE_ITYPE + } else if rt == objabi.R_RISCV_PCREL_STYPE { + rt = objabi.R_RISCV_TLS_IE_STYPE + } + } rel := obj.Addrel(cursym) rel.Off = int32(p.Pc) @@ -2006,7 +2016,7 @@ func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { ctxt.Arch.ByteOrder.PutUint32(p, symcode[i]) } - obj.MarkUnsafePoints(ctxt, cursym.Func.Text, newprog, isUnsafePoint, nil) + obj.MarkUnsafePoints(ctxt, cursym.Func().Text, newprog, isUnsafePoint, nil) } func isUnsafePoint(p *obj.Prog) bool { diff --git a/src/cmd/internal/obj/riscv/testdata/testbranch/branch_test.go b/src/cmd/internal/obj/riscv/testdata/testbranch/branch_test.go index 803ba8c77c..279aeb2c32 100644 --- a/src/cmd/internal/obj/riscv/testdata/testbranch/branch_test.go +++ b/src/cmd/internal/obj/riscv/testdata/testbranch/branch_test.go @@ -11,6 +11,8 @@ import ( ) func testBEQZ(a int64) (r bool) +func testBGE(a, b int64) (r bool) +func testBGEU(a, b int64) (r bool) func testBGEZ(a int64) (r bool) func testBGT(a, b int64) (r bool) func testBGTU(a, b int64) (r bool) @@ -18,6 +20,8 @@ func testBGTZ(a int64) (r bool) func testBLE(a, b int64) (r bool) func testBLEU(a, b int64) (r bool) func testBLEZ(a int64) (r bool) +func testBLT(a, b int64) (r bool) +func testBLTU(a, b int64) (r bool) func testBLTZ(a int64) (r bool) func testBNEZ(a int64) (r bool) @@ -29,30 +33,75 @@ func TestBranchCondition(t *testing.T) { fn func(a, b int64) bool want bool }{ - {"BGT", 0, 1, testBGT, true}, + {"BGE", 0, 1, testBGE, false}, + {"BGE", 0, 0, testBGE, true}, + {"BGE", 0, -1, testBGE, true}, + {"BGE", -1, 0, testBGE, false}, + {"BGE", 1, 0, testBGE, true}, + {"BGEU", 0, 1, testBGEU, false}, + {"BGEU", 0, 0, testBGEU, true}, + {"BGEU", 0, -1, testBGEU, false}, + {"BGEU", -1, 0, testBGEU, true}, + {"BGEU", 1, 0, testBGEU, true}, + {"BGT", 0, 1, testBGT, false}, {"BGT", 0, 0, testBGT, false}, - {"BGT", 0, -1, testBGT, false}, - {"BGT", -1, 0, testBGT, true}, - {"BGT", 1, 0, testBGT, false}, - {"BGTU", 0, 1, testBGTU, true}, - {"BGTU", 0, -1, testBGTU, true}, - {"BGTU", -1, 0, testBGTU, false}, - {"BGTU", 1, 0, testBGTU, false}, - {"BLE", 0, 1, testBLE, false}, - {"BLE", 0, -1, testBLE, true}, + {"BGT", 0, -1, testBGT, true}, + {"BGT", -1, 0, testBGT, false}, + {"BGT", 1, 0, testBGT, true}, + {"BGTU", 0, 1, testBGTU, false}, + {"BGTU", 0, 0, testBGTU, false}, + {"BGTU", 0, -1, testBGTU, false}, + {"BGTU", -1, 0, testBGTU, true}, + {"BGTU", 1, 0, testBGTU, true}, + {"BLE", 0, 1, testBLE, true}, {"BLE", 0, 0, testBLE, true}, - {"BLE", -1, 0, testBLE, false}, - {"BLE", 1, 0, testBLE, true}, - {"BLEU", 0, 1, testBLEU, false}, - {"BLEU", 0, -1, testBLEU, false}, + {"BLE", 0, -1, testBLE, false}, + {"BLE", -1, 0, testBLE, true}, + {"BLE", 1, 0, testBLE, false}, + {"BLEU", 0, 1, testBLEU, true}, {"BLEU", 0, 0, testBLEU, true}, - {"BLEU", -1, 0, testBLEU, true}, - {"BLEU", 1, 0, testBLEU, true}, + {"BLEU", 0, -1, testBLEU, true}, + {"BLEU", -1, 0, testBLEU, false}, + {"BLEU", 1, 0, testBLEU, false}, + {"BLT", 0, 1, testBLT, true}, + {"BLT", 0, 0, testBLT, false}, + {"BLT", 0, -1, testBLT, false}, + {"BLT", -1, 0, testBLT, true}, + {"BLT", 1, 0, testBLT, false}, + {"BLTU", 0, 1, testBLTU, true}, + {"BLTU", 0, 0, testBLTU, false}, + {"BLTU", 0, -1, testBLTU, true}, + {"BLTU", -1, 0, testBLTU, false}, + {"BLTU", 1, 0, testBLTU, false}, } for _, test := range tests { t.Run(test.ins, func(t *testing.T) { + var fn func(a, b int64) bool + switch test.ins { + case "BGE": + fn = func(a, b int64) bool { return a >= b } + case "BGEU": + fn = func(a, b int64) bool { return uint64(a) >= uint64(b) } + case "BGT": + fn = func(a, b int64) bool { return a > b } + case "BGTU": + fn = func(a, b int64) bool { return uint64(a) > uint64(b) } + case "BLE": + fn = func(a, b int64) bool { return a <= b } + case "BLEU": + fn = func(a, b int64) bool { return uint64(a) <= uint64(b) } + case "BLT": + fn = func(a, b int64) bool { return a < b } + case "BLTU": + fn = func(a, b int64) bool { return uint64(a) < uint64(b) } + default: + t.Fatalf("Unknown instruction %q", test.ins) + } + if got := fn(test.a, test.b); got != test.want { + t.Errorf("Go %v %v, %v = %v, want %v", test.ins, test.a, test.b, got, test.want) + } if got := test.fn(test.a, test.b); got != test.want { - t.Errorf("%v %v, %v = %v, want %v", test.ins, test.a, test.b, got, test.want) + t.Errorf("Assembly %v %v, %v = %v, want %v", test.ins, test.a, test.b, got, test.want) } }) } diff --git a/src/cmd/internal/obj/riscv/testdata/testbranch/branch_test.s b/src/cmd/internal/obj/riscv/testdata/testbranch/branch_test.s index 6cff235848..8dd6f563af 100644 --- a/src/cmd/internal/obj/riscv/testdata/testbranch/branch_test.s +++ b/src/cmd/internal/obj/riscv/testdata/testbranch/branch_test.s @@ -16,6 +16,28 @@ b: MOV X6, r+8(FP) RET +// func testBGE(a, b int64) (r bool) +TEXT ·testBGE(SB),NOSPLIT,$0-0 + MOV a+0(FP), X5 + MOV b+8(FP), X6 + MOV $1, X7 + BGE X5, X6, b + MOV $0, X7 +b: + MOV X7, r+16(FP) + RET + +// func testBGEU(a, b int64) (r bool) +TEXT ·testBGEU(SB),NOSPLIT,$0-0 + MOV a+0(FP), X5 + MOV b+8(FP), X6 + MOV $1, X7 + BGEU X5, X6, b + MOV $0, X7 +b: + MOV X7, r+16(FP) + RET + // func testBGEZ(a int64) (r bool) TEXT ·testBGEZ(SB),NOSPLIT,$0-0 MOV a+0(FP), X5 @@ -90,6 +112,28 @@ b: MOV X6, r+8(FP) RET +// func testBLT(a, b int64) (r bool) +TEXT ·testBLT(SB),NOSPLIT,$0-0 + MOV a+0(FP), X5 + MOV b+8(FP), X6 + MOV $1, X7 + BLT X5, X6, b + MOV $0, X7 +b: + MOV X7, r+16(FP) + RET + +// func testBLTU(a, b int64) (r bool) +TEXT ·testBLTU(SB),NOSPLIT,$0-0 + MOV a+0(FP), X5 + MOV b+8(FP), X6 + MOV $1, X7 + BLTU X5, X6, b + MOV $0, X7 +b: + MOV X7, r+16(FP) + RET + // func testBLTZ(a int64) (r bool) TEXT ·testBLTZ(SB),NOSPLIT,$0-0 MOV a+0(FP), X5 diff --git a/src/cmd/internal/obj/s390x/asmz.go b/src/cmd/internal/obj/s390x/asmz.go index 68f01f1c5d..06921085c9 100644 --- a/src/cmd/internal/obj/s390x/asmz.go +++ b/src/cmd/internal/obj/s390x/asmz.go @@ -447,7 +447,7 @@ func spanz(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { ctxt.Retpoline = false // don't keep printing } - p := cursym.Func.Text + p := cursym.Func().Text if p == nil || p.Link == nil { // handle external functions and ELF section symbols return } @@ -461,6 +461,7 @@ func spanz(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { buffer := make([]byte, 0) changed := true loop := 0 + nrelocs0 := len(c.cursym.R) for changed { if loop > 100 { c.ctxt.Diag("stuck in spanz loop") @@ -468,8 +469,11 @@ func spanz(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } changed = false buffer = buffer[:0] - c.cursym.R = make([]obj.Reloc, 0) - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for i := range c.cursym.R[nrelocs0:] { + c.cursym.R[nrelocs0+i] = obj.Reloc{} + } + c.cursym.R = c.cursym.R[:nrelocs0] // preserve marker relocations generated by the compiler + for p := c.cursym.Func().Text; p != nil; p = p.Link { pc := int64(len(buffer)) if pc != p.Pc { changed = true @@ -500,7 +504,7 @@ func spanz(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // We use REGTMP as a scratch register during call injection, // so instruction sequences that use REGTMP are unsafe to // preempt asynchronously. - obj.MarkUnsafePoints(c.ctxt, c.cursym.Func.Text, c.newprog, c.isUnsafePoint, nil) + obj.MarkUnsafePoints(c.ctxt, c.cursym.Func().Text, c.newprog, c.isUnsafePoint, nil) } // Return whether p is an unsafe point. @@ -714,7 +718,7 @@ func (c *ctxtz) oplook(p *obj.Prog) *Optab { p.From.Class = int8(c.aclass(&p.From) + 1) p.To.Class = int8(c.aclass(&p.To) + 1) for i := range p.RestArgs { - p.RestArgs[i].Class = int8(c.aclass(&p.RestArgs[i]) + 1) + p.RestArgs[i].Addr.Class = int8(c.aclass(&p.RestArgs[i].Addr) + 1) } // Mirrors the argument list in Optab. @@ -3696,7 +3700,7 @@ func (c *ctxtz) asmout(p *obj.Prog, asm *[]byte) { } case 80: // sync - zRR(op_BCR, uint32(NotEqual), 0, asm) + zRR(op_BCR, 14, 0, asm) // fast-BCR-serialization case 81: // float to fixed and fixed to float moves (no conversion) switch p.As { diff --git a/src/cmd/internal/obj/s390x/objz.go b/src/cmd/internal/obj/s390x/objz.go index 625bb0f7b4..970cf827d6 100644 --- a/src/cmd/internal/obj/s390x/objz.go +++ b/src/cmd/internal/obj/s390x/objz.go @@ -205,13 +205,13 @@ func (c *ctxtz) rewriteToUseGot(p *obj.Prog) { func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { // TODO(minux): add morestack short-cuts with small fixed frame-size. - if cursym.Func.Text == nil || cursym.Func.Text.Link == nil { + if cursym.Func().Text == nil || cursym.Func().Text.Link == nil { return } c := ctxtz{ctxt: ctxt, cursym: cursym, newprog: newprog} - p := c.cursym.Func.Text + p := c.cursym.Func().Text textstksiz := p.To.Offset if textstksiz == -8 { // Compatibility hack. @@ -227,8 +227,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } } - c.cursym.Func.Args = p.To.Val.(int32) - c.cursym.Func.Locals = int32(textstksiz) + c.cursym.Func().Args = p.To.Val.(int32) + c.cursym.Func().Locals = int32(textstksiz) /* * find leaf subroutines @@ -237,7 +237,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { */ var q *obj.Prog - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { switch p.As { case obj.ATEXT: q = p @@ -245,7 +245,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { case ABL, ABCL: q = p - c.cursym.Func.Text.Mark &^= LEAF + c.cursym.Func().Text.Mark &^= LEAF fallthrough case ABC, @@ -294,7 +294,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { var pPre *obj.Prog var pPreempt *obj.Prog wasSplit := false - for p := c.cursym.Func.Text; p != nil; p = p.Link { + for p := c.cursym.Func().Text; p != nil; p = p.Link { pLast = p switch p.As { case obj.ATEXT: @@ -356,19 +356,19 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q.Spadj = autosize q = c.ctxt.EndUnsafePoint(q, c.newprog, -1) - } else if c.cursym.Func.Text.Mark&LEAF == 0 { + } else if c.cursym.Func().Text.Mark&LEAF == 0 { // A very few functions that do not return to their caller // (e.g. gogo) are not identified as leaves but still have // no frame. - c.cursym.Func.Text.Mark |= LEAF + c.cursym.Func().Text.Mark |= LEAF } - if c.cursym.Func.Text.Mark&LEAF != 0 { + if c.cursym.Func().Text.Mark&LEAF != 0 { c.cursym.Set(obj.AttrLeaf, true) break } - if c.cursym.Func.Text.From.Sym.Wrapper() { + if c.cursym.Func().Text.From.Sym.Wrapper() { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOVD g_panic(g), R3 @@ -461,7 +461,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { case obj.ARET: retTarget := p.To.Sym - if c.cursym.Func.Text.Mark&LEAF != 0 { + if c.cursym.Func().Text.Mark&LEAF != 0 { if autosize == 0 { p.As = ABR p.From = obj.Addr{} @@ -497,8 +497,10 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p.From.Type = obj.TYPE_MEM p.From.Reg = REGSP p.From.Offset = 0 - p.To.Type = obj.TYPE_REG - p.To.Reg = REG_LR + p.To = obj.Addr{ + Type: obj.TYPE_REG, + Reg: REG_LR, + } q = p @@ -696,7 +698,7 @@ func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog, p.To.Type = obj.TYPE_BRANCH if c.cursym.CFunc() { p.To.Sym = c.ctxt.Lookup("runtime.morestackc") - } else if !c.cursym.Func.Text.From.Sym.NeedCtxt() { + } else if !c.cursym.Func().Text.From.Sym.NeedCtxt() { p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt") } else { p.To.Sym = c.ctxt.Lookup("runtime.morestack") @@ -709,7 +711,7 @@ func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog, p.As = ABR p.To.Type = obj.TYPE_BRANCH - p.To.SetTarget(c.cursym.Func.Text.Link) + p.To.SetTarget(c.cursym.Func().Text.Link) return p } diff --git a/src/cmd/internal/obj/s390x/rotate.go b/src/cmd/internal/obj/s390x/rotate.go index fd2d5482db..7dbc45e648 100644 --- a/src/cmd/internal/obj/s390x/rotate.go +++ b/src/cmd/internal/obj/s390x/rotate.go @@ -4,6 +4,10 @@ package s390x +import ( + "math/bits" +) + // RotateParams represents the immediates required for a "rotate // then ... selected bits instruction". // @@ -24,12 +28,18 @@ package s390x // input left by. Note that this rotation is performed // before the masked region is used. type RotateParams struct { - Start uint8 // big-endian start bit index [0..63] - End uint8 // big-endian end bit index [0..63] - Amount uint8 // amount to rotate left + Start int8 // big-endian start bit index [0..63] + End int8 // big-endian end bit index [0..63] + Amount int8 // amount to rotate left } -func NewRotateParams(start, end, amount int64) RotateParams { +// NewRotateParams creates a set of parameters representing a +// rotation left by the amount provided and a selection of the bits +// between the provided start and end indexes (inclusive). +// +// 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 { if start&^63 != 0 { panic("start out of bounds") } @@ -40,8 +50,66 @@ func NewRotateParams(start, end, amount int64) RotateParams { panic("amount out of bounds") } return RotateParams{ - Start: uint8(start), - End: uint8(end), - Amount: uint8(amount), + Start: start, + End: end, + Amount: amount, } } + +// 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 { + r.Amount += amount + r.Amount &= 63 + return r +} + +// OutMask provides a mask representing the selected bits. +func (r RotateParams) OutMask() uint64 { + // Note: z must be unsigned for bootstrap compiler + z := uint8(63-r.End+r.Start) & 63 // number of zero bits in mask + return bits.RotateLeft64(^uint64(0)<<z, -int(r.Start)) +} + +// InMask provides a mask representing the selected bits relative +// to the source value (i.e. pre-rotation). +func (r RotateParams) InMask() uint64 { + return bits.RotateLeft64(r.OutMask(), -int(r.Amount)) +} + +// OutMerge tries to generate a new set of parameters representing +// the intersection between the selected bits and the provided mask. +// If the intersection is unrepresentable (0 or not contiguous) nil +// will be returned. +func (r RotateParams) OutMerge(mask uint64) *RotateParams { + mask &= r.OutMask() + if mask == 0 { + return nil + } + + // normalize the mask so that the set bits are left aligned + o := bits.LeadingZeros64(^mask) + mask = bits.RotateLeft64(mask, o) + z := bits.LeadingZeros64(mask) + mask = bits.RotateLeft64(mask, z) + + // check that the normalized mask is contiguous + l := bits.LeadingZeros64(^mask) + if l+bits.TrailingZeros64(mask) != 64 { + return nil + } + + // update start and end positions (rotation amount remains the same) + r.Start = int8(o+z) & 63 + r.End = (r.Start + int8(l) - 1) & 63 + return &r +} + +// InMerge tries to generate a new set of parameters representing +// the intersection between the selected bits and the provided mask +// as applied to the source value (i.e. pre-rotation). +// If the intersection is unrepresentable (0 or not contiguous) nil +// will be returned. +func (r RotateParams) InMerge(mask uint64) *RotateParams { + return r.OutMerge(bits.RotateLeft64(mask, int(r.Amount))) +} diff --git a/src/cmd/internal/obj/s390x/rotate_test.go b/src/cmd/internal/obj/s390x/rotate_test.go new file mode 100644 index 0000000000..fa5b5bdecd --- /dev/null +++ b/src/cmd/internal/obj/s390x/rotate_test.go @@ -0,0 +1,122 @@ +// Copyright 2020 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. + +package s390x + +import ( + "testing" +) + +func TestRotateParamsMask(t *testing.T) { + tests := []struct { + start, end, amount int8 + inMask, outMask uint64 + }{ + // start before end, no rotation + {start: 0, end: 63, amount: 0, inMask: ^uint64(0), outMask: ^uint64(0)}, + {start: 1, end: 63, amount: 0, inMask: ^uint64(0) >> 1, outMask: ^uint64(0) >> 1}, + {start: 0, end: 62, amount: 0, inMask: ^uint64(1), outMask: ^uint64(1)}, + {start: 1, end: 62, amount: 0, inMask: ^uint64(3) >> 1, outMask: ^uint64(3) >> 1}, + + // end before start, no rotation + {start: 63, end: 0, amount: 0, inMask: 1<<63 | 1, outMask: 1<<63 | 1}, + {start: 62, end: 0, amount: 0, inMask: 1<<63 | 3, outMask: 1<<63 | 3}, + {start: 63, end: 1, amount: 0, inMask: 3<<62 | 1, outMask: 3<<62 | 1}, + {start: 62, end: 1, amount: 0, inMask: 3<<62 | 3, outMask: 3<<62 | 3}, + + // rotation + {start: 32, end: 63, amount: 32, inMask: 0xffffffff00000000, outMask: 0x00000000ffffffff}, + {start: 48, end: 15, amount: 16, inMask: 0xffffffff00000000, outMask: 0xffff00000000ffff}, + {start: 0, end: 7, amount: -8 & 63, inMask: 0xff, outMask: 0xff << 56}, + } + for i, test := range tests { + r := NewRotateParams(test.start, test.end, test.amount) + if m := r.OutMask(); m != test.outMask { + t.Errorf("out mask %v: want %#x, got %#x", i, test.outMask, m) + } + if m := r.InMask(); m != test.inMask { + t.Errorf("in mask %v: want %#x, got %#x", i, test.inMask, m) + } + } +} + +func TestRotateParamsMerge(t *testing.T) { + tests := []struct { + // inputs + src RotateParams + mask uint64 + + // results + in *RotateParams + out *RotateParams + }{ + { + src: RotateParams{Start: 48, End: 15, Amount: 16}, + mask: 0xffffffffffffffff, + in: &RotateParams{Start: 48, End: 15, Amount: 16}, + out: &RotateParams{Start: 48, End: 15, Amount: 16}, + }, + { + src: RotateParams{Start: 16, End: 47, Amount: 0}, + mask: 0x00000000ffffffff, + in: &RotateParams{Start: 32, End: 47, Amount: 0}, + out: &RotateParams{Start: 32, End: 47, Amount: 0}, + }, + { + src: RotateParams{Start: 16, End: 47, Amount: 0}, + mask: 0xffff00000000ffff, + in: nil, + out: nil, + }, + { + src: RotateParams{Start: 0, End: 63, Amount: 0}, + mask: 0xf7f0000000000000, + in: nil, + out: nil, + }, + { + src: RotateParams{Start: 0, End: 63, Amount: 1}, + mask: 0x000000000000ff00, + in: &RotateParams{Start: 47, End: 54, Amount: 1}, + out: &RotateParams{Start: 48, End: 55, Amount: 1}, + }, + { + src: RotateParams{Start: 32, End: 63, Amount: 32}, + mask: 0xffff00000000ffff, + in: &RotateParams{Start: 32, End: 47, Amount: 32}, + out: &RotateParams{Start: 48, End: 63, Amount: 32}, + }, + { + src: RotateParams{Start: 0, End: 31, Amount: 32}, + mask: 0x8000000000000000, + in: nil, + out: &RotateParams{Start: 0, End: 0, Amount: 32}, + }, + { + src: RotateParams{Start: 0, End: 31, Amount: 32}, + mask: 0x0000000080000000, + in: &RotateParams{Start: 0, End: 0, Amount: 32}, + out: nil, + }, + } + + eq := func(x, y *RotateParams) bool { + if x == nil && y == nil { + return true + } + if x == nil || y == nil { + return false + } + return *x == *y + } + + for _, test := range tests { + if r := test.src.InMerge(test.mask); !eq(r, test.in) { + t.Errorf("%v merged with %#x (input): want %v, got %v", test.src, test.mask, test.in, r) + } + if r := test.src.OutMerge(test.mask); !eq(r, test.out) { + t.Errorf("%v merged with %#x (output): want %v, got %v", test.src, test.mask, test.out, r) + } + } +} diff --git a/src/cmd/internal/obj/sizeof_test.go b/src/cmd/internal/obj/sizeof_test.go index b5e170c694..69e60473f5 100644 --- a/src/cmd/internal/obj/sizeof_test.go +++ b/src/cmd/internal/obj/sizeof_test.go @@ -21,7 +21,7 @@ func TestSizeof(t *testing.T) { _64bit uintptr // size on 64bit platforms }{ {Addr{}, 32, 48}, - {LSym{}, 76, 128}, + {LSym{}, 72, 120}, {Prog{}, 132, 200}, } diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go index d58877ee15..4515bdd0d3 100644 --- a/src/cmd/internal/obj/sym.go +++ b/src/cmd/internal/obj/sym.go @@ -38,6 +38,7 @@ import ( "log" "math" "sort" + "strings" ) func Linknew(arch *LinkArch) *Link { @@ -204,7 +205,9 @@ func (ctxt *Link) NumberSyms() { // if Pkgpath is unknown, cannot hash symbols with relocations, as it // may reference named symbols whose names are not fully expanded. if s.ContentAddressable() && (ctxt.Pkgpath != "" || len(s.R) == 0) { - if len(s.P) <= 8 && len(s.R) == 0 { // we can use short hash only for symbols without relocations + if s.Size <= 8 && len(s.R) == 0 && !strings.HasPrefix(s.Name, "type.") { + // We can use short hash only for symbols without relocations. + // Don't use short hash for type symbols, as they need special handling. s.PkgIdx = goobj.PkgIdxHashed64 s.SymIdx = hashed64idx if hashed64idx != int32(len(ctxt.hashed64defs)) { @@ -355,7 +358,8 @@ func (ctxt *Link) traverseSyms(flag traverseFlag, fn func(*LSym)) { } func (ctxt *Link) traverseFuncAux(flag traverseFlag, fsym *LSym, fn func(parent *LSym, aux *LSym)) { - pc := &fsym.Func.Pcln + fninfo := fsym.Func() + pc := &fninfo.Pcln if flag&traverseAux == 0 { // NB: should it become necessary to walk aux sym reloc references // without walking the aux syms themselves, this can be changed. @@ -386,7 +390,8 @@ func (ctxt *Link) traverseFuncAux(flag traverseFlag, fsym *LSym, fn func(parent fn(fsym, filesym) } } - dwsyms := []*LSym{fsym.Func.dwarfRangesSym, fsym.Func.dwarfLocSym, fsym.Func.dwarfDebugLinesSym, fsym.Func.dwarfInfoSym} + + dwsyms := []*LSym{fninfo.dwarfRangesSym, fninfo.dwarfLocSym, fninfo.dwarfDebugLinesSym, fninfo.dwarfInfoSym} for _, dws := range dwsyms { if dws == nil || dws.Size == 0 { continue diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go index a30ccf0564..b9bacb7a22 100644 --- a/src/cmd/internal/obj/util.go +++ b/src/cmd/internal/obj/util.go @@ -175,9 +175,11 @@ func (p *Prog) WriteInstructionString(w io.Writer) { sep = ", " } for i := range p.RestArgs { - io.WriteString(w, sep) - WriteDconv(w, p, &p.RestArgs[i]) - sep = ", " + if p.RestArgs[i].Pos == Source { + io.WriteString(w, sep) + WriteDconv(w, p, &p.RestArgs[i].Addr) + sep = ", " + } } if p.As == ATEXT { @@ -198,6 +200,13 @@ func (p *Prog) WriteInstructionString(w io.Writer) { if p.RegTo2 != REG_NONE { fmt.Fprintf(w, "%s%v", sep, Rconv(int(p.RegTo2))) } + for i := range p.RestArgs { + if p.RestArgs[i].Pos == Destination { + io.WriteString(w, sep) + WriteDconv(w, p, &p.RestArgs[i].Addr) + sep = ", " + } + } } func (ctxt *Link) NewProg() *Prog { @@ -210,13 +219,30 @@ func (ctxt *Link) CanReuseProgs() bool { return ctxt.Debugasm == 0 } +// Dconv accepts an argument 'a' within a prog 'p' and returns a string +// with a formatted version of the argument. func Dconv(p *Prog, a *Addr) string { buf := new(bytes.Buffer) - WriteDconv(buf, p, a) + writeDconv(buf, p, a, false) + return buf.String() +} + +// DconvDconvWithABIDetail accepts an argument 'a' within a prog 'p' +// and returns a string with a formatted version of the argument, in +// which text symbols are rendered with explicit ABI selectors. +func DconvWithABIDetail(p *Prog, a *Addr) string { + buf := new(bytes.Buffer) + writeDconv(buf, p, a, true) return buf.String() } +// WriteDconv accepts an argument 'a' within a prog 'p' +// and writes a formatted version of the arg to the writer. func WriteDconv(w io.Writer, p *Prog, a *Addr) { + writeDconv(w, p, a, false) +} + +func writeDconv(w io.Writer, p *Prog, a *Addr, abiDetail bool) { switch a.Type { default: fmt.Fprintf(w, "type=%d", a.Type) @@ -250,7 +276,7 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) { case TYPE_BRANCH: if a.Sym != nil { - fmt.Fprintf(w, "%s(SB)", a.Sym.Name) + fmt.Fprintf(w, "%s%s(SB)", a.Sym.Name, abiDecorate(a, abiDetail)) } else if a.Target() != nil { fmt.Fprint(w, a.Target().Pc) } else { @@ -259,7 +285,7 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) { case TYPE_INDIR: io.WriteString(w, "*") - a.WriteNameTo(w) + a.writeNameTo(w, abiDetail) case TYPE_MEM: a.WriteNameTo(w) @@ -299,7 +325,7 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) { case TYPE_ADDR: io.WriteString(w, "$") - a.WriteNameTo(w) + a.writeNameTo(w, abiDetail) case TYPE_SHIFT: v := int(a.Offset) @@ -335,6 +361,11 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) { } func (a *Addr) WriteNameTo(w io.Writer) { + a.writeNameTo(w, false) +} + +func (a *Addr) writeNameTo(w io.Writer, abiDetail bool) { + switch a.Name { default: fmt.Fprintf(w, "name=%d", a.Name) @@ -356,7 +387,7 @@ func (a *Addr) WriteNameTo(w io.Writer) { reg = Rconv(int(a.Reg)) } if a.Sym != nil { - fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg) + fmt.Fprintf(w, "%s%s%s(%s)", a.Sym.Name, abiDecorate(a, abiDetail), offConv(a.Offset), reg) } else { fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg) } @@ -596,3 +627,10 @@ func Bool2int(b bool) int { } return i } + +func abiDecorate(a *Addr, abiDetail bool) string { + if !abiDetail || a.Sym == nil { + return "" + } + return fmt.Sprintf("<%s>", a.Sym.ABI()) +} diff --git a/src/cmd/internal/obj/wasm/wasmobj.go b/src/cmd/internal/obj/wasm/wasmobj.go index 70e8e51e65..2e9890d86c 100644 --- a/src/cmd/internal/obj/wasm/wasmobj.go +++ b/src/cmd/internal/obj/wasm/wasmobj.go @@ -129,7 +129,6 @@ var ( morestackNoCtxt *obj.LSym gcWriteBarrier *obj.LSym sigpanic *obj.LSym - sigpanic0 *obj.LSym deferreturn *obj.LSym jmpdefer *obj.LSym ) @@ -142,9 +141,8 @@ const ( func instinit(ctxt *obj.Link) { morestack = ctxt.Lookup("runtime.morestack") morestackNoCtxt = ctxt.Lookup("runtime.morestack_noctxt") - gcWriteBarrier = ctxt.Lookup("runtime.gcWriteBarrier") + gcWriteBarrier = ctxt.LookupABI("runtime.gcWriteBarrier", obj.ABIInternal) sigpanic = ctxt.LookupABI("runtime.sigpanic", obj.ABIInternal) - sigpanic0 = ctxt.LookupABI("runtime.sigpanic", 0) // sigpanic called from assembly, which has ABI0 deferreturn = ctxt.LookupABI("runtime.deferreturn", obj.ABIInternal) // jmpdefer is defined in assembly as ABI0, but what we're // looking for is the *call* to jmpdefer from the Go function @@ -182,14 +180,14 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { return p } - framesize := s.Func.Text.To.Offset + framesize := s.Func().Text.To.Offset if framesize < 0 { panic("bad framesize") } - s.Func.Args = s.Func.Text.To.Val.(int32) - s.Func.Locals = int32(framesize) + s.Func().Args = s.Func().Text.To.Val.(int32) + s.Func().Locals = int32(framesize) - if s.Func.Text.From.Sym.Wrapper() { + if s.Func().Text.From.Sym.Wrapper() { // if g._panic != nil && g._panic.argp == FP { // g._panic.argp = bottom-of-frame // } @@ -222,7 +220,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { Offset: 0, // panic.argp } - p := s.Func.Text + p := s.Func().Text p = appendp(p, AMOVD, gpanic, regAddr(REG_R0)) p = appendp(p, AGet, regAddr(REG_R0)) @@ -245,7 +243,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { } if framesize > 0 { - p := s.Func.Text + p := s.Func().Text p = appendp(p, AGet, regAddr(REG_SP)) p = appendp(p, AI32Const, constAddr(framesize)) p = appendp(p, AI32Sub) @@ -260,8 +258,8 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { pc := int64(0) // pc is only incremented when necessary, this avoids bloat of the BrTable instruction var tableIdxs []uint64 tablePC := int64(0) - base := ctxt.PosTable.Pos(s.Func.Text.Pos).Base() - for p := s.Func.Text; p != nil; p = p.Link { + base := ctxt.PosTable.Pos(s.Func().Text.Pos).Base() + for p := s.Func().Text; p != nil; p = p.Link { prevBase := base base = ctxt.PosTable.Pos(p.Pos).Base() switch p.As { @@ -313,8 +311,8 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { tableIdxs = append(tableIdxs, uint64(numResumePoints)) s.Size = pc + 1 - if !s.Func.Text.From.Sym.NoSplit() { - p := s.Func.Text + if !s.Func().Text.From.Sym.NoSplit() { + p := s.Func().Text if framesize <= objabi.StackSmall { // small stack: SP <= stackguard @@ -352,7 +350,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { p = appendp(p, AIf) p = appendp(p, obj.ACALL, constAddr(0)) - if s.Func.Text.From.Sym.NeedCtxt() { + if s.Func().Text.From.Sym.NeedCtxt() { p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: morestack} } else { p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: morestackNoCtxt} @@ -365,7 +363,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { var entryPointLoopBranches []*obj.Prog var unwindExitBranches []*obj.Prog currentDepth := 0 - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { switch p.As { case ABlock, ALoop, AIf: currentDepth++ @@ -493,7 +491,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { } // return value of call is on the top of the stack, indicating whether to unwind the WebAssembly stack - if call.As == ACALLNORESUME && call.To.Sym != sigpanic && call.To.Sym != sigpanic0 { // sigpanic unwinds the stack, but it never resumes + if call.As == ACALLNORESUME && call.To.Sym != sigpanic { // sigpanic unwinds the stack, but it never resumes // trying to unwind WebAssembly stack but call has no resume point, terminate with error p = appendp(p, AIf) p = appendp(p, obj.AUNDEF) @@ -562,7 +560,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { } } - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { switch p.From.Name { case obj.NAME_AUTO: p.From.Offset += int64(framesize) @@ -712,7 +710,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { } { - p := s.Func.Text + p := s.Func().Text if len(unwindExitBranches) > 0 { p = appendp(p, ABlock) // unwindExit, used to return 1 when unwinding the stack for _, b := range unwindExitBranches { @@ -749,7 +747,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { currentDepth = 0 blockDepths := make(map[*obj.Prog]int) - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { switch p.As { case ABlock, ALoop, AIf: currentDepth++ @@ -850,7 +848,7 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { hasLocalSP = true var regUsed [MAXREG - MINREG]bool - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { if p.From.Reg != 0 { regUsed[p.From.Reg-MINREG] = true } @@ -896,7 +894,7 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { updateLocalSP(w) } - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { switch p.As { case AGet: if p.From.Type != obj.TYPE_REG { @@ -1007,6 +1005,7 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { panic("bad name for Call") } r := obj.Addrel(s) + r.Siz = 1 // actually variable sized r.Off = int32(w.Len()) r.Type = objabi.R_CALL if p.Mark&WasmImport != 0 { @@ -1033,6 +1032,7 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { case AI32Const, AI64Const: if p.From.Name == obj.NAME_EXTERN { r := obj.Addrel(s) + r.Siz = 1 // actually variable sized r.Off = int32(w.Len()) r.Type = objabi.R_ADDR r.Sym = p.From.Sym diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go index fb99c620ad..a6b85ac4a0 100644 --- a/src/cmd/internal/obj/x86/asm6.go +++ b/src/cmd/internal/obj/x86/asm6.go @@ -1851,9 +1851,9 @@ func spadjop(ctxt *obj.Link, l, q obj.As) obj.As { return q } -// If the environment variable GOAMD64=alignedjumps the assembler will ensure that -// no standalone or macro-fused jump will straddle or end on a 32 byte boundary -// by inserting NOPs before the jumps +// isJump returns whether p is a jump instruction. +// It is used to ensure that no standalone or macro-fused jump will straddle +// or end on a 32 byte boundary by inserting NOPs before the jumps. func isJump(p *obj.Prog) bool { return p.To.Target() != nil || p.As == obj.AJMP || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO @@ -1987,11 +1987,6 @@ func makePjcCtx(ctxt *obj.Link) padJumpsCtx { return padJumpsCtx(0) } - if objabi.GOAMD64 != "alignedjumps" { - return padJumpsCtx(0) - - } - return padJumpsCtx(32) } @@ -2050,7 +2045,7 @@ func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { ctxt.Diag("x86 tables not initialized, call x86.instinit first") } - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { if p.To.Type == obj.TYPE_BRANCH && p.To.Target() == nil { p.To.SetTarget(p) } @@ -2085,7 +2080,7 @@ func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { } var count int64 // rough count of number of instructions - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { count++ p.Back = branchShort // use short branches first time through if q := p.To.Target(); q != nil && (q.Back&branchShort != 0) { @@ -2100,19 +2095,20 @@ func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { var c int32 errors := ctxt.Errors var nops []nopPad // Padding for a particular assembly (reuse slice storage if multiple assemblies) + nrelocs0 := len(s.R) for { // This loop continues while there are reasons to re-assemble // whole block, like the presence of long forward jumps. reAssemble := false - for i := range s.R { - s.R[i] = obj.Reloc{} + for i := range s.R[nrelocs0:] { + s.R[nrelocs0+i] = obj.Reloc{} } - s.R = s.R[:0] + s.R = s.R[:nrelocs0] // preserve marker relocations generated by the compiler s.P = s.P[:0] c = 0 var pPrev *obj.Prog nops = nops[:0] - for p := s.Func.Text; p != nil; p = p.Link { + for p := s.Func().Text; p != nil; p = p.Link { c0 := c c = pjc.padJump(ctxt, s, p, c) @@ -2226,7 +2222,7 @@ func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { // the first instruction.) return p.From.Index == REG_TLS } - obj.MarkUnsafePoints(ctxt, s.Func.Text, newprog, useTLS, nil) + obj.MarkUnsafePoints(ctxt, s.Func().Text, newprog, useTLS, nil) } } @@ -4269,7 +4265,7 @@ func (ab *AsmBuf) doasm(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog) { args = append(args, ft) } for i := range p.RestArgs { - args = append(args, oclass(ctxt, p, &p.RestArgs[i])*Ymax) + args = append(args, oclass(ctxt, p, &p.RestArgs[i].Addr)*Ymax) } if tt != Ynone*Ymax { args = append(args, tt) @@ -5437,10 +5433,10 @@ func (ab *AsmBuf) asmins(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog) { // unpackOps4 extracts 4 operands from p. func unpackOps4(p *obj.Prog) (arg0, arg1, arg2, dst *obj.Addr) { - return &p.From, &p.RestArgs[0], &p.RestArgs[1], &p.To + return &p.From, &p.RestArgs[0].Addr, &p.RestArgs[1].Addr, &p.To } // unpackOps5 extracts 5 operands from p. func unpackOps5(p *obj.Prog) (arg0, arg1, arg2, arg3, dst *obj.Addr) { - return &p.From, &p.RestArgs[0], &p.RestArgs[1], &p.RestArgs[2], &p.To + return &p.From, &p.RestArgs[0].Addr, &p.RestArgs[1].Addr, &p.RestArgs[2].Addr, &p.To } diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go index 18a6afcd77..184fb4308b 100644 --- a/src/cmd/internal/obj/x86/obj6.go +++ b/src/cmd/internal/obj/x86/obj6.go @@ -324,9 +324,9 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { // flags and duffzero on 386 does not otherwise do so). var sym *obj.LSym if p.As == obj.ADUFFZERO { - sym = ctxt.Lookup("runtime.duffzero") + sym = ctxt.LookupABI("runtime.duffzero", obj.ABIInternal) } else { - sym = ctxt.Lookup("runtime.duffcopy") + sym = ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal) } offset := p.To.Offset p.As = mov @@ -563,11 +563,11 @@ func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { } func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { - if cursym.Func.Text == nil || cursym.Func.Text.Link == nil { + if cursym.Func().Text == nil || cursym.Func().Text.Link == nil { return } - p := cursym.Func.Text + p := cursym.Func().Text autoffset := int32(p.To.Offset) if autoffset < 0 { autoffset = 0 @@ -602,12 +602,12 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } textarg := int64(p.To.Val.(int32)) - cursym.Func.Args = int32(textarg) - cursym.Func.Locals = int32(p.To.Offset) + cursym.Func().Args = int32(textarg) + cursym.Func().Locals = int32(p.To.Offset) // TODO(rsc): Remove. - if ctxt.Arch.Family == sys.I386 && cursym.Func.Locals < 0 { - cursym.Func.Locals = 0 + if ctxt.Arch.Family == sys.I386 && cursym.Func().Locals < 0 { + cursym.Func().Locals = 0 } // TODO(rsc): Remove 'ctxt.Arch.Family == sys.AMD64 &&'. @@ -642,7 +642,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p = load_g_cx(ctxt, p, newprog) // load g into CX } - if !cursym.Func.Text.From.Sym.NoSplit() { + if !cursym.Func().Text.From.Sym.NoSplit() { p = stacksplit(ctxt, cursym, p, newprog, autoffset, int32(textarg)) // emit split check } @@ -690,7 +690,7 @@ 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() { // if g._panic != nil && g._panic.argp == FP { // g._panic.argp = bottom-of-frame // } @@ -808,7 +808,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } var deltasp int32 - for p = cursym.Func.Text; p != nil; p = p.Link { + for p = cursym.Func().Text; p != nil; p = p.Link { pcsize := ctxt.Arch.RegSize switch p.From.Name { case obj.NAME_AUTO: @@ -1103,7 +1103,7 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA end := ctxt.EndUnsafePoint(jls, newprog, -1) var last *obj.Prog - for last = cursym.Func.Text; last.Link != nil; last = last.Link { + for last = cursym.Func().Text; last.Link != nil; last = last.Link { } // Now we are at the end of the function, but logically @@ -1117,7 +1117,7 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA pcdata = ctxt.StartUnsafePoint(pcdata, newprog) call := obj.Appendp(pcdata, newprog) - call.Pos = cursym.Func.Text.Pos + call.Pos = cursym.Func().Text.Pos call.As = obj.ACALL call.To.Type = obj.TYPE_BRANCH call.To.Name = obj.NAME_EXTERN @@ -1125,7 +1125,7 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA switch { case cursym.CFunc(): morestack = "runtime.morestackc" - case !cursym.Func.Text.From.Sym.NeedCtxt(): + case !cursym.Func().Text.From.Sym.NeedCtxt(): morestack = "runtime.morestack_noctxt" } call.To.Sym = ctxt.Lookup(morestack) @@ -1144,7 +1144,7 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA jmp := obj.Appendp(pcdata, newprog) jmp.As = obj.AJMP jmp.To.Type = obj.TYPE_BRANCH - jmp.To.SetTarget(cursym.Func.Text.Link) + jmp.To.SetTarget(cursym.Func().Text.Link) jmp.Spadj = +framesize jls.To.SetTarget(call) diff --git a/src/cmd/internal/objabi/flag.go b/src/cmd/internal/objabi/flag.go index 79ad2ccf74..3fd73f3c57 100644 --- a/src/cmd/internal/objabi/flag.go +++ b/src/cmd/internal/objabi/flag.go @@ -5,6 +5,7 @@ package objabi import ( + "bytes" "flag" "fmt" "io" @@ -59,6 +60,9 @@ func expandArgs(in []string) (out []string) { log.Fatal(err) } args := strings.Split(strings.TrimSpace(strings.Replace(string(slurp), "\r", "", -1)), "\n") + for i, arg := range args { + args[i] = DecodeArg(arg) + } out = append(out, expandArgs(args)...) } else if out != nil { out = append(out, s) @@ -160,3 +164,38 @@ func (f fn1) Set(s string) error { } func (f fn1) String() string { return "" } + +// DecodeArg decodes an argument. +// +// This function is public for testing with the parallel encoder. +func DecodeArg(arg string) string { + // If no encoding, fastpath out. + if !strings.ContainsAny(arg, "\\\n") { + return arg + } + + // We can't use strings.Builder as this must work at bootstrap. + var b bytes.Buffer + var wasBS bool + for _, r := range arg { + if wasBS { + switch r { + case '\\': + b.WriteByte('\\') + case 'n': + b.WriteByte('\n') + default: + // This shouldn't happen. The only backslashes that reach here + // should encode '\n' and '\\' exclusively. + panic("badly formatted input") + } + } else if r == '\\' { + wasBS = true + continue + } else { + b.WriteRune(r) + } + wasBS = false + } + return b.String() +} diff --git a/src/cmd/internal/objabi/flag_test.go b/src/cmd/internal/objabi/flag_test.go new file mode 100644 index 0000000000..935b9c2193 --- /dev/null +++ b/src/cmd/internal/objabi/flag_test.go @@ -0,0 +1,26 @@ +// Copyright 2020 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. + +package objabi + +import "testing" + +func TestDecodeArg(t *testing.T) { + t.Parallel() + tests := []struct { + arg, want string + }{ + {"", ""}, + {"hello", "hello"}, + {"hello\\n", "hello\n"}, + {"hello\\nthere", "hello\nthere"}, + {"hello\\\\there", "hello\\there"}, + {"\\\\\\n", "\\\n"}, + } + for _, test := range tests { + if got := DecodeArg(test.arg); got != test.want { + t.Errorf("decodoeArg(%q) = %q, want %q", test.arg, got, test.want) + } + } +} diff --git a/src/cmd/internal/objabi/funcdata.go b/src/cmd/internal/objabi/funcdata.go index c9480bf2f0..faa2863325 100644 --- a/src/cmd/internal/objabi/funcdata.go +++ b/src/cmd/internal/objabi/funcdata.go @@ -11,17 +11,15 @@ package objabi // ../../../runtime/symtab.go. const ( - PCDATA_RegMapIndex = 0 // if !go115ReduceLiveness - PCDATA_UnsafePoint = 0 // if go115ReduceLiveness + PCDATA_UnsafePoint = 0 PCDATA_StackMapIndex = 1 PCDATA_InlTreeIndex = 2 FUNCDATA_ArgsPointerMaps = 0 FUNCDATA_LocalsPointerMaps = 1 - FUNCDATA_RegPointerMaps = 2 // if !go115ReduceLiveness - FUNCDATA_StackObjects = 3 - FUNCDATA_InlTree = 4 - FUNCDATA_OpenCodedDeferInfo = 5 + FUNCDATA_StackObjects = 2 + FUNCDATA_InlTree = 3 + FUNCDATA_OpenCodedDeferInfo = 4 // ArgsSizeUnknown is set in Func.argsize to mark all functions // whose argument size is unknown (C vararg functions, and @@ -32,11 +30,6 @@ const ( // Special PCDATA values. const ( - // PCDATA_RegMapIndex values. - // - // Only if !go115ReduceLiveness. - PCDATA_RegMapUnsafe = PCDATA_UnsafePointUnsafe // Unsafe for async preemption - // PCDATA_UnsafePoint values. PCDATA_UnsafePointSafe = -1 // Safe for async preemption PCDATA_UnsafePointUnsafe = -2 // Unsafe for async preemption diff --git a/src/cmd/internal/objabi/funcid.go b/src/cmd/internal/objabi/funcid.go index 6c9336f31c..1d098ee172 100644 --- a/src/cmd/internal/objabi/funcid.go +++ b/src/cmd/internal/objabi/funcid.go @@ -26,7 +26,7 @@ const ( FuncID_gcBgMarkWorker FuncID_systemstack_switch FuncID_systemstack - FuncID_cgocallback_gofunc + FuncID_cgocallback FuncID_gogo FuncID_externalthreadhandler FuncID_debugCallV1 @@ -70,8 +70,8 @@ func GetFuncID(name string, isWrapper bool) FuncID { return FuncID_systemstack_switch case "runtime.systemstack": return FuncID_systemstack - case "runtime.cgocallback_gofunc": - return FuncID_cgocallback_gofunc + case "runtime.cgocallback": + return FuncID_cgocallback case "runtime.gogo": return FuncID_gogo case "runtime.externalthreadhandler": diff --git a/src/cmd/internal/objabi/head.go b/src/cmd/internal/objabi/head.go index 95b8db3809..48ff292307 100644 --- a/src/cmd/internal/objabi/head.go +++ b/src/cmd/internal/objabi/head.go @@ -54,7 +54,7 @@ func (h *HeadType) Set(s string) error { switch s { case "aix": *h = Haix - case "darwin": + case "darwin", "ios": *h = Hdarwin case "dragonfly": *h = Hdragonfly diff --git a/src/cmd/internal/objabi/line.go b/src/cmd/internal/objabi/line.go index 178c8363d9..0733b65138 100644 --- a/src/cmd/internal/objabi/line.go +++ b/src/cmd/internal/objabi/line.go @@ -37,25 +37,36 @@ func AbsFile(dir, file, rewrites string) string { abs = filepath.Join(dir, file) } + abs, rewritten := ApplyRewrites(abs, rewrites) + if !rewritten && hasPathPrefix(abs, GOROOT) { + abs = "$GOROOT" + abs[len(GOROOT):] + } + + if abs == "" { + abs = "??" + } + return abs +} + +// ApplyRewrites returns the filename for file in the given directory, +// as rewritten by the rewrites argument. +// +// The rewrites argument is a ;-separated list of rewrites. +// Each rewrite is of the form "prefix" or "prefix=>replace", +// where prefix must match a leading sequence of path elements +// and is either removed entirely or replaced by the replacement. +func ApplyRewrites(file, rewrites string) (string, bool) { start := 0 for i := 0; i <= len(rewrites); i++ { if i == len(rewrites) || rewrites[i] == ';' { - if new, ok := applyRewrite(abs, rewrites[start:i]); ok { - abs = new - goto Rewritten + if new, ok := applyRewrite(file, rewrites[start:i]); ok { + return new, true } start = i + 1 } } - if hasPathPrefix(abs, GOROOT) { - abs = "$GOROOT" + abs[len(GOROOT):] - } -Rewritten: - if abs == "" { - abs = "??" - } - return abs + return file, false } // applyRewrite applies the rewrite to the path, diff --git a/src/cmd/internal/objabi/path.go b/src/cmd/internal/objabi/path.go index 2a42179a36..fd1c9981c6 100644 --- a/src/cmd/internal/objabi/path.go +++ b/src/cmd/internal/objabi/path.go @@ -39,3 +39,25 @@ func PathToPrefix(s string) string { return string(p) } + +// IsRuntimePackagePath examines 'pkgpath' and returns TRUE if it +// belongs to the collection of "runtime-related" packages, including +// "runtime" itself, "reflect", "syscall", and the +// "runtime/internal/*" packages. The compiler and/or assembler in +// some cases need to be aware of when they are building such a +// package, for example to enable features such as ABI selectors in +// assembly sources. +func IsRuntimePackagePath(pkgpath string) bool { + rval := false + switch pkgpath { + case "runtime": + rval = true + case "reflect": + rval = true + case "syscall": + rval = true + default: + rval = strings.HasPrefix(pkgpath, "runtime/internal") + } + return rval +} diff --git a/src/cmd/internal/objabi/reloctype.go b/src/cmd/internal/objabi/reloctype.go index f029a3c396..649f690194 100644 --- a/src/cmd/internal/objabi/reloctype.go +++ b/src/cmd/internal/objabi/reloctype.go @@ -89,6 +89,17 @@ const ( // should be linked into the final binary, even if there are no other // direct references. (This is used for types reachable by reflection.) R_USETYPE + // R_USEIFACE marks a type is converted to an interface in the function this + // relocation is applied to. The target is a type descriptor. + // This is a marker relocation (0-sized), for the linker's reachabililty + // analysis. + R_USEIFACE + // R_USEIFACEMETHOD marks an interface method that is used in the function + // this relocation is applied to. The target is an interface type descriptor. + // The addend is the offset of the method in the type descriptor. + // This is a marker relocation (0-sized), for the linker's reachabililty + // analysis. + R_USEIFACEMETHOD // R_METHODOFF resolves to a 32-bit offset from the beginning of the section // holding the data being relocated to the referenced symbol. // It is a variant of R_ADDROFF used when linking from the uncommonType of a @@ -145,6 +156,9 @@ const ( // R_ARM64_LDST8 sets a LD/ST immediate value to bits [11:0] of a local address. R_ARM64_LDST8 + // R_ARM64_LDST16 sets a LD/ST immediate value to bits [11:1] of a local address. + R_ARM64_LDST16 + // R_ARM64_LDST32 sets a LD/ST immediate value to bits [11:2] of a local address. R_ARM64_LDST32 @@ -212,6 +226,14 @@ const ( // AUIPC + S-type instruction pair. R_RISCV_PCREL_STYPE + // R_RISCV_TLS_IE_ITYPE resolves a 32-bit TLS initial-exec TOC offset + // address using an AUIPC + I-type instruction pair. + R_RISCV_TLS_IE_ITYPE + + // R_RISCV_TLS_IE_STYPE resolves a 32-bit TLS initial-exec TOC offset + // address using an AUIPC + S-type instruction pair. + R_RISCV_TLS_IE_STYPE + // R_PCRELDBL relocates s390x 2-byte aligned PC-relative addresses. // TODO(mundaym): remove once variants can be serialized - see issue 14218. R_PCRELDBL diff --git a/src/cmd/internal/objabi/reloctype_string.go b/src/cmd/internal/objabi/reloctype_string.go index 83dfe71e07..658a44f8b8 100644 --- a/src/cmd/internal/objabi/reloctype_string.go +++ b/src/cmd/internal/objabi/reloctype_string.go @@ -4,9 +4,75 @@ package objabi import "strconv" -const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_WEAKADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CALLRISCVR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_LDST8R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF" +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[R_ADDR-1] + _ = x[R_ADDRPOWER-2] + _ = x[R_ADDRARM64-3] + _ = x[R_ADDRMIPS-4] + _ = x[R_ADDROFF-5] + _ = x[R_WEAKADDROFF-6] + _ = x[R_SIZE-7] + _ = x[R_CALL-8] + _ = x[R_CALLARM-9] + _ = x[R_CALLARM64-10] + _ = x[R_CALLIND-11] + _ = x[R_CALLPOWER-12] + _ = x[R_CALLMIPS-13] + _ = x[R_CALLRISCV-14] + _ = x[R_CONST-15] + _ = x[R_PCREL-16] + _ = x[R_TLS_LE-17] + _ = x[R_TLS_IE-18] + _ = x[R_GOTOFF-19] + _ = x[R_PLT0-20] + _ = x[R_PLT1-21] + _ = x[R_PLT2-22] + _ = x[R_USEFIELD-23] + _ = x[R_USETYPE-24] + _ = x[R_USEIFACE-25] + _ = x[R_USEIFACEMETHOD-26] + _ = x[R_METHODOFF-27] + _ = x[R_POWER_TOC-28] + _ = x[R_GOTPCREL-29] + _ = x[R_JMPMIPS-30] + _ = x[R_DWARFSECREF-31] + _ = x[R_DWARFFILEREF-32] + _ = x[R_ARM64_TLS_LE-33] + _ = x[R_ARM64_TLS_IE-34] + _ = x[R_ARM64_GOTPCREL-35] + _ = x[R_ARM64_GOT-36] + _ = x[R_ARM64_PCREL-37] + _ = x[R_ARM64_LDST8-38] + _ = x[R_ARM64_LDST16-39] + _ = x[R_ARM64_LDST32-40] + _ = x[R_ARM64_LDST64-41] + _ = x[R_ARM64_LDST128-42] + _ = x[R_POWER_TLS_LE-43] + _ = x[R_POWER_TLS_IE-44] + _ = x[R_POWER_TLS-45] + _ = x[R_ADDRPOWER_DS-46] + _ = x[R_ADDRPOWER_GOT-47] + _ = x[R_ADDRPOWER_PCREL-48] + _ = x[R_ADDRPOWER_TOCREL-49] + _ = x[R_ADDRPOWER_TOCREL_DS-50] + _ = x[R_RISCV_PCREL_ITYPE-51] + _ = x[R_RISCV_PCREL_STYPE-52] + _ = x[R_RISCV_TLS_IE_ITYPE-53] + _ = x[R_RISCV_TLS_IE_STYPE-54] + _ = x[R_PCRELDBL-55] + _ = x[R_ADDRMIPSU-56] + _ = x[R_ADDRMIPSTLS-57] + _ = x[R_ADDRCUOFF-58] + _ = x[R_WASMIMPORT-59] + _ = x[R_XCOFFREF-60] +} + +const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_WEAKADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CALLRISCVR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IE_ITYPER_RISCV_TLS_IE_STYPER_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF" -var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 60, 66, 72, 81, 92, 101, 112, 122, 133, 140, 147, 155, 163, 171, 177, 183, 189, 199, 208, 219, 230, 240, 249, 262, 276, 290, 304, 320, 331, 344, 357, 371, 385, 400, 414, 428, 439, 453, 468, 485, 503, 524, 543, 562, 572, 583, 596, 607, 619, 629} +var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 60, 66, 72, 81, 92, 101, 112, 122, 133, 140, 147, 155, 163, 171, 177, 183, 189, 199, 208, 218, 234, 245, 256, 266, 275, 288, 302, 316, 330, 346, 357, 370, 383, 397, 411, 425, 440, 454, 468, 479, 493, 508, 525, 543, 564, 583, 602, 622, 642, 652, 663, 676, 687, 699, 709} func (i RelocType) String() string { i -= 1 diff --git a/src/cmd/internal/objabi/util.go b/src/cmd/internal/objabi/util.go index d2d6fdbda8..a73ab479a1 100644 --- a/src/cmd/internal/objabi/util.go +++ b/src/cmd/internal/objabi/util.go @@ -25,7 +25,6 @@ var ( GOARCH = envOr("GOARCH", defaultGOARCH) GOOS = envOr("GOOS", defaultGOOS) GO386 = envOr("GO386", defaultGO386) - GOAMD64 = goamd64() GOARM = goarm() GOMIPS = gomips() GOMIPS64 = gomips64() @@ -37,17 +36,16 @@ var ( const ( ElfRelocOffset = 256 - MachoRelocOffset = 2048 // reserve enough space for ELF relocations - Go115AMD64 = "alignedjumps" // Should be "alignedjumps" or "normaljumps"; this replaces environment variable introduced in CL 219357. + MachoRelocOffset = 2048 // reserve enough space for ELF relocations ) -// TODO(1.16): assuming no issues in 1.15 release, remove this and related constant. -func goamd64() string { - return Go115AMD64 -} - func goarm() int { - switch v := envOr("GOARM", defaultGOARM); v { + def := defaultGOARM + if GOOS == "android" && GOARCH == "arm" { + // Android arm devices always support GOARM=7. + def = "7" + } + switch v := envOr("GOARM", def); v { case "5": return 5 case "6": @@ -139,7 +137,7 @@ func init() { } // Note: must agree with runtime.framepointer_enabled. -var Framepointer_enabled = GOARCH == "amd64" || GOARCH == "arm64" && (GOOS == "linux" || GOOS == "darwin") +var Framepointer_enabled = GOARCH == "amd64" || GOARCH == "arm64" && (GOOS == "linux" || GOOS == "darwin" || GOOS == "ios") func addexp(s string) { // Could do general integer parsing here, but the runtime copy doesn't yet. diff --git a/src/cmd/internal/objfile/goobj.go b/src/cmd/internal/objfile/goobj.go index af9ada3324..f19bec5dcb 100644 --- a/src/cmd/internal/objfile/goobj.go +++ b/src/cmd/internal/objfile/goobj.go @@ -234,7 +234,15 @@ func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) { if f.arch == nil { return "", 0, nil } - pcdataBase := r.PcdataBase() + getSymData := func(s goobj.SymRef) []byte { + if s.PkgIdx != goobj.PkgIdxHashed { + // We don't need the data for non-hashed symbols, yet. + panic("not supported") + } + i := uint32(s.SymIdx + uint32(r.NSym()+r.NHashed64def())) + return r.BytesAt(r.DataOff(i), r.DataSize(i)) + } + ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef()) for i := uint32(0); i < ndef; i++ { osym := r.Sym(i) @@ -259,15 +267,11 @@ func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) { } b := r.BytesAt(r.DataOff(isym), r.DataSize(isym)) var info *goobj.FuncInfo - lengths := info.ReadFuncInfoLengths(b) - off, end := info.ReadPcline(b) - pcline := r.BytesAt(pcdataBase+off, int(end-off)) + pcline := getSymData(info.ReadPcline(b)) line := int(pcValue(pcline, pc-addr, f.arch)) - off, end = info.ReadPcfile(b) - pcfile := r.BytesAt(pcdataBase+off, int(end-off)) + pcfile := getSymData(info.ReadPcfile(b)) fileID := pcValue(pcfile, pc-addr, f.arch) - globalFileID := info.ReadFile(b, lengths.FileOff, uint32(fileID)) - fileName := r.File(int(globalFileID)) + fileName := r.File(int(fileID)) // Note: we provide only the name in the Func structure. // We could provide more if needed. return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: osym.Name(r)}} diff --git a/src/cmd/internal/objfile/macho.go b/src/cmd/internal/objfile/macho.go index fdb7e76dfc..1d6963f7c4 100644 --- a/src/cmd/internal/objfile/macho.go +++ b/src/cmd/internal/objfile/macho.go @@ -60,7 +60,7 @@ func (f *machoFile) symbols() ([]Sym, error) { } else if int(s.Sect) <= len(f.macho.Sections) { sect := f.macho.Sections[s.Sect-1] switch sect.Seg { - case "__TEXT": + case "__TEXT", "__DATA_CONST": sym.Code = 'R' case "__DATA": sym.Code = 'D' diff --git a/src/cmd/internal/pkgpath/pkgpath.go b/src/cmd/internal/pkgpath/pkgpath.go new file mode 100644 index 0000000000..40a040a81a --- /dev/null +++ b/src/cmd/internal/pkgpath/pkgpath.go @@ -0,0 +1,174 @@ +// Copyright 2020 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. + +// Package pkgpath determines the package path used by gccgo/GoLLVM symbols. +// This package is not used for the gc compiler. +package pkgpath + +import ( + "bytes" + "errors" + "fmt" + "io/ioutil" + "os" + "os/exec" + "strings" +) + +// ToSymbolFunc returns a function that may be used to convert a +// package path into a string suitable for use as a symbol. +// cmd is the gccgo/GoLLVM compiler in use, and tmpdir is a temporary +// directory to pass to ioutil.TempFile. +// For example, this returns a function that converts "net/http" +// into a string like "net..z2fhttp". The actual string varies for +// different gccgo/GoLLVM versions, which is why this returns a function +// that does the conversion appropriate for the compiler in use. +func ToSymbolFunc(cmd, tmpdir string) (func(string) string, error) { + // To determine the scheme used by cmd, we compile a small + // file and examine the assembly code. Older versions of gccgo + // use a simple mangling scheme where there can be collisions + // between packages whose paths are different but mangle to + // the same string. More recent versions use a new mangler + // that avoids these collisions. + const filepat = "*_gccgo_manglechck.go" + f, err := ioutil.TempFile(tmpdir, filepat) + if err != nil { + return nil, err + } + gofilename := f.Name() + f.Close() + defer os.Remove(gofilename) + + if err := ioutil.WriteFile(gofilename, []byte(mangleCheckCode), 0644); err != nil { + return nil, err + } + + command := exec.Command(cmd, "-S", "-o", "-", gofilename) + buf, err := command.Output() + if err != nil { + return nil, err + } + + // Original mangling: go.l__ufer.Run + // Mangling v2: go.l..u00e4ufer.Run + // Mangling v3: go_0l_u00e4ufer.Run + if bytes.Contains(buf, []byte("go_0l_u00e4ufer.Run")) { + return toSymbolV3, nil + } else if bytes.Contains(buf, []byte("go.l..u00e4ufer.Run")) { + return toSymbolV2, nil + } else if bytes.Contains(buf, []byte("go.l__ufer.Run")) { + return toSymbolV1, nil + } else { + return nil, errors.New(cmd + ": unrecognized mangling scheme") + } +} + +// mangleCheckCode is the package we compile to determine the mangling scheme. +const mangleCheckCode = ` +package läufer +func Run(x int) int { + return 1 +} +` + +// toSymbolV1 converts a package path using the original mangling scheme. +func toSymbolV1(ppath string) string { + clean := func(r rune) rune { + switch { + case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z', + '0' <= r && r <= '9': + return r + } + return '_' + } + return strings.Map(clean, ppath) +} + +// toSymbolV2 converts a package path using the second mangling scheme. +func toSymbolV2(ppath string) string { + // This has to build at boostrap time, so it has to build + // with Go 1.4, so we don't use strings.Builder. + bsl := make([]byte, 0, len(ppath)) + changed := false + for _, c := range ppath { + if ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || c == '_' { + bsl = append(bsl, byte(c)) + continue + } + var enc string + switch { + case c == '.': + enc = ".x2e" + case c < 0x80: + enc = fmt.Sprintf("..z%02x", c) + case c < 0x10000: + enc = fmt.Sprintf("..u%04x", c) + default: + enc = fmt.Sprintf("..U%08x", c) + } + bsl = append(bsl, enc...) + changed = true + } + if !changed { + return ppath + } + return string(bsl) +} + +// v3UnderscoreCodes maps from a character that supports an underscore +// encoding to the underscore encoding character. +var v3UnderscoreCodes = map[byte]byte{ + '_': '_', + '.': '0', + '/': '1', + '*': '2', + ',': '3', + '{': '4', + '}': '5', + '[': '6', + ']': '7', + '(': '8', + ')': '9', + '"': 'a', + ' ': 'b', + ';': 'c', +} + +// toSymbolV3 converts a package path using the third mangling scheme. +func toSymbolV3(ppath string) string { + // This has to build at boostrap time, so it has to build + // with Go 1.4, so we don't use strings.Builder. + bsl := make([]byte, 0, len(ppath)) + changed := false + for _, c := range ppath { + if ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') { + bsl = append(bsl, byte(c)) + continue + } + + if c < 0x80 { + if u, ok := v3UnderscoreCodes[byte(c)]; ok { + bsl = append(bsl, '_', u) + changed = true + continue + } + } + + var enc string + switch { + case c < 0x80: + enc = fmt.Sprintf("_x%02x", c) + case c < 0x10000: + enc = fmt.Sprintf("_u%04x", c) + default: + enc = fmt.Sprintf("_U%08x", c) + } + bsl = append(bsl, enc...) + changed = true + } + if !changed { + return ppath + } + return string(bsl) +} diff --git a/src/cmd/internal/pkgpath/pkgpath_test.go b/src/cmd/internal/pkgpath/pkgpath_test.go new file mode 100644 index 0000000000..232e803a60 --- /dev/null +++ b/src/cmd/internal/pkgpath/pkgpath_test.go @@ -0,0 +1,141 @@ +// Copyright 2020 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. + +package pkgpath + +import ( + "os" + "testing" +) + +const testEnvName = "GO_PKGPATH_TEST_COMPILER" + +// This init function supports TestToSymbolFunc. For simplicity, +// we use the test binary itself as a sample gccgo driver. +// We set an environment variable to specify how it should behave. +func init() { + switch os.Getenv(testEnvName) { + case "": + return + case "v1": + os.Stdout.WriteString(`.string "go.l__ufer.Run"`) + os.Exit(0) + case "v2": + os.Stdout.WriteString(`.string "go.l..u00e4ufer.Run"`) + os.Exit(0) + case "v3": + os.Stdout.WriteString(`.string "go_0l_u00e4ufer.Run"`) + os.Exit(0) + case "error": + os.Stdout.WriteString(`unknown string`) + os.Exit(0) + } +} + +func TestToSymbolFunc(t *testing.T) { + const input = "pä世🜃" + tests := []struct { + env string + fail bool + mangled string + }{ + { + env: "v1", + mangled: "p___", + }, + { + env: "v2", + mangled: "p..u00e4..u4e16..U0001f703", + }, + { + env: "v3", + mangled: "p_u00e4_u4e16_U0001f703", + }, + { + env: "error", + fail: true, + }, + } + + cmd := os.Args[0] + tmpdir := t.TempDir() + + defer os.Unsetenv(testEnvName) + + for _, test := range tests { + t.Run(test.env, func(t *testing.T) { + os.Setenv(testEnvName, test.env) + + fn, err := ToSymbolFunc(cmd, tmpdir) + if err != nil { + if !test.fail { + t.Errorf("ToSymbolFunc(%q, %q): unexpected error %v", cmd, tmpdir, err) + } + } else if test.fail { + t.Errorf("ToSymbolFunc(%q, %q) succeeded but expected to fail", cmd, tmpdir) + } else if got, want := fn(input), test.mangled; got != want { + t.Errorf("ToSymbolFunc(%q, %q)(%q) = %q, want %q", cmd, tmpdir, input, got, want) + } + }) + } +} + +var symbolTests = []struct { + input, v1, v2, v3 string +}{ + { + "", + "", + "", + "", + }, + { + "bytes", + "bytes", + "bytes", + "bytes", + }, + { + "net/http", + "net_http", + "net..z2fhttp", + "net_1http", + }, + { + "golang.org/x/net/http", + "golang_org_x_net_http", + "golang.x2eorg..z2fx..z2fnet..z2fhttp", + "golang_0org_1x_1net_1http", + }, + { + "pä世.🜃", + "p____", + "p..u00e4..u4e16.x2e..U0001f703", + "p_u00e4_u4e16_0_U0001f703", + }, +} + +func TestV1(t *testing.T) { + for _, test := range symbolTests { + if got, want := toSymbolV1(test.input), test.v1; got != want { + t.Errorf("toSymbolV1(%q) = %q, want %q", test.input, got, want) + } + } +} + +func TestV2(t *testing.T) { + for _, test := range symbolTests { + if got, want := toSymbolV2(test.input), test.v2; got != want { + t.Errorf("toSymbolV2(%q) = %q, want %q", test.input, got, want) + } + } +} + +func TestV3(t *testing.T) { + for _, test := range symbolTests { + if got, want := toSymbolV3(test.input), test.v3; got != want { + t.Errorf("toSymbolV3(%q) = %q, want %q", test.input, got, want) + } + } +} diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go index c27b3b986d..ef7c017bd4 100644 --- a/src/cmd/internal/sys/supported.go +++ b/src/cmd/internal/sys/supported.go @@ -13,7 +13,9 @@ func RaceDetectorSupported(goos, goarch string) bool { switch goos { case "linux": return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64" - case "darwin", "freebsd", "netbsd", "windows": + case "darwin": + return goarch == "amd64" || goarch == "arm64" + case "freebsd", "netbsd", "windows": return goarch == "amd64" default: return false @@ -32,13 +34,14 @@ func MSanSupported(goos, goarch string) bool { } // MustLinkExternal reports whether goos/goarch requires external linking. +// (This is the opposite of internal/testenv.CanInternalLink. Keep them in sync.) func MustLinkExternal(goos, goarch string) bool { switch goos { case "android": if goarch != "arm64" { return true } - case "darwin": + case "ios": if goarch == "arm64" { return true } @@ -69,7 +72,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", - "darwin/amd64", + "darwin/amd64", "darwin/arm64", "windows/amd64", "windows/386": return true } @@ -83,10 +86,11 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "pie": switch platform { - case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x", + case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x", "android/amd64", "android/arm", "android/arm64", "android/386", "freebsd/amd64", - "darwin/amd64", + "darwin/amd64", "darwin/arm64", + "ios/amd64", "ios/arm64", "aix/ppc64", "windows/386", "windows/amd64", "windows/arm": return true @@ -104,7 +108,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { switch platform { case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/s390x", "linux/ppc64le", "android/amd64", "android/arm", "android/arm64", "android/386", - "darwin/amd64", + "darwin/amd64", "darwin/arm64", "freebsd/amd64": return true } @@ -114,3 +118,14 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { return false } } + +func InternalLinkPIESupported(goos, goarch string) bool { + switch goos + "/" + goarch { + case "darwin/amd64", "darwin/arm64", + "linux/amd64", "linux/arm64", + "android/arm64", + "windows-amd64", "windows-386", "windows-arm": + return true + } + return false +} diff --git a/src/cmd/internal/sys/supported_test.go b/src/cmd/internal/sys/supported_test.go new file mode 100644 index 0000000000..1217814af5 --- /dev/null +++ b/src/cmd/internal/sys/supported_test.go @@ -0,0 +1,18 @@ +// Copyright 2020 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. + +package sys + +import ( + "internal/testenv" + "runtime" + "testing" +) + +func TestMustLinkExternalMatchesTestenv(t *testing.T) { + // MustLinkExternal and testenv.CanInternalLink are the exact opposite. + if b := MustLinkExternal(runtime.GOOS, runtime.GOARCH); b != !testenv.CanInternalLink() { + t.Fatalf("MustLinkExternal() == %v, testenv.CanInternalLink() == %v, don't match", b, testenv.CanInternalLink()) + } +} |
