[dev.boringcrypto] all: merge master into dev.boringcrypto

Change-Id: I1aa33cabd0c55fe64994b08f8a3f7b6bbfb3282c
This commit is contained in:
Roland Shoemaker 2021-11-05 11:08:36 -07:00
commit ed07c49cb6
2814 changed files with 92583 additions and 37673 deletions

2
.github/SUPPORT.md vendored
View File

@ -11,4 +11,4 @@ For asking questions, see:
* [Stack Overflow](https://stackoverflow.com/questions/tagged/go) with questions tagged "go" * [Stack Overflow](https://stackoverflow.com/questions/tagged/go) with questions tagged "go"
* **IRC** channel #go-nuts on Freenode * **IRC** channel #go-nuts on Libera

View File

@ -817,6 +817,7 @@ Lehner Florian <dev@der-flo.net>
Leigh McCulloch <leighmcc@gmail.com> Leigh McCulloch <leighmcc@gmail.com>
Leo Antunes <leo@costela.net> Leo Antunes <leo@costela.net>
Leon Klingele <git@leonklingele.de> Leon Klingele <git@leonklingele.de>
Leonard Wang <wangdeyu0907@gmail.com> <wangdeyu@golangcn.org>
Leonel Quinteros <leonel.quinteros@gmail.com> Leonel Quinteros <leonel.quinteros@gmail.com>
Lev Shamardin <shamardin@gmail.com> Lev Shamardin <shamardin@gmail.com>
Lewin Bormann <lewin.bormann@gmail.com> Lewin Bormann <lewin.bormann@gmail.com>
@ -1015,6 +1016,7 @@ Nathan Youngman <git@nathany.com>
Nathaniel Cook <nvcook42@gmail.com> Nathaniel Cook <nvcook42@gmail.com>
Naveen Kumar Sangi <naveenkumarsangi@protonmail.com> Naveen Kumar Sangi <naveenkumarsangi@protonmail.com>
Neelesh Chandola <neelesh.c98@gmail.com> Neelesh Chandola <neelesh.c98@gmail.com>
Neil Alexander <neilalexander@neilalexander.dev>
Neil Lyons <nwjlyons@googlemail.com> Neil Lyons <nwjlyons@googlemail.com>
Netflix, Inc. Netflix, Inc.
Neuman Vong <neuman.vong@gmail.com> Neuman Vong <neuman.vong@gmail.com>
@ -1479,6 +1481,7 @@ Zheng Dayu <davidzheng23@gmail.com>
Zhongtao Chen <chenzhongtao@126.com> Zhongtao Chen <chenzhongtao@126.com>
Zhou Peng <p@ctriple.cn> Zhou Peng <p@ctriple.cn>
Ziad Hatahet <hatahet@gmail.com> Ziad Hatahet <hatahet@gmail.com>
Zizhao Zhang <btw515wolf2@gmail.com>
Zorion Arrizabalaga <zorionk@gmail.com> Zorion Arrizabalaga <zorionk@gmail.com>
Максим Федосеев <max.faceless.frei@gmail.com> Максим Федосеев <max.faceless.frei@gmail.com>
Роман Хавроненко <hagen1778@gmail.com> Роман Хавроненко <hagen1778@gmail.com>

View File

@ -1109,7 +1109,6 @@ Ian Lance Taylor <iant@golang.org>
Ian Leue <ian@appboy.com> Ian Leue <ian@appboy.com>
Ian Mckay <iann0036@gmail.com> Ian Mckay <iann0036@gmail.com>
Ian Tay <iantay@google.com> Ian Tay <iantay@google.com>
Ian Woolf <btw515wolf2@gmail.com>
Ian Zapolsky <ianzapolsky@gmail.com> Ian Zapolsky <ianzapolsky@gmail.com>
Ibrahim AshShohail <ibra.sho@gmail.com> Ibrahim AshShohail <ibra.sho@gmail.com>
Icarus Sparry <golang@icarus.freeuk.com> Icarus Sparry <golang@icarus.freeuk.com>
@ -1570,7 +1569,7 @@ Leigh McCulloch <leighmcc@gmail.com>
Leo Antunes <leo@costela.net> Leo Antunes <leo@costela.net>
Leo Rudberg <ljr@google.com> Leo Rudberg <ljr@google.com>
Leon Klingele <git@leonklingele.de> Leon Klingele <git@leonklingele.de>
Leonard Wang <wangdeyu0907@gmail.com> Leonard Wang <wangdeyu0907@gmail.com> <wangdeyu@golangcn.org>
Leonardo Comelli <leonardo.comelli@gmail.com> Leonardo Comelli <leonardo.comelli@gmail.com>
Leonel Quinteros <leonel.quinteros@gmail.com> Leonel Quinteros <leonel.quinteros@gmail.com>
Lev Shamardin <shamardin@gmail.com> Lev Shamardin <shamardin@gmail.com>
@ -1902,6 +1901,7 @@ Naveen Kumar Sangi <naveenkumarsangi@protonmail.com>
Neeilan Selvalingam <neeilan96@gmail.com> Neeilan Selvalingam <neeilan96@gmail.com>
Neelesh Chandola <neelesh.c98@gmail.com> Neelesh Chandola <neelesh.c98@gmail.com>
Nehal J Wani <nehaljw.kkd1@gmail.com> Nehal J Wani <nehaljw.kkd1@gmail.com>
Neil Alexander <neilalexander@neilalexander.dev>
Neil Lyons <nwjlyons@googlemail.com> Neil Lyons <nwjlyons@googlemail.com>
Neuman Vong <neuman.vong@gmail.com> Neuman Vong <neuman.vong@gmail.com>
Neven Sajko <nsajko@gmail.com> Neven Sajko <nsajko@gmail.com>
@ -2749,6 +2749,7 @@ Zhongwei Yao <zhongwei.yao@arm.com>
Zhou Peng <p@ctriple.cn> Zhou Peng <p@ctriple.cn>
Ziad Hatahet <hatahet@gmail.com> Ziad Hatahet <hatahet@gmail.com>
Ziheng Liu <lzhfromustc@gmail.com> Ziheng Liu <lzhfromustc@gmail.com>
Zizhao Zhang <btw515wolf2@gmail.com>
Zorion Arrizabalaga <zorionk@gmail.com> Zorion Arrizabalaga <zorionk@gmail.com>
Zvonimir Pavlinovic <zpavlinovic@google.com> Zvonimir Pavlinovic <zpavlinovic@google.com>
Zyad A. Ali <zyad.ali.me@gmail.com> Zyad A. Ali <zyad.ali.me@gmail.com>

View File

@ -32,7 +32,7 @@ for source installation instructions.
Go is the work of thousands of contributors. We appreciate your help! Go is the work of thousands of contributors. We appreciate your help!
To contribute, please read the contribution guidelines at https://golang.org/doc/contribute.html. To contribute, please read the contribution guidelines at https://golang.org/doc/contribute.
Note that the Go project uses the issue tracker for bug reports and Note that the Go project uses the issue tracker for bug reports and
proposals only. See https://golang.org/wiki/Questions for a list of proposals only. See https://golang.org/wiki/Questions for a list of

View File

@ -492,6 +492,7 @@ pkg syscall (windows-amd64), type CertRevocationInfo struct, OidSpecificInfo uin
pkg syscall (windows-amd64), type CertSimpleChain struct, TrustListInfo uintptr pkg syscall (windows-amd64), type CertSimpleChain struct, TrustListInfo uintptr
pkg syscall (windows-amd64), type RawSockaddrAny struct, Pad [96]int8 pkg syscall (windows-amd64), type RawSockaddrAny struct, Pad [96]int8
pkg testing, func MainStart(func(string, string) (bool, error), []InternalTest, []InternalBenchmark, []InternalExample) *M pkg testing, func MainStart(func(string, string) (bool, error), []InternalTest, []InternalBenchmark, []InternalExample) *M
pkg testing, func MainStart(testDeps, []InternalTest, []InternalBenchmark, []InternalExample) *M
pkg testing, func RegisterCover(Cover) pkg testing, func RegisterCover(Cover)
pkg text/scanner, const GoTokens = 1012 pkg text/scanner, const GoTokens = 1012
pkg text/template/parse, type DotNode bool pkg text/template/parse, type DotNode bool

View File

@ -2603,7 +2603,34 @@ pkg runtime/debug, type GCStats struct, Pause []time.Duration
pkg runtime/debug, type GCStats struct, PauseQuantiles []time.Duration pkg runtime/debug, type GCStats struct, PauseQuantiles []time.Duration
pkg runtime/debug, type GCStats struct, PauseTotal time.Duration pkg runtime/debug, type GCStats struct, PauseTotal time.Duration
pkg sort, func Reverse(Interface) Interface pkg sort, func Reverse(Interface) Interface
pkg strconv, const IntSize = 64 pkg strconv (darwin-amd64), const IntSize = 64
pkg strconv (darwin-amd64-cgo), const IntSize = 64
pkg strconv (freebsd-386), const IntSize = 32
pkg strconv (freebsd-386-cgo), const IntSize = 32
pkg strconv (freebsd-amd64), const IntSize = 64
pkg strconv (freebsd-amd64-cgo), const IntSize = 64
pkg strconv (freebsd-arm), const IntSize = 32
pkg strconv (freebsd-arm-cgo), const IntSize = 32
pkg strconv (linux-386), const IntSize = 32
pkg strconv (linux-386-cgo), const IntSize = 32
pkg strconv (linux-amd64), const IntSize = 64
pkg strconv (linux-amd64-cgo), const IntSize = 64
pkg strconv (linux-arm), const IntSize = 32
pkg strconv (linux-arm-cgo), const IntSize = 32
pkg strconv (netbsd-386), const IntSize = 32
pkg strconv (netbsd-386-cgo), const IntSize = 32
pkg strconv (netbsd-amd64), const IntSize = 64
pkg strconv (netbsd-amd64-cgo), const IntSize = 64
pkg strconv (netbsd-arm), const IntSize = 32
pkg strconv (netbsd-arm-cgo), const IntSize = 32
pkg strconv (netbsd-arm64), const IntSize = 64
pkg strconv (netbsd-arm64-cgo), const IntSize = 64
pkg strconv (openbsd-386), const IntSize = 32
pkg strconv (openbsd-386-cgo), const IntSize = 32
pkg strconv (openbsd-amd64), const IntSize = 64
pkg strconv (openbsd-amd64-cgo), const IntSize = 64
pkg strconv (windows-386), const IntSize = 32
pkg strconv (windows-amd64), const IntSize = 64
pkg strings, func TrimPrefix(string, string) string pkg strings, func TrimPrefix(string, string) string
pkg strings, func TrimSuffix(string, string) string pkg strings, func TrimSuffix(string, string) string
pkg strings, method (*Reader) WriteTo(io.Writer) (int64, error) pkg strings, method (*Reader) WriteTo(io.Writer) (int64, error)
@ -49366,7 +49393,7 @@ pkg syscall (windows-386), const IP_MULTICAST_TTL = 10
pkg syscall (windows-386), const IP_TOS = 3 pkg syscall (windows-386), const IP_TOS = 3
pkg syscall (windows-386), const IP_TTL = 4 pkg syscall (windows-386), const IP_TTL = 4
pkg syscall (windows-386), const ImplementsGetwd = true pkg syscall (windows-386), const ImplementsGetwd = true
pkg syscall (windows-386), const InvalidHandle = 18446744073709551615 pkg syscall (windows-386), const InvalidHandle = 4294967295
pkg syscall (windows-386), const KEY_ALL_ACCESS = 983103 pkg syscall (windows-386), const KEY_ALL_ACCESS = 983103
pkg syscall (windows-386), const KEY_CREATE_LINK = 32 pkg syscall (windows-386), const KEY_CREATE_LINK = 32
pkg syscall (windows-386), const KEY_CREATE_SUB_KEY = 4 pkg syscall (windows-386), const KEY_CREATE_SUB_KEY = 4

View File

@ -63,12 +63,93 @@ pkg image/draw, type RGBA64Image interface, RGBA64At(int, int) color.RGBA64
pkg image/draw, type RGBA64Image interface, Set(int, int, color.Color) pkg image/draw, type RGBA64Image interface, Set(int, int, color.Color)
pkg image/draw, type RGBA64Image interface, SetRGBA64(int, int, color.RGBA64) pkg image/draw, type RGBA64Image interface, SetRGBA64(int, int, color.RGBA64)
pkg io/fs, func FileInfoToDirEntry(FileInfo) DirEntry pkg io/fs, func FileInfoToDirEntry(FileInfo) DirEntry
pkg math (darwin-amd64), const MaxInt = 9223372036854775807
pkg math (darwin-amd64), const MaxUint = 18446744073709551615
pkg math (darwin-amd64), const MinInt = -9223372036854775808
pkg math (darwin-amd64-cgo), const MaxInt = 9223372036854775807
pkg math (darwin-amd64-cgo), const MaxUint = 18446744073709551615
pkg math (darwin-amd64-cgo), const MinInt = -9223372036854775808
pkg math (freebsd-386), const MaxInt = 2147483647
pkg math (freebsd-386), const MaxUint = 4294967295
pkg math (freebsd-386), const MinInt = -2147483648
pkg math (freebsd-386-cgo), const MaxInt = 2147483647
pkg math (freebsd-386-cgo), const MaxUint = 4294967295
pkg math (freebsd-386-cgo), const MinInt = -2147483648
pkg math (freebsd-amd64), const MaxInt = 9223372036854775807
pkg math (freebsd-amd64), const MaxUint = 18446744073709551615
pkg math (freebsd-amd64), const MinInt = -9223372036854775808
pkg math (freebsd-amd64-cgo), const MaxInt = 9223372036854775807
pkg math (freebsd-amd64-cgo), const MaxUint = 18446744073709551615
pkg math (freebsd-amd64-cgo), const MinInt = -9223372036854775808
pkg math (freebsd-arm), const MaxInt = 2147483647
pkg math (freebsd-arm), const MaxUint = 4294967295
pkg math (freebsd-arm), const MinInt = -2147483648
pkg math (freebsd-arm-cgo), const MaxInt = 2147483647
pkg math (freebsd-arm-cgo), const MaxUint = 4294967295
pkg math (freebsd-arm-cgo), const MinInt = -2147483648
pkg math (linux-386), const MaxInt = 2147483647
pkg math (linux-386), const MaxUint = 4294967295
pkg math (linux-386), const MinInt = -2147483648
pkg math (linux-386-cgo), const MaxInt = 2147483647
pkg math (linux-386-cgo), const MaxUint = 4294967295
pkg math (linux-386-cgo), const MinInt = -2147483648
pkg math (linux-amd64), const MaxInt = 9223372036854775807
pkg math (linux-amd64), const MaxUint = 18446744073709551615
pkg math (linux-amd64), const MinInt = -9223372036854775808
pkg math (linux-amd64-cgo), const MaxInt = 9223372036854775807
pkg math (linux-amd64-cgo), const MaxUint = 18446744073709551615
pkg math (linux-amd64-cgo), const MinInt = -9223372036854775808
pkg math (linux-arm), const MaxInt = 2147483647
pkg math (linux-arm), const MaxUint = 4294967295
pkg math (linux-arm), const MinInt = -2147483648
pkg math (linux-arm-cgo), const MaxInt = 2147483647
pkg math (linux-arm-cgo), const MaxUint = 4294967295
pkg math (linux-arm-cgo), const MinInt = -2147483648
pkg math (netbsd-386), const MaxInt = 2147483647
pkg math (netbsd-386), const MaxUint = 4294967295
pkg math (netbsd-386), const MinInt = -2147483648
pkg math (netbsd-386-cgo), const MaxInt = 2147483647
pkg math (netbsd-386-cgo), const MaxUint = 4294967295
pkg math (netbsd-386-cgo), const MinInt = -2147483648
pkg math (netbsd-amd64), const MaxInt = 9223372036854775807
pkg math (netbsd-amd64), const MaxUint = 18446744073709551615
pkg math (netbsd-amd64), const MinInt = -9223372036854775808
pkg math (netbsd-amd64-cgo), const MaxInt = 9223372036854775807
pkg math (netbsd-amd64-cgo), const MaxUint = 18446744073709551615
pkg math (netbsd-amd64-cgo), const MinInt = -9223372036854775808
pkg math (netbsd-arm), const MaxInt = 2147483647
pkg math (netbsd-arm), const MaxUint = 4294967295
pkg math (netbsd-arm), const MinInt = -2147483648
pkg math (netbsd-arm-cgo), const MaxInt = 2147483647
pkg math (netbsd-arm-cgo), const MaxUint = 4294967295
pkg math (netbsd-arm-cgo), const MinInt = -2147483648
pkg math (netbsd-arm64), const MaxInt = 9223372036854775807
pkg math (netbsd-arm64), const MaxUint = 18446744073709551615
pkg math (netbsd-arm64), const MinInt = -9223372036854775808
pkg math (netbsd-arm64-cgo), const MaxInt = 9223372036854775807
pkg math (netbsd-arm64-cgo), const MaxUint = 18446744073709551615
pkg math (netbsd-arm64-cgo), const MinInt = -9223372036854775808
pkg math (openbsd-386), const MaxInt = 2147483647
pkg math (openbsd-386), const MaxUint = 4294967295
pkg math (openbsd-386), const MinInt = -2147483648
pkg math (openbsd-386-cgo), const MaxInt = 2147483647
pkg math (openbsd-386-cgo), const MaxUint = 4294967295
pkg math (openbsd-386-cgo), const MinInt = -2147483648
pkg math (openbsd-amd64), const MaxInt = 9223372036854775807
pkg math (openbsd-amd64), const MaxUint = 18446744073709551615
pkg math (openbsd-amd64), const MinInt = -9223372036854775808
pkg math (openbsd-amd64-cgo), const MaxInt = 9223372036854775807
pkg math (openbsd-amd64-cgo), const MaxUint = 18446744073709551615
pkg math (openbsd-amd64-cgo), const MinInt = -9223372036854775808
pkg math (windows-386), const MaxInt = 2147483647
pkg math (windows-386), const MaxUint = 4294967295
pkg math (windows-386), const MinInt = -2147483648
pkg math (windows-amd64), const MaxInt = 9223372036854775807
pkg math (windows-amd64), const MaxUint = 18446744073709551615
pkg math (windows-amd64), const MinInt = -9223372036854775808
pkg math, const MaxFloat64 = 1.79769e+308 // 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368 pkg math, const MaxFloat64 = 1.79769e+308 // 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368
pkg math, const MaxInt = 9223372036854775807
pkg math, const MaxInt ideal-int pkg math, const MaxInt ideal-int
pkg math, const MaxUint = 18446744073709551615
pkg math, const MaxUint ideal-int pkg math, const MaxUint ideal-int
pkg math, const MinInt = -9223372036854775808
pkg math, const MinInt ideal-int pkg math, const MinInt ideal-int
pkg math, const SmallestNonzeroFloat32 = 1.4013e-45 // 1/713623846352979940529142984724747568191373312 pkg math, const SmallestNonzeroFloat32 = 1.4013e-45 // 1/713623846352979940529142984724747568191373312
pkg math, const SmallestNonzeroFloat64 = 4.94066e-324 // 1/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784 pkg math, const SmallestNonzeroFloat64 = 4.94066e-324 // 1/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784

View File

@ -50,7 +50,34 @@ pkg image/png, type EncoderBufferPool interface, Put(*EncoderBuffer)
pkg math/big, method (*Int) IsInt64() bool pkg math/big, method (*Int) IsInt64() bool
pkg math/big, method (*Int) IsUint64() bool pkg math/big, method (*Int) IsUint64() bool
pkg math/big, type Word uint pkg math/big, type Word uint
pkg math/bits, const UintSize = 64 pkg math/bits (darwin-amd64), const UintSize = 64
pkg math/bits (darwin-amd64-cgo), const UintSize = 64
pkg math/bits (freebsd-386), const UintSize = 32
pkg math/bits (freebsd-386-cgo), const UintSize = 32
pkg math/bits (freebsd-amd64), const UintSize = 64
pkg math/bits (freebsd-amd64-cgo), const UintSize = 64
pkg math/bits (freebsd-arm), const UintSize = 32
pkg math/bits (freebsd-arm-cgo), const UintSize = 32
pkg math/bits (linux-386), const UintSize = 32
pkg math/bits (linux-386-cgo), const UintSize = 32
pkg math/bits (linux-amd64), const UintSize = 64
pkg math/bits (linux-amd64-cgo), const UintSize = 64
pkg math/bits (linux-arm), const UintSize = 32
pkg math/bits (linux-arm-cgo), const UintSize = 32
pkg math/bits (netbsd-386), const UintSize = 32
pkg math/bits (netbsd-386-cgo), const UintSize = 32
pkg math/bits (netbsd-amd64), const UintSize = 64
pkg math/bits (netbsd-amd64-cgo), const UintSize = 64
pkg math/bits (netbsd-arm), const UintSize = 32
pkg math/bits (netbsd-arm-cgo), const UintSize = 32
pkg math/bits (netbsd-arm64), const UintSize = 64
pkg math/bits (netbsd-arm64-cgo), const UintSize = 64
pkg math/bits (openbsd-386), const UintSize = 32
pkg math/bits (openbsd-386-cgo), const UintSize = 32
pkg math/bits (openbsd-amd64), const UintSize = 64
pkg math/bits (openbsd-amd64-cgo), const UintSize = 64
pkg math/bits (windows-386), const UintSize = 32
pkg math/bits (windows-amd64), const UintSize = 64
pkg math/bits, const UintSize ideal-int pkg math/bits, const UintSize ideal-int
pkg math/bits, func LeadingZeros(uint) int pkg math/bits, func LeadingZeros(uint) int
pkg math/bits, func LeadingZeros16(uint16) int pkg math/bits, func LeadingZeros16(uint16) int

View File

@ -0,0 +1,47 @@
pkg debug/buildinfo, func Read(io.ReaderAt) (*debug.BuildInfo, error)
pkg debug/buildinfo, func ReadFile(string) (*debug.BuildInfo, error)
pkg debug/buildinfo, type BuildInfo = debug.BuildInfo
pkg runtime/debug, method (*BuildInfo) MarshalText() ([]byte, error)
pkg runtime/debug, method (*BuildInfo) UnmarshalText() ([]byte, error)
pkg runtime/debug, type BuildInfo struct, GoVersion string
pkg runtime/debug, type BuildInfo struct, Settings []BuildSetting
pkg runtime/debug, type BuildSetting struct
pkg runtime/debug, type BuildSetting struct, Key string
pkg runtime/debug, type BuildSetting struct, Value string
pkg testing, func Fuzz(func(*F)) FuzzResult
pkg testing, func MainStart(testDeps, []InternalTest, []InternalBenchmark, []InternalFuzzTarget, []InternalExample) *M
pkg testing, func RunFuzzTargets(func(string, string) (bool, error), []InternalFuzzTarget) bool
pkg testing, func RunFuzzing(func(string, string) (bool, error), []InternalFuzzTarget) bool
pkg testing, method (*B) Setenv(string, string)
pkg testing, method (*F) Add(...interface{})
pkg testing, method (*F) Cleanup(func())
pkg testing, method (*F) Error(...interface{})
pkg testing, method (*F) Errorf(string, ...interface{})
pkg testing, method (*F) Fail()
pkg testing, method (*F) FailNow()
pkg testing, method (*F) Failed() bool
pkg testing, method (*F) Fatal(...interface{})
pkg testing, method (*F) Fatalf(string, ...interface{})
pkg testing, method (*F) Fuzz(interface{})
pkg testing, method (*F) Helper()
pkg testing, method (*F) Log(...interface{})
pkg testing, method (*F) Logf(string, ...interface{})
pkg testing, method (*F) Name() string
pkg testing, method (*F) Setenv(string, string)
pkg testing, method (*F) Skip(...interface{})
pkg testing, method (*F) SkipNow()
pkg testing, method (*F) Skipf(string, ...interface{})
pkg testing, method (*F) Skipped() bool
pkg testing, method (*F) TempDir() string
pkg testing, method (*T) Setenv(string, string)
pkg testing, method (FuzzResult) String() string
pkg testing, type F struct
pkg testing, type FuzzResult struct
pkg testing, type FuzzResult struct, Crasher entry
pkg testing, type FuzzResult struct, Error error
pkg testing, type FuzzResult struct, N int
pkg testing, type FuzzResult struct, T time.Duration
pkg testing, type InternalFuzzTarget struct
pkg testing, type InternalFuzzTarget struct, Fn func(*F)
pkg testing, type InternalFuzzTarget struct, Name string
pkg net/http, method (*Cookie) Valid() error

View File

@ -125,8 +125,8 @@ it is a distinct program, so there are some differences.
One is in constant evaluation. One is in constant evaluation.
Constant expressions in the assembler are parsed using Go's operator Constant expressions in the assembler are parsed using Go's operator
precedence, not the C-like precedence of the original. precedence, not the C-like precedence of the original.
Thus <code>3&amp;1<<2</code> is 4, not 0—it parses as <code>(3&amp;1)<<2</code> Thus <code>3&amp;1&lt;&lt;2</code> is 4, not 0—it parses as <code>(3&amp;1)&lt;&lt;2</code>
not <code>3&amp;(1<<2)</code>. not <code>3&amp;(1&lt;&lt;2)</code>.
Also, constants are always evaluated as 64-bit unsigned integers. Also, constants are always evaluated as 64-bit unsigned integers.
Thus <code>-2</code> is not the integer value minus two, Thus <code>-2</code> is not the integer value minus two,
but the unsigned 64-bit integer with the same bit pattern. but the unsigned 64-bit integer with the same bit pattern.
@ -914,8 +914,6 @@ This assembler is used by GOARCH values ppc64 and ppc64le.
Reference: <a href="/pkg/cmd/internal/obj/ppc64">Go PPC64 Assembly Instructions Reference Manual</a> Reference: <a href="/pkg/cmd/internal/obj/ppc64">Go PPC64 Assembly Instructions Reference Manual</a>
</p> </p>
</ul>
<h3 id="s390x">IBM z/Architecture, a.k.a. s390x</h3> <h3 id="s390x">IBM z/Architecture, a.k.a. s390x</h3>
<p> <p>

File diff suppressed because it is too large Load Diff

242
doc/go1.18.html Normal file
View File

@ -0,0 +1,242 @@
<!--{
"Title": "Go 1.18 Release Notes",
"Path": "/doc/go1.18"
}-->
<!--
NOTE: In this document and others in this directory, the convention is to
set fixed-width phrases with non-fixed-width spaces, as in
<code>hello</code> <code>world</code>.
Do not send CLs removing the interior tags from such phrases.
-->
<style>
main ul li { margin: 0.5em 0; }
</style>
<h2 id="introduction">DRAFT RELEASE NOTES — Introduction to Go 1.18</h2>
<p>
<strong>
Go 1.18 is not yet released. These are work-in-progress
release notes. Go 1.18 is expected to be released in February 2022.
</strong>
</p>
<h2 id="language">Changes to the language</h2>
<p>
TODO: complete this section
</p>
<h2 id="ports">Ports</h2>
<p id="freebsd">
Go 1.18 is the last release that is supported on FreeBSD 11.x, which has
already reached end-of-life. Go 1.19 will require FreeBSD 12.2+ or FreeBSD
13.0+.
FreeBSD 13.0+ will require a kernel with the COMPAT_FREEBSD12 option set (this is the default).
</p>
<h2 id="tools">Tools</h2>
<p>
TODO: complete this section, or delete if not needed
</p>
<h3 id="go-command">Go command</h3>
<p><!-- golang.org/issue/43684 -->
<code>go</code> <code>get</code> no longer builds or installs packages in
module-aware mode. <code>go</code> <code>get</code> is now dedicated to
adjusting dependencies in <code>go.mod</code>. Effectively, the
<code>-d</code> flag is always enabled. To install the latest version
of an executable outside the context of the current module, use
<a href="https://golang.org/ref/mod#go-install"><code>go</code>
<code>install</code> <code>example.com/cmd@latest</code></a>. Any
<a href="https://golang.org/ref/mod#version-queries">version query</a>
may be used instead of <code>latest</code>. This form of <code>go</code>
<code>install</code> was added in Go 1.16, so projects supporting older
versions may need to provide install instructions for both <code>go</code>
<code>install</code> and <code>go</code> <code>get</code>. <code>go</code>
<code>get</code> now reports an error when used outside a module, since there
is no <code>go.mod</code> file to update. In GOPATH mode (with
<code>GO111MODULE=off</code>), <code>go</code> <code>get</code> still builds
and installs packages, as before.
</p>
<p><!-- golang.org/issue/37475 -->
The <code>go</code> command now embeds version control information in
binaries including the currently checked-out revision, commit time, and a
flag indicating whether edited or untracked files are present. Version
control information is embedded if the <code>go</code> command is invoked in
a directory within a Git, Mercurial, Fossil, or Bazaar repository, and the
<code>main</code> package and its containing main module are in the same
repository. This information may be omitted using the flag
<code>-buildvcs=false</code>.
</p>
<p><!-- golang.org/issue/37475 -->
Additionally, the <code>go</code> command embeds information about the build
including build and tool tags (set with <code>-tags</code>), compiler,
assembler, and linker flags (like <code>-gcflags</code>), whether cgo was
enabled, and if it was, the values of the cgo environment variables
(like <code>CGO_CFLAGS</code>). This information may be omitted using the
flag <code>-buildinfo=false</code>. Both VCS and build information may be
read together with module information using <code>go</code>
<code>version</code> <code>-m</code> <code>file</code> or
<code>runtime/debug.ReadBuildInfo</code> (for the currently running binary)
or the new <a href="#debug/buildinfo"><code>debug/buildinfo</code></a>
package.
</p>
<p><!-- https://golang.org/issue/44435 -->
If the main module's <code>go.mod</code> file
specifies <a href="/ref/mod#go-mod-file-go"><code>go</code> <code>1.17</code></a>
or higher, <code>go</code> <code>mod</code> <code>download</code> without
arguments now downloads source code for only the modules
explicitly <a href="/ref/mod#go-mod-file-require">required</a> in the main
module's <code>go.mod</code> file. (In a <code>go</code> <code>1.17</code> or
higher module, that set already includes all dependencies needed to build the
packages and tests in the main module.)
To also download source code for transitive dependencies, use
<code>go</code> <code>mod</code> <code>download</code> <code>all</code>.
</p>
<p>
TODO: complete this section, or delete if not needed
</p>
<h3 id="gofmt"><code>gofmt</code></h3>
<p><!-- https://golang.org/issue/43566 -->
<code>gofmt</code> now reads and formats input files concurrently, with a
memory limit proportional to <code>GOMAXPROCS</code>. On a machine with
multiple CPUs, <code>gofmt</code> should now be significantly faster.
</p>
<h2 id="runtime">Runtime</h2>
<p>
TODO: complete this section, or delete if not needed
</p>
<h2 id="compiler">Compiler</h2>
<p>
TODO: complete this section, or delete if not needed
</p>
<h2 id="linker">Linker</h2>
<p>
TODO: complete this section, or delete if not needed
</p>
<h2 id="library">Core library</h2>
<h3>TODO</h3>
<p>
TODO: complete this section
</p>
<h3 id="netip">New <code>net/netip</code> package</h3>
<p>
The new <a href="/pkg/net/netip/"><code>net/netip</code></a>
package defines a new IP address type, <a href="/pkg/net/netip/#Addr"><code>Addr</code></a>.
Compared to the existing
<a href="/pkg/net/#IP"><code>net.IP</code></a> type, the <code>netip.Addr</code> type takes less
memory, is immutable, and is comparable so it supports <code>==</code>
and can be used as a map key.
</p>
<p>
In addition to <code>Addr</code>, the package defines
<a href="/pkg/net/netip/#AddrPort"><code>AddrPort</code></a>, representing
an IP and port, and
<a href="/pkg/net/netip/#Prefix"><code>Prefix</code></a>, representing
a network CIDR prefix.
</p>
<p>
The <code>net</code> package now has methods to send and receive UDP packets
using <code>netip.Addr</code> values instead of the relatively heavy
<code>*net.UDPAddr</code> values.
</p>
<h3 id="minor_library_changes">Minor changes to the library</h3>
<p>
As always, there are various minor changes and updates to the library,
made with the Go 1 <a href="/doc/go1compat">promise of compatibility</a>
in mind.
</p>
<p>
TODO: complete this section
</p>
<dl id="debug/buildinfo"><dt><a href="/pkg/debug/buildinfo">debug/buildinfo</a></dt>
<dd>
<p><!-- golang.org/issue/39301 -->
This new package provides access to module versions, version control
information, and build flags embedded in executable files built by
the <code>go</code> command. The same information is also available via
<a href="/pkg/runtime/debug#ReadBuildInfo"><code>runtime/debug.ReadBuildInfo</code></a>
for the currently running binary and via <code>go</code>
<code>version</code> <code>-m</code> on the command line.
</p>
</dd>
</dl>
<dl id="image/draw"><dt><a href="/pkg/image/draw/">image/draw</a></dt>
<dd>
<p><!-- CL 340049 -->
The <code>Draw</code> and <code>DrawMask</code> fallback implementations
(used when the arguments are not the most common image types) are now
faster when those arguments implement the optional
<a href="/pkg/image/draw/#RGBA64Image"><code>draw.RGBA64Image</code></a>
and <a href="/pkg/image/#RGBA64Image"><code>image.RGBA64Image</code></a>
interfaces that were added in Go 1.17.
</p>
</dd>
</dl><!-- image/draw -->
<dl id="reflect"><dt><a href="/pkg/reflect/">reflect</a></dt>
<dd>
<p><!-- CL 356049, 320929 -->
The new
<a href="/pkg/reflect/#Value.SetIterKey"><code>Value.SetIterKey</code></a>
and <a href="/pkg/reflect/#Value.SetIterValue"><code>Value.SetIterValue</code></a>
methods set a Value using a map iterator as the source. They are equivalent to
<code>Value.Set(iter.Key())</code> and <code>Value.Set(iter.Value())</code> but
do fewer allocations.
</p>
</dd>
<dd>
<p><!-- CL 350691 -->
The new
<a href="/pkg/reflect/#Value.UnsafePointer"><code>Value.UnsafePointer</code></a>
method returns the Value's value as an <a href="/pkg/unsafe/#Pointer"><code>unsafe.Pointer</code></a>.
This allows callers to migrate from <a href="/pkg/reflect/#Value.UnsafeAddr"><code>Value.UnsafeAddr</code></a>
and <a href="/pkg/reflect/#Value.Pointer"><code>Value.Pointer</code></a>
to eliminate the need to perform uintptr to unsafe.Pointer conversions at the callsite (as unsafe.Pointer rules require).
</p>
</dd>
</dl><!-- reflect -->
<dl id="syscall"><dt><a href="/pkg/syscall/">syscall</a></dt>
<dd>
<p><!-- CL 336550 -->
The new function <a href="/pkg/syscall/?GOOS=windows#SyscallN"><code>SyscallN</code></a>
has been introduced for Windows, allowing for calls with arbitrary number
of arguments. As a result,
<a href="/pkg/syscall/?GOOS=windows#Syscall"><code>Syscall</code></a>,
<a href="/pkg/syscall/?GOOS=windows#Syscall6"><code>Syscall6</code></a>,
<a href="/pkg/syscall/?GOOS=windows#Syscall9"><code>Syscall9</code></a>,
<a href="/pkg/syscall/?GOOS=windows#Syscall12"><code>Syscall12</code></a>,
<a href="/pkg/syscall/?GOOS=windows#Syscall15"><code>Syscall15</code></a>, and
<a href="/pkg/syscall/?GOOS=windows#Syscall18"><code>Syscall18</code></a> are
deprecated in favor of <a href="/pkg/syscall/?GOOS=windows#SyscallN"><code>SyscallN</code></a>.
</p>
</dd>
</dl><!-- syscall -->

View File

@ -1,6 +1,6 @@
<!--{ <!--{
"Title": "The Go Programming Language Specification", "Title": "The Go Programming Language Specification",
"Subtitle": "Version of Jul 26, 2021", "Subtitle": "Version of Oct 15, 2021",
"Path": "/ref/spec" "Path": "/ref/spec"
}--> }-->
@ -3000,6 +3000,18 @@ method value; the saved copy is then used as the receiver in any calls,
which may be executed later. which may be executed later.
</p> </p>
<pre>
type S struct { *T }
type T int
func (t T) M() { print(t) }
t := new(T)
s := S{T: t}
f := t.M // receiver *t is evaluated and stored in f
g := s.M // receiver *(s.T) is evaluated and stored in g
*t = 42 // does not affect stored receivers in f and g
</pre>
<p> <p>
The type <code>T</code> may be an interface or non-interface type. The type <code>T</code> may be an interface or non-interface type.
</p> </p>
@ -3602,7 +3614,7 @@ var i = 1&lt;&lt;s // 1 has type int
var j int32 = 1&lt;&lt;s // 1 has type int32; j == 0 var j int32 = 1&lt;&lt;s // 1 has type int32; j == 0
var k = uint64(1&lt;&lt;s) // 1 has type uint64; k == 1&lt;&lt;33 var k = uint64(1&lt;&lt;s) // 1 has type uint64; k == 1&lt;&lt;33
var m int = 1.0&lt;&lt;s // 1.0 has type int; m == 1&lt;&lt;33 var m int = 1.0&lt;&lt;s // 1.0 has type int; m == 1&lt;&lt;33
var n = 1.0&lt;&lt;s == j // 1.0 has type int; n == true var n = 1.0&lt;&lt;s == j // 1.0 has type int32; n == true
var o = 1&lt;&lt;s == 2&lt;&lt;s // 1 and 2 have type int; o == false var o = 1&lt;&lt;s == 2&lt;&lt;s // 1 and 2 have type int; o == false
var p = 1&lt;&lt;s == 1&lt;&lt;33 // 1 has type int; p == true var p = 1&lt;&lt;s == 1&lt;&lt;33 // 1 has type int; p == true
var u = 1.0&lt;&lt;s // illegal: 1.0 has type float64, cannot shift var u = 1.0&lt;&lt;s // illegal: 1.0 has type float64, cannot shift
@ -4338,7 +4350,7 @@ t0 := (*[0]string)(t) // t0 == nil
t1 := (*[1]string)(t) // panics: len([1]string) > len(t) t1 := (*[1]string)(t) // panics: len([1]string) > len(t)
u := make([]byte, 0) u := make([]byte, 0)
u0 = (*[0]byte)(u) // u0 != nil u0 := (*[0]byte)(u) // u0 != nil
</pre> </pre>
<h3 id="Constant_expressions">Constant expressions</h3> <h3 id="Constant_expressions">Constant expressions</h3>
@ -4549,9 +4561,8 @@ SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | S
<h3 id="Terminating_statements">Terminating statements</h3> <h3 id="Terminating_statements">Terminating statements</h3>
<p> <p>
A <i>terminating statement</i> prevents execution of all statements that lexically A <i>terminating statement</i> interrupts the regular flow of control in
appear after it in the same <a href="#Blocks">block</a>. The following statements a <a href="#Blocks">block</a>. The following statements are terminating:
are terminating:
</p> </p>
<ol> <ol>
@ -4587,7 +4598,8 @@ are terminating:
A <a href="#For_statements">"for" statement</a> in which: A <a href="#For_statements">"for" statement</a> in which:
<ul> <ul>
<li>there are no "break" statements referring to the "for" statement, and</li> <li>there are no "break" statements referring to the "for" statement, and</li>
<li>the loop condition is absent.</li> <li>the loop condition is absent, and</li>
<li>the "for" statement does not use a range clause.</li>
</ul> </ul>
</li> </li>

View File

@ -36,14 +36,13 @@ func check(t *testing.T, file string) {
continue continue
} }
frags := bytes.SplitAfterN(line, []byte("ERROR HERE: "), 2) _, frag, ok := bytes.Cut(line, []byte("ERROR HERE: "))
if len(frags) == 1 { if !ok {
continue continue
} }
frag := fmt.Sprintf(":%d:.*%s", i+1, frags[1]) re, err := regexp.Compile(fmt.Sprintf(":%d:.*%s", i+1, frag))
re, err := regexp.Compile(frag)
if err != nil { if err != nil {
t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frags[1]) t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frag)
continue continue
} }
errors = append(errors, re) errors = append(errors, re)

View File

@ -59,6 +59,7 @@ func Test28896(t *testing.T) { test28896(t) }
func Test30065(t *testing.T) { test30065(t) } func Test30065(t *testing.T) { test30065(t) }
func Test32579(t *testing.T) { test32579(t) } func Test32579(t *testing.T) { test32579(t) }
func Test31891(t *testing.T) { test31891(t) } func Test31891(t *testing.T) { test31891(t) }
func Test42018(t *testing.T) { test42018(t) }
func Test45451(t *testing.T) { test45451(t) } func Test45451(t *testing.T) { test45451(t) }
func TestAlign(t *testing.T) { testAlign(t) } func TestAlign(t *testing.T) { testAlign(t) }
func TestAtol(t *testing.T) { testAtol(t) } func TestAtol(t *testing.T) { testAtol(t) }

View File

@ -0,0 +1,14 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !windows
// +build !windows
package cgotest
import "testing"
func test42018(t *testing.T) {
t.Skip("skipping Windows-only test")
}

View File

@ -0,0 +1,46 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cgotest
/*
typedef void *HANDLE;
struct HWND__{int unused;}; typedef struct HWND__ *HWND;
*/
import "C"
import (
"testing"
"unsafe"
)
func test42018(t *testing.T) {
// Test that Windows handles are marked go:notinheap, by growing the
// stack and checking for pointer adjustments. Trick from
// test/fixedbugs/issue40954.go.
var i int
handle := C.HANDLE(unsafe.Pointer(uintptr(unsafe.Pointer(&i))))
recurseHANDLE(100, handle, uintptr(unsafe.Pointer(&i)))
hwnd := C.HWND(unsafe.Pointer(uintptr(unsafe.Pointer(&i))))
recurseHWND(400, hwnd, uintptr(unsafe.Pointer(&i)))
}
func recurseHANDLE(n int, p C.HANDLE, v uintptr) {
if n > 0 {
recurseHANDLE(n-1, p, v)
}
if uintptr(unsafe.Pointer(p)) != v {
panic("adjusted notinheap pointer")
}
}
func recurseHWND(n int, p C.HWND, v uintptr) {
if n > 0 {
recurseHWND(n-1, p, v)
}
if uintptr(unsafe.Pointer(p)) != v {
panic("adjusted notinheap pointer")
}
}

9
misc/cgo/test/testdata/issue43639.go vendored Normal file
View File

@ -0,0 +1,9 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cgotest
// Issue 43639: No runtime test needed, make sure package cgotest/issue43639 compiles well.
import _ "cgotest/issue43639"

View File

@ -0,0 +1,8 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package issue43639
// #cgo CFLAGS: -W -Wall -Werror
import "C"

View File

@ -0,0 +1,17 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cgotest
// #include <stddef.h>
import "C"
func generic[T, U any](t T, u U) {}
func useGeneric() {
const zero C.size_t = 0
generic(zero, zero)
generic[C.size_t, C.size_t](0, 0)
}

View File

@ -931,3 +931,55 @@ func TestManyCalls(t *testing.T) {
t.Error(err) t.Error(err)
} }
} }
// Issue 49288.
func TestPreemption(t *testing.T) {
if runtime.Compiler == "gccgo" {
t.Skip("skipping asynchronous preemption test with gccgo")
}
t.Parallel()
if !testWork {
defer func() {
os.Remove("testp8" + exeSuffix)
os.Remove("libgo8.a")
os.Remove("libgo8.h")
}()
}
cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo8.a", "./libgo8")
if out, err := cmd.CombinedOutput(); err != nil {
t.Logf("%s", out)
t.Fatal(err)
}
checkLineComments(t, "libgo8.h")
ccArgs := append(cc, "-o", "testp8"+exeSuffix, "main8.c", "libgo8.a")
if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
t.Logf("%s", out)
t.Fatal(err)
}
argv := cmdToRun("./testp8")
cmd = exec.Command(argv[0], argv[1:]...)
var sb strings.Builder
cmd.Stdout = &sb
cmd.Stderr = &sb
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
timer := time.AfterFunc(time.Minute,
func() {
t.Error("test program timed out")
cmd.Process.Kill()
},
)
defer timer.Stop()
if err := cmd.Wait(); err != nil {
t.Log(sb.String())
t.Error(err)
}
}

View File

@ -0,0 +1,36 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import "C"
import (
"os"
"runtime"
"sync/atomic"
)
var started int32
// Start a goroutine that loops forever.
func init() {
runtime.GOMAXPROCS(1)
go func() {
for {
atomic.StoreInt32(&started, 1)
}
}()
}
//export GoFunction8
func GoFunction8() {
for atomic.LoadInt32(&started) == 0 {
runtime.Gosched()
}
os.Exit(0)
}
func main() {
}

16
misc/cgo/testcarchive/testdata/main8.c vendored Normal file
View File

@ -0,0 +1,16 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Test preemption.
#include <stdlib.h>
#include "libgo8.h"
int main() {
GoFunction8();
// That should have exited the program.
abort();
}

View File

@ -200,7 +200,7 @@ func adbRun(t *testing.T, env []string, adbargs ...string) string {
args := append(adbCmd(), "exec-out") args := append(adbCmd(), "exec-out")
// Propagate LD_LIBRARY_PATH to the adb shell invocation. // Propagate LD_LIBRARY_PATH to the adb shell invocation.
for _, e := range env { for _, e := range env {
if strings.Index(e, "LD_LIBRARY_PATH=") != -1 { if strings.Contains(e, "LD_LIBRARY_PATH=") {
adbargs = append([]string{e}, adbargs...) adbargs = append([]string{e}, adbargs...)
break break
} }
@ -326,7 +326,7 @@ func createHeaders() error {
base, name := filepath.Split(args[0]) base, name := filepath.Split(args[0])
args[0] = filepath.Join(base, "llvm-dlltool") args[0] = filepath.Join(base, "llvm-dlltool")
var machine string var machine string
switch strings.SplitN(name, "-", 2)[0] { switch prefix, _, _ := strings.Cut(name, "-"); prefix {
case "i686": case "i686":
machine = "i386" machine = "i386"
case "x86_64": case "x86_64":

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux,!arm64 netbsd openbsd // +build darwin dragonfly freebsd linux,!arm64,!riscv64 netbsd openbsd
package main package main

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build linux,arm64 // +build linux,arm64 linux,riscv64
package main package main

View File

@ -0,0 +1,18 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// +build ignore
package main
/*
// from <linux/kcm.h>
struct issue48396 {
int fd;
int bpf_fd;
};
*/
import "C"
type Issue48396 C.struct_issue48396

View File

@ -28,6 +28,9 @@ var v7 = S{}
// Test that #define'd type is fully defined // Test that #define'd type is fully defined
var _ = issue38649{X: 0} var _ = issue38649{X: 0}
// Test that prefixes do not cause duplicate field names.
var _ = Issue48396{Fd: 1, Bpf_fd: 2}
func main() { func main() {
pass := true pass := true

View File

@ -25,6 +25,7 @@ var filePrefixes = []string{
"issue37621", "issue37621",
"issue38649", "issue38649",
"issue39534", "issue39534",
"issue48396",
} }
func TestGoDefs(t *testing.T) { func TestGoDefs(t *testing.T) {

View File

@ -0,0 +1,66 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sanitizers_test
import (
"strings"
"testing"
)
func TestASAN(t *testing.T) {
goos, err := goEnv("GOOS")
if err != nil {
t.Fatal(err)
}
goarch, err := goEnv("GOARCH")
if err != nil {
t.Fatal(err)
}
// The asan tests require support for the -asan option.
if !aSanSupported(goos, goarch) {
t.Skipf("skipping on %s/%s; -asan option is not supported.", goos, goarch)
}
t.Parallel()
requireOvercommit(t)
config := configure("address")
config.skipIfCSanitizerBroken(t)
mustRun(t, config.goCmd("build", "std"))
cases := []struct {
src string
memoryAccessError string
}{
{src: "asan1_fail.go", memoryAccessError: "heap-use-after-free"},
{src: "asan2_fail.go", memoryAccessError: "heap-buffer-overflow"},
{src: "asan3_fail.go", memoryAccessError: "use-after-poison"},
{src: "asan4_fail.go", memoryAccessError: "use-after-poison"},
{src: "asan_useAfterReturn.go"},
}
for _, tc := range cases {
tc := tc
name := strings.TrimSuffix(tc.src, ".go")
t.Run(name, func(t *testing.T) {
t.Parallel()
dir := newTempDir(t)
defer dir.RemoveAll(t)
outPath := dir.Join(name)
mustRun(t, config.goCmd("build", "-o", outPath, srcPath(tc.src)))
cmd := hangProneCmd(outPath)
if tc.memoryAccessError != "" {
out, err := cmd.CombinedOutput()
if err != nil && strings.Contains(string(out), tc.memoryAccessError) {
return
}
t.Fatalf("%#q exited without expected memory access error\n%s; got failure\n%s", strings.Join(cmd.Args, " "), tc.memoryAccessError, out)
}
mustRun(t, cmd)
})
}
}

View File

@ -267,6 +267,9 @@ func configure(sanitizer string) *config {
c.ldFlags = append(c.ldFlags, "-fPIC", "-static-libtsan") c.ldFlags = append(c.ldFlags, "-fPIC", "-static-libtsan")
} }
case "address":
c.goFlags = append(c.goFlags, "-asan")
default: default:
panic(fmt.Sprintf("unrecognized sanitizer: %q", sanitizer)) panic(fmt.Sprintf("unrecognized sanitizer: %q", sanitizer))
} }
@ -344,7 +347,7 @@ func (c *config) checkCSanitizer() (skip bool, err error) {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return true, fmt.Errorf("%#q failed to produce executable: %v", strings.Join(cmd.Args, " "), err) return true, fmt.Errorf("%#q failed to produce executable: %v", strings.Join(cmd.Args, " "), err)
} }
snippet := bytes.SplitN(out, []byte{'\n'}, 2)[0] snippet, _, _ := bytes.Cut(out, []byte("\n"))
return true, fmt.Errorf("%#q generated broken executable: %v\n%s", strings.Join(cmd.Args, " "), err, snippet) return true, fmt.Errorf("%#q generated broken executable: %v\n%s", strings.Join(cmd.Args, " "), err, snippet)
} }
@ -450,3 +453,14 @@ func mSanSupported(goos, goarch string) bool {
return false return false
} }
} }
// aSanSupported is a copy of the function cmd/internal/sys.ASanSupported,
// because the internal pacakage can't be used here.
func aSanSupported(goos, goarch string) bool {
switch goos {
case "linux":
return goarch == "amd64" || goarch == "arm64"
default:
return false
}
}

View File

@ -0,0 +1,28 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
/*
#include <stdlib.h>
#include <stdio.h>
int *p;
int* test() {
p = (int *)malloc(2 * sizeof(int));
free(p);
return p;
}
*/
import "C"
import "fmt"
func main() {
// C passes Go an invalid pointer.
a := C.test()
// Use after free
*a = 2
// We shouldn't get here; asan should stop us first.
fmt.Println(*a)
}

View File

@ -0,0 +1,34 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
/*
#include <stdlib.h>
#include <stdio.h>
int *p;
int* f() {
int i;
p = (int *)malloc(5*sizeof(int));
for (i = 0; i < 5; i++) {
p[i] = i+10;
}
return p;
}
*/
import "C"
import (
"fmt"
"unsafe"
)
func main() {
a := C.f()
q5 := (*C.int)(unsafe.Add(unsafe.Pointer(a), 4*5))
// Access to C pointer out of bounds.
*q5 = 100
// We shouldn't get here; asan should stop us first.
fmt.Printf("q5: %d, %x\n", *q5, q5)
}

View File

@ -0,0 +1,23 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
/*
#include <stdlib.h>
#include <stdio.h>
void test(int *a) {
// Access Go pointer out of bounds.
int c = a[5]; // BOOM
// We shouldn't get here; asan should stop us first.
printf("a[5]=%d\n", c);
}
*/
import "C"
func main() {
cIntSlice := []C.int{200, 201, 203, 203, 204}
C.test(&cIntSlice[0])
}

View File

@ -0,0 +1,22 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
/*
#include <stdlib.h>
#include <stdio.h>
void test(int* a) {
// Access Go pointer out of bounds.
a[3] = 300; // BOOM
// We shouldn't get here; asan should stop us first.
printf("a[3]=%d\n", a[3]);
}*/
import "C"
func main() {
var cIntArray [2]C.int
C.test(&cIntArray[0]) // cIntArray is moved to heap.
}

View File

@ -0,0 +1,26 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
// The -fsanitize=address option of C compier can detect stack-use-after-return bugs.
// In the following program, the local variable 'local' was moved to heap by the Go
// compiler because foo() is returning the reference to 'local', and return stack of
// foo() will be invalid. Thus for main() to use the reference to 'local', the 'local'
// must be available even after foo() has finished. Therefore, Go has no such issue.
import "fmt"
var ptr *int
func main() {
foo()
fmt.Printf("ptr=%x, %v", *ptr, ptr)
}
func foo() {
var local int
local = 1
ptr = &local // local is moved to heap.
}

View File

@ -1033,7 +1033,7 @@ func TestGlobal(t *testing.T) {
// Run a test using -linkshared of an installed shared package. // Run a test using -linkshared of an installed shared package.
// Issue 26400. // Issue 26400.
func TestTestInstalledShared(t *testing.T) { func TestTestInstalledShared(t *testing.T) {
goCmd(nil, "test", "-linkshared", "-test.short", "sync/atomic") goCmd(t, "test", "-linkshared", "-test.short", "sync/atomic")
} }
// Test generated pointer method with -linkshared. // Test generated pointer method with -linkshared.
@ -1045,8 +1045,8 @@ func TestGeneratedMethod(t *testing.T) {
// Test use of shared library struct with generated hash function. // Test use of shared library struct with generated hash function.
// Issue 30768. // Issue 30768.
func TestGeneratedHash(t *testing.T) { func TestGeneratedHash(t *testing.T) {
goCmd(nil, "install", "-buildmode=shared", "-linkshared", "./issue30768/issue30768lib") goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue30768/issue30768lib")
goCmd(nil, "test", "-linkshared", "./issue30768") goCmd(t, "test", "-linkshared", "./issue30768")
} }
// Test that packages can be added not in dependency order (here a depends on b, and a adds // Test that packages can be added not in dependency order (here a depends on b, and a adds
@ -1070,3 +1070,11 @@ func TestIssue44031(t *testing.T) {
goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue44031/b") goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue44031/b")
goCmd(t, "run", "-linkshared", "./issue44031/main") goCmd(t, "run", "-linkshared", "./issue44031/main")
} }
// Test that we use a variable from shared libraries (which implement an
// interface in shared libraries.). A weak reference is used in the itab
// in main process. It can cause unreacheble panic. See issue 47873.
func TestIssue47873(t *testing.T) {
goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue47837/a")
goCmd(t, "run", "-linkshared", "./issue47837/main")
}

View File

@ -0,0 +1,19 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package a
type A interface {
M()
}
//go:noinline
func TheFuncWithArgA(a A) {
a.M()
}
type ImplA struct{}
//go:noinline
func (A *ImplA) M() {}

View File

@ -0,0 +1,14 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"testshared/issue47837/a"
)
func main() {
var vara a.ImplA
a.TheFuncWithArgA(&vara)
}

View File

@ -8,4 +8,4 @@
// directory.) // directory.)
module misc module misc
go 1.12 go 1.18

View File

@ -148,9 +148,8 @@ func runOnDevice(appdir string) error {
// Device IDs as listed with ios-deploy -c. // Device IDs as listed with ios-deploy -c.
deviceID = os.Getenv("GOIOS_DEVICE_ID") deviceID = os.Getenv("GOIOS_DEVICE_ID")
parts := strings.SplitN(appID, ".", 2) if _, id, ok := strings.Cut(appID, "."); ok {
if len(parts) == 2 { bundleID = id
bundleID = parts[1]
} }
if err := signApp(appdir); err != nil { if err := signApp(appdir); err != nil {
@ -291,11 +290,10 @@ func findDevImage() (string, error) {
var iosVer, buildVer string var iosVer, buildVer string
lines := bytes.Split(out, []byte("\n")) lines := bytes.Split(out, []byte("\n"))
for _, line := range lines { for _, line := range lines {
spl := bytes.SplitN(line, []byte(": "), 2) key, val, ok := strings.Cut(string(line), ": ")
if len(spl) != 2 { if !ok {
continue continue
} }
key, val := string(spl[0]), string(spl[1])
switch key { switch key {
case "ProductVersion": case "ProductVersion":
iosVer = val iosVer = val

View File

@ -81,10 +81,8 @@ func crawl(url string, sourceURL string) {
} }
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
var frag string if u, frag, ok := strings.Cut(url, "#"); ok {
if i := strings.Index(url, "#"); i >= 0 { url = u
frag = url[i+1:]
url = url[:i]
if frag != "" { if frag != "" {
uf := urlFrag{url, frag} uf := urlFrag{url, frag}
neededFrags[uf] = append(neededFrags[uf], sourceURL) neededFrags[uf] = append(neededFrags[uf], sourceURL)

View File

@ -11,4 +11,4 @@ while [ -h "$SOURCE" ]; do
done done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
exec node "$DIR/wasm_exec.js" "$@" exec node "$DIR/wasm_exec_node.js" "$@"

View File

@ -2,47 +2,18 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
"use strict";
(() => { (() => {
// Map multiple JavaScript environments to a single common API,
// preferring web standards over Node.js API.
//
// Environments considered:
// - Browsers
// - Node.js
// - Electron
// - Parcel
// - Webpack
if (typeof global !== "undefined") {
// global already exists
} else if (typeof window !== "undefined") {
window.global = window;
} else if (typeof self !== "undefined") {
self.global = self;
} else {
throw new Error("cannot export Go (neither global, window nor self is defined)");
}
if (!global.require && typeof require !== "undefined") {
global.require = require;
}
if (!global.fs && global.require) {
const fs = require("fs");
if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) {
global.fs = fs;
}
}
const enosys = () => { const enosys = () => {
const err = new Error("not implemented"); const err = new Error("not implemented");
err.code = "ENOSYS"; err.code = "ENOSYS";
return err; return err;
}; };
if (!global.fs) { if (!globalThis.fs) {
let outputBuf = ""; let outputBuf = "";
global.fs = { globalThis.fs = {
constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
writeSync(fd, buf) { writeSync(fd, buf) {
outputBuf += decoder.decode(buf); outputBuf += decoder.decode(buf);
@ -87,8 +58,8 @@
}; };
} }
if (!global.process) { if (!globalThis.process) {
global.process = { globalThis.process = {
getuid() { return -1; }, getuid() { return -1; },
getgid() { return -1; }, getgid() { return -1; },
geteuid() { return -1; }, geteuid() { return -1; },
@ -102,47 +73,26 @@
} }
} }
if (!global.crypto && global.require) { if (!globalThis.crypto) {
const nodeCrypto = require("crypto"); throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)");
global.crypto = {
getRandomValues(b) {
nodeCrypto.randomFillSync(b);
},
};
}
if (!global.crypto) {
throw new Error("global.crypto is not available, polyfill required (getRandomValues only)");
} }
if (!global.performance) { if (!globalThis.performance) {
global.performance = { throw new Error("globalThis.performance is not available, polyfill required (performance.now only)");
now() {
const [sec, nsec] = process.hrtime();
return sec * 1000 + nsec / 1000000;
},
};
} }
if (!global.TextEncoder && global.require) { if (!globalThis.TextEncoder) {
global.TextEncoder = require("util").TextEncoder; throw new Error("globalThis.TextEncoder is not available, polyfill required");
}
if (!global.TextEncoder) {
throw new Error("global.TextEncoder is not available, polyfill required");
} }
if (!global.TextDecoder && global.require) { if (!globalThis.TextDecoder) {
global.TextDecoder = require("util").TextDecoder; throw new Error("globalThis.TextDecoder is not available, polyfill required");
} }
if (!global.TextDecoder) {
throw new Error("global.TextDecoder is not available, polyfill required");
}
// End of polyfills for common API.
const encoder = new TextEncoder("utf-8"); const encoder = new TextEncoder("utf-8");
const decoder = new TextDecoder("utf-8"); const decoder = new TextDecoder("utf-8");
global.Go = class { globalThis.Go = class {
constructor() { constructor() {
this.argv = ["js"]; this.argv = ["js"];
this.env = {}; this.env = {};
@ -517,7 +467,7 @@
null, null,
true, true,
false, false,
global, globalThis,
this, this,
]; ];
this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
@ -526,7 +476,7 @@
[null, 2], [null, 2],
[true, 3], [true, 3],
[false, 4], [false, 4],
[global, 5], [globalThis, 5],
[this, 6], [this, 6],
]); ]);
this._idPool = []; // unused ids that have been garbage collected this._idPool = []; // unused ids that have been garbage collected
@ -567,6 +517,13 @@
offset += 8; offset += 8;
}); });
// The linker guarantees global data starts from at least wasmMinDataAddr.
// Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
const wasmMinDataAddr = 4096 + 8192;
if (offset >= wasmMinDataAddr) {
throw new Error("total length of command line and environment variables exceeds limit");
}
this._inst.exports.run(argc, argv); this._inst.exports.run(argc, argv);
if (this.exited) { if (this.exited) {
this._resolveExitPromise(); this._resolveExitPromise();
@ -594,36 +551,4 @@
}; };
} }
} }
if (
typeof module !== "undefined" &&
global.require &&
global.require.main === module &&
global.process &&
global.process.versions &&
!global.process.versions.electron
) {
if (process.argv.length < 3) {
console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
process.exit(1);
}
const go = new Go();
go.argv = process.argv.slice(2);
go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env);
go.exit = process.exit;
WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
process.on("exit", (code) => { // Node.js exits if no event handler is pending
if (code === 0 && !go.exited) {
// deadlock, make Go print error and stack traces
go._pendingEvent = { id: 0 };
go._resume();
}
});
return go.run(result.instance);
}).catch((err) => {
console.error(err);
process.exit(1);
});
}
})(); })();

View File

@ -0,0 +1,49 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
"use strict";
if (process.argv.length < 3) {
console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
process.exit(1);
}
globalThis.require = require;
globalThis.fs = require("fs");
globalThis.TextEncoder = require("util").TextEncoder;
globalThis.TextDecoder = require("util").TextDecoder;
globalThis.performance = {
now() {
const [sec, nsec] = process.hrtime();
return sec * 1000 + nsec / 1000000;
},
};
const crypto = require("crypto");
globalThis.crypto = {
getRandomValues(b) {
crypto.randomFillSync(b);
},
};
require("./wasm_exec");
const go = new Go();
go.argv = process.argv.slice(2);
go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env);
go.exit = process.exit;
WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
process.on("exit", (code) => { // Node.js exits if no event handler is pending
if (code === 0 && !go.exited) {
// deadlock, make Go print error and stack traces
go._pendingEvent = { id: 0 };
go._resume();
}
});
return go.run(result.instance);
}).catch((err) => {
console.error(err);
process.exit(1);
});

View File

@ -316,10 +316,10 @@ func invertSparseEntries(src []sparseEntry, size int64) []sparseEntry {
// fileState tracks the number of logical (includes sparse holes) and physical // fileState tracks the number of logical (includes sparse holes) and physical
// (actual in tar archive) bytes remaining for the current file. // (actual in tar archive) bytes remaining for the current file.
// //
// Invariant: LogicalRemaining >= PhysicalRemaining // Invariant: logicalRemaining >= physicalRemaining
type fileState interface { type fileState interface {
LogicalRemaining() int64 logicalRemaining() int64
PhysicalRemaining() int64 physicalRemaining() int64
} }
// allowedFormats determines which formats can be used. // allowedFormats determines which formats can be used.
@ -413,22 +413,22 @@ func (h Header) allowedFormats() (format Format, paxHdrs map[string]string, err
// Check basic fields. // Check basic fields.
var blk block var blk block
v7 := blk.V7() v7 := blk.toV7()
ustar := blk.USTAR() ustar := blk.toUSTAR()
gnu := blk.GNU() gnu := blk.toGNU()
verifyString(h.Name, len(v7.Name()), "Name", paxPath) verifyString(h.Name, len(v7.name()), "Name", paxPath)
verifyString(h.Linkname, len(v7.LinkName()), "Linkname", paxLinkpath) verifyString(h.Linkname, len(v7.linkName()), "Linkname", paxLinkpath)
verifyString(h.Uname, len(ustar.UserName()), "Uname", paxUname) verifyString(h.Uname, len(ustar.userName()), "Uname", paxUname)
verifyString(h.Gname, len(ustar.GroupName()), "Gname", paxGname) verifyString(h.Gname, len(ustar.groupName()), "Gname", paxGname)
verifyNumeric(h.Mode, len(v7.Mode()), "Mode", paxNone) verifyNumeric(h.Mode, len(v7.mode()), "Mode", paxNone)
verifyNumeric(int64(h.Uid), len(v7.UID()), "Uid", paxUid) verifyNumeric(int64(h.Uid), len(v7.uid()), "Uid", paxUid)
verifyNumeric(int64(h.Gid), len(v7.GID()), "Gid", paxGid) verifyNumeric(int64(h.Gid), len(v7.gid()), "Gid", paxGid)
verifyNumeric(h.Size, len(v7.Size()), "Size", paxSize) verifyNumeric(h.Size, len(v7.size()), "Size", paxSize)
verifyNumeric(h.Devmajor, len(ustar.DevMajor()), "Devmajor", paxNone) verifyNumeric(h.Devmajor, len(ustar.devMajor()), "Devmajor", paxNone)
verifyNumeric(h.Devminor, len(ustar.DevMinor()), "Devminor", paxNone) verifyNumeric(h.Devminor, len(ustar.devMinor()), "Devminor", paxNone)
verifyTime(h.ModTime, len(v7.ModTime()), "ModTime", paxMtime) verifyTime(h.ModTime, len(v7.modTime()), "ModTime", paxMtime)
verifyTime(h.AccessTime, len(gnu.AccessTime()), "AccessTime", paxAtime) verifyTime(h.AccessTime, len(gnu.accessTime()), "AccessTime", paxAtime)
verifyTime(h.ChangeTime, len(gnu.ChangeTime()), "ChangeTime", paxCtime) verifyTime(h.ChangeTime, len(gnu.changeTime()), "ChangeTime", paxCtime)
// Check for header-only types. // Check for header-only types.
var whyOnlyPAX, whyOnlyGNU string var whyOnlyPAX, whyOnlyGNU string

View File

@ -156,28 +156,28 @@ var zeroBlock block
type block [blockSize]byte type block [blockSize]byte
// Convert block to any number of formats. // Convert block to any number of formats.
func (b *block) V7() *headerV7 { return (*headerV7)(b) } func (b *block) toV7() *headerV7 { return (*headerV7)(b) }
func (b *block) GNU() *headerGNU { return (*headerGNU)(b) } func (b *block) toGNU() *headerGNU { return (*headerGNU)(b) }
func (b *block) STAR() *headerSTAR { return (*headerSTAR)(b) } func (b *block) toSTAR() *headerSTAR { return (*headerSTAR)(b) }
func (b *block) USTAR() *headerUSTAR { return (*headerUSTAR)(b) } func (b *block) toUSTAR() *headerUSTAR { return (*headerUSTAR)(b) }
func (b *block) Sparse() sparseArray { return sparseArray(b[:]) } func (b *block) toSparse() sparseArray { return sparseArray(b[:]) }
// GetFormat checks that the block is a valid tar header based on the checksum. // GetFormat checks that the block is a valid tar header based on the checksum.
// It then attempts to guess the specific format based on magic values. // It then attempts to guess the specific format based on magic values.
// If the checksum fails, then FormatUnknown is returned. // If the checksum fails, then FormatUnknown is returned.
func (b *block) GetFormat() Format { func (b *block) getFormat() Format {
// Verify checksum. // Verify checksum.
var p parser var p parser
value := p.parseOctal(b.V7().Chksum()) value := p.parseOctal(b.toV7().chksum())
chksum1, chksum2 := b.ComputeChecksum() chksum1, chksum2 := b.computeChecksum()
if p.err != nil || (value != chksum1 && value != chksum2) { if p.err != nil || (value != chksum1 && value != chksum2) {
return FormatUnknown return FormatUnknown
} }
// Guess the magic values. // Guess the magic values.
magic := string(b.USTAR().Magic()) magic := string(b.toUSTAR().magic())
version := string(b.USTAR().Version()) version := string(b.toUSTAR().version())
trailer := string(b.STAR().Trailer()) trailer := string(b.toSTAR().trailer())
switch { switch {
case magic == magicUSTAR && trailer == trailerSTAR: case magic == magicUSTAR && trailer == trailerSTAR:
return formatSTAR return formatSTAR
@ -190,23 +190,23 @@ func (b *block) GetFormat() Format {
} }
} }
// SetFormat writes the magic values necessary for specified format // setFormat writes the magic values necessary for specified format
// and then updates the checksum accordingly. // and then updates the checksum accordingly.
func (b *block) SetFormat(format Format) { func (b *block) setFormat(format Format) {
// Set the magic values. // Set the magic values.
switch { switch {
case format.has(formatV7): case format.has(formatV7):
// Do nothing. // Do nothing.
case format.has(FormatGNU): case format.has(FormatGNU):
copy(b.GNU().Magic(), magicGNU) copy(b.toGNU().magic(), magicGNU)
copy(b.GNU().Version(), versionGNU) copy(b.toGNU().version(), versionGNU)
case format.has(formatSTAR): case format.has(formatSTAR):
copy(b.STAR().Magic(), magicUSTAR) copy(b.toSTAR().magic(), magicUSTAR)
copy(b.STAR().Version(), versionUSTAR) copy(b.toSTAR().version(), versionUSTAR)
copy(b.STAR().Trailer(), trailerSTAR) copy(b.toSTAR().trailer(), trailerSTAR)
case format.has(FormatUSTAR | FormatPAX): case format.has(FormatUSTAR | FormatPAX):
copy(b.USTAR().Magic(), magicUSTAR) copy(b.toUSTAR().magic(), magicUSTAR)
copy(b.USTAR().Version(), versionUSTAR) copy(b.toUSTAR().version(), versionUSTAR)
default: default:
panic("invalid format") panic("invalid format")
} }
@ -214,17 +214,17 @@ func (b *block) SetFormat(format Format) {
// Update checksum. // Update checksum.
// This field is special in that it is terminated by a NULL then space. // This field is special in that it is terminated by a NULL then space.
var f formatter var f formatter
field := b.V7().Chksum() field := b.toV7().chksum()
chksum, _ := b.ComputeChecksum() // Possible values are 256..128776 chksum, _ := b.computeChecksum() // Possible values are 256..128776
f.formatOctal(field[:7], chksum) // Never fails since 128776 < 262143 f.formatOctal(field[:7], chksum) // Never fails since 128776 < 262143
field[7] = ' ' field[7] = ' '
} }
// ComputeChecksum computes the checksum for the header block. // computeChecksum computes the checksum for the header block.
// POSIX specifies a sum of the unsigned byte values, but the Sun tar used // POSIX specifies a sum of the unsigned byte values, but the Sun tar used
// signed byte values. // signed byte values.
// We compute and return both. // We compute and return both.
func (b *block) ComputeChecksum() (unsigned, signed int64) { func (b *block) computeChecksum() (unsigned, signed int64) {
for i, c := range b { for i, c := range b {
if 148 <= i && i < 156 { if 148 <= i && i < 156 {
c = ' ' // Treat the checksum field itself as all spaces. c = ' ' // Treat the checksum field itself as all spaces.
@ -236,68 +236,68 @@ func (b *block) ComputeChecksum() (unsigned, signed int64) {
} }
// Reset clears the block with all zeros. // Reset clears the block with all zeros.
func (b *block) Reset() { func (b *block) reset() {
*b = block{} *b = block{}
} }
type headerV7 [blockSize]byte type headerV7 [blockSize]byte
func (h *headerV7) Name() []byte { return h[000:][:100] } func (h *headerV7) name() []byte { return h[000:][:100] }
func (h *headerV7) Mode() []byte { return h[100:][:8] } func (h *headerV7) mode() []byte { return h[100:][:8] }
func (h *headerV7) UID() []byte { return h[108:][:8] } func (h *headerV7) uid() []byte { return h[108:][:8] }
func (h *headerV7) GID() []byte { return h[116:][:8] } func (h *headerV7) gid() []byte { return h[116:][:8] }
func (h *headerV7) Size() []byte { return h[124:][:12] } func (h *headerV7) size() []byte { return h[124:][:12] }
func (h *headerV7) ModTime() []byte { return h[136:][:12] } func (h *headerV7) modTime() []byte { return h[136:][:12] }
func (h *headerV7) Chksum() []byte { return h[148:][:8] } func (h *headerV7) chksum() []byte { return h[148:][:8] }
func (h *headerV7) TypeFlag() []byte { return h[156:][:1] } func (h *headerV7) typeFlag() []byte { return h[156:][:1] }
func (h *headerV7) LinkName() []byte { return h[157:][:100] } func (h *headerV7) linkName() []byte { return h[157:][:100] }
type headerGNU [blockSize]byte type headerGNU [blockSize]byte
func (h *headerGNU) V7() *headerV7 { return (*headerV7)(h) } func (h *headerGNU) v7() *headerV7 { return (*headerV7)(h) }
func (h *headerGNU) Magic() []byte { return h[257:][:6] } func (h *headerGNU) magic() []byte { return h[257:][:6] }
func (h *headerGNU) Version() []byte { return h[263:][:2] } func (h *headerGNU) version() []byte { return h[263:][:2] }
func (h *headerGNU) UserName() []byte { return h[265:][:32] } func (h *headerGNU) userName() []byte { return h[265:][:32] }
func (h *headerGNU) GroupName() []byte { return h[297:][:32] } func (h *headerGNU) groupName() []byte { return h[297:][:32] }
func (h *headerGNU) DevMajor() []byte { return h[329:][:8] } func (h *headerGNU) devMajor() []byte { return h[329:][:8] }
func (h *headerGNU) DevMinor() []byte { return h[337:][:8] } func (h *headerGNU) devMinor() []byte { return h[337:][:8] }
func (h *headerGNU) AccessTime() []byte { return h[345:][:12] } func (h *headerGNU) accessTime() []byte { return h[345:][:12] }
func (h *headerGNU) ChangeTime() []byte { return h[357:][:12] } func (h *headerGNU) changeTime() []byte { return h[357:][:12] }
func (h *headerGNU) Sparse() sparseArray { return sparseArray(h[386:][:24*4+1]) } func (h *headerGNU) sparse() sparseArray { return sparseArray(h[386:][:24*4+1]) }
func (h *headerGNU) RealSize() []byte { return h[483:][:12] } func (h *headerGNU) realSize() []byte { return h[483:][:12] }
type headerSTAR [blockSize]byte type headerSTAR [blockSize]byte
func (h *headerSTAR) V7() *headerV7 { return (*headerV7)(h) } func (h *headerSTAR) v7() *headerV7 { return (*headerV7)(h) }
func (h *headerSTAR) Magic() []byte { return h[257:][:6] } func (h *headerSTAR) magic() []byte { return h[257:][:6] }
func (h *headerSTAR) Version() []byte { return h[263:][:2] } func (h *headerSTAR) version() []byte { return h[263:][:2] }
func (h *headerSTAR) UserName() []byte { return h[265:][:32] } func (h *headerSTAR) userName() []byte { return h[265:][:32] }
func (h *headerSTAR) GroupName() []byte { return h[297:][:32] } func (h *headerSTAR) groupName() []byte { return h[297:][:32] }
func (h *headerSTAR) DevMajor() []byte { return h[329:][:8] } func (h *headerSTAR) devMajor() []byte { return h[329:][:8] }
func (h *headerSTAR) DevMinor() []byte { return h[337:][:8] } func (h *headerSTAR) devMinor() []byte { return h[337:][:8] }
func (h *headerSTAR) Prefix() []byte { return h[345:][:131] } func (h *headerSTAR) prefix() []byte { return h[345:][:131] }
func (h *headerSTAR) AccessTime() []byte { return h[476:][:12] } func (h *headerSTAR) accessTime() []byte { return h[476:][:12] }
func (h *headerSTAR) ChangeTime() []byte { return h[488:][:12] } func (h *headerSTAR) changeTime() []byte { return h[488:][:12] }
func (h *headerSTAR) Trailer() []byte { return h[508:][:4] } func (h *headerSTAR) trailer() []byte { return h[508:][:4] }
type headerUSTAR [blockSize]byte type headerUSTAR [blockSize]byte
func (h *headerUSTAR) V7() *headerV7 { return (*headerV7)(h) } func (h *headerUSTAR) v7() *headerV7 { return (*headerV7)(h) }
func (h *headerUSTAR) Magic() []byte { return h[257:][:6] } func (h *headerUSTAR) magic() []byte { return h[257:][:6] }
func (h *headerUSTAR) Version() []byte { return h[263:][:2] } func (h *headerUSTAR) version() []byte { return h[263:][:2] }
func (h *headerUSTAR) UserName() []byte { return h[265:][:32] } func (h *headerUSTAR) userName() []byte { return h[265:][:32] }
func (h *headerUSTAR) GroupName() []byte { return h[297:][:32] } func (h *headerUSTAR) groupName() []byte { return h[297:][:32] }
func (h *headerUSTAR) DevMajor() []byte { return h[329:][:8] } func (h *headerUSTAR) devMajor() []byte { return h[329:][:8] }
func (h *headerUSTAR) DevMinor() []byte { return h[337:][:8] } func (h *headerUSTAR) devMinor() []byte { return h[337:][:8] }
func (h *headerUSTAR) Prefix() []byte { return h[345:][:155] } func (h *headerUSTAR) prefix() []byte { return h[345:][:155] }
type sparseArray []byte type sparseArray []byte
func (s sparseArray) Entry(i int) sparseElem { return sparseElem(s[i*24:]) } func (s sparseArray) entry(i int) sparseElem { return sparseElem(s[i*24:]) }
func (s sparseArray) IsExtended() []byte { return s[24*s.MaxEntries():][:1] } func (s sparseArray) isExtended() []byte { return s[24*s.maxEntries():][:1] }
func (s sparseArray) MaxEntries() int { return len(s) / 24 } func (s sparseArray) maxEntries() int { return len(s) / 24 }
type sparseElem []byte type sparseElem []byte
func (s sparseElem) Offset() []byte { return s[00:][:12] } func (s sparseElem) offset() []byte { return s[00:][:12] }
func (s sparseElem) Length() []byte { return s[12:][:12] } func (s sparseElem) length() []byte { return s[12:][:12] }

View File

@ -65,7 +65,7 @@ func (tr *Reader) next() (*Header, error) {
format := FormatUSTAR | FormatPAX | FormatGNU format := FormatUSTAR | FormatPAX | FormatGNU
for { for {
// Discard the remainder of the file and any padding. // Discard the remainder of the file and any padding.
if err := discard(tr.r, tr.curr.PhysicalRemaining()); err != nil { if err := discard(tr.r, tr.curr.physicalRemaining()); err != nil {
return nil, err return nil, err
} }
if _, err := tryReadFull(tr.r, tr.blk[:tr.pad]); err != nil { if _, err := tryReadFull(tr.r, tr.blk[:tr.pad]); err != nil {
@ -355,7 +355,7 @@ func (tr *Reader) readHeader() (*Header, *block, error) {
} }
// Verify the header matches a known format. // Verify the header matches a known format.
format := tr.blk.GetFormat() format := tr.blk.getFormat()
if format == FormatUnknown { if format == FormatUnknown {
return nil, nil, ErrHeader return nil, nil, ErrHeader
} }
@ -364,30 +364,30 @@ func (tr *Reader) readHeader() (*Header, *block, error) {
hdr := new(Header) hdr := new(Header)
// Unpack the V7 header. // Unpack the V7 header.
v7 := tr.blk.V7() v7 := tr.blk.toV7()
hdr.Typeflag = v7.TypeFlag()[0] hdr.Typeflag = v7.typeFlag()[0]
hdr.Name = p.parseString(v7.Name()) hdr.Name = p.parseString(v7.name())
hdr.Linkname = p.parseString(v7.LinkName()) hdr.Linkname = p.parseString(v7.linkName())
hdr.Size = p.parseNumeric(v7.Size()) hdr.Size = p.parseNumeric(v7.size())
hdr.Mode = p.parseNumeric(v7.Mode()) hdr.Mode = p.parseNumeric(v7.mode())
hdr.Uid = int(p.parseNumeric(v7.UID())) hdr.Uid = int(p.parseNumeric(v7.uid()))
hdr.Gid = int(p.parseNumeric(v7.GID())) hdr.Gid = int(p.parseNumeric(v7.gid()))
hdr.ModTime = time.Unix(p.parseNumeric(v7.ModTime()), 0) hdr.ModTime = time.Unix(p.parseNumeric(v7.modTime()), 0)
// Unpack format specific fields. // Unpack format specific fields.
if format > formatV7 { if format > formatV7 {
ustar := tr.blk.USTAR() ustar := tr.blk.toUSTAR()
hdr.Uname = p.parseString(ustar.UserName()) hdr.Uname = p.parseString(ustar.userName())
hdr.Gname = p.parseString(ustar.GroupName()) hdr.Gname = p.parseString(ustar.groupName())
hdr.Devmajor = p.parseNumeric(ustar.DevMajor()) hdr.Devmajor = p.parseNumeric(ustar.devMajor())
hdr.Devminor = p.parseNumeric(ustar.DevMinor()) hdr.Devminor = p.parseNumeric(ustar.devMinor())
var prefix string var prefix string
switch { switch {
case format.has(FormatUSTAR | FormatPAX): case format.has(FormatUSTAR | FormatPAX):
hdr.Format = format hdr.Format = format
ustar := tr.blk.USTAR() ustar := tr.blk.toUSTAR()
prefix = p.parseString(ustar.Prefix()) prefix = p.parseString(ustar.prefix())
// For Format detection, check if block is properly formatted since // For Format detection, check if block is properly formatted since
// the parser is more liberal than what USTAR actually permits. // the parser is more liberal than what USTAR actually permits.
@ -396,23 +396,23 @@ func (tr *Reader) readHeader() (*Header, *block, error) {
hdr.Format = FormatUnknown // Non-ASCII characters in block. hdr.Format = FormatUnknown // Non-ASCII characters in block.
} }
nul := func(b []byte) bool { return int(b[len(b)-1]) == 0 } nul := func(b []byte) bool { return int(b[len(b)-1]) == 0 }
if !(nul(v7.Size()) && nul(v7.Mode()) && nul(v7.UID()) && nul(v7.GID()) && if !(nul(v7.size()) && nul(v7.mode()) && nul(v7.uid()) && nul(v7.gid()) &&
nul(v7.ModTime()) && nul(ustar.DevMajor()) && nul(ustar.DevMinor())) { nul(v7.modTime()) && nul(ustar.devMajor()) && nul(ustar.devMinor())) {
hdr.Format = FormatUnknown // Numeric fields must end in NUL hdr.Format = FormatUnknown // Numeric fields must end in NUL
} }
case format.has(formatSTAR): case format.has(formatSTAR):
star := tr.blk.STAR() star := tr.blk.toSTAR()
prefix = p.parseString(star.Prefix()) prefix = p.parseString(star.prefix())
hdr.AccessTime = time.Unix(p.parseNumeric(star.AccessTime()), 0) hdr.AccessTime = time.Unix(p.parseNumeric(star.accessTime()), 0)
hdr.ChangeTime = time.Unix(p.parseNumeric(star.ChangeTime()), 0) hdr.ChangeTime = time.Unix(p.parseNumeric(star.changeTime()), 0)
case format.has(FormatGNU): case format.has(FormatGNU):
hdr.Format = format hdr.Format = format
var p2 parser var p2 parser
gnu := tr.blk.GNU() gnu := tr.blk.toGNU()
if b := gnu.AccessTime(); b[0] != 0 { if b := gnu.accessTime(); b[0] != 0 {
hdr.AccessTime = time.Unix(p2.parseNumeric(b), 0) hdr.AccessTime = time.Unix(p2.parseNumeric(b), 0)
} }
if b := gnu.ChangeTime(); b[0] != 0 { if b := gnu.changeTime(); b[0] != 0 {
hdr.ChangeTime = time.Unix(p2.parseNumeric(b), 0) hdr.ChangeTime = time.Unix(p2.parseNumeric(b), 0)
} }
@ -439,8 +439,8 @@ func (tr *Reader) readHeader() (*Header, *block, error) {
// See https://golang.org/issues/21005 // See https://golang.org/issues/21005
if p2.err != nil { if p2.err != nil {
hdr.AccessTime, hdr.ChangeTime = time.Time{}, time.Time{} hdr.AccessTime, hdr.ChangeTime = time.Time{}, time.Time{}
ustar := tr.blk.USTAR() ustar := tr.blk.toUSTAR()
if s := p.parseString(ustar.Prefix()); isASCII(s) { if s := p.parseString(ustar.prefix()); isASCII(s) {
prefix = s prefix = s
} }
hdr.Format = FormatUnknown // Buggy file is not GNU hdr.Format = FormatUnknown // Buggy file is not GNU
@ -465,38 +465,38 @@ func (tr *Reader) readOldGNUSparseMap(hdr *Header, blk *block) (sparseDatas, err
// Make sure that the input format is GNU. // Make sure that the input format is GNU.
// Unfortunately, the STAR format also has a sparse header format that uses // Unfortunately, the STAR format also has a sparse header format that uses
// the same type flag but has a completely different layout. // the same type flag but has a completely different layout.
if blk.GetFormat() != FormatGNU { if blk.getFormat() != FormatGNU {
return nil, ErrHeader return nil, ErrHeader
} }
hdr.Format.mayOnlyBe(FormatGNU) hdr.Format.mayOnlyBe(FormatGNU)
var p parser var p parser
hdr.Size = p.parseNumeric(blk.GNU().RealSize()) hdr.Size = p.parseNumeric(blk.toGNU().realSize())
if p.err != nil { if p.err != nil {
return nil, p.err return nil, p.err
} }
s := blk.GNU().Sparse() s := blk.toGNU().sparse()
spd := make(sparseDatas, 0, s.MaxEntries()) spd := make(sparseDatas, 0, s.maxEntries())
for { for {
for i := 0; i < s.MaxEntries(); i++ { for i := 0; i < s.maxEntries(); i++ {
// This termination condition is identical to GNU and BSD tar. // This termination condition is identical to GNU and BSD tar.
if s.Entry(i).Offset()[0] == 0x00 { if s.entry(i).offset()[0] == 0x00 {
break // Don't return, need to process extended headers (even if empty) break // Don't return, need to process extended headers (even if empty)
} }
offset := p.parseNumeric(s.Entry(i).Offset()) offset := p.parseNumeric(s.entry(i).offset())
length := p.parseNumeric(s.Entry(i).Length()) length := p.parseNumeric(s.entry(i).length())
if p.err != nil { if p.err != nil {
return nil, p.err return nil, p.err
} }
spd = append(spd, sparseEntry{Offset: offset, Length: length}) spd = append(spd, sparseEntry{Offset: offset, Length: length})
} }
if s.IsExtended()[0] > 0 { if s.isExtended()[0] > 0 {
// There are more entries. Read an extension header and parse its entries. // There are more entries. Read an extension header and parse its entries.
if _, err := mustReadFull(tr.r, blk[:]); err != nil { if _, err := mustReadFull(tr.r, blk[:]); err != nil {
return nil, err return nil, err
} }
s = blk.Sparse() s = blk.toSparse()
continue continue
} }
return spd, nil // Done return spd, nil // Done
@ -678,11 +678,13 @@ func (fr *regFileReader) WriteTo(w io.Writer) (int64, error) {
return io.Copy(w, struct{ io.Reader }{fr}) return io.Copy(w, struct{ io.Reader }{fr})
} }
func (fr regFileReader) LogicalRemaining() int64 { // logicalRemaining implements fileState.logicalRemaining.
func (fr regFileReader) logicalRemaining() int64 {
return fr.nb return fr.nb
} }
func (fr regFileReader) PhysicalRemaining() int64 { // logicalRemaining implements fileState.physicalRemaining.
func (fr regFileReader) physicalRemaining() int64 {
return fr.nb return fr.nb
} }
@ -694,9 +696,9 @@ type sparseFileReader struct {
} }
func (sr *sparseFileReader) Read(b []byte) (n int, err error) { func (sr *sparseFileReader) Read(b []byte) (n int, err error) {
finished := int64(len(b)) >= sr.LogicalRemaining() finished := int64(len(b)) >= sr.logicalRemaining()
if finished { if finished {
b = b[:sr.LogicalRemaining()] b = b[:sr.logicalRemaining()]
} }
b0 := b b0 := b
@ -724,7 +726,7 @@ func (sr *sparseFileReader) Read(b []byte) (n int, err error) {
return n, errMissData // Less data in dense file than sparse file return n, errMissData // Less data in dense file than sparse file
case err != nil: case err != nil:
return n, err return n, err
case sr.LogicalRemaining() == 0 && sr.PhysicalRemaining() > 0: case sr.logicalRemaining() == 0 && sr.physicalRemaining() > 0:
return n, errUnrefData // More data in dense file than sparse file return n, errUnrefData // More data in dense file than sparse file
case finished: case finished:
return n, io.EOF return n, io.EOF
@ -746,7 +748,7 @@ func (sr *sparseFileReader) WriteTo(w io.Writer) (n int64, err error) {
var writeLastByte bool var writeLastByte bool
pos0 := sr.pos pos0 := sr.pos
for sr.LogicalRemaining() > 0 && !writeLastByte && err == nil { for sr.logicalRemaining() > 0 && !writeLastByte && err == nil {
var nf int64 // Size of fragment var nf int64 // Size of fragment
holeStart, holeEnd := sr.sp[0].Offset, sr.sp[0].endOffset() holeStart, holeEnd := sr.sp[0].Offset, sr.sp[0].endOffset()
if sr.pos < holeStart { // In a data fragment if sr.pos < holeStart { // In a data fragment
@ -754,7 +756,7 @@ func (sr *sparseFileReader) WriteTo(w io.Writer) (n int64, err error) {
nf, err = io.CopyN(ws, sr.fr, nf) nf, err = io.CopyN(ws, sr.fr, nf)
} else { // In a hole fragment } else { // In a hole fragment
nf = holeEnd - sr.pos nf = holeEnd - sr.pos
if sr.PhysicalRemaining() == 0 { if sr.physicalRemaining() == 0 {
writeLastByte = true writeLastByte = true
nf-- nf--
} }
@ -779,18 +781,18 @@ func (sr *sparseFileReader) WriteTo(w io.Writer) (n int64, err error) {
return n, errMissData // Less data in dense file than sparse file return n, errMissData // Less data in dense file than sparse file
case err != nil: case err != nil:
return n, err return n, err
case sr.LogicalRemaining() == 0 && sr.PhysicalRemaining() > 0: case sr.logicalRemaining() == 0 && sr.physicalRemaining() > 0:
return n, errUnrefData // More data in dense file than sparse file return n, errUnrefData // More data in dense file than sparse file
default: default:
return n, nil return n, nil
} }
} }
func (sr sparseFileReader) LogicalRemaining() int64 { func (sr sparseFileReader) logicalRemaining() int64 {
return sr.sp[len(sr.sp)-1].endOffset() - sr.pos return sr.sp[len(sr.sp)-1].endOffset() - sr.pos
} }
func (sr sparseFileReader) PhysicalRemaining() int64 { func (sr sparseFileReader) physicalRemaining() int64 {
return sr.fr.PhysicalRemaining() return sr.fr.physicalRemaining()
} }
type zeroReader struct{} type zeroReader struct{}

View File

@ -1021,12 +1021,12 @@ func TestParsePAX(t *testing.T) {
func TestReadOldGNUSparseMap(t *testing.T) { func TestReadOldGNUSparseMap(t *testing.T) {
populateSparseMap := func(sa sparseArray, sps []string) []string { populateSparseMap := func(sa sparseArray, sps []string) []string {
for i := 0; len(sps) > 0 && i < sa.MaxEntries(); i++ { for i := 0; len(sps) > 0 && i < sa.maxEntries(); i++ {
copy(sa.Entry(i), sps[0]) copy(sa.entry(i), sps[0])
sps = sps[1:] sps = sps[1:]
} }
if len(sps) > 0 { if len(sps) > 0 {
copy(sa.IsExtended(), "\x80") copy(sa.isExtended(), "\x80")
} }
return sps return sps
} }
@ -1034,19 +1034,19 @@ func TestReadOldGNUSparseMap(t *testing.T) {
makeInput := func(format Format, size string, sps ...string) (out []byte) { makeInput := func(format Format, size string, sps ...string) (out []byte) {
// Write the initial GNU header. // Write the initial GNU header.
var blk block var blk block
gnu := blk.GNU() gnu := blk.toGNU()
sparse := gnu.Sparse() sparse := gnu.sparse()
copy(gnu.RealSize(), size) copy(gnu.realSize(), size)
sps = populateSparseMap(sparse, sps) sps = populateSparseMap(sparse, sps)
if format != FormatUnknown { if format != FormatUnknown {
blk.SetFormat(format) blk.setFormat(format)
} }
out = append(out, blk[:]...) out = append(out, blk[:]...)
// Write extended sparse blocks. // Write extended sparse blocks.
for len(sps) > 0 { for len(sps) > 0 {
var blk block var blk block
sps = populateSparseMap(blk.Sparse(), sps) sps = populateSparseMap(blk.toSparse(), sps)
out = append(out, blk[:]...) out = append(out, blk[:]...)
} }
return out return out
@ -1359,7 +1359,7 @@ func TestFileReader(t *testing.T) {
wantCnt int64 wantCnt int64
wantErr error wantErr error
} }
testRemaining struct { // LogicalRemaining() == wantLCnt, PhysicalRemaining() == wantPCnt testRemaining struct { // logicalRemaining() == wantLCnt, physicalRemaining() == wantPCnt
wantLCnt int64 wantLCnt int64
wantPCnt int64 wantPCnt int64
} }
@ -1596,11 +1596,11 @@ func TestFileReader(t *testing.T) {
t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops)) t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops))
} }
case testRemaining: case testRemaining:
if got := fr.LogicalRemaining(); got != tf.wantLCnt { if got := fr.logicalRemaining(); got != tf.wantLCnt {
t.Errorf("test %d.%d, LogicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt) t.Errorf("test %d.%d, logicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt)
} }
if got := fr.PhysicalRemaining(); got != tf.wantPCnt { if got := fr.physicalRemaining(); got != tf.wantPCnt {
t.Errorf("test %d.%d, PhysicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt) t.Errorf("test %d.%d, physicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt)
} }
default: default:
t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf) t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf)

View File

@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || linux || dragonfly || openbsd || solaris //go:build aix || linux || dragonfly || openbsd || solaris
// +build aix linux dragonfly openbsd solaris
package tar package tar

View File

@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build darwin || freebsd || netbsd //go:build darwin || freebsd || netbsd
// +build darwin freebsd netbsd
package tar package tar

View File

@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || linux || darwin || dragonfly || freebsd || openbsd || netbsd || solaris //go:build aix || linux || darwin || dragonfly || freebsd || openbsd || netbsd || solaris
// +build aix linux darwin dragonfly freebsd openbsd netbsd solaris
package tar package tar

View File

@ -14,7 +14,7 @@ import (
// hasNUL reports whether the NUL character exists within s. // hasNUL reports whether the NUL character exists within s.
func hasNUL(s string) bool { func hasNUL(s string) bool {
return strings.IndexByte(s, 0) >= 0 return strings.Contains(s, "\x00")
} }
// isASCII reports whether the input is an ASCII C-style string. // isASCII reports whether the input is an ASCII C-style string.
@ -201,10 +201,7 @@ func parsePAXTime(s string) (time.Time, error) {
const maxNanoSecondDigits = 9 const maxNanoSecondDigits = 9
// Split string into seconds and sub-seconds parts. // Split string into seconds and sub-seconds parts.
ss, sn := s, "" ss, sn, _ := strings.Cut(s, ".")
if pos := strings.IndexByte(s, '.'); pos >= 0 {
ss, sn = s[:pos], s[pos+1:]
}
// Parse the seconds. // Parse the seconds.
secs, err := strconv.ParseInt(ss, 10, 64) secs, err := strconv.ParseInt(ss, 10, 64)
@ -254,48 +251,32 @@ func formatPAXTime(ts time.Time) (s string) {
// return the remainder as r. // return the remainder as r.
func parsePAXRecord(s string) (k, v, r string, err error) { func parsePAXRecord(s string) (k, v, r string, err error) {
// The size field ends at the first space. // The size field ends at the first space.
sp := strings.IndexByte(s, ' ') nStr, rest, ok := strings.Cut(s, " ")
if sp == -1 { if !ok {
return "", "", s, ErrHeader return "", "", s, ErrHeader
} }
// Parse the first token as a decimal integer. // Parse the first token as a decimal integer.
n, perr := strconv.ParseInt(s[:sp], 10, 0) // Intentionally parse as native int n, perr := strconv.ParseInt(nStr, 10, 0) // Intentionally parse as native int
if perr != nil || n < 5 || int64(len(s)) < n { if perr != nil || n < 5 || n > int64(len(s)) {
return "", "", s, ErrHeader return "", "", s, ErrHeader
} }
n -= int64(len(nStr) + 1) // convert from index in s to index in rest
afterSpace := int64(sp + 1) if n <= 0 {
beforeLastNewLine := n - 1
// In some cases, "length" was perhaps padded/malformed, and
// trying to index past where the space supposedly is goes past
// the end of the actual record.
// For example:
// "0000000000000000000000000000000030 mtime=1432668921.098285006\n30 ctime=2147483649.15163319"
// ^ ^
// | |
// | afterSpace=35
// |
// beforeLastNewLine=29
// yet indexOf(firstSpace) MUST BE before endOfRecord.
//
// See https://golang.org/issues/40196.
if afterSpace >= beforeLastNewLine {
return "", "", s, ErrHeader return "", "", s, ErrHeader
} }
// Extract everything between the space and the final newline. // Extract everything between the space and the final newline.
rec, nl, rem := s[afterSpace:beforeLastNewLine], s[beforeLastNewLine:n], s[n:] rec, nl, rem := rest[:n-1], rest[n-1:n], rest[n:]
if nl != "\n" { if nl != "\n" {
return "", "", s, ErrHeader return "", "", s, ErrHeader
} }
// The first equals separates the key from the value. // The first equals separates the key from the value.
eq := strings.IndexByte(rec, '=') k, v, ok = strings.Cut(rec, "=")
if eq == -1 { if !ok {
return "", "", s, ErrHeader return "", "", s, ErrHeader
} }
k, v = rec[:eq], rec[eq+1:]
if !validPAXRecord(k, v) { if !validPAXRecord(k, v) {
return "", "", s, ErrHeader return "", "", s, ErrHeader
@ -333,7 +314,7 @@ func formatPAXRecord(k, v string) (string, error) {
// for the PAX version of the USTAR string fields. // for the PAX version of the USTAR string fields.
// The key must not contain an '=' character. // The key must not contain an '=' character.
func validPAXRecord(k, v string) bool { func validPAXRecord(k, v string) bool {
if k == "" || strings.IndexByte(k, '=') >= 0 { if k == "" || strings.Contains(k, "=") {
return false return false
} }
switch k { switch k {

View File

@ -50,7 +50,7 @@ func (tw *Writer) Flush() error {
if tw.err != nil { if tw.err != nil {
return tw.err return tw.err
} }
if nb := tw.curr.LogicalRemaining(); nb > 0 { if nb := tw.curr.logicalRemaining(); nb > 0 {
return fmt.Errorf("archive/tar: missed writing %d bytes", nb) return fmt.Errorf("archive/tar: missed writing %d bytes", nb)
} }
if _, tw.err = tw.w.Write(zeroBlock[:tw.pad]); tw.err != nil { if _, tw.err = tw.w.Write(zeroBlock[:tw.pad]); tw.err != nil {
@ -117,8 +117,8 @@ func (tw *Writer) writeUSTARHeader(hdr *Header) error {
// Pack the main header. // Pack the main header.
var f formatter var f formatter
blk := tw.templateV7Plus(hdr, f.formatString, f.formatOctal) blk := tw.templateV7Plus(hdr, f.formatString, f.formatOctal)
f.formatString(blk.USTAR().Prefix(), namePrefix) f.formatString(blk.toUSTAR().prefix(), namePrefix)
blk.SetFormat(FormatUSTAR) blk.setFormat(FormatUSTAR)
if f.err != nil { if f.err != nil {
return f.err // Should never happen since header is validated return f.err // Should never happen since header is validated
} }
@ -208,7 +208,7 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error {
var f formatter // Ignore errors since they are expected var f formatter // Ignore errors since they are expected
fmtStr := func(b []byte, s string) { f.formatString(b, toASCII(s)) } fmtStr := func(b []byte, s string) { f.formatString(b, toASCII(s)) }
blk := tw.templateV7Plus(hdr, fmtStr, f.formatOctal) blk := tw.templateV7Plus(hdr, fmtStr, f.formatOctal)
blk.SetFormat(FormatPAX) blk.setFormat(FormatPAX)
if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil { if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil {
return err return err
} }
@ -250,10 +250,10 @@ func (tw *Writer) writeGNUHeader(hdr *Header) error {
var spb []byte var spb []byte
blk := tw.templateV7Plus(hdr, f.formatString, f.formatNumeric) blk := tw.templateV7Plus(hdr, f.formatString, f.formatNumeric)
if !hdr.AccessTime.IsZero() { if !hdr.AccessTime.IsZero() {
f.formatNumeric(blk.GNU().AccessTime(), hdr.AccessTime.Unix()) f.formatNumeric(blk.toGNU().accessTime(), hdr.AccessTime.Unix())
} }
if !hdr.ChangeTime.IsZero() { if !hdr.ChangeTime.IsZero() {
f.formatNumeric(blk.GNU().ChangeTime(), hdr.ChangeTime.Unix()) f.formatNumeric(blk.toGNU().changeTime(), hdr.ChangeTime.Unix())
} }
// TODO(dsnet): Re-enable this when adding sparse support. // TODO(dsnet): Re-enable this when adding sparse support.
// See https://golang.org/issue/22735 // See https://golang.org/issue/22735
@ -293,7 +293,7 @@ func (tw *Writer) writeGNUHeader(hdr *Header) error {
f.formatNumeric(blk.GNU().RealSize(), realSize) f.formatNumeric(blk.GNU().RealSize(), realSize)
} }
*/ */
blk.SetFormat(FormatGNU) blk.setFormat(FormatGNU)
if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil { if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil {
return err return err
} }
@ -321,28 +321,28 @@ type (
// The block returned is only valid until the next call to // The block returned is only valid until the next call to
// templateV7Plus or writeRawFile. // templateV7Plus or writeRawFile.
func (tw *Writer) templateV7Plus(hdr *Header, fmtStr stringFormatter, fmtNum numberFormatter) *block { func (tw *Writer) templateV7Plus(hdr *Header, fmtStr stringFormatter, fmtNum numberFormatter) *block {
tw.blk.Reset() tw.blk.reset()
modTime := hdr.ModTime modTime := hdr.ModTime
if modTime.IsZero() { if modTime.IsZero() {
modTime = time.Unix(0, 0) modTime = time.Unix(0, 0)
} }
v7 := tw.blk.V7() v7 := tw.blk.toV7()
v7.TypeFlag()[0] = hdr.Typeflag v7.typeFlag()[0] = hdr.Typeflag
fmtStr(v7.Name(), hdr.Name) fmtStr(v7.name(), hdr.Name)
fmtStr(v7.LinkName(), hdr.Linkname) fmtStr(v7.linkName(), hdr.Linkname)
fmtNum(v7.Mode(), hdr.Mode) fmtNum(v7.mode(), hdr.Mode)
fmtNum(v7.UID(), int64(hdr.Uid)) fmtNum(v7.uid(), int64(hdr.Uid))
fmtNum(v7.GID(), int64(hdr.Gid)) fmtNum(v7.gid(), int64(hdr.Gid))
fmtNum(v7.Size(), hdr.Size) fmtNum(v7.size(), hdr.Size)
fmtNum(v7.ModTime(), modTime.Unix()) fmtNum(v7.modTime(), modTime.Unix())
ustar := tw.blk.USTAR() ustar := tw.blk.toUSTAR()
fmtStr(ustar.UserName(), hdr.Uname) fmtStr(ustar.userName(), hdr.Uname)
fmtStr(ustar.GroupName(), hdr.Gname) fmtStr(ustar.groupName(), hdr.Gname)
fmtNum(ustar.DevMajor(), hdr.Devmajor) fmtNum(ustar.devMajor(), hdr.Devmajor)
fmtNum(ustar.DevMinor(), hdr.Devminor) fmtNum(ustar.devMinor(), hdr.Devminor)
return &tw.blk return &tw.blk
} }
@ -351,7 +351,7 @@ func (tw *Writer) templateV7Plus(hdr *Header, fmtStr stringFormatter, fmtNum num
// It uses format to encode the header format and will write data as the body. // It uses format to encode the header format and will write data as the body.
// It uses default values for all of the other fields (as BSD and GNU tar does). // It uses default values for all of the other fields (as BSD and GNU tar does).
func (tw *Writer) writeRawFile(name, data string, flag byte, format Format) error { func (tw *Writer) writeRawFile(name, data string, flag byte, format Format) error {
tw.blk.Reset() tw.blk.reset()
// Best effort for the filename. // Best effort for the filename.
name = toASCII(name) name = toASCII(name)
@ -361,15 +361,15 @@ func (tw *Writer) writeRawFile(name, data string, flag byte, format Format) erro
name = strings.TrimRight(name, "/") name = strings.TrimRight(name, "/")
var f formatter var f formatter
v7 := tw.blk.V7() v7 := tw.blk.toV7()
v7.TypeFlag()[0] = flag v7.typeFlag()[0] = flag
f.formatString(v7.Name(), name) f.formatString(v7.name(), name)
f.formatOctal(v7.Mode(), 0) f.formatOctal(v7.mode(), 0)
f.formatOctal(v7.UID(), 0) f.formatOctal(v7.uid(), 0)
f.formatOctal(v7.GID(), 0) f.formatOctal(v7.gid(), 0)
f.formatOctal(v7.Size(), int64(len(data))) // Must be < 8GiB f.formatOctal(v7.size(), int64(len(data))) // Must be < 8GiB
f.formatOctal(v7.ModTime(), 0) f.formatOctal(v7.modTime(), 0)
tw.blk.SetFormat(format) tw.blk.setFormat(format)
if f.err != nil { if f.err != nil {
return f.err // Only occurs if size condition is violated return f.err // Only occurs if size condition is violated
} }
@ -511,10 +511,13 @@ func (fw *regFileWriter) ReadFrom(r io.Reader) (int64, error) {
return io.Copy(struct{ io.Writer }{fw}, r) return io.Copy(struct{ io.Writer }{fw}, r)
} }
func (fw regFileWriter) LogicalRemaining() int64 { // logicalRemaining implements fileState.logicalRemaining.
func (fw regFileWriter) logicalRemaining() int64 {
return fw.nb return fw.nb
} }
func (fw regFileWriter) PhysicalRemaining() int64 {
// logicalRemaining implements fileState.physicalRemaining.
func (fw regFileWriter) physicalRemaining() int64 {
return fw.nb return fw.nb
} }
@ -526,9 +529,9 @@ type sparseFileWriter struct {
} }
func (sw *sparseFileWriter) Write(b []byte) (n int, err error) { func (sw *sparseFileWriter) Write(b []byte) (n int, err error) {
overwrite := int64(len(b)) > sw.LogicalRemaining() overwrite := int64(len(b)) > sw.logicalRemaining()
if overwrite { if overwrite {
b = b[:sw.LogicalRemaining()] b = b[:sw.logicalRemaining()]
} }
b0 := b b0 := b
@ -556,7 +559,7 @@ func (sw *sparseFileWriter) Write(b []byte) (n int, err error) {
return n, errMissData // Not possible; implies bug in validation logic return n, errMissData // Not possible; implies bug in validation logic
case err != nil: case err != nil:
return n, err return n, err
case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0: case sw.logicalRemaining() == 0 && sw.physicalRemaining() > 0:
return n, errUnrefData // Not possible; implies bug in validation logic return n, errUnrefData // Not possible; implies bug in validation logic
case overwrite: case overwrite:
return n, ErrWriteTooLong return n, ErrWriteTooLong
@ -578,12 +581,12 @@ func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) {
var readLastByte bool var readLastByte bool
pos0 := sw.pos pos0 := sw.pos
for sw.LogicalRemaining() > 0 && !readLastByte && err == nil { for sw.logicalRemaining() > 0 && !readLastByte && err == nil {
var nf int64 // Size of fragment var nf int64 // Size of fragment
dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset() dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset()
if sw.pos < dataStart { // In a hole fragment if sw.pos < dataStart { // In a hole fragment
nf = dataStart - sw.pos nf = dataStart - sw.pos
if sw.PhysicalRemaining() == 0 { if sw.physicalRemaining() == 0 {
readLastByte = true readLastByte = true
nf-- nf--
} }
@ -613,18 +616,18 @@ func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) {
return n, errMissData // Not possible; implies bug in validation logic return n, errMissData // Not possible; implies bug in validation logic
case err != nil: case err != nil:
return n, err return n, err
case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0: case sw.logicalRemaining() == 0 && sw.physicalRemaining() > 0:
return n, errUnrefData // Not possible; implies bug in validation logic return n, errUnrefData // Not possible; implies bug in validation logic
default: default:
return n, ensureEOF(rs) return n, ensureEOF(rs)
} }
} }
func (sw sparseFileWriter) LogicalRemaining() int64 { func (sw sparseFileWriter) logicalRemaining() int64 {
return sw.sp[len(sw.sp)-1].endOffset() - sw.pos return sw.sp[len(sw.sp)-1].endOffset() - sw.pos
} }
func (sw sparseFileWriter) PhysicalRemaining() int64 { func (sw sparseFileWriter) physicalRemaining() int64 {
return sw.fw.PhysicalRemaining() return sw.fw.physicalRemaining()
} }
// zeroWriter may only be written with NULs, otherwise it returns errWriteHole. // zeroWriter may only be written with NULs, otherwise it returns errWriteHole.

View File

@ -987,11 +987,9 @@ func TestIssue12594(t *testing.T) {
// The prefix field should never appear in the GNU format. // The prefix field should never appear in the GNU format.
var blk block var blk block
copy(blk[:], b.Bytes()) copy(blk[:], b.Bytes())
prefix := string(blk.USTAR().Prefix()) prefix := string(blk.toUSTAR().prefix())
if i := strings.IndexByte(prefix, 0); i >= 0 { prefix, _, _ = strings.Cut(prefix, "\x00") // Truncate at the NUL terminator
prefix = prefix[:i] // Truncate at the NUL terminator if blk.getFormat() == FormatGNU && len(prefix) > 0 && strings.HasPrefix(name, prefix) {
}
if blk.GetFormat() == FormatGNU && len(prefix) > 0 && strings.HasPrefix(name, prefix) {
t.Errorf("test %d, found prefix in GNU format: %s", i, prefix) t.Errorf("test %d, found prefix in GNU format: %s", i, prefix)
} }
@ -1029,7 +1027,7 @@ func TestFileWriter(t *testing.T) {
wantCnt int64 wantCnt int64
wantErr error wantErr error
} }
testRemaining struct { // LogicalRemaining() == wantLCnt, PhysicalRemaining() == wantPCnt testRemaining struct { // logicalRemaining() == wantLCnt, physicalRemaining() == wantPCnt
wantLCnt int64 wantLCnt int64
wantPCnt int64 wantPCnt int64
} }
@ -1292,11 +1290,11 @@ func TestFileWriter(t *testing.T) {
t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops)) t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops))
} }
case testRemaining: case testRemaining:
if got := fw.LogicalRemaining(); got != tf.wantLCnt { if got := fw.logicalRemaining(); got != tf.wantLCnt {
t.Errorf("test %d.%d, LogicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt) t.Errorf("test %d.%d, logicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt)
} }
if got := fw.PhysicalRemaining(); got != tf.wantPCnt { if got := fw.physicalRemaining(); got != tf.wantPCnt {
t.Errorf("test %d.%d, PhysicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt) t.Errorf("test %d.%d, physicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt)
} }
default: default:
t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf) t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf)

View File

@ -102,7 +102,7 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
// indicate it contains up to 1 << 128 - 1 files. Since each file has a // indicate it contains up to 1 << 128 - 1 files. Since each file has a
// header which will be _at least_ 30 bytes we can safely preallocate // header which will be _at least_ 30 bytes we can safely preallocate
// if (data size / 30) >= end.directoryRecords. // if (data size / 30) >= end.directoryRecords.
if (uint64(size)-end.directorySize)/30 >= end.directoryRecords { if end.directorySize < uint64(size) && (uint64(size)-end.directorySize)/30 >= end.directoryRecords {
z.File = make([]*File, 0, end.directoryRecords) z.File = make([]*File, 0, end.directoryRecords)
} }
z.Comment = end.comment z.Comment = end.comment
@ -741,6 +741,9 @@ func (r *Reader) initFileList() {
for _, file := range r.File { for _, file := range r.File {
isDir := len(file.Name) > 0 && file.Name[len(file.Name)-1] == '/' isDir := len(file.Name) > 0 && file.Name[len(file.Name)-1] == '/'
name := toValidName(file.Name) name := toValidName(file.Name)
if name == "" {
continue
}
for dir := path.Dir(name); dir != "."; dir = path.Dir(dir) { for dir := path.Dir(name); dir != "."; dir = path.Dir(dir) {
dirs[dir] = true dirs[dir] = true
} }
@ -782,8 +785,11 @@ func fileEntryLess(x, y string) bool {
func (r *Reader) Open(name string) (fs.File, error) { func (r *Reader) Open(name string) (fs.File, error) {
r.initFileList() r.initFileList()
if !fs.ValidPath(name) {
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrInvalid}
}
e := r.openLookup(name) e := r.openLookup(name)
if e == nil || !fs.ValidPath(name) { if e == nil {
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
} }
if e.isDir { if e.isDir {
@ -797,7 +803,7 @@ func (r *Reader) Open(name string) (fs.File, error) {
} }
func split(name string) (dir, elem string, isDir bool) { func split(name string) (dir, elem string, isDir bool) {
if name[len(name)-1] == '/' { if len(name) > 0 && name[len(name)-1] == '/' {
isDir = true isDir = true
name = name[:len(name)-1] name = name[:len(name)-1]
} }

View File

@ -13,6 +13,7 @@ import (
"io/fs" "io/fs"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"regexp" "regexp"
"strings" "strings"
"testing" "testing"
@ -1202,6 +1203,15 @@ func TestCVE202127919(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("Error reading file: %v", err) t.Errorf("Error reading file: %v", err)
} }
if len(r.File) != 1 {
t.Fatalf("No entries in the file list")
}
if r.File[0].Name != "../test.txt" {
t.Errorf("Unexpected entry name: %s", r.File[0].Name)
}
if _, err := r.File[0].Open(); err != nil {
t.Errorf("Error opening file: %v", err)
}
} }
func TestReadDataDescriptor(t *testing.T) { func TestReadDataDescriptor(t *testing.T) {
@ -1384,3 +1394,139 @@ func TestCVE202133196(t *testing.T) {
t.Errorf("Archive has unexpected number of files, got %d, want 5", len(r.File)) t.Errorf("Archive has unexpected number of files, got %d, want 5", len(r.File))
} }
} }
func TestCVE202139293(t *testing.T) {
// directory size is so large, that the check in Reader.init
// overflows when subtracting from the archive size, causing
// the pre-allocation check to be bypassed.
data := []byte{
0x50, 0x4b, 0x06, 0x06, 0x05, 0x06, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b,
0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b,
0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0x50, 0xfe, 0x00, 0xff, 0x00, 0x3a, 0x00, 0x00, 0x00, 0xff,
}
_, err := NewReader(bytes.NewReader(data), int64(len(data)))
if err != ErrFormat {
t.Fatalf("unexpected error, got: %v, want: %v", err, ErrFormat)
}
}
func TestCVE202141772(t *testing.T) {
// Archive contains a file whose name is exclusively made up of '/', '\'
// characters, or "../", "..\" paths, which would previously cause a panic.
//
// Length Method Size Cmpr Date Time CRC-32 Name
// -------- ------ ------- ---- ---------- ----- -------- ----
// 0 Stored 0 0% 08-05-2021 18:32 00000000 /
// 0 Stored 0 0% 09-14-2021 12:59 00000000 //
// 0 Stored 0 0% 09-14-2021 12:59 00000000 \
// 11 Stored 11 0% 09-14-2021 13:04 0d4a1185 /test.txt
// -------- ------- --- -------
// 11 11 0% 4 files
data := []byte{
0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x08,
0x00, 0x00, 0x06, 0x94, 0x05, 0x53, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2f, 0x50,
0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00,
0x00, 0x78, 0x67, 0x2e, 0x53, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x50,
0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00,
0x00, 0x78, 0x67, 0x2e, 0x53, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x5c, 0x50, 0x4b,
0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
0x91, 0x68, 0x2e, 0x53, 0x85, 0x11, 0x4a, 0x0d,
0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x2f, 0x74, 0x65, 0x73,
0x74, 0x2e, 0x74, 0x78, 0x74, 0x68, 0x65, 0x6c,
0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x0a, 0x00,
0x00, 0x08, 0x00, 0x00, 0x06, 0x94, 0x05, 0x53,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0xed, 0x41, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x50,
0x4b, 0x01, 0x02, 0x3f, 0x00, 0x0a, 0x00, 0x00,
0x00, 0x00, 0x00, 0x78, 0x67, 0x2e, 0x53, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
0x00, 0x1f, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x0a,
0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x18, 0x00, 0x93, 0x98, 0x25, 0x57, 0x25,
0xa9, 0xd7, 0x01, 0x93, 0x98, 0x25, 0x57, 0x25,
0xa9, 0xd7, 0x01, 0x93, 0x98, 0x25, 0x57, 0x25,
0xa9, 0xd7, 0x01, 0x50, 0x4b, 0x01, 0x02, 0x3f,
0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78,
0x67, 0x2e, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x20, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00,
0x00, 0x5c, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x93, 0x98,
0x25, 0x57, 0x25, 0xa9, 0xd7, 0x01, 0x93, 0x98,
0x25, 0x57, 0x25, 0xa9, 0xd7, 0x01, 0x93, 0x98,
0x25, 0x57, 0x25, 0xa9, 0xd7, 0x01, 0x50, 0x4b,
0x01, 0x02, 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x91, 0x68, 0x2e, 0x53, 0x85, 0x11,
0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00,
0x00, 0x00, 0x09, 0x00, 0x24, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x5e, 0x00, 0x00, 0x00, 0x2f, 0x74, 0x65, 0x73,
0x74, 0x2e, 0x74, 0x78, 0x74, 0x0a, 0x00, 0x20,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18,
0x00, 0xa9, 0x80, 0x51, 0x01, 0x26, 0xa9, 0xd7,
0x01, 0x31, 0xd1, 0x57, 0x01, 0x26, 0xa9, 0xd7,
0x01, 0xdf, 0x48, 0x85, 0xf9, 0x25, 0xa9, 0xd7,
0x01, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00,
0x00, 0x04, 0x00, 0x04, 0x00, 0x31, 0x01, 0x00,
0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
}
r, err := NewReader(bytes.NewReader([]byte(data)), int64(len(data)))
if err != nil {
t.Fatalf("Error reading the archive: %v", err)
}
entryNames := []string{`/`, `//`, `\`, `/test.txt`}
var names []string
for _, f := range r.File {
names = append(names, f.Name)
if _, err := f.Open(); err != nil {
t.Errorf("Error opening %q: %v", f.Name, err)
}
if _, err := r.Open(f.Name); err == nil {
t.Errorf("Opening %q with fs.FS API succeeded", f.Name)
}
}
if !reflect.DeepEqual(names, entryNames) {
t.Errorf("Unexpected file entries: %q", names)
}
if _, err := r.Open(""); err == nil {
t.Errorf("Opening %q with fs.FS API succeeded", "")
}
if _, err := r.Open("test.txt"); err != nil {
t.Errorf("Error opening %q with fs.FS API: %v", "test.txt", err)
}
dirEntries, err := fs.ReadDir(r, ".")
if err != nil {
t.Fatalf("Error reading the root directory: %v", err)
}
if len(dirEntries) != 1 || dirEntries[0].Name() != "test.txt" {
t.Errorf("Unexpected directory entries")
for _, dirEntry := range dirEntries {
_, err := r.Open(dirEntry.Name())
t.Logf("%q (Open error: %v)", dirEntry.Name(), err)
}
t.FailNow()
}
info, err := dirEntries[0].Info()
if err != nil {
t.Fatalf("Error reading info entry: %v", err)
}
if name := info.Name(); name != "test.txt" {
t.Errorf("Inconsistent name in info entry: %v", name)
}
}

View File

@ -362,7 +362,7 @@ func TestWriterDirAttributes(t *testing.T) {
} }
binary.LittleEndian.PutUint32(sig[:], uint32(dataDescriptorSignature)) binary.LittleEndian.PutUint32(sig[:], uint32(dataDescriptorSignature))
if bytes.Index(b, sig[:]) != -1 { if bytes.Contains(b, sig[:]) {
t.Error("there should be no data descriptor") t.Error("there should be no data descriptor")
} }
} }

View File

@ -68,7 +68,12 @@ func (b *Reader) Size() int { return len(b.buf) }
// Reset discards any buffered data, resets all state, and switches // Reset discards any buffered data, resets all state, and switches
// the buffered reader to read from r. // the buffered reader to read from r.
// Calling Reset on the zero value of Reader initializes the internal buffer
// to the default size.
func (b *Reader) Reset(r io.Reader) { func (b *Reader) Reset(r io.Reader) {
if b.buf == nil {
b.buf = make([]byte, defaultBufSize)
}
b.reset(b.buf, r) b.reset(b.buf, r)
} }
@ -168,6 +173,10 @@ func (b *Reader) Discard(n int) (discarded int, err error) {
if n == 0 { if n == 0 {
return return
} }
b.lastByte = -1
b.lastRuneSize = -1
remain := n remain := n
for { for {
skip := b.Buffered() skip := b.Buffered()
@ -261,8 +270,8 @@ func (b *Reader) ReadByte() (byte, error) {
// UnreadByte unreads the last byte. Only the most recently read byte can be unread. // UnreadByte unreads the last byte. Only the most recently read byte can be unread.
// //
// UnreadByte returns an error if the most recent method called on the // UnreadByte returns an error if the most recent method called on the
// Reader was not a read operation. Notably, Peek is not considered a // Reader was not a read operation. Notably, Peek, Discard, and WriteTo are not
// read operation. // considered read operations.
func (b *Reader) UnreadByte() error { func (b *Reader) UnreadByte() error {
if b.lastByte < 0 || b.r == 0 && b.w > 0 { if b.lastByte < 0 || b.r == 0 && b.w > 0 {
return ErrInvalidUnreadByte return ErrInvalidUnreadByte
@ -497,6 +506,9 @@ func (b *Reader) ReadString(delim byte) (string, error) {
// If the underlying reader supports the WriteTo method, // If the underlying reader supports the WriteTo method,
// this calls the underlying WriteTo without buffering. // this calls the underlying WriteTo without buffering.
func (b *Reader) WriteTo(w io.Writer) (n int64, err error) { func (b *Reader) WriteTo(w io.Writer) (n int64, err error) {
b.lastByte = -1
b.lastRuneSize = -1
n, err = b.writeBuf(w) n, err = b.writeBuf(w)
if err != nil { if err != nil {
return return
@ -590,7 +602,12 @@ func (b *Writer) Size() int { return len(b.buf) }
// Reset discards any unflushed buffered data, clears any error, and // Reset discards any unflushed buffered data, clears any error, and
// resets b to write its output to w. // resets b to write its output to w.
// Calling Reset on the zero value of Writer initializes the internal buffer
// to the default size.
func (b *Writer) Reset(w io.Writer) { func (b *Writer) Reset(w io.Writer) {
if b.buf == nil {
b.buf = make([]byte, defaultBufSize)
}
b.err = nil b.err = nil
b.n = 0 b.n = 0
b.wr = w b.wr = w
@ -623,6 +640,14 @@ func (b *Writer) Flush() error {
// Available returns how many bytes are unused in the buffer. // Available returns how many bytes are unused in the buffer.
func (b *Writer) Available() int { return len(b.buf) - b.n } func (b *Writer) Available() int { return len(b.buf) - b.n }
// AvailableBuffer returns an empty buffer with b.Available() capacity.
// This buffer is intended to be appended to and
// passed to an immediately succeeding Write call.
// The buffer is only valid until the next write operation on b.
func (b *Writer) AvailableBuffer() []byte {
return b.buf[b.n:][:0]
}
// Buffered returns the number of bytes that have been written into the current buffer. // Buffered returns the number of bytes that have been written into the current buffer.
func (b *Writer) Buffered() int { return b.n } func (b *Writer) Buffered() int { return b.n }
@ -720,19 +745,14 @@ func (b *Writer) WriteString(s string) (int, error) {
} }
// ReadFrom implements io.ReaderFrom. If the underlying writer // ReadFrom implements io.ReaderFrom. If the underlying writer
// supports the ReadFrom method, and b has no buffered data yet, // supports the ReadFrom method, this calls the underlying ReadFrom.
// this calls the underlying ReadFrom without buffering. // If there is buffered data and an underlying ReadFrom, this fills
// the buffer and writes it before calling ReadFrom.
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) { func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
if b.err != nil { if b.err != nil {
return 0, b.err return 0, b.err
} }
if b.Buffered() == 0 { readerFrom, readerFromOK := b.wr.(io.ReaderFrom)
if w, ok := b.wr.(io.ReaderFrom); ok {
n, err = w.ReadFrom(r)
b.err = err
return n, err
}
}
var m int var m int
for { for {
if b.Available() == 0 { if b.Available() == 0 {
@ -740,6 +760,12 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
return n, err1 return n, err1
} }
} }
if readerFromOK && b.Buffered() == 0 {
nn, err := readerFrom.ReadFrom(r)
b.err = err
n += nn
return n, err
}
nr := 0 nr := 0
for nr < maxConsecutiveEmptyReads { for nr < maxConsecutiveEmptyReads {
m, err = r.Read(b.buf[b.n:]) m, err = r.Read(b.buf[b.n:])

View File

@ -10,6 +10,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"math/rand"
"strconv"
"strings" "strings"
"testing" "testing"
"testing/iotest" "testing/iotest"
@ -302,6 +304,40 @@ func TestNoUnreadByteAfterPeek(t *testing.T) {
} }
} }
func TestNoUnreadRuneAfterDiscard(t *testing.T) {
br := NewReader(strings.NewReader("example"))
br.ReadRune()
br.Discard(1)
if err := br.UnreadRune(); err == nil {
t.Error("UnreadRune didn't fail after Discard")
}
}
func TestNoUnreadByteAfterDiscard(t *testing.T) {
br := NewReader(strings.NewReader("example"))
br.ReadByte()
br.Discard(1)
if err := br.UnreadByte(); err == nil {
t.Error("UnreadByte didn't fail after Discard")
}
}
func TestNoUnreadRuneAfterWriteTo(t *testing.T) {
br := NewReader(strings.NewReader("example"))
br.WriteTo(io.Discard)
if err := br.UnreadRune(); err == nil {
t.Error("UnreadRune didn't fail after WriteTo")
}
}
func TestNoUnreadByteAfterWriteTo(t *testing.T) {
br := NewReader(strings.NewReader("example"))
br.WriteTo(io.Discard)
if err := br.UnreadByte(); err == nil {
t.Error("UnreadByte didn't fail after WriteTo")
}
}
func TestUnreadByte(t *testing.T) { func TestUnreadByte(t *testing.T) {
segments := []string{"Hello, ", "world"} segments := []string{"Hello, ", "world"}
r := NewReader(&StringReader{data: segments}) r := NewReader(&StringReader{data: segments})
@ -608,6 +644,37 @@ func TestWriter(t *testing.T) {
} }
} }
func TestWriterAppend(t *testing.T) {
got := new(bytes.Buffer)
var want []byte
rn := rand.New(rand.NewSource(0))
w := NewWriterSize(got, 64)
for i := 0; i < 100; i++ {
// Obtain a buffer to append to.
b := w.AvailableBuffer()
if w.Available() != cap(b) {
t.Fatalf("Available() = %v, want %v", w.Available(), cap(b))
}
// While not recommended, it is valid to append to a shifted buffer.
// This forces Write to copy the the input.
if rn.Intn(8) == 0 && cap(b) > 0 {
b = b[1:1:cap(b)]
}
// Append a random integer of varying width.
n := int64(rn.Intn(1 << rn.Intn(30)))
want = append(strconv.AppendInt(want, n, 10), ' ')
b = append(strconv.AppendInt(b, n, 10), ' ')
w.Write(b)
}
w.Flush()
if !bytes.Equal(got.Bytes(), want) {
t.Errorf("output mismatch:\ngot %s\nwant %s", got.Bytes(), want)
}
}
// Check that write errors are returned properly. // Check that write errors are returned properly.
type errorWriterTest struct { type errorWriterTest struct {
@ -1284,6 +1351,54 @@ func TestWriterReadFromErrNoProgress(t *testing.T) {
} }
} }
type readFromWriter struct {
buf []byte
writeBytes int
readFromBytes int
}
func (w *readFromWriter) Write(p []byte) (int, error) {
w.buf = append(w.buf, p...)
w.writeBytes += len(p)
return len(p), nil
}
func (w *readFromWriter) ReadFrom(r io.Reader) (int64, error) {
b, err := io.ReadAll(r)
w.buf = append(w.buf, b...)
w.readFromBytes += len(b)
return int64(len(b)), err
}
// Test that calling (*Writer).ReadFrom with a partially-filled buffer
// fills the buffer before switching over to ReadFrom.
func TestWriterReadFromWithBufferedData(t *testing.T) {
const bufsize = 16
input := createTestInput(64)
rfw := &readFromWriter{}
w := NewWriterSize(rfw, bufsize)
const writeSize = 8
if n, err := w.Write(input[:writeSize]); n != writeSize || err != nil {
t.Errorf("w.Write(%v bytes) = %v, %v; want %v, nil", writeSize, n, err, writeSize)
}
n, err := w.ReadFrom(bytes.NewReader(input[writeSize:]))
if wantn := len(input[writeSize:]); int(n) != wantn || err != nil {
t.Errorf("io.Copy(w, %v bytes) = %v, %v; want %v, nil", wantn, n, err, wantn)
}
if err := w.Flush(); err != nil {
t.Errorf("w.Flush() = %v, want nil", err)
}
if got, want := rfw.writeBytes, bufsize; got != want {
t.Errorf("wrote %v bytes with Write, want %v", got, want)
}
if got, want := rfw.readFromBytes, len(input)-bufsize; got != want {
t.Errorf("wrote %v bytes with ReadFrom, want %v", got, want)
}
}
func TestReadZero(t *testing.T) { func TestReadZero(t *testing.T) {
for _, size := range []int{100, 2} { for _, size := range []int{100, 2} {
t.Run(fmt.Sprintf("bufsize=%d", size), func(t *testing.T) { t.Run(fmt.Sprintf("bufsize=%d", size), func(t *testing.T) {
@ -1312,6 +1427,7 @@ func TestReaderReset(t *testing.T) {
if string(buf) != "foo" { if string(buf) != "foo" {
t.Errorf("buf = %q; want foo", buf) t.Errorf("buf = %q; want foo", buf)
} }
r.Reset(strings.NewReader("bar bar")) r.Reset(strings.NewReader("bar bar"))
all, err := io.ReadAll(r) all, err := io.ReadAll(r)
if err != nil { if err != nil {
@ -1320,12 +1436,23 @@ func TestReaderReset(t *testing.T) {
if string(all) != "bar bar" { if string(all) != "bar bar" {
t.Errorf("ReadAll = %q; want bar bar", all) t.Errorf("ReadAll = %q; want bar bar", all)
} }
*r = Reader{} // zero out the Reader
r.Reset(strings.NewReader("bar bar"))
all, err = io.ReadAll(r)
if err != nil {
t.Fatal(err)
}
if string(all) != "bar bar" {
t.Errorf("ReadAll = %q; want bar bar", all)
}
} }
func TestWriterReset(t *testing.T) { func TestWriterReset(t *testing.T) {
var buf1, buf2 bytes.Buffer var buf1, buf2, buf3 bytes.Buffer
w := NewWriter(&buf1) w := NewWriter(&buf1)
w.WriteString("foo") w.WriteString("foo")
w.Reset(&buf2) // and not flushed w.Reset(&buf2) // and not flushed
w.WriteString("bar") w.WriteString("bar")
w.Flush() w.Flush()
@ -1335,6 +1462,17 @@ func TestWriterReset(t *testing.T) {
if buf2.String() != "bar" { if buf2.String() != "bar" {
t.Errorf("buf2 = %q; want bar", buf2.String()) t.Errorf("buf2 = %q; want bar", buf2.String())
} }
*w = Writer{} // zero out the Writer
w.Reset(&buf3) // and not flushed
w.WriteString("bar")
w.Flush()
if buf1.String() != "" {
t.Errorf("buf1 = %q; want empty", buf1.String())
}
if buf3.String() != "bar" {
t.Errorf("buf3 = %q; want bar", buf3.String())
}
} }
func TestReaderDiscard(t *testing.T) { func TestReaderDiscard(t *testing.T) {

View File

@ -20,6 +20,18 @@ func ExampleWriter() {
// Output: Hello, world! // Output: Hello, world!
} }
func ExampleWriter_AvailableBuffer() {
w := bufio.NewWriter(os.Stdout)
for _, i := range []int64{1, 2, 3, 4} {
b := w.AvailableBuffer()
b = strconv.AppendInt(b, i, 10)
b = append(b, ' ')
w.Write(b)
}
w.Flush()
// Output: 1 2 3 4
}
// The simplest use of a Scanner, to read standard input as a set of lines. // The simplest use of a Scanner, to read standard input as a set of lines.
func ExampleScanner_lines() { func ExampleScanner_lines() {
scanner := bufio.NewScanner(os.Stdin) scanner := bufio.NewScanner(os.Stdin)

View File

@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// //
//go:build linux //go:build linux
// +build linux
package bytes_test package bytes_test
@ -66,7 +65,11 @@ func TestIndexByteNearPageBoundary(t *testing.T) {
func TestIndexNearPageBoundary(t *testing.T) { func TestIndexNearPageBoundary(t *testing.T) {
t.Parallel() t.Parallel()
var q [64]byte q := dangerousSlice(t)
if len(q) > 64 {
// Only worry about when we're near the end of a page.
q = q[len(q)-64:]
}
b := dangerousSlice(t) b := dangerousSlice(t)
if len(b) > 256 { if len(b) > 256 {
// Only worry about when we're near the end of a page. // Only worry about when we're near the end of a page.
@ -82,4 +85,16 @@ func TestIndexNearPageBoundary(t *testing.T) {
} }
q[j-1] = 0 q[j-1] = 0
} }
// Test differing alignments and sizes of q which always end on a page boundary.
q[len(q)-1] = 1 // difference is only found on the last byte
for j := 0; j < len(q); j++ {
for i := range b {
idx := Index(b[i:], q[j:])
if idx != -1 {
t.Fatalf("Index(b[%d:], q[%d:])=%d, want -1\n", i, j, idx)
}
}
}
q[len(q)-1] = 0
} }

View File

@ -21,7 +21,7 @@ func Equal(a, b []byte) bool {
} }
// Compare returns an integer comparing two byte slices lexicographically. // Compare returns an integer comparing two byte slices lexicographically.
// The result will be 0 if a==b, -1 if a < b, and +1 if a > b. // The result will be 0 if a == b, -1 if a < b, and +1 if a > b.
// A nil argument is equivalent to an empty slice. // A nil argument is equivalent to an empty slice.
func Compare(a, b []byte) int { func Compare(a, b []byte) int {
return bytealg.Compare(a, b) return bytealg.Compare(a, b)
@ -699,7 +699,7 @@ func ToValidUTF8(s, replacement []byte) []byte {
if c < utf8.RuneSelf { if c < utf8.RuneSelf {
i++ i++
invalid = false invalid = false
b = append(b, byte(c)) b = append(b, c)
continue continue
} }
_, wid := utf8.DecodeRune(s[i:]) _, wid := utf8.DecodeRune(s[i:])
@ -867,6 +867,8 @@ func lastIndexFunc(s []byte, f func(r rune) bool, truth bool) int {
// most-significant bit of the highest word, map to the full range of all // most-significant bit of the highest word, map to the full range of all
// 128 ASCII characters. The 128-bits of the upper 16 bytes will be zeroed, // 128 ASCII characters. The 128-bits of the upper 16 bytes will be zeroed,
// ensuring that any non-ASCII character will be reported as not in the set. // ensuring that any non-ASCII character will be reported as not in the set.
// This allocates a total of 32 bytes even though the upper half
// is unused to avoid bounds checks in asciiSet.contains.
type asciiSet [8]uint32 type asciiSet [8]uint32
// makeASCIISet creates a set of ASCII characters and reports whether all // makeASCIISet creates a set of ASCII characters and reports whether all
@ -877,53 +879,133 @@ func makeASCIISet(chars string) (as asciiSet, ok bool) {
if c >= utf8.RuneSelf { if c >= utf8.RuneSelf {
return as, false return as, false
} }
as[c>>5] |= 1 << uint(c&31) as[c/32] |= 1 << (c % 32)
} }
return as, true return as, true
} }
// contains reports whether c is inside the set. // contains reports whether c is inside the set.
func (as *asciiSet) contains(c byte) bool { func (as *asciiSet) contains(c byte) bool {
return (as[c>>5] & (1 << uint(c&31))) != 0 return (as[c/32] & (1 << (c % 32))) != 0
} }
func makeCutsetFunc(cutset string) func(r rune) bool { // containsRune is a simplified version of strings.ContainsRune
if len(cutset) == 1 && cutset[0] < utf8.RuneSelf { // to avoid importing the strings package.
return func(r rune) bool { // We avoid bytes.ContainsRune to avoid allocating a temporary copy of s.
return r == rune(cutset[0]) func containsRune(s string, r rune) bool {
for _, c := range s {
if c == r {
return true
} }
} }
if as, isASCII := makeASCIISet(cutset); isASCII { return false
return func(r rune) bool {
return r < utf8.RuneSelf && as.contains(byte(r))
}
}
return func(r rune) bool {
for _, c := range cutset {
if c == r {
return true
}
}
return false
}
} }
// Trim returns a subslice of s by slicing off all leading and // Trim returns a subslice of s by slicing off all leading and
// trailing UTF-8-encoded code points contained in cutset. // trailing UTF-8-encoded code points contained in cutset.
func Trim(s []byte, cutset string) []byte { func Trim(s []byte, cutset string) []byte {
return TrimFunc(s, makeCutsetFunc(cutset)) if len(s) == 0 || cutset == "" {
return s
}
if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
return trimLeftByte(trimRightByte(s, cutset[0]), cutset[0])
}
if as, ok := makeASCIISet(cutset); ok {
return trimLeftASCII(trimRightASCII(s, &as), &as)
}
return trimLeftUnicode(trimRightUnicode(s, cutset), cutset)
} }
// TrimLeft returns a subslice of s by slicing off all leading // TrimLeft returns a subslice of s by slicing off all leading
// UTF-8-encoded code points contained in cutset. // UTF-8-encoded code points contained in cutset.
func TrimLeft(s []byte, cutset string) []byte { func TrimLeft(s []byte, cutset string) []byte {
return TrimLeftFunc(s, makeCutsetFunc(cutset)) if len(s) == 0 || cutset == "" {
return s
}
if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
return trimLeftByte(s, cutset[0])
}
if as, ok := makeASCIISet(cutset); ok {
return trimLeftASCII(s, &as)
}
return trimLeftUnicode(s, cutset)
}
func trimLeftByte(s []byte, c byte) []byte {
for len(s) > 0 && s[0] == c {
s = s[1:]
}
return s
}
func trimLeftASCII(s []byte, as *asciiSet) []byte {
for len(s) > 0 {
if !as.contains(s[0]) {
break
}
s = s[1:]
}
return s
}
func trimLeftUnicode(s []byte, cutset string) []byte {
for len(s) > 0 {
r, n := rune(s[0]), 1
if r >= utf8.RuneSelf {
r, n = utf8.DecodeRune(s)
}
if !containsRune(cutset, r) {
break
}
s = s[n:]
}
return s
} }
// TrimRight returns a subslice of s by slicing off all trailing // TrimRight returns a subslice of s by slicing off all trailing
// UTF-8-encoded code points that are contained in cutset. // UTF-8-encoded code points that are contained in cutset.
func TrimRight(s []byte, cutset string) []byte { func TrimRight(s []byte, cutset string) []byte {
return TrimRightFunc(s, makeCutsetFunc(cutset)) if len(s) == 0 || cutset == "" {
return s
}
if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
return trimRightByte(s, cutset[0])
}
if as, ok := makeASCIISet(cutset); ok {
return trimRightASCII(s, &as)
}
return trimRightUnicode(s, cutset)
}
func trimRightByte(s []byte, c byte) []byte {
for len(s) > 0 && s[len(s)-1] == c {
s = s[:len(s)-1]
}
return s
}
func trimRightASCII(s []byte, as *asciiSet) []byte {
for len(s) > 0 {
if !as.contains(s[len(s)-1]) {
break
}
s = s[:len(s)-1]
}
return s
}
func trimRightUnicode(s []byte, cutset string) []byte {
for len(s) > 0 {
r, n := rune(s[len(s)-1]), 1
if r >= utf8.RuneSelf {
r, n = utf8.DecodeLastRune(s)
}
if !containsRune(cutset, r) {
break
}
s = s[:len(s)-n]
}
return s
} }
// TrimSpace returns a subslice of s by slicing off all leading and // TrimSpace returns a subslice of s by slicing off all leading and
@ -1174,3 +1256,16 @@ func Index(s, sep []byte) int {
} }
return -1 return -1
} }
// Cut slices s around the first instance of sep,
// returning the text before and after sep.
// The found result reports whether sep appears in s.
// If sep does not appear in s, cut returns s, nil, false.
//
// Cut returns slices of the original slice s, not copies.
func Cut(s, sep []byte) (before, after []byte, found bool) {
if i := Index(s, sep); i >= 0 {
return s[:i], s[i+len(sep):], true
}
return s, nil, false
}

View File

@ -1251,7 +1251,9 @@ var trimTests = []TrimTest{
{"TrimLeft", "abba", "ab", ""}, {"TrimLeft", "abba", "ab", ""},
{"TrimRight", "abba", "ab", ""}, {"TrimRight", "abba", "ab", ""},
{"TrimLeft", "abba", "a", "bba"}, {"TrimLeft", "abba", "a", "bba"},
{"TrimLeft", "abba", "b", "abba"},
{"TrimRight", "abba", "a", "abb"}, {"TrimRight", "abba", "a", "abb"},
{"TrimRight", "abba", "b", "abba"},
{"Trim", "<tag>", "<>", "tag"}, {"Trim", "<tag>", "<>", "tag"},
{"Trim", "* listitem", " *", "listitem"}, {"Trim", "* listitem", " *", "listitem"},
{"Trim", `"quote"`, `"`, "quote"}, {"Trim", `"quote"`, `"`, "quote"},
@ -1565,6 +1567,29 @@ func TestEqualFold(t *testing.T) {
} }
} }
var cutTests = []struct {
s, sep string
before, after string
found bool
}{
{"abc", "b", "a", "c", true},
{"abc", "a", "", "bc", true},
{"abc", "c", "ab", "", true},
{"abc", "abc", "", "", true},
{"abc", "", "", "abc", true},
{"abc", "d", "abc", "", false},
{"", "d", "", "", false},
{"", "", "", "", true},
}
func TestCut(t *testing.T) {
for _, tt := range cutTests {
if before, after, found := Cut([]byte(tt.s), []byte(tt.sep)); string(before) != tt.before || string(after) != tt.after || found != tt.found {
t.Errorf("Cut(%q, %q) = %q, %q, %v, want %q, %q, %v", tt.s, tt.sep, before, after, found, tt.before, tt.after, tt.found)
}
}
}
func TestBufferGrowNegative(t *testing.T) { func TestBufferGrowNegative(t *testing.T) {
defer func() { defer func() {
if err := recover(); err == nil { if err := recover(); err == nil {
@ -1963,6 +1988,13 @@ func BenchmarkTrimASCII(b *testing.B) {
} }
} }
func BenchmarkTrimByte(b *testing.B) {
x := []byte(" the quick brown fox ")
for i := 0; i < b.N; i++ {
Trim(x, " ")
}
}
func BenchmarkIndexPeriodic(b *testing.B) { func BenchmarkIndexPeriodic(b *testing.B) {
key := []byte{1, 1} key := []byte{1, 1}
for _, skip := range [...]int{2, 4, 8, 16, 32, 64} { for _, skip := range [...]int{2, 4, 8, 16, 32, 64} {

View File

@ -54,6 +54,19 @@ func ExampleBuffer_Len() {
// Output: 5 // Output: 5
} }
func ExampleBuffer_Next() {
var b bytes.Buffer
b.Grow(64)
b.Write([]byte("abcde"))
fmt.Printf("%s\n", string(b.Next(2)))
fmt.Printf("%s\n", string(b.Next(2)))
fmt.Printf("%s", string(b.Next(2)))
// Output:
// ab
// cd
// e
}
func ExampleCompare() { func ExampleCompare() {
// Interpret Compare's result by comparing it to zero. // Interpret Compare's result by comparing it to zero.
var a, b []byte var a, b []byte
@ -92,36 +105,6 @@ func ExampleCompare_search() {
} }
} }
func ExampleTrimSuffix() {
var b = []byte("Hello, goodbye, etc!")
b = bytes.TrimSuffix(b, []byte("goodbye, etc!"))
b = bytes.TrimSuffix(b, []byte("gopher"))
b = append(b, bytes.TrimSuffix([]byte("world!"), []byte("x!"))...)
os.Stdout.Write(b)
// Output: Hello, world!
}
func ExampleTrimPrefix() {
var b = []byte("Goodbye,, world!")
b = bytes.TrimPrefix(b, []byte("Goodbye,"))
b = bytes.TrimPrefix(b, []byte("See ya,"))
fmt.Printf("Hello%s", b)
// Output: Hello, world!
}
func ExampleFields() {
fmt.Printf("Fields are: %q", bytes.Fields([]byte(" foo bar baz ")))
// Output: Fields are: ["foo" "bar" "baz"]
}
func ExampleFieldsFunc() {
f := func(c rune) bool {
return !unicode.IsLetter(c) && !unicode.IsNumber(c)
}
fmt.Printf("Fields are: %q", bytes.FieldsFunc([]byte(" foo1;bar2,baz3..."), f))
// Output: Fields are: ["foo1" "bar2" "baz3"]
}
func ExampleContains() { func ExampleContains() {
fmt.Println(bytes.Contains([]byte("seafood"), []byte("foo"))) fmt.Println(bytes.Contains([]byte("seafood"), []byte("foo")))
fmt.Println(bytes.Contains([]byte("seafood"), []byte("bar"))) fmt.Println(bytes.Contains([]byte("seafood"), []byte("bar")))
@ -168,6 +151,22 @@ func ExampleCount() {
// 5 // 5
} }
func ExampleCut() {
show := func(s, sep string) {
before, after, found := bytes.Cut([]byte(s), []byte(sep))
fmt.Printf("Cut(%q, %q) = %q, %q, %v\n", s, sep, before, after, found)
}
show("Gopher", "Go")
show("Gopher", "ph")
show("Gopher", "er")
show("Gopher", "Badger")
// Output:
// Cut("Gopher", "Go") = "", "pher", true
// Cut("Gopher", "ph") = "Go", "er", true
// Cut("Gopher", "er") = "Goph", "", true
// Cut("Gopher", "Badger") = "Gopher", "", false
}
func ExampleEqual() { func ExampleEqual() {
fmt.Println(bytes.Equal([]byte("Go"), []byte("Go"))) fmt.Println(bytes.Equal([]byte("Go"), []byte("Go")))
fmt.Println(bytes.Equal([]byte("Go"), []byte("C++"))) fmt.Println(bytes.Equal([]byte("Go"), []byte("C++")))
@ -181,6 +180,19 @@ func ExampleEqualFold() {
// Output: true // Output: true
} }
func ExampleFields() {
fmt.Printf("Fields are: %q", bytes.Fields([]byte(" foo bar baz ")))
// Output: Fields are: ["foo" "bar" "baz"]
}
func ExampleFieldsFunc() {
f := func(c rune) bool {
return !unicode.IsLetter(c) && !unicode.IsNumber(c)
}
fmt.Printf("Fields are: %q", bytes.FieldsFunc([]byte(" foo1;bar2,baz3..."), f))
// Output: Fields are: ["foo1" "bar2" "baz3"]
}
func ExampleHasPrefix() { func ExampleHasPrefix() {
fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("Go"))) fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("Go")))
fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("C"))) fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("C")))
@ -246,6 +258,12 @@ func ExampleIndexRune() {
// -1 // -1
} }
func ExampleJoin() {
s := [][]byte{[]byte("foo"), []byte("bar"), []byte("baz")}
fmt.Printf("%s", bytes.Join(s, []byte(", ")))
// Output: foo, bar, baz
}
func ExampleLastIndex() { func ExampleLastIndex() {
fmt.Println(bytes.Index([]byte("go gopher"), []byte("go"))) fmt.Println(bytes.Index([]byte("go gopher"), []byte("go")))
fmt.Println(bytes.LastIndex([]byte("go gopher"), []byte("go"))) fmt.Println(bytes.LastIndex([]byte("go gopher"), []byte("go")))
@ -286,10 +304,12 @@ func ExampleLastIndexFunc() {
// -1 // -1
} }
func ExampleJoin() { func ExampleReader_Len() {
s := [][]byte{[]byte("foo"), []byte("bar"), []byte("baz")} fmt.Println(bytes.NewReader([]byte("Hi!")).Len())
fmt.Printf("%s", bytes.Join(s, []byte(", "))) fmt.Println(bytes.NewReader([]byte("こんにちは!")).Len())
// Output: foo, bar, baz // Output:
// 3
// 16
} }
func ExampleRepeat() { func ExampleRepeat() {
@ -399,20 +419,6 @@ func ExampleTrimFunc() {
// go-gopher! // go-gopher!
} }
func ExampleMap() {
rot13 := func(r rune) rune {
switch {
case r >= 'A' && r <= 'Z':
return 'A' + (r-'A'+13)%26
case r >= 'a' && r <= 'z':
return 'a' + (r-'a'+13)%26
}
return r
}
fmt.Printf("%s", bytes.Map(rot13, []byte("'Twas brillig and the slithy gopher...")))
// Output: 'Gjnf oevyyvt naq gur fyvgul tbcure...
}
func ExampleTrimLeft() { func ExampleTrimLeft() {
fmt.Print(string(bytes.TrimLeft([]byte("453gopher8257"), "0123456789"))) fmt.Print(string(bytes.TrimLeft([]byte("453gopher8257"), "0123456789")))
// Output: // Output:
@ -429,11 +435,28 @@ func ExampleTrimLeftFunc() {
// go-gopher!567 // go-gopher!567
} }
func ExampleTrimPrefix() {
var b = []byte("Goodbye,, world!")
b = bytes.TrimPrefix(b, []byte("Goodbye,"))
b = bytes.TrimPrefix(b, []byte("See ya,"))
fmt.Printf("Hello%s", b)
// Output: Hello, world!
}
func ExampleTrimSpace() { func ExampleTrimSpace() {
fmt.Printf("%s", bytes.TrimSpace([]byte(" \t\n a lone gopher \n\t\r\n"))) fmt.Printf("%s", bytes.TrimSpace([]byte(" \t\n a lone gopher \n\t\r\n")))
// Output: a lone gopher // Output: a lone gopher
} }
func ExampleTrimSuffix() {
var b = []byte("Hello, goodbye, etc!")
b = bytes.TrimSuffix(b, []byte("goodbye, etc!"))
b = bytes.TrimSuffix(b, []byte("gopher"))
b = append(b, bytes.TrimSuffix([]byte("world!"), []byte("x!"))...)
os.Stdout.Write(b)
// Output: Hello, world!
}
func ExampleTrimRight() { func ExampleTrimRight() {
fmt.Print(string(bytes.TrimRight([]byte("453gopher8257"), "0123456789"))) fmt.Print(string(bytes.TrimRight([]byte("453gopher8257"), "0123456789")))
// Output: // Output:
@ -450,21 +473,6 @@ func ExampleTrimRightFunc() {
// 1234go-gopher! // 1234go-gopher!
} }
func ExampleToUpper() {
fmt.Printf("%s", bytes.ToUpper([]byte("Gopher")))
// Output: GOPHER
}
func ExampleToUpperSpecial() {
str := []byte("ahoj vývojári golang")
totitle := bytes.ToUpperSpecial(unicode.AzeriCase, str)
fmt.Println("Original : " + string(str))
fmt.Println("ToUpper : " + string(totitle))
// Output:
// Original : ahoj vývojári golang
// ToUpper : AHOJ VÝVOJÁRİ GOLANG
}
func ExampleToLower() { func ExampleToLower() {
fmt.Printf("%s", bytes.ToLower([]byte("Gopher"))) fmt.Printf("%s", bytes.ToLower([]byte("Gopher")))
// Output: gopher // Output: gopher
@ -480,10 +488,17 @@ func ExampleToLowerSpecial() {
// ToLower : ahoj vývojári golang // ToLower : ahoj vývojári golang
} }
func ExampleReader_Len() { func ExampleToUpper() {
fmt.Println(bytes.NewReader([]byte("Hi!")).Len()) fmt.Printf("%s", bytes.ToUpper([]byte("Gopher")))
fmt.Println(bytes.NewReader([]byte("こんにちは!")).Len()) // Output: GOPHER
// Output: }
// 3
// 16 func ExampleToUpperSpecial() {
str := []byte("ahoj vývojári golang")
totitle := bytes.ToUpperSpecial(unicode.AzeriCase, str)
fmt.Println("Original : " + string(str))
fmt.Println("ToUpper : " + string(totitle))
// Output:
// Original : ahoj vývojári golang
// ToUpper : AHOJ VÝVOJÁRİ GOLANG
} }

View File

@ -653,10 +653,15 @@ func (w *Walker) ImportFrom(fromPath, fromDir string, mode types.ImportMode) (*t
} }
// Type-check package files. // Type-check package files.
var sizes types.Sizes
if w.context != nil {
sizes = types.SizesFor(w.context.Compiler, w.context.GOARCH)
}
conf := types.Config{ conf := types.Config{
IgnoreFuncBodies: true, IgnoreFuncBodies: true,
FakeImportC: true, FakeImportC: true,
Importer: w, Importer: w,
Sizes: sizes,
} }
pkg, err = conf.Check(name, fset, files, nil) pkg, err = conf.Check(name, fset, files, nil)
if err != nil { if err != nil {
@ -701,6 +706,36 @@ func sortedMethodNames(typ *types.Interface) []string {
return list return list
} }
// sortedEmbeddeds returns constraint types embedded in an
// interface. It does not include embedded interface types or methods.
func (w *Walker) sortedEmbeddeds(typ *types.Interface) []string {
n := typ.NumEmbeddeds()
list := make([]string, 0, n)
for i := 0; i < n; i++ {
emb := typ.EmbeddedType(i)
switch emb := emb.(type) {
case *types.Interface:
list = append(list, w.sortedEmbeddeds(emb)...)
case *types.Union:
var buf bytes.Buffer
nu := emb.Len()
for i := 0; i < nu; i++ {
if i > 0 {
buf.WriteString(" | ")
}
term := emb.Term(i)
if term.Tilde() {
buf.WriteByte('~')
}
w.writeType(&buf, term.Type())
}
list = append(list, buf.String())
}
}
sort.Strings(list)
return list
}
func (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) { func (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) {
switch typ := typ.(type) { switch typ := typ.(type) {
case *types.Basic: case *types.Basic:
@ -758,9 +793,16 @@ func (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) {
case *types.Interface: case *types.Interface:
buf.WriteString("interface{") buf.WriteString("interface{")
if typ.NumMethods() > 0 { if typ.NumMethods() > 0 || typ.NumEmbeddeds() > 0 {
buf.WriteByte(' ') buf.WriteByte(' ')
}
if typ.NumMethods() > 0 {
buf.WriteString(strings.Join(sortedMethodNames(typ), ", ")) buf.WriteString(strings.Join(sortedMethodNames(typ), ", "))
}
if typ.NumEmbeddeds() > 0 {
buf.WriteString(strings.Join(w.sortedEmbeddeds(typ), ", "))
}
if typ.NumMethods() > 0 || typ.NumEmbeddeds() > 0 {
buf.WriteByte(' ') buf.WriteByte(' ')
} }
buf.WriteString("}") buf.WriteString("}")
@ -795,12 +837,19 @@ func (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) {
} }
buf.WriteString(typ.Obj().Name()) buf.WriteString(typ.Obj().Name())
case *types.TypeParam:
// Type parameter names may change, so use a placeholder instead.
fmt.Fprintf(buf, "$%d", typ.Index())
default: default:
panic(fmt.Sprintf("unknown type %T", typ)) panic(fmt.Sprintf("unknown type %T", typ))
} }
} }
func (w *Walker) writeSignature(buf *bytes.Buffer, sig *types.Signature) { func (w *Walker) writeSignature(buf *bytes.Buffer, sig *types.Signature) {
if tparams := sig.TypeParams(); tparams != nil {
w.writeTypeParams(buf, tparams, true)
}
w.writeParams(buf, sig.Params(), sig.Variadic()) w.writeParams(buf, sig.Params(), sig.Variadic())
switch res := sig.Results(); res.Len() { switch res := sig.Results(); res.Len() {
case 0: case 0:
@ -814,6 +863,23 @@ func (w *Walker) writeSignature(buf *bytes.Buffer, sig *types.Signature) {
} }
} }
func (w *Walker) writeTypeParams(buf *bytes.Buffer, tparams *types.TypeParamList, withConstraints bool) {
buf.WriteByte('[')
c := tparams.Len()
for i := 0; i < c; i++ {
if i > 0 {
buf.WriteString(", ")
}
tp := tparams.At(i)
w.writeType(buf, tp)
if withConstraints {
buf.WriteByte(' ')
w.writeType(buf, tp.Constraint())
}
}
buf.WriteByte(']')
}
func (w *Walker) writeParams(buf *bytes.Buffer, t *types.Tuple, variadic bool) { func (w *Walker) writeParams(buf *bytes.Buffer, t *types.Tuple, variadic bool) {
buf.WriteByte('(') buf.WriteByte('(')
for i, n := 0, t.Len(); i < n; i++ { for i, n := 0, t.Len(); i < n; i++ {
@ -867,6 +933,12 @@ func (w *Walker) emitObj(obj types.Object) {
func (w *Walker) emitType(obj *types.TypeName) { func (w *Walker) emitType(obj *types.TypeName) {
name := obj.Name() name := obj.Name()
if tparams := obj.Type().(*types.Named).TypeParams(); tparams != nil {
var buf bytes.Buffer
buf.WriteString(name)
w.writeTypeParams(&buf, tparams, true)
name = buf.String()
}
typ := obj.Type() typ := obj.Type()
if obj.IsAlias() { if obj.IsAlias() {
w.emitf("type %s = %s", name, w.typeString(typ)) w.emitf("type %s = %s", name, w.typeString(typ))
@ -990,7 +1062,13 @@ func (w *Walker) emitMethod(m *types.Selection) {
log.Fatalf("exported method with unexported receiver base type: %s", m) log.Fatalf("exported method with unexported receiver base type: %s", m)
} }
} }
w.emitf("method (%s) %s%s", w.typeString(recv), m.Obj().Name(), w.signatureString(sig)) tps := ""
if rtp := sig.RecvTypeParams(); rtp != nil {
var buf bytes.Buffer
w.writeTypeParams(&buf, rtp, false)
tps = buf.String()
}
w.emitf("method (%s%s) %s%s", w.typeString(recv), tps, m.Obj().Name(), w.signatureString(sig))
} }
func (w *Walker) emitf(format string, args ...interface{}) { func (w *Walker) emitf(format string, args ...interface{}) {

View File

@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build ignore //go:build ignore
// +build ignore
// The run program is invoked via the dist tool. // The run program is invoked via the dist tool.
// To invoke manually: go tool dist test -run api --no-rebuild // To invoke manually: go tool dist test -run api --no-rebuild

View File

@ -0,0 +1,5 @@
pkg p4, func NewPair[$0 interface{ M }, $1 interface{ ~int }]($0, $1) Pair
pkg p4, method (Pair[$0, $1]) Second() $1
pkg p4, method (Pair[$0, $1]) First() $0
pkg p4, type Pair[$0 interface{ M }, $1 interface{ ~int }] struct
pkg p4, func Clone[$0 interface{ ~[]$1 }, $1 interface{}]($0) $0

26
src/cmd/api/testdata/src/pkg/p4/p4.go vendored Normal file
View File

@ -0,0 +1,26 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p4
type Pair[T1 interface { M() }, T2 ~int] struct {
f1 T1
f2 T2
}
func NewPair[T1 interface { M() }, T2 ~int](v1 T1, v2 T2) Pair[T1, T2] {
return Pair[T1, T2]{f1: v1, f2: v2}
}
func (p Pair[X1, _]) First() X1 {
return p.f1
}
func (p Pair[_, X2]) Second() X2 {
return p.f2
}
func Clone[S ~[]T, T any](s S) S {
return append(S(nil), s...)
}

View File

@ -50,7 +50,7 @@ func nilRegisterNumber(name string, n int16) (int16, bool) {
// Set configures the architecture specified by GOARCH and returns its representation. // Set configures the architecture specified by GOARCH and returns its representation.
// It returns nil if GOARCH is not recognized. // It returns nil if GOARCH is not recognized.
func Set(GOARCH string) *Arch { func Set(GOARCH string, shared bool) *Arch {
switch GOARCH { switch GOARCH {
case "386": case "386":
return archX86(&x86.Link386) return archX86(&x86.Link386)
@ -73,7 +73,7 @@ func Set(GOARCH string) *Arch {
case "ppc64le": case "ppc64le":
return archPPC64(&ppc64.Linkppc64le) return archPPC64(&ppc64.Linkppc64le)
case "riscv64": case "riscv64":
return archRISCV64() return archRISCV64(shared)
case "s390x": case "s390x":
return archS390x() return archS390x()
case "wasm": case "wasm":
@ -178,6 +178,10 @@ func archX86(linkArch *obj.LinkArch) *Arch {
instructions["PSLLDQ"] = x86.APSLLO instructions["PSLLDQ"] = x86.APSLLO
instructions["PSRLDQ"] = x86.APSRLO instructions["PSRLDQ"] = x86.APSRLO
instructions["PADDD"] = x86.APADDL instructions["PADDD"] = x86.APADDL
// Spellings originally used in CL 97235.
instructions["MOVBELL"] = x86.AMOVBEL
instructions["MOVBEQQ"] = x86.AMOVBEQ
instructions["MOVBEWW"] = x86.AMOVBEW
return &Arch{ return &Arch{
LinkArch: linkArch, LinkArch: linkArch,
@ -374,6 +378,9 @@ func archPPC64(linkArch *obj.LinkArch) *Arch {
for i := ppc64.REG_MSR; i <= ppc64.REG_CR; i++ { for i := ppc64.REG_MSR; i <= ppc64.REG_CR; i++ {
register[obj.Rconv(i)] = int16(i) register[obj.Rconv(i)] = int16(i)
} }
for i := ppc64.REG_CR0LT; i <= ppc64.REG_CR7SO; i++ {
register[obj.Rconv(i)] = int16(i)
}
register["CR"] = ppc64.REG_CR register["CR"] = ppc64.REG_CR
register["XER"] = ppc64.REG_XER register["XER"] = ppc64.REG_XER
register["LR"] = ppc64.REG_LR register["LR"] = ppc64.REG_LR
@ -534,12 +541,18 @@ func archMips64(linkArch *obj.LinkArch) *Arch {
} }
} }
func archRISCV64() *Arch { func archRISCV64(shared bool) *Arch {
register := make(map[string]int16) register := make(map[string]int16)
// Standard register names. // Standard register names.
for i := riscv.REG_X0; i <= riscv.REG_X31; i++ { for i := riscv.REG_X0; i <= riscv.REG_X31; i++ {
if i == riscv.REG_G { // Disallow X3 in shared mode, as this will likely be used as the
// GP register, which could result in problems in non-Go code,
// including signal handlers.
if shared && i == riscv.REG_GP {
continue
}
if i == riscv.REG_TP || i == riscv.REG_G {
continue continue
} }
name := fmt.Sprintf("X%d", i-riscv.REG_X0) name := fmt.Sprintf("X%d", i-riscv.REG_X0)

View File

@ -165,27 +165,21 @@ func ARM64RegisterExtension(a *obj.Addr, ext string, reg, num int16, isAmount, i
} }
} }
if reg <= arm64.REG_R31 && reg >= arm64.REG_R0 { if reg <= arm64.REG_R31 && reg >= arm64.REG_R0 {
if !isAmount {
return errors.New("invalid register extension")
}
switch ext { switch ext {
case "UXTB": case "UXTB":
if !isAmount {
return errors.New("invalid register extension")
}
if a.Type == obj.TYPE_MEM { if a.Type == obj.TYPE_MEM {
return errors.New("invalid shift for the register offset addressing mode") return errors.New("invalid shift for the register offset addressing mode")
} }
a.Reg = arm64.REG_UXTB + Rnum a.Reg = arm64.REG_UXTB + Rnum
case "UXTH": case "UXTH":
if !isAmount {
return errors.New("invalid register extension")
}
if a.Type == obj.TYPE_MEM { if a.Type == obj.TYPE_MEM {
return errors.New("invalid shift for the register offset addressing mode") return errors.New("invalid shift for the register offset addressing mode")
} }
a.Reg = arm64.REG_UXTH + Rnum a.Reg = arm64.REG_UXTH + Rnum
case "UXTW": case "UXTW":
if !isAmount {
return errors.New("invalid register extension")
}
// effective address of memory is a base register value and an offset register value. // effective address of memory is a base register value and an offset register value.
if a.Type == obj.TYPE_MEM { if a.Type == obj.TYPE_MEM {
a.Index = arm64.REG_UXTW + Rnum a.Index = arm64.REG_UXTW + Rnum
@ -193,48 +187,33 @@ func ARM64RegisterExtension(a *obj.Addr, ext string, reg, num int16, isAmount, i
a.Reg = arm64.REG_UXTW + Rnum a.Reg = arm64.REG_UXTW + Rnum
} }
case "UXTX": case "UXTX":
if !isAmount {
return errors.New("invalid register extension")
}
if a.Type == obj.TYPE_MEM { if a.Type == obj.TYPE_MEM {
return errors.New("invalid shift for the register offset addressing mode") return errors.New("invalid shift for the register offset addressing mode")
} }
a.Reg = arm64.REG_UXTX + Rnum a.Reg = arm64.REG_UXTX + Rnum
case "SXTB": case "SXTB":
if !isAmount { if a.Type == obj.TYPE_MEM {
return errors.New("invalid register extension") return errors.New("invalid shift for the register offset addressing mode")
} }
a.Reg = arm64.REG_SXTB + Rnum a.Reg = arm64.REG_SXTB + Rnum
case "SXTH": case "SXTH":
if !isAmount {
return errors.New("invalid register extension")
}
if a.Type == obj.TYPE_MEM { if a.Type == obj.TYPE_MEM {
return errors.New("invalid shift for the register offset addressing mode") return errors.New("invalid shift for the register offset addressing mode")
} }
a.Reg = arm64.REG_SXTH + Rnum a.Reg = arm64.REG_SXTH + Rnum
case "SXTW": case "SXTW":
if !isAmount {
return errors.New("invalid register extension")
}
if a.Type == obj.TYPE_MEM { if a.Type == obj.TYPE_MEM {
a.Index = arm64.REG_SXTW + Rnum a.Index = arm64.REG_SXTW + Rnum
} else { } else {
a.Reg = arm64.REG_SXTW + Rnum a.Reg = arm64.REG_SXTW + Rnum
} }
case "SXTX": case "SXTX":
if !isAmount {
return errors.New("invalid register extension")
}
if a.Type == obj.TYPE_MEM { if a.Type == obj.TYPE_MEM {
a.Index = arm64.REG_SXTX + Rnum a.Index = arm64.REG_SXTX + Rnum
} else { } else {
a.Reg = arm64.REG_SXTX + Rnum a.Reg = arm64.REG_SXTX + Rnum
} }
case "LSL": case "LSL":
if !isAmount {
return errors.New("invalid register extension")
}
a.Index = arm64.REG_LSL + Rnum a.Index = arm64.REG_LSL + Rnum
default: default:
return errors.New("unsupported general register extension type: " + ext) return errors.New("unsupported general register extension type: " + ext)

View File

@ -793,6 +793,13 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
return return
} }
} }
if p.arch.Family == sys.RISCV64 {
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
prog.SetRestArgs([]obj.Addr{a[2]})
prog.To = a[3]
break
}
if p.arch.Family == sys.S390X { if p.arch.Family == sys.S390X {
if a[1].Type != obj.TYPE_REG { if a[1].Type != obj.TYPE_REG {
p.errorf("second operand must be a register in %s instruction", op) p.errorf("second operand must be a register in %s instruction", op)

View File

@ -19,7 +19,7 @@ import (
func setArch(goarch string) (*arch.Arch, *obj.Link) { func setArch(goarch string) (*arch.Arch, *obj.Link) {
buildcfg.GOOS = "linux" // obj can handle this OS for all architectures. buildcfg.GOOS = "linux" // obj can handle this OS for all architectures.
buildcfg.GOARCH = goarch buildcfg.GOARCH = goarch
architecture := arch.Set(goarch) architecture := arch.Set(goarch, false)
if architecture == nil { if architecture == nil {
panic("asm: unrecognized architecture " + goarch) panic("asm: unrecognized architecture " + goarch)
} }

View File

@ -2495,30 +2495,30 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
MOVAPS X11, (BX) // 440f291b MOVAPS X11, (BX) // 440f291b
MOVAPS X2, (R11) // 410f2913 MOVAPS X2, (R11) // 410f2913
MOVAPS X11, (R11) // 450f291b MOVAPS X11, (R11) // 450f291b
MOVBEWW DX, (BX) // 660f38f113 MOVBEW DX, (BX) // 660f38f113
MOVBEWW R11, (BX) // 66440f38f11b MOVBEW R11, (BX) // 66440f38f11b
MOVBEWW DX, (R11) // 66410f38f113 MOVBEW DX, (R11) // 66410f38f113
MOVBEWW R11, (R11) // 66450f38f11b MOVBEW R11, (R11) // 66450f38f11b
MOVBEWW (BX), DX // 660f38f013 MOVBEW (BX), DX // 660f38f013
MOVBEWW (R11), DX // 66410f38f013 MOVBEW (R11), DX // 66410f38f013
MOVBEWW (BX), R11 // 66440f38f01b MOVBEW (BX), R11 // 66440f38f01b
MOVBEWW (R11), R11 // 66450f38f01b MOVBEW (R11), R11 // 66450f38f01b
MOVBELL DX, (BX) // 0f38f113 MOVBEL DX, (BX) // 0f38f113
MOVBELL R11, (BX) // 440f38f11b MOVBEL R11, (BX) // 440f38f11b
MOVBELL DX, (R11) // 410f38f113 MOVBEL DX, (R11) // 410f38f113
MOVBELL R11, (R11) // 450f38f11b MOVBEL R11, (R11) // 450f38f11b
MOVBELL (BX), DX // 0f38f013 MOVBEL (BX), DX // 0f38f013
MOVBELL (R11), DX // 410f38f013 MOVBEL (R11), DX // 410f38f013
MOVBELL (BX), R11 // 440f38f01b MOVBEL (BX), R11 // 440f38f01b
MOVBELL (R11), R11 // 450f38f01b MOVBEL (R11), R11 // 450f38f01b
MOVBEQQ DX, (BX) // 480f38f113 MOVBEQ DX, (BX) // 480f38f113
MOVBEQQ R11, (BX) // 4c0f38f11b MOVBEQ R11, (BX) // 4c0f38f11b
MOVBEQQ DX, (R11) // 490f38f113 MOVBEQ DX, (R11) // 490f38f113
MOVBEQQ R11, (R11) // 4d0f38f11b MOVBEQ R11, (R11) // 4d0f38f11b
MOVBEQQ (BX), DX // 480f38f013 MOVBEQ (BX), DX // 480f38f013
MOVBEQQ (R11), DX // 490f38f013 MOVBEQ (R11), DX // 490f38f013
MOVBEQQ (BX), R11 // 4c0f38f01b MOVBEQ (BX), R11 // 4c0f38f01b
MOVBEQQ (R11), R11 // 4d0f38f01b MOVBEQ (R11), R11 // 4d0f38f01b
MOVQ (BX), M2 // 0f6e13 or 0f6f13 or 480f6e13 MOVQ (BX), M2 // 0f6e13 or 0f6f13 or 480f6e13
MOVQ (R11), M2 // 410f6e13 or 410f6f13 or 490f6e13 MOVQ (R11), M2 // 410f6e13 or 410f6f13 or 490f6e13
MOVQ DX, M2 // 0f6ed2 or 480f6ed2 MOVQ DX, M2 // 0f6ed2 or 480f6ed2

View File

@ -334,6 +334,8 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
EONW $0x6006000060060, R5 // EONW $1689262177517664, R5 // 1b0c8052db00a072a5003b4a EONW $0x6006000060060, R5 // EONW $1689262177517664, R5 // 1b0c8052db00a072a5003b4a
ORNW $0x6006000060060, R5 // ORNW $1689262177517664, R5 // 1b0c8052db00a072a5003b2a ORNW $0x6006000060060, R5 // ORNW $1689262177517664, R5 // 1b0c8052db00a072a5003b2a
BICSW $0x6006000060060, R5 // BICSW $1689262177517664, R5 // 1b0c8052db00a072a5003b6a BICSW $0x6006000060060, R5 // BICSW $1689262177517664, R5 // 1b0c8052db00a072a5003b6a
AND $1, ZR // fb0340b2ff031b8a
ANDW $1, ZR // fb030032ff031b0a
// TODO: this could have better encoding // TODO: this could have better encoding
ANDW $-1, R10 // 1b0080124a011b0a ANDW $-1, R10 // 1b0080124a011b0a
AND $8, R0, RSP // 1f007d92 AND $8, R0, RSP // 1f007d92
@ -369,9 +371,9 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
MOVD $-1, R1 // 01008092 MOVD $-1, R1 // 01008092
MOVD $0x210000, R0 // MOVD $2162688, R0 // 2004a0d2 MOVD $0x210000, R0 // MOVD $2162688, R0 // 2004a0d2
MOVD $0xffffffffffffaaaa, R1 // MOVD $-21846, R1 // a1aa8a92 MOVD $0xffffffffffffaaaa, R1 // MOVD $-21846, R1 // a1aa8a92
MOVW $1, ZR MOVW $1, ZR // 3f008052
MOVW $1, R1 MOVW $1, R1
MOVD $1, ZR MOVD $1, ZR // 3f0080d2
MOVD $1, R1 MOVD $1, R1
MOVK $1, R1 MOVK $1, R1
MOVD $0x1000100010001000, RSP // MOVD $1152939097061330944, RSP // ff8304b2 MOVD $0x1000100010001000, RSP // MOVD $1152939097061330944, RSP // ff8304b2
@ -386,10 +388,10 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
VMOVQ $0x8040201008040202, $0x7040201008040201, V20 // VMOVQ $-9205322385119247870, $8088500183983456769, V20 VMOVQ $0x8040201008040202, $0x7040201008040201, V20 // VMOVQ $-9205322385119247870, $8088500183983456769, V20
// mov(to/from sp) // mov(to/from sp)
MOVD $0x1002(RSP), R1 // MOVD $4098(RSP), R1 // fb074091610b0091 MOVD $0x1002(RSP), R1 // MOVD $4098(RSP), R1 // e107409121080091
MOVD $0x1708(RSP), RSP // MOVD $5896(RSP), RSP // fb0740917f231c91 MOVD $0x1708(RSP), RSP // MOVD $5896(RSP), RSP // ff074091ff231c91
MOVD $0x2001(R7), R1 // MOVD $8193(R7), R1 // fb08409161070091 MOVD $0x2001(R7), R1 // MOVD $8193(R7), R1 // e108409121040091
MOVD $0xffffff(R7), R1 // MOVD $16777215(R7), R1 // fbfc7f9161ff3f91 MOVD $0xffffff(R7), R1 // MOVD $16777215(R7), R1 // e1fc7f9121fc3f91
MOVD $-0x1(R7), R1 // MOVD $-1(R7), R1 // e10400d1 MOVD $-0x1(R7), R1 // MOVD $-1(R7), R1 // e10400d1
MOVD $-0x30(R7), R1 // MOVD $-48(R7), R1 // e1c000d1 MOVD $-0x30(R7), R1 // MOVD $-48(R7), R1 // e1c000d1
MOVD $-0x708(R7), R1 // MOVD $-1800(R7), R1 // e1201cd1 MOVD $-0x708(R7), R1 // MOVD $-1800(R7), R1 // e1201cd1

View File

@ -3,7 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
TEXT errors(SB),$0 TEXT errors(SB),$0
AND $1, RSP // ERROR "illegal combination" AND $1, RSP // ERROR "illegal source register"
ANDS $1, R0, RSP // ERROR "illegal combination" ANDS $1, R0, RSP // ERROR "illegal combination"
ADDSW R7->32, R14, R13 // ERROR "shift amount out of range 0 to 31" ADDSW R7->32, R14, R13 // ERROR "shift amount out of range 0 to 31"
ADD R1.UXTB<<5, R2, R3 // ERROR "shift amount out of range 0 to 4" ADD R1.UXTB<<5, R2, R3 // ERROR "shift amount out of range 0 to 4"
@ -406,12 +406,12 @@ TEXT errors(SB),$0
VBIF V0.D2, V1.D2, V2.D2 // ERROR "invalid arrangement" VBIF V0.D2, V1.D2, V2.D2 // ERROR "invalid arrangement"
VUADDW V9.B8, V12.H8, V14.B8 // ERROR "invalid arrangement" VUADDW V9.B8, V12.H8, V14.B8 // ERROR "invalid arrangement"
VUADDW2 V9.B8, V12.S4, V14.S4 // ERROR "operand mismatch" VUADDW2 V9.B8, V12.S4, V14.S4 // ERROR "operand mismatch"
VUMAX V1.D2, V2.D2, V3.D2 // ERROR "invalid arrangement" VUMAX V1.D2, V2.D2, V3.D2 // ERROR "invalid arrangement"
VUMIN V1.D2, V2.D2, V3.D2 // ERROR "invalid arrangement" VUMIN V1.D2, V2.D2, V3.D2 // ERROR "invalid arrangement"
VUMAX V1.B8, V2.B8, V3.B16 // ERROR "operand mismatch" VUMAX V1.B8, V2.B8, V3.B16 // ERROR "operand mismatch"
VUMIN V1.H4, V2.S4, V3.H4 // ERROR "operand mismatch" VUMIN V1.H4, V2.S4, V3.H4 // ERROR "operand mismatch"
VSLI $64, V7.D2, V8.D2 // ERROR "shift out of range" VSLI $64, V7.D2, V8.D2 // ERROR "shift out of range"
VUSRA $0, V7.D2, V8.D2 // ERROR "shift out of range" VUSRA $0, V7.D2, V8.D2 // ERROR "shift out of range"
CASPD (R3, R4), (R2), (R8, R9) // ERROR "source register pair must start from even register" CASPD (R3, R4), (R2), (R8, R9) // ERROR "source register pair must start from even register"
CASPD (R2, R3), (R2), (R9, R10) // ERROR "destination register pair must start from even register" CASPD (R2, R3), (R2), (R9, R10) // ERROR "destination register pair must start from even register"
CASPD (R2, R4), (R2), (R8, R9) // ERROR "source register pair must be contiguous" CASPD (R2, R4), (R2), (R8, R9) // ERROR "source register pair must be contiguous"
@ -419,4 +419,17 @@ TEXT errors(SB),$0
ADD R1>>2, RSP, R3 // ERROR "illegal combination" ADD R1>>2, RSP, R3 // ERROR "illegal combination"
ADDS R2<<3, R3, RSP // ERROR "unexpected SP reference" ADDS R2<<3, R3, RSP // ERROR "unexpected SP reference"
CMP R1<<5, RSP // ERROR "the left shift amount out of range 0 to 4" CMP R1<<5, RSP // ERROR "the left shift amount out of range 0 to 4"
MOVD.P y+8(FP), R1 // ERROR "illegal combination"
MOVD.W x-8(SP), R1 // ERROR "illegal combination"
LDP.P x+8(FP), (R0, R1) // ERROR "illegal combination"
LDP.W x+8(SP), (R0, R1) // ERROR "illegal combination"
ADD $0x1234567, R27, R3 // ERROR "cannot use REGTMP as source"
ADD $0x3fffffffc000, R27, R5 // ERROR "cannot use REGTMP as source"
AND $0x22220000, R27, R4 // ERROR "cannot use REGTMP as source"
ANDW $0x6006000060060, R27, R5 // ERROR "cannot use REGTMP as source"
STP (R3, R4), 0x1234567(R27) // ERROR "REGTMP used in large offset store"
LDP 0x1234567(R27), (R3, R4) // ERROR "REGTMP used in large offset load"
STP (R26, R27), 700(R2) // ERROR "cannot use REGTMP as source"
MOVK $0, R10 // ERROR "zero shifts cannot be handled correctly"
MOVK $(0<<32), R10 // ERROR "zero shifts cannot be handled correctly"
RET RET

View File

@ -342,14 +342,14 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
NOP F2 NOP F2
NOP $4 NOP $4
CRAND CR1, CR2, CR3 // 4c620a02 CRAND CR0GT, CR0EQ, CR0SO // 4c620a02
CRANDN CR1, CR2, CR3 // 4c620902 CRANDN CR0GT, CR0EQ, CR0SO // 4c620902
CREQV CR1, CR2, CR3 // 4c620a42 CREQV CR0GT, CR0EQ, CR0SO // 4c620a42
CRNAND CR1, CR2, CR3 // 4c6209c2 CRNAND CR0GT, CR0EQ, CR0SO // 4c6209c2
CRNOR CR1, CR2, CR3 // 4c620842 CRNOR CR0GT, CR0EQ, CR0SO // 4c620842
CROR CR1, CR2, CR3 // 4c620b82 CROR CR0GT, CR0EQ, CR0SO // 4c620b82
CRORN CR1, CR2, CR3 // 4c620b42 CRORN CR0GT, CR0EQ, CR0SO // 4c620b42
CRXOR CR1, CR2, CR3 // 4c620982 CRXOR CR0GT, CR0EQ, CR0SO // 4c620982
ISEL $1, R3, R4, R5 // 7ca3205e ISEL $1, R3, R4, R5 // 7ca3205e
ISEL $0, R3, R4, R5 // 7ca3201e ISEL $0, R3, R4, R5 // 7ca3201e
@ -649,6 +649,8 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
LXVB16X (R3)(R4), VS1 // 7c241ed8 LXVB16X (R3)(R4), VS1 // 7c241ed8
LXVW4X (R3)(R4), VS1 // 7c241e18 LXVW4X (R3)(R4), VS1 // 7c241e18
LXV 16(R3), VS1 // f4230011 LXV 16(R3), VS1 // f4230011
LXV 16(R3), VS33 // f4230019
LXV 16(R3), V1 // f4230019
LXVL R3, R4, VS1 // 7c23221a LXVL R3, R4, VS1 // 7c23221a
LXVLL R3, R4, VS1 // 7c23225a LXVLL R3, R4, VS1 // 7c23225a
LXVX R3, R4, VS1 // 7c232218 LXVX R3, R4, VS1 // 7c232218
@ -668,8 +670,13 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
MTFPRD R3, F0 // 7c030166 MTFPRD R3, F0 // 7c030166
MFVRD V0, R3 // 7c030067 MFVRD V0, R3 // 7c030067
MFVSRLD VS63,R4 // 7fe40267 MFVSRLD VS63,R4 // 7fe40267
MFVSRLD V31,R4 // 7fe40267
MFVSRWZ VS33,R4 // 7c2400e7 MFVSRWZ VS33,R4 // 7c2400e7
MFVSRWZ V1,R4 // 7c2400e7
MTVSRD R3, VS1 // 7c230166 MTVSRD R3, VS1 // 7c230166
MTVSRDD R3, R4, VS1 // 7c232366
MTVSRDD R3, R4, VS33 // 7c232367
MTVSRDD R3, R4, V1 // 7c232367
MTVRD R3, V13 // 7da30167 MTVRD R3, V13 // 7da30167
MTVSRWA R4, VS31 // 7fe401a6 MTVSRWA R4, VS31 // 7fe401a6
MTVSRWS R4, VS32 // 7c040327 MTVSRWS R4, VS32 // 7c040327
@ -678,6 +685,8 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
XXBRW VS1, VS2 // f04f0f6c XXBRW VS1, VS2 // f04f0f6c
XXBRH VS2, VS3 // f067176c XXBRH VS2, VS3 // f067176c
XXLAND VS1, VS2, VS3 // f0611410 XXLAND VS1, VS2, VS3 // f0611410
XXLAND V1, V2, V3 // f0611417
XXLAND VS33, VS34, VS35 // f0611417
XXLANDC VS1, VS2, VS3 // f0611450 XXLANDC VS1, VS2, VS3 // f0611450
XXLEQV VS0, VS1, VS2 // f0400dd0 XXLEQV VS0, VS1, VS2 // f0400dd0
XXLNAND VS0, VS1, VS2 // f0400d90 XXLNAND VS0, VS1, VS2 // f0400d90
@ -687,11 +696,17 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
XXLORQ VS1, VS2, VS3 // f0611490 XXLORQ VS1, VS2, VS3 // f0611490
XXLXOR VS1, VS2, VS3 // f06114d0 XXLXOR VS1, VS2, VS3 // f06114d0
XXSEL VS1, VS2, VS3, VS4 // f08110f0 XXSEL VS1, VS2, VS3, VS4 // f08110f0
XXSEL VS33, VS34, VS35, VS36 // f08110ff
XXSEL V1, V2, V3, V4 // f08110ff
XXMRGHW VS1, VS2, VS3 // f0611090 XXMRGHW VS1, VS2, VS3 // f0611090
XXMRGLW VS1, VS2, VS3 // f0611190 XXMRGLW VS1, VS2, VS3 // f0611190
XXSPLTW VS1, $1, VS2 // f0410a90 XXSPLTW VS1, $1, VS2 // f0410a90
XXSPLTW VS33, $1, VS34 // f0410a93
XXSPLTW V1, $1, V2 // f0410a93
XXPERM VS1, VS2, VS3 // f06110d0 XXPERM VS1, VS2, VS3 // f06110d0
XXSLDWI VS1, VS2, $1, VS3 // f0611110 XXSLDWI VS1, VS2, $1, VS3 // f0611110
XXSLDWI V1, V2, $1, V3 // f0611117
XXSLDWI VS33, VS34, $1, VS35 // f0611117
XSCVDPSP VS1, VS2 // f0400c24 XSCVDPSP VS1, VS2 // f0400c24
XVCVDPSP VS1, VS2 // f0400e24 XVCVDPSP VS1, VS2 // f0400e24
XSCVSXDDP VS1, VS2 // f0400de0 XSCVSXDDP VS1, VS2 // f0400de0
@ -736,4 +751,17 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
MOVD XER, R3 // 7c6102a6 MOVD XER, R3 // 7c6102a6
MOVFL CR3, CR1 // 4c8c0000 MOVFL CR3, CR1 // 4c8c0000
MOVW CR0, R1 // 7c380026
MOVW CR7, R1 // 7c301026
MOVW CR, R1 // 7c200026
MOVW R1, CR // 7c2ff120
MOVFL R1, CR // 7c2ff120
MOVW R1, CR2 // 7c320120
MOVFL R1, CR2 // 7c320120
MOVFL R1, $255 // 7c2ff120
MOVFL R1, $1 // 7c301120
MOVFL R1, $128 // 7c380120
MOVFL R1, $3 // 7c203120
RET RET

View File

@ -10,20 +10,35 @@ start:
// 2.4: Integer Computational Instructions // 2.4: Integer Computational Instructions
ADDI $2047, X5, X6 // 1383f27f
ADDI $-2048, X5, X6 // 13830280
ADDI $2047, X5 // 9382f27f ADDI $2047, X5 // 9382f27f
ADDI $-2048, X5 // 93820280 ADDI $-2048, X5 // 93820280
ADDI $2048, X5 // 9382024093820240
ADDI $-2049, X5 // 938202c09382f2bf
ADDI $4094, X5 // 9382f27f9382f27f
ADDI $-4096, X5 // 9382028093820280
ADDI $4095, X5 // b71f00009b8fffffb382f201
ADDI $-4097, X5 // b7ffffff9b8fffffb382f201
ADDI $2047, X5, X6 // 1383f27f
ADDI $-2048, X5, X6 // 13830280
ADDI $2048, X5, X6 // 1383024013030340
ADDI $-2049, X5, X6 // 138302c01303f3bf
ADDI $4094, X5, X6 // 1383f27f1303f37f
ADDI $-4096, X5, X6 // 1383028013030380
ADDI $4095, X5, X6 // b71f00009b8fffff3383f201
ADDI $-4097, X5, X6 // b7ffffff9b8fffff3383f201
SLTI $55, X5, X7 // 93a37203 SLTI $55, X5, X7 // 93a37203
SLTIU $55, X5, X7 // 93b37203 SLTIU $55, X5, X7 // 93b37203
ANDI $1, X5, X6 // 13f31200 ANDI $1, X5, X6 // 13f31200
ANDI $1, X5 // 93f21200 ANDI $1, X5 // 93f21200
ANDI $2048, X5 // b71f00009b8f0f80b3f2f201
ORI $1, X5, X6 // 13e31200 ORI $1, X5, X6 // 13e31200
ORI $1, X5 // 93e21200 ORI $1, X5 // 93e21200
ORI $2048, X5 // b71f00009b8f0f80b3e2f201
XORI $1, X5, X6 // 13c31200 XORI $1, X5, X6 // 13c31200
XORI $1, X5 // 93c21200 XORI $1, X5 // 93c21200
XORI $2048, X5 // b71f00009b8f0f80b3c2f201
SLLI $1, X5, X6 // 13931200 SLLI $1, X5, X6 // 13931200
SLLI $1, X5 // 93921200 SLLI $1, X5 // 93921200
@ -86,20 +101,15 @@ start:
SRA $1, X5 // 93d21240 SRA $1, X5 // 93d21240
// 2.5: Control Transfer Instructions // 2.5: Control Transfer Instructions
JAL X5, 2(PC) // ef028000
// These jumps and branches get printed as a jump or branch
// to 2 because they transfer control to the second instruction
// in the function (the first instruction being an invisible
// stack pointer adjustment).
JAL X5, start // JAL X5, 2 // eff25ff0
JALR X6, (X5) // 67830200 JALR X6, (X5) // 67830200
JALR X6, 4(X5) // 67834200 JALR X6, 4(X5) // 67834200
BEQ X5, X6, start // BEQ X5, X6, 2 // e38c62ee BEQ X5, X6, 2(PC) // 63846200
BNE X5, X6, start // BNE X5, X6, 2 // e39a62ee BNE X5, X6, 2(PC) // 63946200
BLT X5, X6, start // BLT X5, X6, 2 // e3c862ee BLT X5, X6, 2(PC) // 63c46200
BLTU X5, X6, start // BLTU X5, X6, 2 // e3e662ee BLTU X5, X6, 2(PC) // 63e46200
BGE X5, X6, start // BGE X5, X6, 2 // e3d462ee BGE X5, X6, 2(PC) // 63d46200
BGEU X5, X6, start // BGEU X5, X6, 2 // e3f262ee BGEU X5, X6, 2(PC) // 63f46200
// 2.6: Load and Store Instructions // 2.6: Load and Store Instructions
LW (X5), X6 // 03a30200 LW (X5), X6 // 03a30200
@ -219,6 +229,10 @@ start:
FMVSX X5, F0 // 538002f0 FMVSX X5, F0 // 538002f0
FMVXW F0, X5 // d30200e0 FMVXW F0, X5 // d30200e0
FMVWX X5, F0 // 538002f0 FMVWX X5, F0 // 538002f0
FMADDS F1, F2, F3, F4 // 43822018
FMSUBS F1, F2, F3, F4 // 47822018
FNMSUBS F1, F2, F3, F4 // 4b822018
FNMADDS F1, F2, F3, F4 // 4f822018
// 11.8: Single-Precision Floating-Point Compare Instructions // 11.8: Single-Precision Floating-Point Compare Instructions
FEQS F0, F1, X7 // d3a300a0 FEQS F0, F1, X7 // d3a300a0
@ -259,6 +273,10 @@ start:
FSGNJXD F1, F0, F2 // 53211022 FSGNJXD F1, F0, F2 // 53211022
FMVXD F0, X5 // d30200e2 FMVXD F0, X5 // d30200e2
FMVDX X5, F0 // 538002f2 FMVDX X5, F0 // 538002f2
FMADDD F1, F2, F3, F4 // 4382201a
FMSUBD F1, F2, F3, F4 // 4782201a
FNMSUBD F1, F2, F3, F4 // 4b82201a
FNMADDD F1, F2, F3, F4 // 4f82201a
// 12.6: Double-Precision Floating-Point Classify Instruction // 12.6: Double-Precision Floating-Point Classify Instruction
FCLASSD F0, X5 // d31200e2 FCLASSD F0, X5 // d31200e2
@ -277,11 +295,17 @@ start:
// MOV pseudo-instructions // MOV pseudo-instructions
MOV X5, X6 // 13830200 MOV X5, X6 // 13830200
MOV $2047, X5 // 9b02f07f MOV $2047, X5 // 9302f07f
MOV $-2048, X5 // 9b020080 MOV $-2048, X5 // 93020080
MOV $2048, X5 // b71200009b820280
MOV $-2049, X5 // b7f2ffff9b82f27f
MOV $4096, X5 // b7120000
MOV $2147479552, X5 // b7f2ff7f
MOV $2147483647, X5 // b70200809b82f2ff
MOV $-2147483647, X5 // b70200809b821200
// Converted to load of symbol. // Converted to load of symbol (AUIPC + LD)
MOV $4294967296, X5 // 97020000 MOV $4294967296, X5 // 9702000083b20200
MOV (X5), X6 // 03b30200 MOV (X5), X6 // 03b30200
MOV 4(X5), X6 // 03b34200 MOV 4(X5), X6 // 03b34200
@ -325,42 +349,44 @@ start:
NEGW X5 // bb025040 NEGW X5 // bb025040
NEGW X5, X6 // 3b035040 NEGW X5, X6 // 3b035040
// These jumps can get printed as jumps to 2 because they go to the // This jumps to the second instruction in the function (the
// second instruction in the function (the first instruction is an // first instruction is an invisible stack pointer adjustment).
// invisible stack pointer adjustment). JMP start // JMP 2
JMP start // JMP 2 // 6ff01fc2
JMP 2(PC) // 6f008000
JMP (X5) // 67800200 JMP (X5) // 67800200
JMP 4(X5) // 67804200 JMP 4(X5) // 67804200
// JMP and CALL to symbol are encoded as: // CALL and JMP to symbol are encoded as JAL (using LR or ZERO
// AUIPC $0, TMP // respectively), with a R_RISCV_CALL relocation. The linker resolves
// JALR $0, TMP // the real address and updates the immediate, using a trampoline in
// with a R_RISCV_PCREL_ITYPE relocation - the linker resolves the // the case where the address is not directly reachable.
// real address and updates the immediates for both instructions. CALL asmtest(SB) // ef000000
CALL asmtest(SB) // 970f0000 JMP asmtest(SB) // 6f000000
JMP asmtest(SB) // 970f0000
// Branch pseudo-instructions // Branch pseudo-instructions
BEQZ X5, start // BEQZ X5, 2 // e38202c0 BEQZ X5, 2(PC) // 63840200
BGEZ X5, start // BGEZ X5, 2 // e3d002c0 BGEZ X5, 2(PC) // 63d40200
BGT X5, X6, start // BGT X5, X6, 2 // e34e53be BGT X5, X6, 2(PC) // 63445300
BGTU X5, X6, start // BGTU X5, X6, 2 // e36c53be BGTU X5, X6, 2(PC) // 63645300
BGTZ X5, start // BGTZ X5, 2 // e34a50be BGTZ X5, 2(PC) // 63445000
BLE X5, X6, start // BLE X5, X6, 2 // e35853be BLE X5, X6, 2(PC) // 63545300
BLEU X5, X6, start // BLEU X5, X6, 2 // e37653be BLEU X5, X6, 2(PC) // 63745300
BLEZ X5, start // BLEZ X5, 2 // e35450be BLEZ X5, 2(PC) // 63545000
BLTZ X5, start // BLTZ X5, 2 // e3c202be BLTZ X5, 2(PC) // 63c40200
BNEZ X5, start // BNEZ X5, 2 // e39002be BNEZ X5, 2(PC) // 63940200
// Set pseudo-instructions // Set pseudo-instructions
SEQZ X15, X15 // 93b71700 SEQZ X15, X15 // 93b71700
SNEZ X15, X15 // b337f000 SNEZ X15, X15 // b337f000
// F extension // F extension
FABSS F0, F1 // d3200020
FNEGS F0, F1 // d3100020 FNEGS F0, F1 // d3100020
FNES F0, F1, X7 // d3a300a093c31300 FNES F0, F1, X7 // d3a300a093c31300
// D extension // D extension
FABSD F0, F1 // d3200022
FNEGD F0, F1 // d3100022 FNEGD F0, F1 // d3100022
FNED F0, F1, X5 // d3a200a293c21200 FNED F0, F1, X5 // d3a200a293c21200
FLTD F0, F1, X5 // d39200a2 FLTD F0, F1, X5 // d39200a2

View File

@ -3,6 +3,14 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
TEXT errors(SB),$0 TEXT errors(SB),$0
MOV $errors(SB), (X5) // ERROR "address load must target register"
MOV $8(SP), (X5) // ERROR "address load must target register"
MOVB $8(SP), X5 // ERROR "unsupported address load"
MOVH $8(SP), X5 // ERROR "unsupported address load"
MOVW $8(SP), X5 // ERROR "unsupported address load"
MOVF $8(SP), X5 // ERROR "unsupported address load"
MOV $1234, 0(SP) // ERROR "constant load must target register"
MOV $1234, 8(SP) // ERROR "constant load must target register"
MOV $0, 0(SP) // ERROR "constant load must target register" MOV $0, 0(SP) // ERROR "constant load must target register"
MOV $0, 8(SP) // ERROR "constant load must target register" MOV $0, 8(SP) // ERROR "constant load must target register"
MOV $1234, 0(SP) // ERROR "constant load must target register" MOV $1234, 0(SP) // ERROR "constant load must target register"
@ -11,4 +19,8 @@ TEXT errors(SB),$0
MOVH $1, X5 // ERROR "unsupported constant load" MOVH $1, X5 // ERROR "unsupported constant load"
MOVW $1, X5 // ERROR "unsupported constant load" MOVW $1, X5 // ERROR "unsupported constant load"
MOVF $1, X5 // ERROR "unsupported constant load" MOVF $1, X5 // ERROR "unsupported constant load"
MOVBU X5, (X6) // ERROR "unsupported unsigned store"
MOVHU X5, (X6) // ERROR "unsupported unsigned store"
MOVWU X5, (X6) // ERROR "unsupported unsigned store"
RET RET

View File

@ -28,6 +28,10 @@ var (
CompilingRuntime = flag.Bool("compiling-runtime", false, "source to be compiled is part of the Go runtime") CompilingRuntime = flag.Bool("compiling-runtime", false, "source to be compiled is part of the Go runtime")
) )
var DebugFlags struct {
MayMoreStack string `help:"call named function before all stack growth checks"`
}
var ( var (
D MultiFlag D MultiFlag
I MultiFlag I MultiFlag
@ -39,6 +43,7 @@ func init() {
flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifier=value; can be set multiple times") flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifier=value; can be set multiple times")
flag.Var(&I, "I", "include directory; can be set multiple times") flag.Var(&I, "I", "include directory; can be set multiple times")
flag.BoolVar(&DebugV, "v", false, "print debug output") flag.BoolVar(&DebugV, "v", false, "print debug output")
flag.Var(objabi.NewDebugFlag(&DebugFlags, nil), "d", "enable debugging settings; try -d help")
objabi.AddVersionFlag() // -V objabi.AddVersionFlag() // -V
objabi.Flagcount("S", "print assembly and machine code", &PrintOut) objabi.Flagcount("S", "print assembly and machine code", &PrintOut)
} }

View File

@ -29,19 +29,20 @@ func main() {
buildcfg.Check() buildcfg.Check()
GOARCH := buildcfg.GOARCH GOARCH := buildcfg.GOARCH
architecture := arch.Set(GOARCH) flags.Parse()
architecture := arch.Set(GOARCH, *flags.Shared || *flags.Dynlink)
if architecture == nil { if architecture == nil {
log.Fatalf("unrecognized architecture %s", GOARCH) log.Fatalf("unrecognized architecture %s", GOARCH)
} }
flags.Parse()
ctxt := obj.Linknew(architecture.LinkArch) ctxt := obj.Linknew(architecture.LinkArch)
ctxt.Debugasm = flags.PrintOut ctxt.Debugasm = flags.PrintOut
ctxt.Debugvlog = flags.DebugV ctxt.Debugvlog = flags.DebugV
ctxt.Flag_dynlink = *flags.Dynlink ctxt.Flag_dynlink = *flags.Dynlink
ctxt.Flag_linkshared = *flags.Linkshared ctxt.Flag_linkshared = *flags.Linkshared
ctxt.Flag_shared = *flags.Shared || *flags.Dynlink ctxt.Flag_shared = *flags.Shared || *flags.Dynlink
ctxt.Flag_maymorestack = flags.DebugFlags.MayMoreStack
ctxt.IsAsm = true ctxt.IsAsm = true
ctxt.Pkgpath = *flags.Importpath ctxt.Pkgpath = *flags.Importpath
switch *flags.Spectre { switch *flags.Spectre {

View File

@ -338,8 +338,7 @@ func (f *File) walk(x interface{}, context astContext, visit func(*File, interfa
// everything else just recurs // everything else just recurs
default: default:
error_(token.NoPos, "unexpected type %T in walk", x) f.walkUnexpected(x, context, visit)
panic("unexpected type")
case nil: case nil:

17
src/cmd/cgo/ast_go1.go Normal file
View File

@ -0,0 +1,17 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build compiler_bootstrap
// +build compiler_bootstrap
package main
import (
"go/token"
)
func (f *File) walkUnexpected(x interface{}, context astContext, visit func(*File, interface{}, astContext)) {
error_(token.NoPos, "unexpected type %T in walk", x)
panic("unexpected type")
}

25
src/cmd/cgo/ast_go118.go Normal file
View File

@ -0,0 +1,25 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !compiler_bootstrap
// +build !compiler_bootstrap
package main
import (
"go/ast"
"go/token"
)
func (f *File) walkUnexpected(x interface{}, context astContext, visit func(*File, interface{}, astContext)) {
switch n := x.(type) {
default:
error_(token.NoPos, "unexpected type %T in walk", x)
panic("unexpected type")
case *ast.IndexListExpr:
f.walk(&n.X, ctxExpr, visit)
f.walk(n.Indices, ctxExpr, visit)
}
}

View File

@ -23,10 +23,13 @@ import (
"internal/xcoff" "internal/xcoff"
"math" "math"
"os" "os"
"os/exec"
"strconv" "strconv"
"strings" "strings"
"unicode" "unicode"
"unicode/utf8" "unicode/utf8"
"cmd/internal/quoted"
) )
var debugDefine = flag.Bool("debug-define", false, "print relevant #defines") var debugDefine = flag.Bool("debug-define", false, "print relevant #defines")
@ -382,7 +385,7 @@ func (p *Package) guessKinds(f *File) []*Name {
stderr = p.gccErrors(b.Bytes()) stderr = p.gccErrors(b.Bytes())
} }
if stderr == "" { if stderr == "" {
fatalf("%s produced no output\non input:\n%s", p.gccBaseCmd()[0], b.Bytes()) fatalf("%s produced no output\non input:\n%s", gccBaseCmd[0], b.Bytes())
} }
completed := false completed := false
@ -457,7 +460,7 @@ func (p *Package) guessKinds(f *File) []*Name {
} }
if !completed { if !completed {
fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", p.gccBaseCmd()[0], b.Bytes(), stderr) fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", gccBaseCmd[0], b.Bytes(), stderr)
} }
for i, n := range names { for i, n := range names {
@ -488,7 +491,7 @@ func (p *Package) guessKinds(f *File) []*Name {
// to users debugging preamble mistakes. See issue 8442. // to users debugging preamble mistakes. See issue 8442.
preambleErrors := p.gccErrors([]byte(f.Preamble)) preambleErrors := p.gccErrors([]byte(f.Preamble))
if len(preambleErrors) > 0 { if len(preambleErrors) > 0 {
error_(token.NoPos, "\n%s errors for preamble:\n%s", p.gccBaseCmd()[0], preambleErrors) error_(token.NoPos, "\n%s errors for preamble:\n%s", gccBaseCmd[0], preambleErrors)
} }
fatalf("unresolved names") fatalf("unresolved names")
@ -1503,7 +1506,7 @@ func (p *Package) rewriteName(f *File, r *Ref, addPosition bool) ast.Expr {
Args: []ast.Expr{getNewIdent(name.Mangle)}, Args: []ast.Expr{getNewIdent(name.Mangle)},
} }
case "type": case "type":
// Okay - might be new(T) // Okay - might be new(T), T(x), Generic[T], etc.
if r.Name.Type == nil { if r.Name.Type == nil {
error_(r.Pos(), "expression C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C) error_(r.Pos(), "expression C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
} }
@ -1545,20 +1548,37 @@ func gofmtPos(n ast.Expr, pos token.Pos) string {
return fmt.Sprintf("/*line :%d:%d*/%s", p.Line, p.Column, s) return fmt.Sprintf("/*line :%d:%d*/%s", p.Line, p.Column, s)
} }
// gccBaseCmd returns the start of the compiler command line. // checkGCCBaseCmd returns the start of the compiler command line.
// It uses $CC if set, or else $GCC, or else the compiler recorded // It uses $CC if set, or else $GCC, or else the compiler recorded
// during the initial build as defaultCC. // during the initial build as defaultCC.
// defaultCC is defined in zdefaultcc.go, written by cmd/dist. // defaultCC is defined in zdefaultcc.go, written by cmd/dist.
func (p *Package) gccBaseCmd() []string { //
// The compiler command line is split into arguments on whitespace. Quotes
// are understood, so arguments may contain whitespace.
//
// checkGCCBaseCmd confirms that the compiler exists in PATH, returning
// an error if it does not.
func checkGCCBaseCmd() ([]string, error) {
// Use $CC if set, since that's what the build uses. // Use $CC if set, since that's what the build uses.
if ret := strings.Fields(os.Getenv("CC")); len(ret) > 0 { value := os.Getenv("CC")
return ret if value == "" {
// Try $GCC if set, since that's what we used to use.
value = os.Getenv("GCC")
} }
// Try $GCC if set, since that's what we used to use. if value == "" {
if ret := strings.Fields(os.Getenv("GCC")); len(ret) > 0 { value = defaultCC(goos, goarch)
return ret
} }
return strings.Fields(defaultCC(goos, goarch)) args, err := quoted.Split(value)
if err != nil {
return nil, err
}
if len(args) == 0 {
return nil, errors.New("CC not set and no default found")
}
if _, err := exec.LookPath(args[0]); err != nil {
return nil, fmt.Errorf("C compiler %q not found: %v", args[0], err)
}
return args[:len(args):len(args)], nil
} }
// gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm". // gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm".
@ -1604,7 +1624,7 @@ func gccTmp() string {
// gccCmd returns the gcc command line to use for compiling // gccCmd returns the gcc command line to use for compiling
// the input. // the input.
func (p *Package) gccCmd() []string { func (p *Package) gccCmd() []string {
c := append(p.gccBaseCmd(), c := append(gccBaseCmd,
"-w", // no warnings "-w", // no warnings
"-Wno-error", // warnings are not errors "-Wno-error", // warnings are not errors
"-o"+gccTmp(), // write object to tmp "-o"+gccTmp(), // write object to tmp
@ -2005,7 +2025,7 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6
// #defines that gcc encountered while processing the input // #defines that gcc encountered while processing the input
// and its included files. // and its included files.
func (p *Package) gccDefines(stdin []byte) string { func (p *Package) gccDefines(stdin []byte) string {
base := append(p.gccBaseCmd(), "-E", "-dM", "-xc") base := append(gccBaseCmd, "-E", "-dM", "-xc")
base = append(base, p.gccMachine()...) base = append(base, p.gccMachine()...)
stdout, _ := runGcc(stdin, append(append(base, p.GccOptions...), "-")) stdout, _ := runGcc(stdin, append(append(base, p.GccOptions...), "-"))
return stdout return stdout
@ -2086,6 +2106,9 @@ type typeConv struct {
// Type names X for which there exists an XGetTypeID function with type func() CFTypeID. // Type names X for which there exists an XGetTypeID function with type func() CFTypeID.
getTypeIDs map[string]bool getTypeIDs map[string]bool
// badStructs contains C structs that should be marked NotInHeap.
notInHeapStructs map[string]bool
// Predeclared types. // Predeclared types.
bool ast.Expr bool ast.Expr
byte ast.Expr // denotes padding byte ast.Expr // denotes padding
@ -2097,6 +2120,7 @@ type typeConv struct {
string ast.Expr string ast.Expr
goVoid ast.Expr // _Ctype_void, denotes C's void goVoid ast.Expr // _Ctype_void, denotes C's void
goVoidPtr ast.Expr // unsafe.Pointer or *byte goVoidPtr ast.Expr // unsafe.Pointer or *byte
goVoidPtrNoHeap ast.Expr // *_Ctype_void_notinheap, like goVoidPtr but marked NotInHeap
ptrSize int64 ptrSize int64
intSize int64 intSize int64
@ -2120,6 +2144,7 @@ func (c *typeConv) Init(ptrSize, intSize int64) {
c.m = make(map[string]*Type) c.m = make(map[string]*Type)
c.ptrs = make(map[string][]*Type) c.ptrs = make(map[string][]*Type)
c.getTypeIDs = make(map[string]bool) c.getTypeIDs = make(map[string]bool)
c.notInHeapStructs = make(map[string]bool)
c.bool = c.Ident("bool") c.bool = c.Ident("bool")
c.byte = c.Ident("byte") c.byte = c.Ident("byte")
c.int8 = c.Ident("int8") c.int8 = c.Ident("int8")
@ -2138,6 +2163,7 @@ func (c *typeConv) Init(ptrSize, intSize int64) {
c.void = c.Ident("void") c.void = c.Ident("void")
c.string = c.Ident("string") c.string = c.Ident("string")
c.goVoid = c.Ident("_Ctype_void") c.goVoid = c.Ident("_Ctype_void")
c.goVoidPtrNoHeap = c.Ident("*_Ctype_void_notinheap")
// Normally cgo translates void* to unsafe.Pointer, // Normally cgo translates void* to unsafe.Pointer,
// but for historical reasons -godefs uses *byte instead. // but for historical reasons -godefs uses *byte instead.
@ -2518,6 +2544,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
tt.C = &TypeRepr{"struct %s", []interface{}{tag}} tt.C = &TypeRepr{"struct %s", []interface{}{tag}}
} }
tt.Go = g tt.Go = g
tt.NotInHeap = c.notInHeapStructs[tag]
typedef[name.Name] = &tt typedef[name.Name] = &tt
} }
@ -2561,6 +2588,30 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
oldType.BadPointer = true oldType.BadPointer = true
} }
} }
if c.badVoidPointerTypedef(dt) {
// Treat this typedef as a pointer to a NotInHeap void.
s := *sub
s.Go = c.goVoidPtrNoHeap
sub = &s
// Make sure we update any previously computed type.
if oldType := typedef[name.Name]; oldType != nil {
oldType.Go = sub.Go
}
}
// Check for non-pointer "struct <tag>{...}; typedef struct <tag> *<name>"
// typedefs that should be marked NotInHeap.
if ptr, ok := dt.Type.(*dwarf.PtrType); ok {
if strct, ok := ptr.Type.(*dwarf.StructType); ok {
if c.badStructPointerTypedef(dt.Name, strct) {
c.notInHeapStructs[strct.StructName] = true
// Make sure we update any previously computed type.
name := "_Ctype_struct_" + strct.StructName
if oldType := typedef[name]; oldType != nil {
oldType.NotInHeap = true
}
}
}
}
t.Go = name t.Go = name
t.BadPointer = sub.BadPointer t.BadPointer = sub.BadPointer
t.NotInHeap = sub.NotInHeap t.NotInHeap = sub.NotInHeap
@ -3010,6 +3061,31 @@ func upper(s string) string {
// so that all fields are exported. // so that all fields are exported.
func godefsFields(fld []*ast.Field) { func godefsFields(fld []*ast.Field) {
prefix := fieldPrefix(fld) prefix := fieldPrefix(fld)
// Issue 48396: check for duplicate field names.
if prefix != "" {
names := make(map[string]bool)
fldLoop:
for _, f := range fld {
for _, n := range f.Names {
name := n.Name
if name == "_" {
continue
}
if name != prefix {
name = strings.TrimPrefix(n.Name, prefix)
}
name = upper(name)
if names[name] {
// Field name conflict: don't remove prefix.
prefix = ""
break fldLoop
}
names[name] = true
}
}
}
npad := 0 npad := 0
for _, f := range fld { for _, f := range fld {
for _, n := range f.Names { for _, n := range f.Names {
@ -3087,6 +3163,48 @@ func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool {
return false return false
} }
// badVoidPointerTypedef is like badPointerTypeDef, but for "void *" typedefs that should be NotInHeap.
func (c *typeConv) badVoidPointerTypedef(dt *dwarf.TypedefType) bool {
// Match the Windows HANDLE type (#42018).
if goos != "windows" || dt.Name != "HANDLE" {
return false
}
// Check that the typedef is "typedef void *<name>".
if ptr, ok := dt.Type.(*dwarf.PtrType); ok {
if _, ok := ptr.Type.(*dwarf.VoidType); ok {
return true
}
}
return false
}
// badStructPointerTypedef is like badVoidPointerTypedefs but for structs.
func (c *typeConv) badStructPointerTypedef(name string, dt *dwarf.StructType) bool {
// Windows handle types can all potentially contain non-pointers.
// badVoidPointerTypedef handles the "void *" HANDLE type, but other
// handles are defined as
//
// struct <name>__{int unused;}; typedef struct <name>__ *name;
//
// by the DECLARE_HANDLE macro in STRICT mode. The macro is declared in
// the Windows ntdef.h header,
//
// https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/shared/ntdef.h#L779
if goos != "windows" {
return false
}
if len(dt.Field) != 1 {
return false
}
if dt.StructName != name+"__" {
return false
}
if f := dt.Field[0]; f.Name != "unused" || f.Type.Common().Name != "int" {
return false
}
return true
}
// baseBadPointerTypedef reports whether the base of a chain of typedefs is a bad typedef // baseBadPointerTypedef reports whether the base of a chain of typedefs is a bad typedef
// as badPointerTypedef reports. // as badPointerTypedef reports.
func (c *typeConv) baseBadPointerTypedef(dt *dwarf.TypedefType) bool { func (c *typeConv) baseBadPointerTypedef(dt *dwarf.TypedefType) bool {

View File

@ -21,7 +21,6 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"reflect" "reflect"
"runtime" "runtime"
@ -248,6 +247,7 @@ var importSyscall = flag.Bool("import_syscall", true, "import syscall in generat
var trimpath = flag.String("trimpath", "", "applies supplied rewrites or trims prefixes to recorded source file paths") var trimpath = flag.String("trimpath", "", "applies supplied rewrites or trims prefixes to recorded source file paths")
var goarch, goos, gomips, gomips64 string var goarch, goos, gomips, gomips64 string
var gccBaseCmd []string
func main() { func main() {
objabi.AddVersionFlag() // -V objabi.AddVersionFlag() // -V
@ -305,10 +305,10 @@ func main() {
p := newPackage(args[:i]) p := newPackage(args[:i])
// We need a C compiler to be available. Check this. // We need a C compiler to be available. Check this.
gccName := p.gccBaseCmd()[0] var err error
_, err := exec.LookPath(gccName) gccBaseCmd, err = checkGCCBaseCmd()
if err != nil { if err != nil {
fatalf("C compiler %q not found: %v", gccName, err) fatalf("%v", err)
os.Exit(2) os.Exit(2)
} }

View File

@ -59,9 +59,9 @@ func (p *Package) writeDefs() {
// Write C main file for using gcc to resolve imports. // Write C main file for using gcc to resolve imports.
fmt.Fprintf(fm, "int main() { return 0; }\n") fmt.Fprintf(fm, "int main() { return 0; }\n")
if *importRuntimeCgo { if *importRuntimeCgo {
fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*), void *a, int c, __SIZE_TYPE__ ctxt) { }\n") fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*) __attribute__((unused)), void *a __attribute__((unused)), int c __attribute__((unused)), __SIZE_TYPE__ ctxt __attribute__((unused))) { }\n")
fmt.Fprintf(fm, "__SIZE_TYPE__ _cgo_wait_runtime_init_done(void) { return 0; }\n") fmt.Fprintf(fm, "__SIZE_TYPE__ _cgo_wait_runtime_init_done(void) { return 0; }\n")
fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__ ctxt) { }\n") fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__ ctxt __attribute__((unused))) { }\n")
fmt.Fprintf(fm, "char* _cgo_topofstack(void) { return (char*)0; }\n") fmt.Fprintf(fm, "char* _cgo_topofstack(void) { return (char*)0; }\n")
} else { } else {
// If we're not importing runtime/cgo, we *are* runtime/cgo, // If we're not importing runtime/cgo, we *are* runtime/cgo,
@ -70,8 +70,8 @@ func (p *Package) writeDefs() {
fmt.Fprintf(fm, "__SIZE_TYPE__ _cgo_wait_runtime_init_done(void);\n") fmt.Fprintf(fm, "__SIZE_TYPE__ _cgo_wait_runtime_init_done(void);\n")
fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__);\n") fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__);\n")
} }
fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n") fmt.Fprintf(fm, "void _cgo_allocate(void *a __attribute__((unused)), int c __attribute__((unused))) { }\n")
fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n") fmt.Fprintf(fm, "void _cgo_panic(void *a __attribute__((unused)), int c __attribute__((unused))) { }\n")
fmt.Fprintf(fm, "void _cgo_reginit(void) { }\n") fmt.Fprintf(fm, "void _cgo_reginit(void) { }\n")
// Write second Go output: definitions of _C_xxx. // Write second Go output: definitions of _C_xxx.
@ -135,6 +135,7 @@ func (p *Package) writeDefs() {
fmt.Fprintf(fgo2, "%s", buf.Bytes()) fmt.Fprintf(fgo2, "%s", buf.Bytes())
fmt.Fprintf(fgo2, "\n\n") fmt.Fprintf(fgo2, "\n\n")
} }
fmt.Fprintf(fgo2, "//go:notinheap\ntype _Ctype_void_notinheap struct{}\n\n")
if *gccgo { if *gccgo {
fmt.Fprintf(fgo2, "type _Ctype_void byte\n") fmt.Fprintf(fgo2, "type _Ctype_void byte\n")
} else { } else {
@ -1054,9 +1055,10 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p){}\n", cPrefix, exp.ExpName) fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p){}\n", cPrefix, exp.ExpName)
fmt.Fprintf(fgo2, "\t")
if gccResult != "void" { if gccResult != "void" {
// Write results back to frame. // Write results back to frame.
fmt.Fprintf(fgo2, "\t")
forFieldList(fntype.Results, forFieldList(fntype.Results,
func(i int, aname string, atype ast.Expr) { func(i int, aname string, atype ast.Expr) {
if i > 0 { if i > 0 {
@ -1458,10 +1460,10 @@ const gccProlog = `
(have a negative array count) and an inscrutable error will come (have a negative array count) and an inscrutable error will come
out of the compiler and hopefully mention "name". out of the compiler and hopefully mention "name".
*/ */
#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2+1]; #define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2UL+1UL];
/* Check at compile time that the sizes we use match our expectations. */ /* Check at compile time that the sizes we use match our expectations. */
#define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), n, _cgo_sizeof_##t##_is_not_##n) #define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), (size_t)n, _cgo_sizeof_##t##_is_not_##n)
__cgo_size_assert(char, 1) __cgo_size_assert(char, 1)
__cgo_size_assert(short, 2) __cgo_size_assert(short, 2)

View File

@ -627,6 +627,105 @@ modifying or saving the FPCR.
Functions are allowed to modify it between calls (as long as they Functions are allowed to modify it between calls (as long as they
restore it), but as of this writing Go code never does. restore it), but as of this writing Go code never does.
### ppc64 architecture
The ppc64 architecture uses R3 R10 and R14 R17 for integer arguments
and results.
It uses F1 F12 for floating-point arguments and results.
Register R31 is a permanent scratch register in Go.
Special-purpose registers used within Go generated code and Go
assembly code are as follows:
| Register | Call meaning | Return meaning | Body meaning |
| --- | --- | --- | --- |
| R0 | Zero value | Same | Same |
| R1 | Stack pointer | Same | Same |
| R2 | TOC register | Same | Same |
| R11 | Closure context pointer | Scratch | Scratch |
| R12 | Function address on indirect calls | Scratch | Scratch |
| R13 | TLS pointer | Same | Same |
| R20,R21 | Scratch | Scratch | Used by duffcopy, duffzero |
| R30 | Current goroutine | Same | Same |
| R31 | Scratch | Scratch | Scratch |
| LR | Link register | Link register | Scratch |
*Rationale*: These register meanings are compatible with Gos
stack-based calling convention.
The link register, LR, holds the function return
address at the function entry and is set to the correct return
address before exiting the function. It is also used
in some cases as the function address when doing an indirect call.
The register R2 contains the address of the TOC (table of contents) which
contains data or code addresses used when generating position independent
code. Non-Go code generated when using cgo contains TOC-relative addresses
which depend on R2 holding a valid TOC. Go code compiled with -shared or
-dynlink initializes and maintains R2 and uses it in some cases for
function calls; Go code compiled without these options does not modify R2.
When making a function call R12 contains the function address for use by the
code to generate R2 at the beginning of the function. R12 can be used for
other purposes within the body of the function, such as trampoline generation.
R20 and R21 are used in duffcopy and duffzero which could be generated
before arguments are saved so should not be used for register arguments.
The Count register CTR can be used as the call target for some branch instructions.
It holds the return address when preemption has occurred.
On PPC64 when a float32 is loaded it becomes a float64 in the register, which is
different from other platforms and that needs to be recognized by the internal
implementation of reflection so that float32 arguments are passed correctly.
Registers R18 - R29 and F13 - F31 are considered scratch registers.
#### Stack layout
The stack pointer, R1, grows down and is aligned to 8 bytes in Go, but changed
to 16 bytes when calling cgo.
A function's stack frame, after the frame is created, is laid out as
follows:
+------------------------------+
| ... locals ... |
| ... outgoing arguments ... |
| 24 TOC register R2 save | When compiled with -shared/-dynlink
| 16 Unused in Go | Not used in Go
| 8 CR save | nonvolatile CR fields
| 0 return PC | ← R1 points to
+------------------------------+ ↓ lower addresses
The "return PC" is loaded to the link register, LR, as part of the
ppc64 `BL` operations.
On entry to a non-leaf function, the stack frame size is subtracted from R1 to
create its stack frame, and saves the value of LR at the bottom of the frame.
A leaf function that does not require any stack space does not modify R1 and
does not save LR.
*NOTE*: We might need to save the frame pointer on the stack as
in the PPC64 ELF v2 ABI so Go can inter-operate with platform debuggers
and profilers.
This stack layout is used by both register-based (ABIInternal) and
stack-based (ABI0) calling conventions.
#### Flags
The condition register consists of 8 condition code register fields
CR0-CR7. Go generated code only sets and uses CR0, commonly set by
compare functions and use to determine the target of a conditional
branch. The generated code does not set or use CR1-CR7.
The floating point status and control register (FPSCR) is initialized
to 0 by the kernel at startup of the Go program and not changed by
the Go generated code.
## Future directions ## Future directions
### Spill path improvements ### Spill path improvements

View File

@ -44,6 +44,8 @@ Flags:
Print compiler version and exit. Print compiler version and exit.
-asmhdr file -asmhdr file
Write assembly header to file. Write assembly header to file.
-asan
Insert calls to C/C++ address sanitizer.
-buildid id -buildid id
Record id as the build id in the export metadata. Record id as the build id in the export metadata.
-blockprofile file -blockprofile file

View File

@ -144,7 +144,7 @@ func (pa *ABIParamAssignment) RegisterTypesAndOffsets() ([]*types.Type, []int64)
} }
func appendParamTypes(rts []*types.Type, t *types.Type) []*types.Type { func appendParamTypes(rts []*types.Type, t *types.Type) []*types.Type {
w := t.Width w := t.Size()
if w == 0 { if w == 0 {
return rts return rts
} }
@ -193,12 +193,12 @@ func appendParamTypes(rts []*types.Type, t *types.Type) []*types.Type {
// to input offsets, and returns the longer slice and the next unused offset. // to input offsets, and returns the longer slice and the next unused offset.
func appendParamOffsets(offsets []int64, at int64, t *types.Type) ([]int64, int64) { func appendParamOffsets(offsets []int64, at int64, t *types.Type) ([]int64, int64) {
at = align(at, t) at = align(at, t)
w := t.Width w := t.Size()
if w == 0 { if w == 0 {
return offsets, at return offsets, at
} }
if t.IsScalar() || t.IsPtrShaped() { if t.IsScalar() || t.IsPtrShaped() {
if t.IsComplex() || int(t.Width) > types.RegSize { // complex and *int64 on 32-bit if t.IsComplex() || int(t.Size()) > types.RegSize { // complex and *int64 on 32-bit
s := w / 2 s := w / 2
return append(offsets, at, at+s), at + w return append(offsets, at, at+s), at + w
} else { } else {
@ -214,7 +214,7 @@ func appendParamOffsets(offsets []int64, at int64, t *types.Type) ([]int64, int6
case types.TSTRUCT: case types.TSTRUCT:
for i, f := range t.FieldSlice() { for i, f := range t.FieldSlice() {
offsets, at = appendParamOffsets(offsets, at, f.Type) offsets, at = appendParamOffsets(offsets, at, f.Type)
if f.Type.Width == 0 && i == t.NumFields()-1 { if f.Type.Size() == 0 && i == t.NumFields()-1 {
at++ // last field has zero width at++ // last field has zero width
} }
} }
@ -531,7 +531,7 @@ type assignState struct {
// align returns a rounded up to t's alignment // align returns a rounded up to t's alignment
func align(a int64, t *types.Type) int64 { func align(a int64, t *types.Type) int64 {
return alignTo(a, int(t.Align)) return alignTo(a, int(uint8(t.Alignment())))
} }
// alignTo returns a rounded up to t, where t must be 0 or a power of 2. // alignTo returns a rounded up to t, where t must be 0 or a power of 2.
@ -546,7 +546,7 @@ func alignTo(a int64, t int) int64 {
// specified type. // specified type.
func (state *assignState) stackSlot(t *types.Type) int64 { func (state *assignState) stackSlot(t *types.Type) int64 {
rv := align(state.stackOffset, t) rv := align(state.stackOffset, t)
state.stackOffset = rv + t.Width state.stackOffset = rv + t.Size()
return rv return rv
} }
@ -554,7 +554,7 @@ func (state *assignState) stackSlot(t *types.Type) int64 {
// that we've just determined to be register-assignable. The number of registers // that we've just determined to be register-assignable. The number of registers
// needed is assumed to be stored in state.pUsed. // needed is assumed to be stored in state.pUsed.
func (state *assignState) allocateRegs(regs []RegIndex, t *types.Type) []RegIndex { func (state *assignState) allocateRegs(regs []RegIndex, t *types.Type) []RegIndex {
if t.Width == 0 { if t.Size() == 0 {
return regs return regs
} }
ri := state.rUsed.intRegs ri := state.rUsed.intRegs
@ -647,7 +647,7 @@ func (state *assignState) floatUsed() int {
// can register allocate, FALSE otherwise (and updates state // can register allocate, FALSE otherwise (and updates state
// accordingly). // accordingly).
func (state *assignState) regassignIntegral(t *types.Type) bool { func (state *assignState) regassignIntegral(t *types.Type) bool {
regsNeeded := int(types.Rnd(t.Width, int64(types.PtrSize)) / int64(types.PtrSize)) regsNeeded := int(types.Rnd(t.Size(), int64(types.PtrSize)) / int64(types.PtrSize))
if t.IsComplex() { if t.IsComplex() {
regsNeeded = 2 regsNeeded = 2
} }
@ -722,14 +722,17 @@ func setup() {
types.NewField(nxp, fname("len"), ui), types.NewField(nxp, fname("len"), ui),
types.NewField(nxp, fname("cap"), ui), types.NewField(nxp, fname("cap"), ui),
}) })
types.CalcStructSize(synthSlice)
synthString = types.NewStruct(types.NoPkg, []*types.Field{ synthString = types.NewStruct(types.NoPkg, []*types.Field{
types.NewField(nxp, fname("data"), unsp), types.NewField(nxp, fname("data"), unsp),
types.NewField(nxp, fname("len"), ui), types.NewField(nxp, fname("len"), ui),
}) })
types.CalcStructSize(synthString)
synthIface = types.NewStruct(types.NoPkg, []*types.Field{ synthIface = types.NewStruct(types.NoPkg, []*types.Field{
types.NewField(nxp, fname("f1"), unsp), types.NewField(nxp, fname("f1"), unsp),
types.NewField(nxp, fname("f2"), unsp), types.NewField(nxp, fname("f2"), unsp),
}) })
types.CalcStructSize(synthIface)
}) })
} }
@ -764,10 +767,10 @@ func (state *assignState) regassign(pt *types.Type) bool {
// ABIParamResultInfo held in 'state'. // ABIParamResultInfo held in 'state'.
func (state *assignState) assignParamOrReturn(pt *types.Type, n types.Object, isReturn bool) ABIParamAssignment { func (state *assignState) assignParamOrReturn(pt *types.Type, n types.Object, isReturn bool) ABIParamAssignment {
state.pUsed = RegAmounts{} state.pUsed = RegAmounts{}
if pt.Width == types.BADWIDTH { if pt.Size() == types.BADWIDTH {
base.Fatalf("should never happen") base.Fatalf("should never happen")
panic("unreachable") panic("unreachable")
} else if pt.Width == 0 { } else if pt.Size() == 0 {
return state.stackAllocate(pt, n) return state.stackAllocate(pt, n)
} else if state.regassign(pt) { } else if state.regassign(pt) {
return state.regAllocate(pt, n, isReturn) return state.regAllocate(pt, n, isReturn)
@ -777,11 +780,11 @@ func (state *assignState) assignParamOrReturn(pt *types.Type, n types.Object, is
} }
// ComputePadding returns a list of "post element" padding values in // ComputePadding returns a list of "post element" padding values in
// the case where we have a structure being passed in registers. Give // the case where we have a structure being passed in registers. Given
// a param assignment corresponding to a struct, it returns a list of // a param assignment corresponding to a struct, it returns a list
// contaning padding values for each field, e.g. the Kth element in // containing padding values for each field, e.g. the Kth element in
// the list is the amount of padding between field K and the following // the list is the amount of padding between field K and the following
// field. For things that are not struct (or structs without padding) // field. For things that are not structs (or structs without padding)
// it returns a list of zeros. Example: // it returns a list of zeros. Example:
// //
// type small struct { // type small struct {
@ -793,8 +796,8 @@ func (state *assignState) assignParamOrReturn(pt *types.Type, n types.Object, is
// //
// For this struct we would return a list [0, 1, 0, 0], meaning that // For this struct we would return a list [0, 1, 0, 0], meaning that
// we have one byte of padding after the second field, and no bytes of // we have one byte of padding after the second field, and no bytes of
// padding after any of the other fields. Input parameter "storage" // padding after any of the other fields. Input parameter "storage" is
// is with enough capacity to accommodate padding elements for // a slice with enough capacity to accommodate padding elements for
// the architected register set in question. // the architected register set in question.
func (pa *ABIParamAssignment) ComputePadding(storage []uint64) []uint64 { func (pa *ABIParamAssignment) ComputePadding(storage []uint64) []uint64 {
nr := len(pa.Registers) nr := len(pa.Registers)

View File

@ -263,6 +263,24 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Reg = lo p.To.Reg = lo
p.SetFrom3Reg(hi) p.SetFrom3Reg(hi)
case ssa.OpAMD64BLSIQ, ssa.OpAMD64BLSIL,
ssa.OpAMD64BLSMSKQ, ssa.OpAMD64BLSMSKL,
ssa.OpAMD64BLSRQ, ssa.OpAMD64BLSRL,
ssa.OpAMD64TZCNTQ, ssa.OpAMD64TZCNTL:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64ANDNQ, ssa.OpAMD64ANDNL:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
p.SetFrom3Reg(v.Args[1].Reg())
case ssa.OpAMD64DIVQU, ssa.OpAMD64DIVLU, ssa.OpAMD64DIVWU: case ssa.OpAMD64DIVQU, ssa.OpAMD64DIVLU, ssa.OpAMD64DIVWU:
// Arg[0] (the dividend) is in AX. // Arg[0] (the dividend) is in AX.
// Arg[1] (the divisor) can be in any other register. // Arg[1] (the divisor) can be in any other register.
@ -600,8 +618,21 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Reg = r p.To.Reg = r
p.SetFrom3Reg(v.Args[0].Reg()) p.SetFrom3Reg(v.Args[0].Reg())
case ssa.OpAMD64ANDQconst:
asm := v.Op.Asm()
// If the constant is positive and fits into 32 bits, use ANDL.
// This saves a few bytes of encoding.
if 0 <= v.AuxInt && v.AuxInt <= (1<<32-1) {
asm = x86.AANDL
}
p := s.Prog(asm)
p.From.Type = obj.TYPE_CONST
p.From.Offset = v.AuxInt
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64SUBQconst, ssa.OpAMD64SUBLconst, case ssa.OpAMD64SUBQconst, ssa.OpAMD64SUBLconst,
ssa.OpAMD64ANDQconst, ssa.OpAMD64ANDLconst, ssa.OpAMD64ANDLconst,
ssa.OpAMD64ORQconst, ssa.OpAMD64ORLconst, ssa.OpAMD64ORQconst, ssa.OpAMD64ORLconst,
ssa.OpAMD64XORQconst, ssa.OpAMD64XORLconst, ssa.OpAMD64XORQconst, ssa.OpAMD64XORLconst,
ssa.OpAMD64SHLQconst, ssa.OpAMD64SHLLconst, ssa.OpAMD64SHLQconst, ssa.OpAMD64SHLLconst,
@ -741,7 +772,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Val = math.Float64frombits(uint64(v.AuxInt)) p.From.Val = math.Float64frombits(uint64(v.AuxInt))
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = x p.To.Reg = x
case ssa.OpAMD64MOVQload, ssa.OpAMD64MOVSSload, ssa.OpAMD64MOVSDload, ssa.OpAMD64MOVLload, ssa.OpAMD64MOVWload, ssa.OpAMD64MOVBload, ssa.OpAMD64MOVBQSXload, ssa.OpAMD64MOVWQSXload, ssa.OpAMD64MOVLQSXload, ssa.OpAMD64MOVOload: case ssa.OpAMD64MOVQload, ssa.OpAMD64MOVLload, ssa.OpAMD64MOVWload, ssa.OpAMD64MOVBload, ssa.OpAMD64MOVOload,
ssa.OpAMD64MOVSSload, ssa.OpAMD64MOVSDload, ssa.OpAMD64MOVBQSXload, ssa.OpAMD64MOVWQSXload, ssa.OpAMD64MOVLQSXload,
ssa.OpAMD64MOVBEQload, ssa.OpAMD64MOVBELload:
p := s.Prog(v.Op.Asm()) p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg() p.From.Reg = v.Args[0].Reg()
@ -757,7 +790,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Reg = v.Reg() p.To.Reg = v.Reg()
case ssa.OpAMD64MOVQstore, ssa.OpAMD64MOVSSstore, ssa.OpAMD64MOVSDstore, ssa.OpAMD64MOVLstore, ssa.OpAMD64MOVWstore, ssa.OpAMD64MOVBstore, ssa.OpAMD64MOVOstore, case ssa.OpAMD64MOVQstore, ssa.OpAMD64MOVSSstore, ssa.OpAMD64MOVSDstore, ssa.OpAMD64MOVLstore, ssa.OpAMD64MOVWstore, ssa.OpAMD64MOVBstore, ssa.OpAMD64MOVOstore,
ssa.OpAMD64ADDQmodify, ssa.OpAMD64SUBQmodify, ssa.OpAMD64ANDQmodify, ssa.OpAMD64ORQmodify, ssa.OpAMD64XORQmodify, ssa.OpAMD64ADDQmodify, ssa.OpAMD64SUBQmodify, ssa.OpAMD64ANDQmodify, ssa.OpAMD64ORQmodify, ssa.OpAMD64XORQmodify,
ssa.OpAMD64ADDLmodify, ssa.OpAMD64SUBLmodify, ssa.OpAMD64ANDLmodify, ssa.OpAMD64ORLmodify, ssa.OpAMD64XORLmodify: ssa.OpAMD64ADDLmodify, ssa.OpAMD64SUBLmodify, ssa.OpAMD64ANDLmodify, ssa.OpAMD64ORLmodify, ssa.OpAMD64XORLmodify,
ssa.OpAMD64MOVBEQstore, ssa.OpAMD64MOVBELstore:
p := s.Prog(v.Op.Asm()) p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[1].Reg() p.From.Reg = v.Args[1].Reg()
@ -822,7 +856,12 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_MEM p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg() p.To.Reg = v.Args[0].Reg()
ssagen.AddAux2(&p.To, v, sc.Off64()) ssagen.AddAux2(&p.To, v, sc.Off64())
case ssa.OpAMD64MOVOstorezero: case ssa.OpAMD64MOVOstoreconst:
sc := v.AuxValAndOff()
if sc.Val() != 0 {
v.Fatalf("MOVO for non zero constants not implemented: %s", v.LongString())
}
if s.ABI != obj.ABIInternal { if s.ABI != obj.ABIInternal {
// zero X15 manually // zero X15 manually
opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15) opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
@ -832,7 +871,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Reg = x86.REG_X15 p.From.Reg = x86.REG_X15
p.To.Type = obj.TYPE_MEM p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg() p.To.Reg = v.Args[0].Reg()
ssagen.AddAux(&p.To, v) ssagen.AddAux2(&p.To, v, sc.Off64())
case ssa.OpAMD64MOVQstoreconstidx1, ssa.OpAMD64MOVQstoreconstidx8, ssa.OpAMD64MOVLstoreconstidx1, ssa.OpAMD64MOVLstoreconstidx4, ssa.OpAMD64MOVWstoreconstidx1, ssa.OpAMD64MOVWstoreconstidx2, ssa.OpAMD64MOVBstoreconstidx1, case ssa.OpAMD64MOVQstoreconstidx1, ssa.OpAMD64MOVQstoreconstidx8, ssa.OpAMD64MOVLstoreconstidx1, ssa.OpAMD64MOVLstoreconstidx4, ssa.OpAMD64MOVWstoreconstidx1, ssa.OpAMD64MOVWstoreconstidx2, ssa.OpAMD64MOVBstoreconstidx1,
ssa.OpAMD64ADDLconstmodifyidx1, ssa.OpAMD64ADDLconstmodifyidx4, ssa.OpAMD64ADDLconstmodifyidx8, ssa.OpAMD64ADDQconstmodifyidx1, ssa.OpAMD64ADDQconstmodifyidx8, ssa.OpAMD64ADDLconstmodifyidx1, ssa.OpAMD64ADDLconstmodifyidx4, ssa.OpAMD64ADDLconstmodifyidx8, ssa.OpAMD64ADDQconstmodifyidx1, ssa.OpAMD64ADDQconstmodifyidx8,
ssa.OpAMD64ANDLconstmodifyidx1, ssa.OpAMD64ANDLconstmodifyidx4, ssa.OpAMD64ANDLconstmodifyidx8, ssa.OpAMD64ANDQconstmodifyidx1, ssa.OpAMD64ANDQconstmodifyidx8, ssa.OpAMD64ANDLconstmodifyidx1, ssa.OpAMD64ANDLconstmodifyidx4, ssa.OpAMD64ANDLconstmodifyidx8, ssa.OpAMD64ANDQconstmodifyidx1, ssa.OpAMD64ANDQconstmodifyidx8,
@ -1002,7 +1042,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
} }
r := v.Reg() r := v.Reg()
getgFromTLS(s, r) getgFromTLS(s, r)
case ssa.OpAMD64CALLstatic: case ssa.OpAMD64CALLstatic, ssa.OpAMD64CALLtail:
if s.ABI == obj.ABI0 && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABIInternal { if s.ABI == obj.ABI0 && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABIInternal {
// zeroing X15 when entering ABIInternal from ABI0 // zeroing X15 when entering ABIInternal from ABI0
if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9 if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9
@ -1011,6 +1051,10 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
// set G register from TLS // set G register from TLS
getgFromTLS(s, x86.REG_R14) getgFromTLS(s, x86.REG_R14)
} }
if v.Op == ssa.OpAMD64CALLtail {
s.TailCall(v)
break
}
s.Call(v) s.Call(v)
if s.ABI == obj.ABIInternal && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABI0 { if s.ABI == obj.ABIInternal && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABI0 {
// zeroing X15 when entering ABIInternal from ABI0 // zeroing X15 when entering ABIInternal from ABI0
@ -1097,7 +1141,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
if v.Args[0].Reg() != v.Reg() { if v.Args[0].Reg() != v.Reg() {
// POPCNT on Intel has a false dependency on the destination register. // POPCNT on Intel has a false dependency on the destination register.
// Xor register with itself to break the dependency. // Xor register with itself to break the dependency.
p := s.Prog(x86.AXORQ) p := s.Prog(x86.AXORL)
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = v.Reg() p.From.Reg = v.Reg()
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
@ -1225,6 +1269,10 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_MEM p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg() p.To.Reg = v.Args[0].Reg()
ssagen.AddAux(&p.To, v) ssagen.AddAux(&p.To, v)
case ssa.OpAMD64PrefetchT0, ssa.OpAMD64PrefetchNTA:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
case ssa.OpClobber: case ssa.OpClobber:
p := s.Prog(x86.AMOVL) p := s.Prog(x86.AMOVL)
p.From.Type = obj.TYPE_CONST p.From.Type = obj.TYPE_CONST
@ -1304,22 +1352,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
p.To.Type = obj.TYPE_BRANCH p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
} }
case ssa.BlockExit: case ssa.BlockExit, ssa.BlockRetJmp:
case ssa.BlockRet: case ssa.BlockRet:
s.Prog(obj.ARET) s.Prog(obj.ARET)
case ssa.BlockRetJmp:
if s.ABI == obj.ABI0 && b.Aux.(*obj.LSym).ABI() == obj.ABIInternal {
// zeroing X15 when entering ABIInternal from ABI0
if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9
opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
}
// set G register from TLS
getgFromTLS(s, x86.REG_R14)
}
p := s.Prog(obj.ARET)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = b.Aux.(*obj.LSym)
case ssa.BlockAMD64EQF: case ssa.BlockAMD64EQF:
s.CombJump(b, next, &eqfJumps) s.CombJump(b, next, &eqfJumps)

View File

@ -0,0 +1,383 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package amd64_test
import (
"bufio"
"debug/elf"
"debug/macho"
"fmt"
"internal/testenv"
"io"
"math"
"math/bits"
"os"
"os/exec"
"regexp"
"runtime"
"strconv"
"strings"
"testing"
)
// Test to make sure that when building for GOAMD64=v1, we don't
// use any >v1 instructions.
func TestGoAMD64v1(t *testing.T) {
if runtime.GOARCH != "amd64" {
t.Skip("amd64-only test")
}
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
t.Skip("test only works on elf or macho platforms")
}
if v := os.Getenv("GOAMD64"); v != "" && v != "v1" {
// Test runs only on v1 (which is the default).
// TODO: use build tags from #45454 instead.
t.Skip("GOAMD64 already set")
}
if os.Getenv("TESTGOAMD64V1") != "" {
t.Skip("recursive call")
}
// Make a binary which will be a modified version of the
// currently running binary.
dst, err := os.CreateTemp("", "TestGoAMD64v1")
if err != nil {
t.Fatalf("failed to create temp file: %v", err)
}
defer os.Remove(dst.Name())
dst.Chmod(0500) // make executable
// Clobber all the non-v1 opcodes.
opcodes := map[string]bool{}
var features []string
for feature, opcodeList := range featureToOpcodes {
if runtimeFeatures[feature] {
features = append(features, fmt.Sprintf("cpu.%s=off", feature))
}
for _, op := range opcodeList {
opcodes[op] = true
}
}
clobber(t, os.Args[0], dst, opcodes)
if err = dst.Close(); err != nil {
t.Fatalf("can't close binary: %v", err)
}
// Run the resulting binary.
cmd := exec.Command(dst.Name())
testenv.CleanCmdEnv(cmd)
cmd.Env = append(cmd.Env, "TESTGOAMD64V1=yes")
cmd.Env = append(cmd.Env, fmt.Sprintf("GODEBUG=%s", strings.Join(features, ",")))
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("couldn't execute test: %s", err)
}
if string(out) != "PASS\n" {
t.Fatalf("test reported error: %s", string(out))
}
}
// Clobber copies the binary src to dst, replacing all the instructions in opcodes with
// faulting instructions.
func clobber(t *testing.T, src string, dst *os.File, opcodes map[string]bool) {
// Run objdump to get disassembly.
var re *regexp.Regexp
var disasm io.Reader
if false {
// TODO: go tool objdump doesn't disassemble the bmi1 instructions
// in question correctly. See issue 48584.
cmd := exec.Command("go", "tool", "objdump", src)
var err error
disasm, err = cmd.StdoutPipe()
if err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
re = regexp.MustCompile(`^[^:]*:[-0-9]+\s+0x([0-9a-f]+)\s+([0-9a-f]+)\s+([A-Z]+)`)
} else {
// TODO: we're depending on platform-native objdump here. Hence the Skipf
// below if it doesn't run for some reason.
cmd := exec.Command("objdump", "-d", src)
var err error
disasm, err = cmd.StdoutPipe()
if err != nil {
t.Skipf("can't run test due to missing objdump: %s", err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
re = regexp.MustCompile(`^\s*([0-9a-f]+):\s*((?:[0-9a-f][0-9a-f] )+)\s*([a-z0-9]+)`)
}
// Find all the instruction addresses we need to edit.
virtualEdits := map[uint64]bool{}
scanner := bufio.NewScanner(disasm)
for scanner.Scan() {
line := scanner.Text()
parts := re.FindStringSubmatch(line)
if len(parts) == 0 {
continue
}
addr, err := strconv.ParseUint(parts[1], 16, 64)
if err != nil {
continue // not a hex address
}
opcode := strings.ToLower(parts[3])
if !opcodes[opcode] {
continue
}
t.Logf("clobbering instruction %s", line)
n := (len(parts[2]) - strings.Count(parts[2], " ")) / 2 // number of bytes in instruction encoding
for i := 0; i < n; i++ {
// Only really need to make the first byte faulting, but might
// as well make all the bytes faulting.
virtualEdits[addr+uint64(i)] = true
}
}
// Figure out where in the binary the edits must be done.
physicalEdits := map[uint64]bool{}
if e, err := elf.Open(src); err == nil {
for _, sec := range e.Sections {
vaddr := sec.Addr
paddr := sec.Offset
size := sec.Size
for a := range virtualEdits {
if a >= vaddr && a < vaddr+size {
physicalEdits[paddr+(a-vaddr)] = true
}
}
}
} else if m, err2 := macho.Open(src); err2 == nil {
for _, sec := range m.Sections {
vaddr := sec.Addr
paddr := uint64(sec.Offset)
size := sec.Size
for a := range virtualEdits {
if a >= vaddr && a < vaddr+size {
physicalEdits[paddr+(a-vaddr)] = true
}
}
}
} else {
t.Log(err)
t.Log(err2)
t.Fatal("executable format not elf or macho")
}
if len(virtualEdits) != len(physicalEdits) {
t.Fatal("couldn't find an instruction in text sections")
}
// Copy source to destination, making edits along the way.
f, err := os.Open(src)
if err != nil {
t.Fatal(err)
}
r := bufio.NewReader(f)
w := bufio.NewWriter(dst)
a := uint64(0)
done := 0
for {
b, err := r.ReadByte()
if err == io.EOF {
break
}
if err != nil {
t.Fatal("can't read")
}
if physicalEdits[a] {
b = 0xcc // INT3 opcode
done++
}
err = w.WriteByte(b)
if err != nil {
t.Fatal("can't write")
}
a++
}
if done != len(physicalEdits) {
t.Fatal("physical edits remaining")
}
w.Flush()
f.Close()
}
func setOf(keys ...string) map[string]bool {
m := make(map[string]bool, len(keys))
for _, key := range keys {
m[key] = true
}
return m
}
var runtimeFeatures = setOf(
"adx", "aes", "avx", "avx2", "bmi1", "bmi2", "erms", "fma",
"pclmulqdq", "popcnt", "rdtscp", "sse3", "sse41", "sse42", "ssse3",
)
var featureToOpcodes = map[string][]string{
// Note: we include *q, *l, and plain opcodes here.
// go tool objdump doesn't include a [QL] on popcnt instructions, until CL 351889
// native objdump doesn't include [QL] on linux.
"popcnt": {"popcntq", "popcntl", "popcnt"},
"bmi1": {"andnq", "andnl", "andn", "blsiq", "blsil", "blsi", "blsmskq", "blsmskl", "blsmsk", "blsrq", "blsrl", "blsr", "tzcntq", "tzcntl", "tzcnt"},
"sse41": {"roundsd"},
"fma": {"vfmadd231sd"},
"movbe": {"movbeqq", "movbeq", "movbell", "movbel", "movbe"},
}
// Test to use POPCNT instruction, if available
func TestPopCnt(t *testing.T) {
for _, tt := range []struct {
x uint64
want int
}{
{0b00001111, 4},
{0b00001110, 3},
{0b00001100, 2},
{0b00000000, 0},
} {
if got := bits.OnesCount64(tt.x); got != tt.want {
t.Errorf("OnesCount64(%#x) = %d, want %d", tt.x, got, tt.want)
}
if got := bits.OnesCount32(uint32(tt.x)); got != tt.want {
t.Errorf("OnesCount32(%#x) = %d, want %d", tt.x, got, tt.want)
}
}
}
// Test to use ANDN, if available
func TestAndNot(t *testing.T) {
for _, tt := range []struct {
x, y, want uint64
}{
{0b00001111, 0b00000011, 0b1100},
{0b00001111, 0b00001100, 0b0011},
{0b00000000, 0b00000000, 0b0000},
} {
if got := tt.x &^ tt.y; got != tt.want {
t.Errorf("%#x &^ %#x = %#x, want %#x", tt.x, tt.y, got, tt.want)
}
if got := uint32(tt.x) &^ uint32(tt.y); got != uint32(tt.want) {
t.Errorf("%#x &^ %#x = %#x, want %#x", tt.x, tt.y, got, tt.want)
}
}
}
// Test to use BLSI, if available
func TestBLSI(t *testing.T) {
for _, tt := range []struct {
x, want uint64
}{
{0b00001111, 0b001},
{0b00001110, 0b010},
{0b00001100, 0b100},
{0b11000110, 0b010},
{0b00000000, 0b000},
} {
if got := tt.x & -tt.x; got != tt.want {
t.Errorf("%#x & (-%#x) = %#x, want %#x", tt.x, tt.x, got, tt.want)
}
if got := uint32(tt.x) & -uint32(tt.x); got != uint32(tt.want) {
t.Errorf("%#x & (-%#x) = %#x, want %#x", tt.x, tt.x, got, tt.want)
}
}
}
// Test to use BLSMSK, if available
func TestBLSMSK(t *testing.T) {
for _, tt := range []struct {
x, want uint64
}{
{0b00001111, 0b001},
{0b00001110, 0b011},
{0b00001100, 0b111},
{0b11000110, 0b011},
{0b00000000, 1<<64 - 1},
} {
if got := tt.x ^ (tt.x - 1); got != tt.want {
t.Errorf("%#x ^ (%#x-1) = %#x, want %#x", tt.x, tt.x, got, tt.want)
}
if got := uint32(tt.x) ^ (uint32(tt.x) - 1); got != uint32(tt.want) {
t.Errorf("%#x ^ (%#x-1) = %#x, want %#x", tt.x, tt.x, got, uint32(tt.want))
}
}
}
// Test to use BLSR, if available
func TestBLSR(t *testing.T) {
for _, tt := range []struct {
x, want uint64
}{
{0b00001111, 0b00001110},
{0b00001110, 0b00001100},
{0b00001100, 0b00001000},
{0b11000110, 0b11000100},
{0b00000000, 0b00000000},
} {
if got := tt.x & (tt.x - 1); got != tt.want {
t.Errorf("%#x & (%#x-1) = %#x, want %#x", tt.x, tt.x, got, tt.want)
}
if got := uint32(tt.x) & (uint32(tt.x) - 1); got != uint32(tt.want) {
t.Errorf("%#x & (%#x-1) = %#x, want %#x", tt.x, tt.x, got, tt.want)
}
}
}
func TestTrailingZeros(t *testing.T) {
for _, tt := range []struct {
x uint64
want int
}{
{0b00001111, 0},
{0b00001110, 1},
{0b00001100, 2},
{0b00001000, 3},
{0b00000000, 64},
} {
if got := bits.TrailingZeros64(tt.x); got != tt.want {
t.Errorf("TrailingZeros64(%#x) = %d, want %d", tt.x, got, tt.want)
}
want := tt.want
if want == 64 {
want = 32
}
if got := bits.TrailingZeros32(uint32(tt.x)); got != want {
t.Errorf("TrailingZeros64(%#x) = %d, want %d", tt.x, got, want)
}
}
}
func TestRound(t *testing.T) {
for _, tt := range []struct {
x, want float64
}{
{1.4, 1},
{1.5, 2},
{1.6, 2},
{2.4, 2},
{2.5, 2},
{2.6, 3},
} {
if got := math.RoundToEven(tt.x); got != tt.want {
t.Errorf("RoundToEven(%f) = %f, want %f", tt.x, got, tt.want)
}
}
}
func TestFMA(t *testing.T) {
for _, tt := range []struct {
x, y, z, want float64
}{
{2, 3, 4, 10},
{3, 4, 5, 17},
} {
if got := math.FMA(tt.x, tt.y, tt.z); got != tt.want {
t.Errorf("FMA(%f,%f,%f) = %f, want %f", tt.x, tt.y, tt.z, got, tt.want)
}
}
}

View File

@ -88,15 +88,18 @@ func (v shift) String() string {
} }
// makeshift encodes a register shifted by a constant // makeshift encodes a register shifted by a constant
func makeshift(reg int16, typ int64, s int64) shift { func makeshift(v *ssa.Value, reg int16, typ int64, s int64) shift {
if s < 0 || s >= 32 {
v.Fatalf("shift out of range: %d", s)
}
return shift(int64(reg&0xf) | typ | (s&31)<<7) return shift(int64(reg&0xf) | typ | (s&31)<<7)
} }
// genshift generates a Prog for r = r0 op (r1 shifted by n) // genshift generates a Prog for r = r0 op (r1 shifted by n)
func genshift(s *ssagen.State, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog { func genshift(s *ssagen.State, v *ssa.Value, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
p := s.Prog(as) p := s.Prog(as)
p.From.Type = obj.TYPE_SHIFT p.From.Type = obj.TYPE_SHIFT
p.From.Offset = int64(makeshift(r1, typ, n)) p.From.Offset = int64(makeshift(v, r1, typ, n))
p.Reg = r0 p.Reg = r0
if r != 0 { if r != 0 {
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
@ -335,7 +338,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0() p.To.Reg = v.Reg0()
case ssa.OpARMSRRconst: case ssa.OpARMSRRconst:
genshift(s, arm.AMOVW, 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt) genshift(s, v, arm.AMOVW, 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt)
case ssa.OpARMADDshiftLL, case ssa.OpARMADDshiftLL,
ssa.OpARMADCshiftLL, ssa.OpARMADCshiftLL,
ssa.OpARMSUBshiftLL, ssa.OpARMSUBshiftLL,
@ -346,11 +349,11 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpARMORshiftLL, ssa.OpARMORshiftLL,
ssa.OpARMXORshiftLL, ssa.OpARMXORshiftLL,
ssa.OpARMBICshiftLL: ssa.OpARMBICshiftLL:
genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt) genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
case ssa.OpARMADDSshiftLL, case ssa.OpARMADDSshiftLL,
ssa.OpARMSUBSshiftLL, ssa.OpARMSUBSshiftLL,
ssa.OpARMRSBSshiftLL: ssa.OpARMRSBSshiftLL:
p := genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LL, v.AuxInt) p := genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LL, v.AuxInt)
p.Scond = arm.C_SBIT p.Scond = arm.C_SBIT
case ssa.OpARMADDshiftRL, case ssa.OpARMADDshiftRL,
ssa.OpARMADCshiftRL, ssa.OpARMADCshiftRL,
@ -362,11 +365,11 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpARMORshiftRL, ssa.OpARMORshiftRL,
ssa.OpARMXORshiftRL, ssa.OpARMXORshiftRL,
ssa.OpARMBICshiftRL: ssa.OpARMBICshiftRL:
genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt) genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
case ssa.OpARMADDSshiftRL, case ssa.OpARMADDSshiftRL,
ssa.OpARMSUBSshiftRL, ssa.OpARMSUBSshiftRL,
ssa.OpARMRSBSshiftRL: ssa.OpARMRSBSshiftRL:
p := genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LR, v.AuxInt) p := genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LR, v.AuxInt)
p.Scond = arm.C_SBIT p.Scond = arm.C_SBIT
case ssa.OpARMADDshiftRA, case ssa.OpARMADDshiftRA,
ssa.OpARMADCshiftRA, ssa.OpARMADCshiftRA,
@ -378,20 +381,20 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpARMORshiftRA, ssa.OpARMORshiftRA,
ssa.OpARMXORshiftRA, ssa.OpARMXORshiftRA,
ssa.OpARMBICshiftRA: ssa.OpARMBICshiftRA:
genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt) genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
case ssa.OpARMADDSshiftRA, case ssa.OpARMADDSshiftRA,
ssa.OpARMSUBSshiftRA, ssa.OpARMSUBSshiftRA,
ssa.OpARMRSBSshiftRA: ssa.OpARMRSBSshiftRA:
p := genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_AR, v.AuxInt) p := genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_AR, v.AuxInt)
p.Scond = arm.C_SBIT p.Scond = arm.C_SBIT
case ssa.OpARMXORshiftRR: case ssa.OpARMXORshiftRR:
genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt) genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt)
case ssa.OpARMMVNshiftLL: case ssa.OpARMMVNshiftLL:
genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt) genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
case ssa.OpARMMVNshiftRL: case ssa.OpARMMVNshiftRL:
genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt) genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
case ssa.OpARMMVNshiftRA: case ssa.OpARMMVNshiftRA:
genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt) genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
case ssa.OpARMMVNshiftLLreg: case ssa.OpARMMVNshiftLLreg:
genregshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL) genregshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL)
case ssa.OpARMMVNshiftRLreg: case ssa.OpARMMVNshiftRLreg:
@ -513,11 +516,11 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg() p.From.Reg = v.Args[0].Reg()
case ssa.OpARMCMPshiftLL, ssa.OpARMCMNshiftLL, ssa.OpARMTSTshiftLL, ssa.OpARMTEQshiftLL: case ssa.OpARMCMPshiftLL, ssa.OpARMCMNshiftLL, ssa.OpARMTSTshiftLL, ssa.OpARMTEQshiftLL:
genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LL, v.AuxInt) genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LL, v.AuxInt)
case ssa.OpARMCMPshiftRL, ssa.OpARMCMNshiftRL, ssa.OpARMTSTshiftRL, ssa.OpARMTEQshiftRL: case ssa.OpARMCMPshiftRL, ssa.OpARMCMNshiftRL, ssa.OpARMTSTshiftRL, ssa.OpARMTEQshiftRL:
genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LR, v.AuxInt) genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LR, v.AuxInt)
case ssa.OpARMCMPshiftRA, ssa.OpARMCMNshiftRA, ssa.OpARMTSTshiftRA, ssa.OpARMTEQshiftRA: case ssa.OpARMCMPshiftRA, ssa.OpARMCMNshiftRA, ssa.OpARMTSTshiftRA, ssa.OpARMTEQshiftRA:
genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_AR, v.AuxInt) genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_AR, v.AuxInt)
case ssa.OpARMCMPshiftLLreg, ssa.OpARMCMNshiftLLreg, ssa.OpARMTSTshiftLLreg, ssa.OpARMTEQshiftLLreg: case ssa.OpARMCMPshiftLLreg, ssa.OpARMCMNshiftLLreg, ssa.OpARMTSTshiftLLreg, ssa.OpARMTEQshiftLLreg:
genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), 0, arm.SHIFT_LL) genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), 0, arm.SHIFT_LL)
case ssa.OpARMCMPshiftRLreg, ssa.OpARMCMNshiftRLreg, ssa.OpARMTSTshiftRLreg, ssa.OpARMTEQshiftRLreg: case ssa.OpARMCMPshiftRLreg, ssa.OpARMCMNshiftRLreg, ssa.OpARMTSTshiftRLreg, ssa.OpARMTEQshiftRLreg:
@ -583,13 +586,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
// this is just shift 0 bits // this is just shift 0 bits
fallthrough fallthrough
case ssa.OpARMMOVWloadshiftLL: case ssa.OpARMMOVWloadshiftLL:
p := genshift(s, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt) p := genshift(s, v, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
p.From.Reg = v.Args[0].Reg() p.From.Reg = v.Args[0].Reg()
case ssa.OpARMMOVWloadshiftRL: case ssa.OpARMMOVWloadshiftRL:
p := genshift(s, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt) p := genshift(s, v, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
p.From.Reg = v.Args[0].Reg() p.From.Reg = v.Args[0].Reg()
case ssa.OpARMMOVWloadshiftRA: case ssa.OpARMMOVWloadshiftRA:
p := genshift(s, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt) p := genshift(s, v, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
p.From.Reg = v.Args[0].Reg() p.From.Reg = v.Args[0].Reg()
case ssa.OpARMMOVWstoreidx, ssa.OpARMMOVBstoreidx, ssa.OpARMMOVHstoreidx: case ssa.OpARMMOVWstoreidx, ssa.OpARMMOVBstoreidx, ssa.OpARMMOVHstoreidx:
// this is just shift 0 bits // this is just shift 0 bits
@ -600,21 +603,21 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Reg = v.Args[2].Reg() p.From.Reg = v.Args[2].Reg()
p.To.Type = obj.TYPE_SHIFT p.To.Type = obj.TYPE_SHIFT
p.To.Reg = v.Args[0].Reg() p.To.Reg = v.Args[0].Reg()
p.To.Offset = int64(makeshift(v.Args[1].Reg(), arm.SHIFT_LL, v.AuxInt)) p.To.Offset = int64(makeshift(v, v.Args[1].Reg(), arm.SHIFT_LL, v.AuxInt))
case ssa.OpARMMOVWstoreshiftRL: case ssa.OpARMMOVWstoreshiftRL:
p := s.Prog(v.Op.Asm()) p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[2].Reg() p.From.Reg = v.Args[2].Reg()
p.To.Type = obj.TYPE_SHIFT p.To.Type = obj.TYPE_SHIFT
p.To.Reg = v.Args[0].Reg() p.To.Reg = v.Args[0].Reg()
p.To.Offset = int64(makeshift(v.Args[1].Reg(), arm.SHIFT_LR, v.AuxInt)) p.To.Offset = int64(makeshift(v, v.Args[1].Reg(), arm.SHIFT_LR, v.AuxInt))
case ssa.OpARMMOVWstoreshiftRA: case ssa.OpARMMOVWstoreshiftRA:
p := s.Prog(v.Op.Asm()) p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[2].Reg() p.From.Reg = v.Args[2].Reg()
p.To.Type = obj.TYPE_SHIFT p.To.Type = obj.TYPE_SHIFT
p.To.Reg = v.Args[0].Reg() p.To.Reg = v.Args[0].Reg()
p.To.Offset = int64(makeshift(v.Args[1].Reg(), arm.SHIFT_AR, v.AuxInt)) p.To.Offset = int64(makeshift(v, v.Args[1].Reg(), arm.SHIFT_AR, v.AuxInt))
case ssa.OpARMMOVBreg, case ssa.OpARMMOVBreg,
ssa.OpARMMOVBUreg, ssa.OpARMMOVBUreg,
ssa.OpARMMOVHreg, ssa.OpARMMOVHreg,
@ -645,7 +648,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
} }
if buildcfg.GOARM >= 6 { if buildcfg.GOARM >= 6 {
// generate more efficient "MOVB/MOVBU/MOVH/MOVHU Reg@>0, Reg" on ARMv6 & ARMv7 // generate more efficient "MOVB/MOVBU/MOVH/MOVHU Reg@>0, Reg" on ARMv6 & ARMv7
genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, 0) genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, 0)
return return
} }
fallthrough fallthrough
@ -696,6 +699,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Reg = v.Reg() p.To.Reg = v.Reg()
case ssa.OpARMCALLstatic, ssa.OpARMCALLclosure, ssa.OpARMCALLinter: case ssa.OpARMCALLstatic, ssa.OpARMCALLclosure, ssa.OpARMCALLinter:
s.Call(v) s.Call(v)
case ssa.OpARMCALLtail:
s.TailCall(v)
case ssa.OpARMCALLudiv: case ssa.OpARMCALLudiv:
p := s.Prog(obj.ACALL) p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM p.To.Type = obj.TYPE_MEM
@ -936,17 +941,11 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
} }
case ssa.BlockExit: case ssa.BlockExit, ssa.BlockRetJmp:
case ssa.BlockRet: case ssa.BlockRet:
s.Prog(obj.ARET) s.Prog(obj.ARET)
case ssa.BlockRetJmp:
p := s.Prog(obj.ARET)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = b.Aux.(*obj.LSym)
case ssa.BlockARMEQ, ssa.BlockARMNE, case ssa.BlockARMEQ, ssa.BlockARMNE,
ssa.BlockARMLT, ssa.BlockARMGE, ssa.BlockARMLT, ssa.BlockARMGE,
ssa.BlockARMLE, ssa.BlockARMGT, ssa.BlockARMLE, ssa.BlockARMGT,

View File

@ -79,15 +79,18 @@ func storeByType(t *types.Type) obj.As {
} }
// makeshift encodes a register shifted by a constant, used as an Offset in Prog // makeshift encodes a register shifted by a constant, used as an Offset in Prog
func makeshift(reg int16, typ int64, s int64) int64 { func makeshift(v *ssa.Value, reg int16, typ int64, s int64) int64 {
if s < 0 || s >= 64 {
v.Fatalf("shift out of range: %d", s)
}
return int64(reg&31)<<16 | typ | (s&63)<<10 return int64(reg&31)<<16 | typ | (s&63)<<10
} }
// genshift generates a Prog for r = r0 op (r1 shifted by n) // genshift generates a Prog for r = r0 op (r1 shifted by n)
func genshift(s *ssagen.State, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog { func genshift(s *ssagen.State, v *ssa.Value, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
p := s.Prog(as) p := s.Prog(as)
p.From.Type = obj.TYPE_SHIFT p.From.Type = obj.TYPE_SHIFT
p.From.Offset = makeshift(r1, typ, n) p.From.Offset = makeshift(v, r1, typ, n)
p.Reg = r0 p.Reg = r0
if r != 0 { if r != 0 {
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
@ -310,11 +313,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg() p.To.Reg = v.Reg()
case ssa.OpARM64MVNshiftLL, ssa.OpARM64NEGshiftLL: case ssa.OpARM64MVNshiftLL, ssa.OpARM64NEGshiftLL:
genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_LL, v.AuxInt) genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_LL, v.AuxInt)
case ssa.OpARM64MVNshiftRL, ssa.OpARM64NEGshiftRL: case ssa.OpARM64MVNshiftRL, ssa.OpARM64NEGshiftRL:
genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt) genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt)
case ssa.OpARM64MVNshiftRA, ssa.OpARM64NEGshiftRA: case ssa.OpARM64MVNshiftRA, ssa.OpARM64NEGshiftRA:
genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt) genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt)
case ssa.OpARM64MVNshiftRO:
genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_ROR, v.AuxInt)
case ssa.OpARM64ADDshiftLL, case ssa.OpARM64ADDshiftLL,
ssa.OpARM64SUBshiftLL, ssa.OpARM64SUBshiftLL,
ssa.OpARM64ANDshiftLL, ssa.OpARM64ANDshiftLL,
@ -323,7 +328,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpARM64EONshiftLL, ssa.OpARM64EONshiftLL,
ssa.OpARM64ORNshiftLL, ssa.OpARM64ORNshiftLL,
ssa.OpARM64BICshiftLL: ssa.OpARM64BICshiftLL:
genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LL, v.AuxInt) genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LL, v.AuxInt)
case ssa.OpARM64ADDshiftRL, case ssa.OpARM64ADDshiftRL,
ssa.OpARM64SUBshiftRL, ssa.OpARM64SUBshiftRL,
ssa.OpARM64ANDshiftRL, ssa.OpARM64ANDshiftRL,
@ -332,7 +337,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpARM64EONshiftRL, ssa.OpARM64EONshiftRL,
ssa.OpARM64ORNshiftRL, ssa.OpARM64ORNshiftRL,
ssa.OpARM64BICshiftRL: ssa.OpARM64BICshiftRL:
genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt) genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt)
case ssa.OpARM64ADDshiftRA, case ssa.OpARM64ADDshiftRA,
ssa.OpARM64SUBshiftRA, ssa.OpARM64SUBshiftRA,
ssa.OpARM64ANDshiftRA, ssa.OpARM64ANDshiftRA,
@ -341,7 +346,14 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpARM64EONshiftRA, ssa.OpARM64EONshiftRA,
ssa.OpARM64ORNshiftRA, ssa.OpARM64ORNshiftRA,
ssa.OpARM64BICshiftRA: ssa.OpARM64BICshiftRA:
genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt) genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt)
case ssa.OpARM64ANDshiftRO,
ssa.OpARM64ORshiftRO,
ssa.OpARM64XORshiftRO,
ssa.OpARM64EONshiftRO,
ssa.OpARM64ORNshiftRO,
ssa.OpARM64BICshiftRO:
genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_ROR, v.AuxInt)
case ssa.OpARM64MOVDconst: case ssa.OpARM64MOVDconst:
p := s.Prog(v.Op.Asm()) p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_CONST p.From.Type = obj.TYPE_CONST
@ -384,11 +396,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Offset = v.AuxInt p.From.Offset = v.AuxInt
p.Reg = v.Args[0].Reg() p.Reg = v.Args[0].Reg()
case ssa.OpARM64CMPshiftLL, ssa.OpARM64CMNshiftLL, ssa.OpARM64TSTshiftLL: case ssa.OpARM64CMPshiftLL, ssa.OpARM64CMNshiftLL, ssa.OpARM64TSTshiftLL:
genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LL, v.AuxInt) genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LL, v.AuxInt)
case ssa.OpARM64CMPshiftRL, ssa.OpARM64CMNshiftRL, ssa.OpARM64TSTshiftRL: case ssa.OpARM64CMPshiftRL, ssa.OpARM64CMNshiftRL, ssa.OpARM64TSTshiftRL:
genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LR, v.AuxInt) genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LR, v.AuxInt)
case ssa.OpARM64CMPshiftRA, ssa.OpARM64CMNshiftRA, ssa.OpARM64TSTshiftRA: case ssa.OpARM64CMPshiftRA, ssa.OpARM64CMNshiftRA, ssa.OpARM64TSTshiftRA:
genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_AR, v.AuxInt) genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_AR, v.AuxInt)
case ssa.OpARM64TSTshiftRO:
genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_ROR, v.AuxInt)
case ssa.OpARM64MOVDaddr: case ssa.OpARM64MOVDaddr:
p := s.Prog(arm64.AMOVD) p := s.Prog(arm64.AMOVD)
p.From.Type = obj.TYPE_ADDR p.From.Type = obj.TYPE_ADDR
@ -1046,6 +1060,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p4.To.SetTarget(p) p4.To.SetTarget(p)
case ssa.OpARM64CALLstatic, ssa.OpARM64CALLclosure, ssa.OpARM64CALLinter: case ssa.OpARM64CALLstatic, ssa.OpARM64CALLclosure, ssa.OpARM64CALLinter:
s.Call(v) s.Call(v)
case ssa.OpARM64CALLtail:
s.TailCall(v)
case ssa.OpARM64LoweredWB: case ssa.OpARM64LoweredWB:
p := s.Prog(obj.ACALL) p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM p.To.Type = obj.TYPE_MEM
@ -1095,6 +1111,12 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Reg = condBits[v.Op] p.From.Reg = condBits[v.Op]
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg() p.To.Reg = v.Reg()
case ssa.OpARM64PRFM:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_CONST
p.To.Offset = v.AuxInt
case ssa.OpARM64LoweredGetClosurePtr: case ssa.OpARM64LoweredGetClosurePtr:
// Closure pointer is R26 (arm64.REGCTXT). // Closure pointer is R26 (arm64.REGCTXT).
ssagen.CheckLoweredGetClosurePtr(v) ssagen.CheckLoweredGetClosurePtr(v)
@ -1110,6 +1132,10 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p := s.Prog(obj.AGETCALLERPC) p := s.Prog(obj.AGETCALLERPC)
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg() p.To.Reg = v.Reg()
case ssa.OpARM64DMB:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_CONST
p.From.Offset = v.AuxInt
case ssa.OpARM64FlagConstant: case ssa.OpARM64FlagConstant:
v.Fatalf("FlagConstant op should never make it to codegen %v", v.LongString()) v.Fatalf("FlagConstant op should never make it to codegen %v", v.LongString())
case ssa.OpARM64InvertFlags: case ssa.OpARM64InvertFlags:
@ -1235,17 +1261,11 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
} }
case ssa.BlockExit: case ssa.BlockExit, ssa.BlockRetJmp:
case ssa.BlockRet: case ssa.BlockRet:
s.Prog(obj.ARET) s.Prog(obj.ARET)
case ssa.BlockRetJmp:
p := s.Prog(obj.ARET)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = b.Aux.(*obj.LSym)
case ssa.BlockARM64EQ, ssa.BlockARM64NE, case ssa.BlockARM64EQ, ssa.BlockARM64NE,
ssa.BlockARM64LT, ssa.BlockARM64GE, ssa.BlockARM64LT, ssa.BlockARM64GE,
ssa.BlockARM64LE, ssa.BlockARM64GT, ssa.BlockARM64LE, ssa.BlockARM64GT,

View File

@ -67,6 +67,7 @@ var NoInstrumentPkgs = []string{
"runtime", "runtime",
"runtime/race", "runtime/race",
"runtime/msan", "runtime/msan",
"runtime/asan",
"internal/cpu", "internal/cpu",
} }

View File

@ -6,15 +6,6 @@
package base package base
import (
"fmt"
"log"
"os"
"reflect"
"strconv"
"strings"
)
// Debug holds the parsed debugging configuration values. // Debug holds the parsed debugging configuration values.
var Debug DebugFlags var Debug DebugFlags
@ -26,7 +17,7 @@ var Debug DebugFlags
// Each setting is name=value; for ints, name is short for name=1. // Each setting is name=value; for ints, name is short for name=1.
type DebugFlags struct { type DebugFlags struct {
Append int `help:"print information about append compilation"` Append int `help:"print information about append compilation"`
Checkptr int `help:"instrument unsafe pointer conversions"` Checkptr int `help:"instrument unsafe pointer conversions\n0: instrumentation disabled\n1: conversions involving unsafe.Pointer are instrumented\n2: conversions to unsafe.Pointer force heap allocation"`
Closure int `help:"print information about closure compilation"` Closure int `help:"print information about closure compilation"`
DclStack int `help:"run internal dclstack check"` DclStack int `help:"run internal dclstack check"`
Defer int `help:"print information about defer compilation"` Defer int `help:"print information about defer compilation"`
@ -40,7 +31,7 @@ type DebugFlags struct {
LocationLists int `help:"print information about DWARF location list creation"` LocationLists int `help:"print information about DWARF location list creation"`
Nil int `help:"print information about nil checks"` Nil int `help:"print information about nil checks"`
NoOpenDefer int `help:"disable open-coded defers"` NoOpenDefer int `help:"disable open-coded defers"`
PCTab string `help:"print named pc-value table"` PCTab string `help:"print named pc-value table\nOne of: pctospadj, pctofile, pctoline, pctoinline, pctopcdata"`
Panic int `help:"show all compiler panics"` Panic int `help:"show all compiler panics"`
Slice int `help:"print information about slice compilation"` Slice int `help:"print information about slice compilation"`
SoftFloat int `help:"force compiler to emit soft-float code"` SoftFloat int `help:"force compiler to emit soft-float code"`
@ -51,142 +42,12 @@ type DebugFlags struct {
UnifiedQuirks int `help:"enable unified IR construction's quirks mode"` UnifiedQuirks int `help:"enable unified IR construction's quirks mode"`
WB int `help:"print information about write barriers"` WB int `help:"print information about write barriers"`
ABIWrap int `help:"print information about ABI wrapper generation"` ABIWrap int `help:"print information about ABI wrapper generation"`
MayMoreStack string `help:"call named function before all stack growth checks"`
any bool // set when any of the values have been set Any bool // set when any of the debug flags have been set
}
// Any reports whether any of the debug flags have been set.
func (d *DebugFlags) Any() bool { return d.any }
type debugField struct {
name string
help string
val interface{} // *int or *string
}
var debugTab []debugField
func init() {
v := reflect.ValueOf(&Debug).Elem()
t := v.Type()
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Name == "any" {
continue
}
name := strings.ToLower(f.Name)
help := f.Tag.Get("help")
if help == "" {
panic(fmt.Sprintf("base.Debug.%s is missing help text", f.Name))
}
ptr := v.Field(i).Addr().Interface()
switch ptr.(type) {
default:
panic(fmt.Sprintf("base.Debug.%s has invalid type %v (must be int or string)", f.Name, f.Type))
case *int, *string:
// ok
}
debugTab = append(debugTab, debugField{name, help, ptr})
}
} }
// DebugSSA is called to set a -d ssa/... option. // DebugSSA is called to set a -d ssa/... option.
// If nil, those options are reported as invalid options. // If nil, those options are reported as invalid options.
// If DebugSSA returns a non-empty string, that text is reported as a compiler error. // If DebugSSA returns a non-empty string, that text is reported as a compiler error.
var DebugSSA func(phase, flag string, val int, valString string) string var DebugSSA func(phase, flag string, val int, valString string) string
// parseDebug parses the -d debug string argument.
func parseDebug(debugstr string) {
// parse -d argument
if debugstr == "" {
return
}
Debug.any = true
Split:
for _, name := range strings.Split(debugstr, ",") {
if name == "" {
continue
}
// display help about the -d option itself and quit
if name == "help" {
fmt.Print(debugHelpHeader)
maxLen := len("ssa/help")
for _, t := range debugTab {
if len(t.name) > maxLen {
maxLen = len(t.name)
}
}
for _, t := range debugTab {
fmt.Printf("\t%-*s\t%s\n", maxLen, t.name, t.help)
}
// ssa options have their own help
fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging")
fmt.Print(debugHelpFooter)
os.Exit(0)
}
val, valstring, haveInt := 1, "", true
if i := strings.IndexAny(name, "=:"); i >= 0 {
var err error
name, valstring = name[:i], name[i+1:]
val, err = strconv.Atoi(valstring)
if err != nil {
val, haveInt = 1, false
}
}
for _, t := range debugTab {
if t.name != name {
continue
}
switch vp := t.val.(type) {
case nil:
// Ignore
case *string:
*vp = valstring
case *int:
if !haveInt {
log.Fatalf("invalid debug value %v", name)
}
*vp = val
default:
panic("bad debugtab type")
}
continue Split
}
// special case for ssa for now
if DebugSSA != nil && strings.HasPrefix(name, "ssa/") {
// expect form ssa/phase/flag
// e.g. -d=ssa/generic_cse/time
// _ in phase name also matches space
phase := name[4:]
flag := "debug" // default flag is debug
if i := strings.Index(phase, "/"); i >= 0 {
flag = phase[i+1:]
phase = phase[:i]
}
err := DebugSSA(phase, flag, val, valstring)
if err != "" {
log.Fatalf(err)
}
continue Split
}
log.Fatalf("unknown debug key -d %s\n", name)
}
}
const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>]
<key> is one of:
`
const debugHelpFooter = `
<value> is key-specific.
Key "checkptr" supports values:
"0": instrumentation disabled
"1": conversions involving unsafe.Pointer are instrumented
"2": conversions to unsafe.Pointer force heap allocation
Key "pctab" supports values:
"pctospadj", "pctofile", "pctoline", "pctoinline", "pctopcdata"
`

View File

@ -64,19 +64,19 @@ type CmdFlags struct {
// V is added by objabi.AddVersionFlag // V is added by objabi.AddVersionFlag
W CountFlag "help:\"debug parse tree after type checking\"" W CountFlag "help:\"debug parse tree after type checking\""
LowerC int "help:\"concurrency during compilation (1 means no concurrency)\"" LowerC int "help:\"concurrency during compilation (1 means no concurrency)\""
LowerD func(string) "help:\"enable debugging settings; try -d help\"" LowerD flag.Value "help:\"enable debugging settings; try -d help\""
LowerE CountFlag "help:\"no limit on number of errors reported\"" LowerE CountFlag "help:\"no limit on number of errors reported\""
LowerH CountFlag "help:\"halt on error\"" LowerH CountFlag "help:\"halt on error\""
LowerJ CountFlag "help:\"debug runtime-initialized variables\"" LowerJ CountFlag "help:\"debug runtime-initialized variables\""
LowerL CountFlag "help:\"disable inlining\"" LowerL CountFlag "help:\"disable inlining\""
LowerM CountFlag "help:\"print optimization decisions\"" LowerM CountFlag "help:\"print optimization decisions\""
LowerO string "help:\"write output to `file`\"" LowerO string "help:\"write output to `file`\""
LowerP *string "help:\"set expected package import `path`\"" // &Ctxt.Pkgpath, set below LowerP *string "help:\"set expected package import `path`\"" // &Ctxt.Pkgpath, set below
LowerR CountFlag "help:\"debug generated wrappers\"" LowerR CountFlag "help:\"debug generated wrappers\""
LowerT bool "help:\"enable tracing for debugging the compiler\"" LowerT bool "help:\"enable tracing for debugging the compiler\""
LowerW CountFlag "help:\"debug type checking\"" LowerW CountFlag "help:\"debug type checking\""
LowerV *bool "help:\"increase debug verbosity\"" LowerV *bool "help:\"increase debug verbosity\""
// Special characters // Special characters
Percent int "flag:\"%\" help:\"debug non-static initializers\"" Percent int "flag:\"%\" help:\"debug non-static initializers\""
@ -84,6 +84,7 @@ type CmdFlags struct {
// Longer names // Longer names
AsmHdr string "help:\"write assembly header to `file`\"" AsmHdr string "help:\"write assembly header to `file`\""
ASan bool "help:\"build code compatible with C/C++ address sanitizer\""
Bench string "help:\"append benchmark times to `file`\"" Bench string "help:\"append benchmark times to `file`\""
BlockProfile string "help:\"write block profile to `file`\"" BlockProfile string "help:\"write block profile to `file`\""
BuildID string "help:\"record `id` as the build id in the export metadata\"" BuildID string "help:\"record `id` as the build id in the export metadata\""
@ -108,7 +109,7 @@ type CmdFlags struct {
Live CountFlag "help:\"debug liveness analysis\"" Live CountFlag "help:\"debug liveness analysis\""
MSan bool "help:\"build code compatible with C/C++ memory sanitizer\"" MSan bool "help:\"build code compatible with C/C++ memory sanitizer\""
MemProfile string "help:\"write memory profile to `file`\"" MemProfile string "help:\"write memory profile to `file`\""
MemProfileRate int64 "help:\"set runtime.MemProfileRate to `rate`\"" MemProfileRate int "help:\"set runtime.MemProfileRate to `rate`\""
MutexProfile string "help:\"write mutex profile to `file`\"" MutexProfile string "help:\"write mutex profile to `file`\""
NoLocalImports bool "help:\"reject local (relative) imports\"" NoLocalImports bool "help:\"reject local (relative) imports\""
Pack bool "help:\"write to file.a instead of file.o\"" Pack bool "help:\"write to file.a instead of file.o\""
@ -140,10 +141,11 @@ type CmdFlags struct {
// ParseFlags parses the command-line flags into Flag. // ParseFlags parses the command-line flags into Flag.
func ParseFlags() { func ParseFlags() {
Flag.G = 3
Flag.I = addImportDir Flag.I = addImportDir
Flag.LowerC = 1 Flag.LowerC = 1
Flag.LowerD = parseDebug Flag.LowerD = objabi.NewDebugFlag(&Debug, DebugSSA)
Flag.LowerP = &Ctxt.Pkgpath Flag.LowerP = &Ctxt.Pkgpath
Flag.LowerV = &Ctxt.Debugvlog Flag.LowerV = &Ctxt.Debugvlog
@ -176,6 +178,9 @@ func ParseFlags() {
if Flag.MSan && !sys.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) { if Flag.MSan && !sys.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH) log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH)
} }
if Flag.ASan && !sys.ASanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
log.Fatalf("%s/%s does not support -asan", buildcfg.GOOS, buildcfg.GOARCH)
}
if Flag.Race && !sys.RaceDetectorSupported(buildcfg.GOOS, buildcfg.GOARCH) { if Flag.Race && !sys.RaceDetectorSupported(buildcfg.GOOS, buildcfg.GOARCH) {
log.Fatalf("%s/%s does not support -race", buildcfg.GOOS, buildcfg.GOARCH) log.Fatalf("%s/%s does not support -race", buildcfg.GOOS, buildcfg.GOARCH)
} }
@ -187,6 +192,7 @@ func ParseFlags() {
Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared
Ctxt.Flag_optimize = Flag.N == 0 Ctxt.Flag_optimize = Flag.N == 0
Ctxt.Debugasm = int(Flag.S) Ctxt.Debugasm = int(Flag.S)
Ctxt.Flag_maymorestack = Debug.MayMoreStack
if flag.NArg() < 1 { if flag.NArg() < 1 {
usage() usage()
@ -216,12 +222,16 @@ func ParseFlags() {
} }
Flag.LowerO = p + suffix Flag.LowerO = p + suffix
} }
switch {
if Flag.Race && Flag.MSan { case Flag.Race && Flag.MSan:
log.Fatal("cannot use both -race and -msan") log.Fatal("cannot use both -race and -msan")
case Flag.Race && Flag.ASan:
log.Fatal("cannot use both -race and -asan")
case Flag.MSan && Flag.ASan:
log.Fatal("cannot use both -msan and -asan")
} }
if Flag.Race || Flag.MSan { if Flag.Race || Flag.MSan || Flag.ASan {
// -race and -msan imply -d=checkptr for now. // -race, -msan and -asan imply -d=checkptr for now.
if Debug.Checkptr == -1 { // if not set explicitly if Debug.Checkptr == -1 { // if not set explicitly
Debug.Checkptr = 1 Debug.Checkptr = 1
} }
@ -321,6 +331,12 @@ func registerFlags() {
case funcType: case funcType:
f := v.Field(i).Interface().(func(string)) f := v.Field(i).Interface().(func(string))
objabi.Flagfn1(name, help, f) objabi.Flagfn1(name, help, f)
default:
if val, ok := v.Field(i).Interface().(flag.Value); ok {
flag.Var(val, name, help)
} else {
panic(fmt.Sprintf("base.Flag.%s has unexpected type %s", f.Name, f.Type))
}
} }
} }
} }
@ -348,7 +364,7 @@ func concurrentBackendAllowed() bool {
// while writing the object file, and that is non-concurrent. // while writing the object file, and that is non-concurrent.
// Adding Debug_vlog, however, causes Debug.S to also print // Adding Debug_vlog, however, causes Debug.S to also print
// while flushing the plist, which happens concurrently. // while flushing the plist, which happens concurrently.
if Ctxt.Debugvlog || Debug.Any() || Flag.Live > 0 { if Ctxt.Debugvlog || Debug.Any || Flag.Live > 0 {
return false return false
} }
// TODO: Test and delete this condition. // TODO: Test and delete this condition.
@ -356,7 +372,7 @@ func concurrentBackendAllowed() bool {
return false return false
} }
// TODO: fix races and enable the following flags // TODO: fix races and enable the following flags
if Ctxt.Flag_shared || Ctxt.Flag_dynlink || Flag.Race { if Ctxt.Flag_dynlink || Flag.Race {
return false return false
} }
return true return true

View File

@ -128,10 +128,21 @@ func (bv BitVec) IsEmpty() bool {
return true return true
} }
func (bv BitVec) Count() int {
n := 0
for _, x := range bv.B {
n += bits.OnesCount32(x)
}
return n
}
func (bv BitVec) Not() { func (bv BitVec) Not() {
for i, x := range bv.B { for i, x := range bv.B {
bv.B[i] = ^x bv.B[i] = ^x
} }
if bv.N%wordBits != 0 {
bv.B[len(bv.B)-1] &= 1<<uint(bv.N%wordBits) - 1 // clear bits past N in the last word
}
} }
// union // union

View File

@ -38,6 +38,7 @@ func Func(fn *ir.Func) {
} }
} }
ir.VisitList(fn.Body, markHiddenClosureDead)
fn.Body = []ir.Node{ir.NewBlockStmt(base.Pos, nil)} fn.Body = []ir.Node{ir.NewBlockStmt(base.Pos, nil)}
} }
@ -62,9 +63,11 @@ func stmts(nn *ir.Nodes) {
if ir.IsConst(n.Cond, constant.Bool) { if ir.IsConst(n.Cond, constant.Bool) {
var body ir.Nodes var body ir.Nodes
if ir.BoolVal(n.Cond) { if ir.BoolVal(n.Cond) {
ir.VisitList(n.Else, markHiddenClosureDead)
n.Else = ir.Nodes{} n.Else = ir.Nodes{}
body = n.Body body = n.Body
} else { } else {
ir.VisitList(n.Body, markHiddenClosureDead)
n.Body = ir.Nodes{} n.Body = ir.Nodes{}
body = n.Else body = n.Else
} }
@ -114,6 +117,7 @@ func stmts(nn *ir.Nodes) {
} }
if cut { if cut {
ir.VisitList((*nn)[i+1:len(*nn)], markHiddenClosureDead)
*nn = (*nn)[:i+1] *nn = (*nn)[:i+1]
break break
} }
@ -150,3 +154,13 @@ func expr(n ir.Node) ir.Node {
} }
return n return n
} }
func markHiddenClosureDead(n ir.Node) {
if n.Op() != ir.OCLOSURE {
return
}
clo := n.(*ir.ClosureExpr)
if clo.Func.IsHiddenClosure() {
clo.Func.SetIsDeadcodeClosure(true)
}
}

View File

@ -214,9 +214,10 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
Type: base.Ctxt.Lookup(typename), Type: base.Ctxt.Lookup(typename),
DeclFile: declpos.RelFilename(), DeclFile: declpos.RelFilename(),
DeclLine: declpos.RelLine(), DeclLine: declpos.RelLine(),
DeclCol: declpos.Col(), DeclCol: declpos.RelCol(),
InlIndex: int32(inlIndex), InlIndex: int32(inlIndex),
ChildIndex: -1, ChildIndex: -1,
DictIndex: n.DictIndex,
}) })
// Record go type of to insure that it gets emitted by the linker. // Record go type of to insure that it gets emitted by the linker.
fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
@ -371,9 +372,10 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
Type: base.Ctxt.Lookup(typename), Type: base.Ctxt.Lookup(typename),
DeclFile: declpos.RelFilename(), DeclFile: declpos.RelFilename(),
DeclLine: declpos.RelLine(), DeclLine: declpos.RelLine(),
DeclCol: declpos.Col(), DeclCol: declpos.RelCol(),
InlIndex: int32(inlIndex), InlIndex: int32(inlIndex),
ChildIndex: -1, ChildIndex: -1,
DictIndex: n.DictIndex,
} }
} }
@ -475,9 +477,10 @@ func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var
StackOffset: ssagen.StackOffset(debug.Slots[debug.VarSlots[varID][0]]), StackOffset: ssagen.StackOffset(debug.Slots[debug.VarSlots[varID][0]]),
DeclFile: declpos.RelFilename(), DeclFile: declpos.RelFilename(),
DeclLine: declpos.RelLine(), DeclLine: declpos.RelLine(),
DeclCol: declpos.Col(), DeclCol: declpos.RelCol(),
InlIndex: int32(inlIndex), InlIndex: int32(inlIndex),
ChildIndex: -1, ChildIndex: -1,
DictIndex: n.DictIndex,
} }
list := debug.LocationLists[varID] list := debug.LocationLists[varID]
if len(list) != 0 { if len(list) != 0 {

Some files were not shown because too many files have changed in this diff Show More