mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +00:00
[dev.boringcrypto] all: merge master into dev.boringcrypto
Change-Id: I1aa33cabd0c55fe64994b08f8a3f7b6bbfb3282c
This commit is contained in:
commit
ed07c49cb6
2
.github/SUPPORT.md
vendored
2
.github/SUPPORT.md
vendored
@ -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
|
||||||
|
3
AUTHORS
3
AUTHORS
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
47
api/next.txt
47
api/next.txt
@ -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
|
@ -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&1<<2</code> is 4, not 0—it parses as <code>(3&1)<<2</code>
|
Thus <code>3&1<<2</code> is 4, not 0—it parses as <code>(3&1)<<2</code>
|
||||||
not <code>3&(1<<2)</code>.
|
not <code>3&(1<<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>
|
||||||
|
1240
doc/go1.17.html
1240
doc/go1.17.html
File diff suppressed because it is too large
Load Diff
242
doc/go1.18.html
Normal file
242
doc/go1.18.html
Normal 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 -->
|
@ -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<<s // 1 has type int
|
|||||||
var j int32 = 1<<s // 1 has type int32; j == 0
|
var j int32 = 1<<s // 1 has type int32; j == 0
|
||||||
var k = uint64(1<<s) // 1 has type uint64; k == 1<<33
|
var k = uint64(1<<s) // 1 has type uint64; k == 1<<33
|
||||||
var m int = 1.0<<s // 1.0 has type int; m == 1<<33
|
var m int = 1.0<<s // 1.0 has type int; m == 1<<33
|
||||||
var n = 1.0<<s == j // 1.0 has type int; n == true
|
var n = 1.0<<s == j // 1.0 has type int32; n == true
|
||||||
var o = 1<<s == 2<<s // 1 and 2 have type int; o == false
|
var o = 1<<s == 2<<s // 1 and 2 have type int; o == false
|
||||||
var p = 1<<s == 1<<33 // 1 has type int; p == true
|
var p = 1<<s == 1<<33 // 1 has type int; p == true
|
||||||
var u = 1.0<<s // illegal: 1.0 has type float64, cannot shift
|
var u = 1.0<<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>
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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) }
|
||||||
|
14
misc/cgo/test/issue42018.go
Normal file
14
misc/cgo/test/issue42018.go
Normal 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")
|
||||||
|
}
|
46
misc/cgo/test/issue42018_windows.go
Normal file
46
misc/cgo/test/issue42018_windows.go
Normal 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
9
misc/cgo/test/testdata/issue43639.go
vendored
Normal 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"
|
8
misc/cgo/test/testdata/issue43639/a.go
vendored
Normal file
8
misc/cgo/test/testdata/issue43639/a.go
vendored
Normal 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"
|
17
misc/cgo/test/typeparam.go
Normal file
17
misc/cgo/test/typeparam.go
Normal 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)
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
36
misc/cgo/testcarchive/testdata/libgo8/a.go
vendored
Normal file
36
misc/cgo/testcarchive/testdata/libgo8/a.go
vendored
Normal 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
16
misc/cgo/testcarchive/testdata/main8.c
vendored
Normal 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();
|
||||||
|
}
|
@ -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":
|
||||||
|
2
misc/cgo/testcshared/testdata/libgo2/dup2.go
vendored
2
misc/cgo/testcshared/testdata/libgo2/dup2.go
vendored
@ -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
|
||||||
|
|
||||||
|
2
misc/cgo/testcshared/testdata/libgo2/dup3.go
vendored
2
misc/cgo/testcshared/testdata/libgo2/dup3.go
vendored
@ -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
|
||||||
|
|
||||||
|
18
misc/cgo/testgodefs/testdata/issue48396.go
vendored
Normal file
18
misc/cgo/testgodefs/testdata/issue48396.go
vendored
Normal 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
|
3
misc/cgo/testgodefs/testdata/main.go
vendored
3
misc/cgo/testgodefs/testdata/main.go
vendored
@ -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
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ var filePrefixes = []string{
|
|||||||
"issue37621",
|
"issue37621",
|
||||||
"issue38649",
|
"issue38649",
|
||||||
"issue39534",
|
"issue39534",
|
||||||
|
"issue48396",
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGoDefs(t *testing.T) {
|
func TestGoDefs(t *testing.T) {
|
||||||
|
66
misc/cgo/testsanitizers/asan_test.go
Normal file
66
misc/cgo/testsanitizers/asan_test.go
Normal 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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
28
misc/cgo/testsanitizers/testdata/asan1_fail.go
vendored
Normal file
28
misc/cgo/testsanitizers/testdata/asan1_fail.go
vendored
Normal 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)
|
||||||
|
}
|
34
misc/cgo/testsanitizers/testdata/asan2_fail.go
vendored
Normal file
34
misc/cgo/testsanitizers/testdata/asan2_fail.go
vendored
Normal 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)
|
||||||
|
}
|
23
misc/cgo/testsanitizers/testdata/asan3_fail.go
vendored
Normal file
23
misc/cgo/testsanitizers/testdata/asan3_fail.go
vendored
Normal 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])
|
||||||
|
}
|
22
misc/cgo/testsanitizers/testdata/asan4_fail.go
vendored
Normal file
22
misc/cgo/testsanitizers/testdata/asan4_fail.go
vendored
Normal 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.
|
||||||
|
}
|
26
misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go
vendored
Normal file
26
misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go
vendored
Normal 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.
|
||||||
|
}
|
@ -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")
|
||||||
|
}
|
||||||
|
19
misc/cgo/testshared/testdata/issue47837/a/a.go
vendored
Normal file
19
misc/cgo/testshared/testdata/issue47837/a/a.go
vendored
Normal 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() {}
|
14
misc/cgo/testshared/testdata/issue47837/main/main.go
vendored
Normal file
14
misc/cgo/testshared/testdata/issue47837/main/main.go
vendored
Normal 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)
|
||||||
|
}
|
@ -8,4 +8,4 @@
|
|||||||
// directory.)
|
// directory.)
|
||||||
module misc
|
module misc
|
||||||
|
|
||||||
go 1.12
|
go 1.18
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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" "$@"
|
||||||
|
@ -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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
})();
|
||||||
|
49
misc/wasm/wasm_exec_node.js
Normal file
49
misc/wasm/wasm_exec_node.js
Normal 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);
|
||||||
|
});
|
@ -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
|
||||||
|
@ -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] }
|
||||||
|
@ -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{}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
|
@ -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]
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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:])
|
||||||
|
@ -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) {
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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} {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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{}) {
|
||||||
|
@ -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
|
||||||
|
5
src/cmd/api/testdata/src/pkg/p4/golden.txt
vendored
Normal file
5
src/cmd/api/testdata/src/pkg/p4/golden.txt
vendored
Normal 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
26
src/cmd/api/testdata/src/pkg/p4/p4.go
vendored
Normal 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...)
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
48
src/cmd/asm/internal/asm/testdata/amd64enc.s
vendored
48
src/cmd/asm/internal/asm/testdata/amd64enc.s
vendored
@ -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
|
||||||
|
14
src/cmd/asm/internal/asm/testdata/arm64.s
vendored
14
src/cmd/asm/internal/asm/testdata/arm64.s
vendored
@ -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
|
||||||
|
21
src/cmd/asm/internal/asm/testdata/arm64error.s
vendored
21
src/cmd/asm/internal/asm/testdata/arm64error.s
vendored
@ -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
|
||||||
|
44
src/cmd/asm/internal/asm/testdata/ppc64.s
vendored
44
src/cmd/asm/internal/asm/testdata/ppc64.s
vendored
@ -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
|
||||||
|
104
src/cmd/asm/internal/asm/testdata/riscv64.s
vendored
104
src/cmd/asm/internal/asm/testdata/riscv64.s
vendored
@ -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
|
||||||
|
12
src/cmd/asm/internal/asm/testdata/riscv64error.s
vendored
12
src/cmd/asm/internal/asm/testdata/riscv64error.s
vendored
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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
17
src/cmd/cgo/ast_go1.go
Normal 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
25
src/cmd/cgo/ast_go118.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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 Go’s
|
||||||
|
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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
383
src/cmd/compile/internal/amd64/versions_test.go
Normal file
383
src/cmd/compile/internal/amd64/versions_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -67,6 +67,7 @@ var NoInstrumentPkgs = []string{
|
|||||||
"runtime",
|
"runtime",
|
||||||
"runtime/race",
|
"runtime/race",
|
||||||
"runtime/msan",
|
"runtime/msan",
|
||||||
|
"runtime/asan",
|
||||||
"internal/cpu",
|
"internal/cpu",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"
|
|
||||||
`
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user