[dev.boringcrypto.go1.17] all: merge go1.17rc2 into dev.boringcrypto.go1.17

Change-Id: I24c7d03fb140d1df8f33b950ee54f4ee51f345d6
This commit is contained in:
Filippo Valsorda 2021-08-10 11:44:14 -04:00
commit ef7be8869c
637 changed files with 19260 additions and 16014 deletions

View File

@ -41,7 +41,7 @@ Aeneas Rekkas (arekkas) <aeneas@ory.am>
Afanasev Stanislav <phpprogger@gmail.com>
Agis Anastasopoulos <agis.anast@gmail.com>
Agniva De Sarker <agnivade@yahoo.co.in>
Ahmed Wahed <oneofone@gmail.com>
Ahmed W. Mones <oneofone@gmail.com>
Ahmet Soormally <ahmet@mangomm.co.uk>
Ahmy Yulrizka <yulrizka@gmail.com>
Aiden Scandella <ai@uber.com>

View File

@ -67,7 +67,7 @@ Aeneas Rekkas (arekkas) <aeneas@ory.am>
Afanasev Stanislav <phpprogger@gmail.com>
Agis Anastasopoulos <agis.anast@gmail.com>
Agniva De Sarker <agnivade@yahoo.co.in>
Ahmed Wahed <oneofone@gmail.com>
Ahmed W. Mones <oneofone@gmail.com>
Ahmet Alp Balkan <ahmetb@google.com>
Ahmet Soormally <ahmet@mangomm.co.uk>
Ahmy Yulrizka <yulrizka@gmail.com>

195
api/go1.17.txt Normal file
View File

@ -0,0 +1,195 @@
pkg archive/zip, method (*File) OpenRaw() (io.Reader, error)
pkg archive/zip, method (*Writer) Copy(*File) error
pkg archive/zip, method (*Writer) CreateRaw(*FileHeader) (io.Writer, error)
pkg compress/lzw, method (*Reader) Close() error
pkg compress/lzw, method (*Reader) Read([]uint8) (int, error)
pkg compress/lzw, method (*Reader) Reset(io.Reader, Order, int)
pkg compress/lzw, method (*Writer) Close() error
pkg compress/lzw, method (*Writer) Reset(io.Writer, Order, int)
pkg compress/lzw, method (*Writer) Write([]uint8) (int, error)
pkg compress/lzw, type Reader struct
pkg compress/lzw, type Writer struct
pkg crypto/tls, method (*CertificateRequestInfo) Context() context.Context
pkg crypto/tls, method (*ClientHelloInfo) Context() context.Context
pkg crypto/tls, method (*Conn) HandshakeContext(context.Context) error
pkg database/sql, method (*NullByte) Scan(interface{}) error
pkg database/sql, method (*NullInt16) Scan(interface{}) error
pkg database/sql, method (NullByte) Value() (driver.Value, error)
pkg database/sql, method (NullInt16) Value() (driver.Value, error)
pkg database/sql, type NullByte struct
pkg database/sql, type NullByte struct, Byte uint8
pkg database/sql, type NullByte struct, Valid bool
pkg database/sql, type NullInt16 struct
pkg database/sql, type NullInt16 struct, Int16 int16
pkg database/sql, type NullInt16 struct, Valid bool
pkg debug/elf, const SHT_MIPS_ABIFLAGS = 1879048234
pkg debug/elf, const SHT_MIPS_ABIFLAGS SectionType
pkg encoding/csv, method (*Reader) FieldPos(int) (int, int)
pkg go/build, type Context struct, ToolTags []string
pkg go/parser, const SkipObjectResolution = 64
pkg go/parser, const SkipObjectResolution Mode
pkg image, method (*Alpha) RGBA64At(int, int) color.RGBA64
pkg image, method (*Alpha) SetRGBA64(int, int, color.RGBA64)
pkg image, method (*Alpha16) RGBA64At(int, int) color.RGBA64
pkg image, method (*Alpha16) SetRGBA64(int, int, color.RGBA64)
pkg image, method (*CMYK) RGBA64At(int, int) color.RGBA64
pkg image, method (*CMYK) SetRGBA64(int, int, color.RGBA64)
pkg image, method (*Gray) RGBA64At(int, int) color.RGBA64
pkg image, method (*Gray) SetRGBA64(int, int, color.RGBA64)
pkg image, method (*Gray16) RGBA64At(int, int) color.RGBA64
pkg image, method (*Gray16) SetRGBA64(int, int, color.RGBA64)
pkg image, method (*NRGBA) RGBA64At(int, int) color.RGBA64
pkg image, method (*NRGBA) SetRGBA64(int, int, color.RGBA64)
pkg image, method (*NRGBA64) RGBA64At(int, int) color.RGBA64
pkg image, method (*NRGBA64) SetRGBA64(int, int, color.RGBA64)
pkg image, method (*NYCbCrA) RGBA64At(int, int) color.RGBA64
pkg image, method (*Paletted) RGBA64At(int, int) color.RGBA64
pkg image, method (*Paletted) SetRGBA64(int, int, color.RGBA64)
pkg image, method (*RGBA) RGBA64At(int, int) color.RGBA64
pkg image, method (*RGBA) SetRGBA64(int, int, color.RGBA64)
pkg image, method (*Uniform) RGBA64At(int, int) color.RGBA64
pkg image, method (*YCbCr) RGBA64At(int, int) color.RGBA64
pkg image, method (Rectangle) RGBA64At(int, int) color.RGBA64
pkg image, type RGBA64Image interface { At, Bounds, ColorModel, RGBA64At }
pkg image, type RGBA64Image interface, At(int, int) color.Color
pkg image, type RGBA64Image interface, Bounds() Rectangle
pkg image, type RGBA64Image interface, ColorModel() color.Model
pkg image, type RGBA64Image interface, RGBA64At(int, int) color.RGBA64
pkg image/draw, type RGBA64Image interface { At, Bounds, ColorModel, RGBA64At, Set, SetRGBA64 }
pkg image/draw, type RGBA64Image interface, At(int, int) color.Color
pkg image/draw, type RGBA64Image interface, Bounds() image.Rectangle
pkg image/draw, type RGBA64Image interface, ColorModel() color.Model
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, SetRGBA64(int, int, color.RGBA64)
pkg io/fs, func FileInfoToDirEntry(FileInfo) DirEntry
pkg math, const MaxFloat64 = 1.79769e+308 // 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368
pkg math, const MaxInt = 9223372036854775807
pkg math, const MaxInt ideal-int
pkg math, const MaxUint = 18446744073709551615
pkg math, const MaxUint ideal-int
pkg math, const MinInt = -9223372036854775808
pkg math, const MinInt ideal-int
pkg math, const SmallestNonzeroFloat32 = 1.4013e-45 // 1/713623846352979940529142984724747568191373312
pkg math, const SmallestNonzeroFloat64 = 4.94066e-324 // 1/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784
pkg net, method (*ParseError) Temporary() bool
pkg net, method (*ParseError) Timeout() bool
pkg net, method (IP) IsPrivate() bool
pkg net/http, func AllowQuerySemicolons(Handler) Handler
pkg net/url, method (Values) Has(string) bool
pkg reflect, func VisibleFields(Type) []StructField
pkg reflect, method (Method) IsExported() bool
pkg reflect, method (StructField) IsExported() bool
pkg reflect, method (Value) CanConvert(Type) bool
pkg runtime/cgo (darwin-amd64-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (darwin-amd64-cgo), method (Handle) Delete()
pkg runtime/cgo (darwin-amd64-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (darwin-amd64-cgo), type Handle uintptr
pkg runtime/cgo (freebsd-386-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (freebsd-386-cgo), method (Handle) Delete()
pkg runtime/cgo (freebsd-386-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (freebsd-386-cgo), type Handle uintptr
pkg runtime/cgo (freebsd-amd64-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (freebsd-amd64-cgo), method (Handle) Delete()
pkg runtime/cgo (freebsd-amd64-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (freebsd-amd64-cgo), type Handle uintptr
pkg runtime/cgo (freebsd-arm-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (freebsd-arm-cgo), method (Handle) Delete()
pkg runtime/cgo (freebsd-arm-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (freebsd-arm-cgo), type Handle uintptr
pkg runtime/cgo (linux-386-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (linux-386-cgo), method (Handle) Delete()
pkg runtime/cgo (linux-386-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (linux-386-cgo), type Handle uintptr
pkg runtime/cgo (linux-amd64-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (linux-amd64-cgo), method (Handle) Delete()
pkg runtime/cgo (linux-amd64-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (linux-amd64-cgo), type Handle uintptr
pkg runtime/cgo (linux-arm-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (linux-arm-cgo), method (Handle) Delete()
pkg runtime/cgo (linux-arm-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (linux-arm-cgo), type Handle uintptr
pkg runtime/cgo (netbsd-386-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (netbsd-386-cgo), method (Handle) Delete()
pkg runtime/cgo (netbsd-386-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (netbsd-386-cgo), type Handle uintptr
pkg runtime/cgo (netbsd-amd64-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (netbsd-amd64-cgo), method (Handle) Delete()
pkg runtime/cgo (netbsd-amd64-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (netbsd-amd64-cgo), type Handle uintptr
pkg runtime/cgo (netbsd-arm-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (netbsd-arm-cgo), method (Handle) Delete()
pkg runtime/cgo (netbsd-arm-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (netbsd-arm-cgo), type Handle uintptr
pkg runtime/cgo (netbsd-arm64-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (netbsd-arm64-cgo), method (Handle) Delete()
pkg runtime/cgo (netbsd-arm64-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (netbsd-arm64-cgo), type Handle uintptr
pkg runtime/cgo (openbsd-386-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (openbsd-386-cgo), method (Handle) Delete()
pkg runtime/cgo (openbsd-386-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (openbsd-386-cgo), type Handle uintptr
pkg runtime/cgo (openbsd-amd64-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (openbsd-amd64-cgo), method (Handle) Delete()
pkg runtime/cgo (openbsd-amd64-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (openbsd-amd64-cgo), type Handle uintptr
pkg strconv, func QuotedPrefix(string) (string, error)
pkg sync/atomic, method (*Value) CompareAndSwap(interface{}, interface{}) bool
pkg sync/atomic, method (*Value) Swap(interface{}) interface{}
pkg syscall (netbsd-386), const SYS_WAIT6 = 481
pkg syscall (netbsd-386), const SYS_WAIT6 ideal-int
pkg syscall (netbsd-386), const WEXITED = 32
pkg syscall (netbsd-386), const WEXITED ideal-int
pkg syscall (netbsd-386-cgo), const SYS_WAIT6 = 481
pkg syscall (netbsd-386-cgo), const SYS_WAIT6 ideal-int
pkg syscall (netbsd-386-cgo), const WEXITED = 32
pkg syscall (netbsd-386-cgo), const WEXITED ideal-int
pkg syscall (netbsd-amd64), const SYS_WAIT6 = 481
pkg syscall (netbsd-amd64), const SYS_WAIT6 ideal-int
pkg syscall (netbsd-amd64), const WEXITED = 32
pkg syscall (netbsd-amd64), const WEXITED ideal-int
pkg syscall (netbsd-amd64-cgo), const SYS_WAIT6 = 481
pkg syscall (netbsd-amd64-cgo), const SYS_WAIT6 ideal-int
pkg syscall (netbsd-amd64-cgo), const WEXITED = 32
pkg syscall (netbsd-amd64-cgo), const WEXITED ideal-int
pkg syscall (netbsd-arm), const SYS_WAIT6 = 481
pkg syscall (netbsd-arm), const SYS_WAIT6 ideal-int
pkg syscall (netbsd-arm), const WEXITED = 32
pkg syscall (netbsd-arm), const WEXITED ideal-int
pkg syscall (netbsd-arm-cgo), const SYS_WAIT6 = 481
pkg syscall (netbsd-arm-cgo), const SYS_WAIT6 ideal-int
pkg syscall (netbsd-arm-cgo), const WEXITED = 32
pkg syscall (netbsd-arm-cgo), const WEXITED ideal-int
pkg syscall (netbsd-arm64), const SYS_WAIT6 = 481
pkg syscall (netbsd-arm64), const SYS_WAIT6 ideal-int
pkg syscall (netbsd-arm64), const WEXITED = 32
pkg syscall (netbsd-arm64), const WEXITED ideal-int
pkg syscall (netbsd-arm64-cgo), const SYS_WAIT6 = 481
pkg syscall (netbsd-arm64-cgo), const SYS_WAIT6 ideal-int
pkg syscall (netbsd-arm64-cgo), const WEXITED = 32
pkg syscall (netbsd-arm64-cgo), const WEXITED ideal-int
pkg syscall (openbsd-386), const MSG_CMSG_CLOEXEC = 2048
pkg syscall (openbsd-386), const MSG_CMSG_CLOEXEC ideal-int
pkg syscall (openbsd-386-cgo), const MSG_CMSG_CLOEXEC = 2048
pkg syscall (openbsd-386-cgo), const MSG_CMSG_CLOEXEC ideal-int
pkg syscall (openbsd-amd64), const MSG_CMSG_CLOEXEC = 2048
pkg syscall (openbsd-amd64), const MSG_CMSG_CLOEXEC ideal-int
pkg syscall (openbsd-amd64-cgo), const MSG_CMSG_CLOEXEC = 2048
pkg syscall (openbsd-amd64-cgo), const MSG_CMSG_CLOEXEC ideal-int
pkg syscall (windows-386), type SysProcAttr struct, AdditionalInheritedHandles []Handle
pkg syscall (windows-386), type SysProcAttr struct, ParentProcess Handle
pkg syscall (windows-amd64), type SysProcAttr struct, AdditionalInheritedHandles []Handle
pkg syscall (windows-amd64), type SysProcAttr struct, ParentProcess Handle
pkg testing, method (*B) Setenv(string, string)
pkg testing, method (*T) Setenv(string, string)
pkg testing, type TB interface, Setenv(string, string)
pkg text/template/parse, const SkipFuncCheck = 2
pkg text/template/parse, const SkipFuncCheck Mode
pkg time, const Layout = "01/02 03:04:05PM '06 -0700"
pkg time, const Layout ideal-string
pkg time, func UnixMicro(int64) Time
pkg time, func UnixMilli(int64) Time
pkg time, method (Time) GoString() string
pkg time, method (Time) IsDST() bool
pkg time, method (Time) UnixMicro() int64
pkg time, method (Time) UnixMilli() int64

View File

@ -1,99 +0,0 @@
pkg compress/lzw, method (*Reader) Close() error
pkg compress/lzw, method (*Reader) Read([]uint8) (int, error)
pkg compress/lzw, method (*Reader) Reset(io.Reader, Order, int)
pkg compress/lzw, method (*Writer) Close() error
pkg compress/lzw, method (*Writer) Reset(io.Writer, Order, int)
pkg compress/lzw, method (*Writer) Write([]uint8) (int, error)
pkg compress/lzw, type Reader struct
pkg compress/lzw, type Writer struct
pkg crypto/tls, method (*CertificateRequestInfo) Context() context.Context
pkg crypto/tls, method (*ClientHelloInfo) Context() context.Context
pkg crypto/tls, method (*Conn) HandshakeContext(context.Context) error
pkg debug/elf, const SHT_MIPS_ABIFLAGS = 1879048234
pkg debug/elf, const SHT_MIPS_ABIFLAGS SectionType
pkg encoding/csv, method (*Reader) FieldPos(int) (int, int)
pkg go/ast, method (*FuncDecl) IsMethod() bool
pkg go/build, type Context struct, ToolTags []string
pkg go/parser, const SkipObjectResolution = 64
pkg go/parser, const SkipObjectResolution Mode
pkg go/types, type Config struct, GoVersion string
pkg io/fs, func FileInfoToDirEntry(FileInfo) DirEntry
pkg net, method (*ParseError) Temporary() bool
pkg net, method (*ParseError) Timeout() bool
pkg net, method (IP) IsPrivate() bool
pkg reflect, func VisibleFields(Type) []StructField
pkg reflect, method (Method) IsExported() bool
pkg reflect, method (StructField) IsExported() bool
pkg runtime/cgo (darwin-amd64-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (darwin-amd64-cgo), method (Handle) Delete()
pkg runtime/cgo (darwin-amd64-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (darwin-amd64-cgo), type Handle uintptr
pkg runtime/cgo (freebsd-386-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (freebsd-386-cgo), method (Handle) Delete()
pkg runtime/cgo (freebsd-386-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (freebsd-386-cgo), type Handle uintptr
pkg runtime/cgo (freebsd-amd64-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (freebsd-amd64-cgo), method (Handle) Delete()
pkg runtime/cgo (freebsd-amd64-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (freebsd-amd64-cgo), type Handle uintptr
pkg runtime/cgo (freebsd-arm-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (freebsd-arm-cgo), method (Handle) Delete()
pkg runtime/cgo (freebsd-arm-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (freebsd-arm-cgo), type Handle uintptr
pkg runtime/cgo (linux-386-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (linux-386-cgo), method (Handle) Delete()
pkg runtime/cgo (linux-386-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (linux-386-cgo), type Handle uintptr
pkg runtime/cgo (linux-amd64-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (linux-amd64-cgo), method (Handle) Delete()
pkg runtime/cgo (linux-amd64-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (linux-amd64-cgo), type Handle uintptr
pkg runtime/cgo (linux-arm-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (linux-arm-cgo), method (Handle) Delete()
pkg runtime/cgo (linux-arm-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (linux-arm-cgo), type Handle uintptr
pkg runtime/cgo (netbsd-386-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (netbsd-386-cgo), method (Handle) Delete()
pkg runtime/cgo (netbsd-386-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (netbsd-386-cgo), type Handle uintptr
pkg runtime/cgo (netbsd-amd64-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (netbsd-amd64-cgo), method (Handle) Delete()
pkg runtime/cgo (netbsd-amd64-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (netbsd-amd64-cgo), type Handle uintptr
pkg runtime/cgo (netbsd-arm-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (netbsd-arm-cgo), method (Handle) Delete()
pkg runtime/cgo (netbsd-arm-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (netbsd-arm-cgo), type Handle uintptr
pkg runtime/cgo (netbsd-arm64-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (netbsd-arm64-cgo), method (Handle) Delete()
pkg runtime/cgo (netbsd-arm64-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (netbsd-arm64-cgo), type Handle uintptr
pkg runtime/cgo (openbsd-386-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (openbsd-386-cgo), method (Handle) Delete()
pkg runtime/cgo (openbsd-386-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (openbsd-386-cgo), type Handle uintptr
pkg runtime/cgo (openbsd-amd64-cgo), func NewHandle(interface{}) Handle
pkg runtime/cgo (openbsd-amd64-cgo), method (Handle) Delete()
pkg runtime/cgo (openbsd-amd64-cgo), method (Handle) Value() interface{}
pkg runtime/cgo (openbsd-amd64-cgo), type Handle uintptr
pkg syscall (openbsd-386), const MSG_CMSG_CLOEXEC = 2048
pkg syscall (openbsd-386), const MSG_CMSG_CLOEXEC ideal-int
pkg syscall (openbsd-386-cgo), const MSG_CMSG_CLOEXEC = 2048
pkg syscall (openbsd-386-cgo), const MSG_CMSG_CLOEXEC ideal-int
pkg syscall (openbsd-amd64), const MSG_CMSG_CLOEXEC = 2048
pkg syscall (openbsd-amd64), const MSG_CMSG_CLOEXEC ideal-int
pkg syscall (openbsd-amd64-cgo), const MSG_CMSG_CLOEXEC = 2048
pkg syscall (openbsd-amd64-cgo), const MSG_CMSG_CLOEXEC ideal-int
pkg syscall (windows-386), type SysProcAttr struct, AdditionalInheritedHandles []Handle
pkg syscall (windows-386), type SysProcAttr struct, ParentProcess Handle
pkg syscall (windows-amd64), type SysProcAttr struct, AdditionalInheritedHandles []Handle
pkg syscall (windows-amd64), type SysProcAttr struct, ParentProcess Handle
pkg testing, method (*B) Setenv(string, string)
pkg testing, method (*T) Setenv(string, string)
pkg text/template/parse, const SkipFuncCheck = 2
pkg text/template/parse, const SkipFuncCheck Mode
pkg time, func UnixMicro(int64) Time
pkg time, func UnixMilli(int64) Time
pkg time, method (*Time) IsDST() bool
pkg time, method (Time) UnixMicro() int64
pkg time, method (Time) UnixMilli() int64

View File

@ -1,2 +1 @@
branch: dev.boringcrypto
parent-branch: master
branch: dev.boringcrypto.go1.17

View File

@ -827,10 +827,6 @@ The other codes are <code>-&gt;</code> (arithmetic right shift),
<h3 id="arm64">ARM64</h3>
<p>
The ARM64 port is in an experimental state.
</p>
<p>
<code>R18</code> is the "platform register", reserved on the Apple platform.
To prevent accidental misuse, the register is named <code>R18_PLATFORM</code>.

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
<!--{
"Title": "The Go Programming Language Specification",
"Subtitle": "Version of Apr 28, 2021",
"Subtitle": "Version of Jul 26, 2021",
"Path": "/ref/spec"
}-->
@ -490,8 +490,8 @@ After a backslash, certain single-character escapes represent special values:
\n U+000A line feed or newline
\r U+000D carriage return
\t U+0009 horizontal tab
\v U+000b vertical tab
\\ U+005c backslash
\v U+000B vertical tab
\\ U+005C backslash
\' U+0027 single quote (valid escape only within rune literals)
\" U+0022 double quote (valid escape only within string literals)
</pre>
@ -4334,7 +4334,10 @@ s4 := (*[4]byte)(s) // panics: len([4]byte) > len(s)
var t []string
t0 := (*[0]string)(t) // t0 == nil
t1 := (*[1]string)(t) // panics: len([1]string) > len(s)
t1 := (*[1]string)(t) // panics: len([1]string) > len(t)
u := make([]byte, 0)
u0 = (*[0]byte)(u) // u0 != nil
</pre>
<h3 id="Constant_expressions">Constant expressions</h3>
@ -4670,7 +4673,7 @@ The following built-in functions are not permitted in statement context:
<pre>
append cap complex imag len make new real
unsafe.Alignof unsafe.Offsetof unsafe.Sizeof
unsafe.Add unsafe.Alignof unsafe.Offsetof unsafe.Sizeof unsafe.Slice
</pre>
<pre>
@ -4909,7 +4912,7 @@ if x := f(); x &lt; y {
<p>
"Switch" statements provide multi-way execution.
An expression or type specifier is compared to the "cases"
An expression or type is compared to the "cases"
inside the "switch" to determine which branch
to execute.
</p>
@ -5020,7 +5023,7 @@ floating point, or string constants in case expressions.
A type switch compares types rather than values. It is otherwise similar
to an expression switch. It is marked by a special switch expression that
has the form of a <a href="#Type_assertions">type assertion</a>
using the reserved word <code>type</code> rather than an actual type:
using the keyword <code>type</code> rather than an actual type:
</p>
<pre>
@ -6782,18 +6785,26 @@ The rules for <a href="/pkg/unsafe#Pointer">valid uses</a> of <code>Pointer</cod
<p>
The function <code>Slice</code> returns a slice whose underlying array starts at <code>ptr</code>
and whose length and capacity are <code>len</code>:
and whose length and capacity are <code>len</code>.
<code>Slice(ptr, len)</code> is equivalent to
</p>
<pre>
(*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]
</pre>
<p>
except that, as a special case, if <code>ptr</code>
is <code>nil</code> and <code>len</code> is zero,
<code>Slice</code> returns <code>nil</code>.
</p>
<p>
The <code>len</code> argument must be of integer type or an untyped <a href="#Constants">constant</a>.
A constant <code>len</code> argument must be non-negative and <a href="#Representability">representable</a> by a value of type <code>int</code>;
if it is an untyped constant it is given type <code>int</code>.
If <code>ptr</code> is <code>nil</code> or <code>len</code> is negative at run time,
At run time, if <code>len</code> is negative,
or if <code>ptr</code> is <code>nil</code> and <code>len</code> is not zero,
a <a href="#Run_time_panics">run-time panic</a> occurs.
</p>

View File

@ -40,7 +40,8 @@ func check(t *testing.T, file string) {
if len(frags) == 1 {
continue
}
re, err := regexp.Compile(string(frags[1]))
frag := fmt.Sprintf(":%d:.*%s", i+1, frags[1])
re, err := regexp.Compile(frag)
if err != nil {
t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frags[1])
continue

View File

@ -40,15 +40,15 @@ func main() {
C.foop = x // ERROR HERE
// issue 13129: used to output error about C.unsignedshort with CC=clang
var x C.ushort
x = int(0) // ERROR HERE: C\.ushort
var x1 C.ushort
x1 = int(0) // ERROR HERE: C\.ushort
// issue 13423
_ = C.fopen() // ERROR HERE
// issue 13467
var x rune = '✈'
var _ rune = C.transform(x) // ERROR HERE: C\.int
var x2 rune = '✈'
var _ rune = C.transform(x2) // ERROR HERE: C\.int
// issue 13635: used to output error about C.unsignedchar.
// This test tests all such types.
@ -91,10 +91,10 @@ func main() {
// issue 26745
_ = func(i int) int {
return C.i + 1 // ERROR HERE: :13
return C.i + 1 // ERROR HERE: 14
}
_ = func(i int) {
C.fi(i) // ERROR HERE: :6
C.fi(i) // ERROR HERE: 7
}
C.fi = C.fi // ERROR HERE

View File

@ -9,6 +9,7 @@ package cgotest
import (
"fmt"
"os"
"sort"
"strings"
"syscall"
"testing"
@ -105,11 +106,23 @@ func compareStatus(filter, expect string) error {
// "Pid:\t".
}
if strings.HasPrefix(line, filter) {
if line != expected {
return fmt.Errorf("%q got:%q want:%q (bad) [pid=%d file:'%s' %v]\n", tf, line, expected, pid, string(d), expectedProc)
if line == expected {
foundAThread = true
break
}
foundAThread = true
break
if filter == "Groups:" && strings.HasPrefix(line, "Groups:\t") {
// https://github.com/golang/go/issues/46145
// Containers don't reliably output this line in sorted order so manually sort and compare that.
a := strings.Split(line[8:], " ")
sort.Strings(a)
got := strings.Join(a, " ")
if got == expected[8:] {
foundAThread = true
break
}
}
return fmt.Errorf("%q got:%q want:%q (bad) [pid=%d file:'%s' %v]\n", tf, line, expected, pid, string(d), expectedProc)
}
}
}

View File

@ -292,11 +292,60 @@ func createHeaders() error {
"-installsuffix", "testcshared",
"-o", libgoname,
filepath.Join(".", "libgo", "libgo.go")}
if GOOS == "windows" && strings.HasSuffix(args[6], ".a") {
args[6] = strings.TrimSuffix(args[6], ".a") + ".dll"
}
cmd = exec.Command(args[0], args[1:]...)
out, err = cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
}
if GOOS == "windows" {
// We can't simply pass -Wl,--out-implib, because this relies on having imports from multiple packages,
// which results in the linkers output implib getting overwritten at each step. So instead build the
// import library the traditional way, using a def file.
err = os.WriteFile("libgo.def",
[]byte("LIBRARY libgo.dll\nEXPORTS\n\tDidInitRun\n\tDidMainRun\n\tDivu\n\tFromPkg\n\t_cgo_dummy_export\n"),
0644)
if err != nil {
return fmt.Errorf("unable to write def file: %v", err)
}
out, err = exec.Command(cc[0], append(cc[1:], "-print-prog-name=dlltool")...).CombinedOutput()
if err != nil {
return fmt.Errorf("unable to find dlltool path: %v\n%s\n", err, out)
}
args := []string{strings.TrimSpace(string(out)), "-D", args[6], "-l", libgoname, "-d", "libgo.def"}
// This is an unfortunate workaround for https://github.com/mstorsjo/llvm-mingw/issues/205 in which
// we basically reimplement the contents of the dlltool.sh wrapper: https://git.io/JZFlU
dlltoolContents, err := os.ReadFile(args[0])
if err != nil {
return fmt.Errorf("unable to read dlltool: %v\n", err)
}
if bytes.HasPrefix(dlltoolContents, []byte("#!/bin/sh")) && bytes.Contains(dlltoolContents, []byte("llvm-dlltool")) {
base, name := filepath.Split(args[0])
args[0] = filepath.Join(base, "llvm-dlltool")
var machine string
switch strings.SplitN(name, "-", 2)[0] {
case "i686":
machine = "i386"
case "x86_64":
machine = "i386:x86-64"
case "armv7":
machine = "arm"
case "aarch64":
machine = "arm64"
}
if len(machine) > 0 {
args = append(args, "-m", machine)
}
}
out, err = exec.Command(args[0], args[1:]...).CombinedOutput()
if err != nil {
return fmt.Errorf("unable to run dlltool to create import library: %v\n%s\n", err, out)
}
}
if runtime.GOOS != GOOS && GOOS == "android" {
args = append(adbCmd(), "push", libgoname, fmt.Sprintf("%s/%s", androiddir, libgoname))
@ -400,7 +449,7 @@ func main() {
defer f.Close()
section := f.Section(".edata")
if section == nil {
t.Fatalf(".edata section is not present")
t.Skip(".edata section is not present")
}
// TODO: deduplicate this struct from cmd/link/internal/ld/pe.go
@ -749,7 +798,12 @@ func TestGo2C2Go(t *testing.T) {
defer os.RemoveAll(tmpdir)
lib := filepath.Join(tmpdir, "libtestgo2c2go."+libSuffix)
run(t, nil, "go", "build", "-buildmode=c-shared", "-o", lib, "./go2c2go/go")
var env []string
if GOOS == "windows" && strings.HasSuffix(lib, ".a") {
env = append(env, "CGO_LDFLAGS=-Wl,--out-implib,"+lib, "CGO_LDFLAGS_ALLOW=.*")
lib = strings.TrimSuffix(lib, ".a") + ".dll"
}
run(t, env, "go", "build", "-buildmode=c-shared", "-o", lib, "./go2c2go/go")
cgoCflags := os.Getenv("CGO_CFLAGS")
if cgoCflags != "" {

View File

@ -263,6 +263,17 @@ func TestIssue25756(t *testing.T) {
}
}
// Test with main using -buildmode=pie with plugin for issue #43228
func TestIssue25756pie(t *testing.T) {
if os.Getenv("GO_BUILDER_NAME") == "darwin-arm64-11_0-toothrot" {
t.Skip("broken on darwin/arm64 builder in sharded mode; see issue 46239")
}
goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin")
goCmd(t, "build", "-buildmode=pie", "-o", "issue25756pie.exe", "./issue25756/main.go")
run(t, "./issue25756pie.exe")
}
func TestMethod(t *testing.T) {
// Exported symbol's method must be live.
goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./method/plugin.go")

View File

@ -401,6 +401,7 @@
storeValue(sp + 56, result);
this.mem.setUint8(sp + 64, 1);
} catch (err) {
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 56, err);
this.mem.setUint8(sp + 64, 0);
}
@ -417,6 +418,7 @@
storeValue(sp + 40, result);
this.mem.setUint8(sp + 48, 1);
} catch (err) {
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 40, err);
this.mem.setUint8(sp + 48, 0);
}
@ -433,6 +435,7 @@
storeValue(sp + 40, result);
this.mem.setUint8(sp + 48, 1);
} catch (err) {
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 40, err);
this.mem.setUint8(sp + 48, 0);
}

View File

@ -96,7 +96,15 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
return err
}
z.r = r
z.File = make([]*File, 0, end.directoryRecords)
// Since the number of directory records is not validated, it is not
// safe to preallocate z.File without first checking that the specified
// number of files is reasonable, since a malformed archive may
// 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
// if (data size / 30) >= end.directoryRecords.
if (uint64(size)-end.directorySize)/30 >= end.directoryRecords {
z.File = make([]*File, 0, end.directoryRecords)
}
z.Comment = end.comment
rs := io.NewSectionReader(r, 0, size)
if _, err = rs.Seek(int64(end.directoryOffset), io.SeekStart); err != nil {

View File

@ -1325,3 +1325,62 @@ func TestReadDataDescriptor(t *testing.T) {
})
}
}
func TestCVE202133196(t *testing.T) {
// Archive that indicates it has 1 << 128 -1 files,
// this would previously cause a panic due to attempting
// to allocate a slice with 1 << 128 -1 elements.
data := []byte{
0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08,
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02,
0x03, 0x62, 0x61, 0x65, 0x03, 0x04, 0x00, 0x00,
0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0xbe, 0x20,
0x5c, 0x6c, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00,
0x00, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00,
0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0xbe, 0x20, 0x5c, 0x6c, 0x09, 0x00,
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x02, 0x03, 0x50, 0x4b, 0x06, 0x06, 0x2c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d,
0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x31, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x50, 0x4b, 0x06, 0x07, 0x00,
0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50,
0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x00, 0x00,
}
_, err := NewReader(bytes.NewReader(data), int64(len(data)))
if err != ErrFormat {
t.Fatalf("unexpected error, got: %v, want: %v", err, ErrFormat)
}
// Also check that an archive containing a handful of empty
// files doesn't cause an issue
b := bytes.NewBuffer(nil)
w := NewWriter(b)
for i := 0; i < 5; i++ {
_, err := w.Create("")
if err != nil {
t.Fatalf("Writer.Create failed: %s", err)
}
}
if err := w.Close(); err != nil {
t.Fatalf("Writer.Close failed: %s", err)
}
r, err := NewReader(bytes.NewReader(b.Bytes()), int64(b.Len()))
if err != nil {
t.Fatalf("NewReader failed: %s", err)
}
if len(r.File) != 5 {
t.Errorf("Archive has unexpected number of files, got %d, want 5", len(r.File))
}
}

View File

@ -1003,7 +1003,8 @@ func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) {
p.errorf("unimplemented two-register form")
}
a.Index = r1
if scale != 0 && p.arch.Family == sys.ARM64 {
if scale != 0 && scale != 1 && p.arch.Family == sys.ARM64 {
// Support (R1)(R2) (no scaling) and (R1)(R2*1).
p.errorf("arm64 doesn't support scaled register format")
} else {
a.Scale = int16(scale)

View File

@ -89,7 +89,7 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
CMP R1<<33, R2
CMP R22.SXTX, RSP // ffe336eb
CMP $0x22220000, RSP // CMP $572653568, RSP // 5b44a4d2ff633beb
CMPW $0x22220000, RSP // CMPW $572653568, RSP // 5b44a452ff633b6b
CMPW $0x22220000, RSP // CMPW $572653568, RSP // 5b44a452ff433b6b
CCMN MI, ZR, R1, $4 // e44341ba
// MADD Rn,Rm,Ra,Rd
MADD R1, R2, R3, R4 // 6408019b
@ -377,6 +377,7 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
MOVD $0x1000100010001000, RSP // MOVD $1152939097061330944, RSP // ff8304b2
MOVW $0x10001000, RSP // MOVW $268439552, RSP // ff830432
ADDW $0x10001000, R1 // ADDW $268439552, R1 // fb83043221001b0b
ADDW $0x22220000, RSP, R3 // ADDW $572653568, RSP, R3 // 5b44a452e3433b0b
// move a large constant to a Vd.
VMOVS $0x80402010, V11 // VMOVS $2151686160, V11
@ -547,6 +548,7 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
// shifted or extended register offset.
MOVD (R2)(R6.SXTW), R4 // 44c866f8
MOVD (R3)(R6), R5 // 656866f8
MOVD (R3)(R6*1), R5 // 656866f8
MOVD (R2)(R6), R4 // 446866f8
MOVWU (R19)(R20<<2), R20 // 747a74b8
MOVD (R2)(R6<<3), R4 // 447866f8
@ -579,6 +581,7 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
MOVB R4, (R2)(R6.SXTX) // 44e82638
MOVB R8, (R3)(R9.UXTW) // 68482938
MOVB R10, (R5)(R8) // aa682838
MOVB R10, (R5)(R8*1) // aa682838
MOVH R11, (R2)(R7.SXTW<<1) // 4bd82778
MOVH R5, (R1)(R2<<1) // 25782278
MOVH R7, (R2)(R5.SXTX<<1) // 47f82578

View File

@ -52,6 +52,16 @@ TEXT errors(SB),$0
NEGSW R7@>2, R5 // ERROR "unsupported shift operator"
CINC CS, R2, R3, R4 // ERROR "illegal combination"
CSEL LT, R1, R2 // ERROR "illegal combination"
CINC AL, R2, R3 // ERROR "invalid condition"
CINC NV, R2, R3 // ERROR "invalid condition"
CINVW AL, R2, R3 // ERROR "invalid condition"
CINV NV, R2, R3 // ERROR "invalid condition"
CNEG AL, R2, R3 // ERROR "invalid condition"
CNEGW NV, R2, R3 // ERROR "invalid condition"
CSET AL, R2 // ERROR "invalid condition"
CSET NV, R2 // ERROR "invalid condition"
CSETMW AL, R2 // ERROR "invalid condition"
CSETM NV, R2 // ERROR "invalid condition"
LDP.P 8(R2), (R2, R3) // ERROR "constrained unpredictable behavior"
LDP.W 8(R3), (R2, R3) // ERROR "constrained unpredictable behavior"
LDP (R1), (R2, R2) // ERROR "constrained unpredictable behavior"

View File

@ -41,8 +41,8 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
MOVDBR (R3)(R4), R5 // 7ca41c28
MOVWBR (R3)(R4), R5 // 7ca41c2c
MOVHBR (R3)(R4), R5 // 7ca41e2c
MOVD $foo+4009806848(FP), R5 // 3fe1ef0138bfcc20
MOVD $foo(SB), R5 // 3fe0000038bf0000
MOVD $foo+4009806848(FP), R5 // 3ca1ef0138a5cc20
MOVD $foo(SB), R5 // 3ca0000038a50000
MOVDU 8(R3), R4 // e8830009
MOVDU (R3)(R4), R5 // 7ca4186a

View File

@ -1638,6 +1638,8 @@ func (p *Package) gccCmd() []string {
c = append(c, "-maix64")
c = append(c, "-mcmodel=large")
}
// disable LTO so we get an object whose symbols we can read
c = append(c, "-fno-lto")
c = append(c, "-") //read input from standard input
return c
}

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Cgo; see gmp.go for an overview.
// Cgo; see doc.go for an overview.
// TODO(rsc):
// Emit correct line number annotations.

View File

@ -168,8 +168,18 @@ func (p *Package) writeDefs() {
if *gccgo {
fmt.Fprintf(fc, "extern byte *%s;\n", n.C)
} else {
fmt.Fprintf(fm, "extern char %s[];\n", n.C)
fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C)
// Force a reference to all symbols so that
// the external linker will add DT_NEEDED
// entries as needed on ELF systems.
// Treat function variables differently
// to avoid type confict errors from LTO
// (Link Time Optimization).
if n.Kind == "fpvar" {
fmt.Fprintf(fm, "extern void %s();\n", n.C)
} else {
fmt.Fprintf(fm, "extern char %s[];\n", n.C)
fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C)
}
fmt.Fprintf(fgo2, "//go:linkname __cgo_%s %s\n", n.C, n.C)
fmt.Fprintf(fgo2, "//go:cgo_import_static %s\n", n.C)
fmt.Fprintf(fgo2, "var __cgo_%s byte\n", n.C)
@ -1042,7 +1052,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
// This unpacks the argument struct above and calls the Go function.
fmt.Fprintf(fgo2, "func _cgoexp%s_%s(a *%s) {\n", cPrefix, exp.ExpName, gotype)
fmt.Fprintf(fm, "int _cgoexp%s_%s;\n", cPrefix, exp.ExpName)
fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p){}\n", cPrefix, exp.ExpName)
if gccResult != "void" {
// Write results back to frame.

View File

@ -233,7 +233,7 @@ stack frame is laid out in the following sequence:
r1.x uintptr
r1.y [2]uintptr
a1Spill uint8
a2Spill uint8
a3Spill uint8
_ [6]uint8 // alignment padding
In the stack frame, only the `a2` field is initialized on entry; the
@ -402,7 +402,7 @@ without corrupting arguments or results.
Special-purpose registers are as follows:
| Register | Call meaning | Return meaning | Body meaning |
| --- | --- | --- |
| --- | --- | --- | --- |
| RSP | Stack pointer | Same | Same |
| RBP | Frame pointer | Same | Same |
| RDX | Closure context pointer | Scratch | Scratch |

View File

@ -446,35 +446,20 @@ func (config *ABIConfig) ABIAnalyze(t *types.Type, setNname bool) *ABIParamResul
return result
}
// parameterUpdateMu protects the Offset field of function/method parameters (a subset of structure Fields)
var parameterUpdateMu sync.Mutex
// FieldOffsetOf returns a concurency-safe version of f.Offset
func FieldOffsetOf(f *types.Field) int64 {
parameterUpdateMu.Lock()
defer parameterUpdateMu.Unlock()
return f.Offset
}
func (config *ABIConfig) updateOffset(result *ABIParamResultInfo, f *types.Field, a ABIParamAssignment, isReturn, setNname bool) {
// Everything except return values in registers has either a frame home (if not in a register) or a frame spill location.
if !isReturn || len(a.Registers) == 0 {
// The type frame offset DOES NOT show effects of minimum frame size.
// Getting this wrong breaks stackmaps, see liveness/plive.go:WriteFuncMap and typebits/typebits.go:Set
parameterUpdateMu.Lock()
defer parameterUpdateMu.Unlock()
off := a.FrameOffset(result)
fOffset := f.Offset
if fOffset == types.BOGUS_FUNARG_OFFSET {
// Set the Offset the first time. After that, we may recompute it, but it should never change.
f.Offset = off
if f.Nname != nil {
// always set it in this case.
if setNname && f.Nname != nil {
f.Nname.(*ir.Name).SetFrameOffset(off)
f.Nname.(*ir.Name).SetIsOutputParamInRegisters(false)
}
} else if fOffset != off {
base.Fatalf("offset for %s at %s changed from %d to %d", f.Sym.Name, base.FmtPos(f.Pos), fOffset, off)
} else {
base.Fatalf("field offset for %s at %s has been set to %d", f.Sym.Name, base.FmtPos(f.Pos), fOffset)
}
} else {
if setNname && f.Nname != nil {

View File

@ -222,9 +222,64 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
}
// Sort decls and vars.
sortDeclsAndVars(fn, decls, vars)
return decls, vars
}
// sortDeclsAndVars sorts the decl and dwarf var lists according to
// parameter declaration order, so as to insure that when a subprogram
// DIE is emitted, its parameter children appear in declaration order.
// Prior to the advent of the register ABI, sorting by frame offset
// would achieve this; with the register we now need to go back to the
// original function signature.
func sortDeclsAndVars(fn *ir.Func, decls []*ir.Name, vars []*dwarf.Var) {
paramOrder := make(map[*ir.Name]int)
idx := 1
for _, selfn := range types.RecvsParamsResults {
fsl := selfn(fn.Type()).FieldSlice()
for _, f := range fsl {
if n, ok := f.Nname.(*ir.Name); ok {
paramOrder[n] = idx
idx++
}
}
}
sort.Stable(varsAndDecls{decls, vars, paramOrder})
}
type varsAndDecls struct {
decls []*ir.Name
vars []*dwarf.Var
paramOrder map[*ir.Name]int
}
func (v varsAndDecls) Len() int {
return len(v.decls)
}
func (v varsAndDecls) Less(i, j int) bool {
nameLT := func(ni, nj *ir.Name) bool {
oi, foundi := v.paramOrder[ni]
oj, foundj := v.paramOrder[nj]
if foundi {
if foundj {
return oi < oj
} else {
return true
}
}
return false
}
return nameLT(v.decls[i], v.decls[j])
}
func (v varsAndDecls) Swap(i, j int) {
v.vars[i], v.vars[j] = v.vars[j], v.vars[i]
v.decls[i], v.decls[j] = v.decls[j], v.decls[i]
}
// Given a function that was inlined at some point during the
// compilation, return a sorted list of nodes corresponding to the
// autos/locals in that function prior to inlining. If this is a
@ -476,6 +531,14 @@ func RecordFlags(flags ...string) {
fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get())
}
// Adds flag to producer string singalling whether regabi is turned on or
// off.
// Once regabi is turned on across the board and the relative GOEXPERIMENT
// knobs no longer exist this code should be removed.
if buildcfg.Experiment.RegabiArgs {
cmd.Write([]byte(" regabi"))
}
if cmd.Len() == 0 {
return
}

View File

@ -2013,14 +2013,14 @@ func HeapAllocReason(n ir.Node) string {
return "too large for stack"
}
if (n.Op() == ir.ONEW || n.Op() == ir.OPTRLIT) && n.Type().Elem().Width >= ir.MaxImplicitStackVarSize {
if (n.Op() == ir.ONEW || n.Op() == ir.OPTRLIT) && n.Type().Elem().Width > ir.MaxImplicitStackVarSize {
return "too large for stack"
}
if n.Op() == ir.OCLOSURE && typecheck.ClosureType(n.(*ir.ClosureExpr)).Size() >= ir.MaxImplicitStackVarSize {
if n.Op() == ir.OCLOSURE && typecheck.ClosureType(n.(*ir.ClosureExpr)).Size() > ir.MaxImplicitStackVarSize {
return "too large for stack"
}
if n.Op() == ir.OCALLPART && typecheck.PartialCallType(n.(*ir.SelectorExpr)).Size() >= ir.MaxImplicitStackVarSize {
if n.Op() == ir.OCALLPART && typecheck.PartialCallType(n.(*ir.SelectorExpr)).Size() > ir.MaxImplicitStackVarSize {
return "too large for stack"
}
@ -2033,7 +2033,7 @@ func HeapAllocReason(n ir.Node) string {
if !ir.IsSmallIntConst(r) {
return "non-constant size"
}
if t := n.Type(); t.Elem().Width != 0 && ir.Int64Val(r) >= ir.MaxImplicitStackVarSize/t.Elem().Width {
if t := n.Type(); t.Elem().Width != 0 && ir.Int64Val(r) > ir.MaxImplicitStackVarSize/t.Elem().Width {
return "too large for stack"
}
}

View File

@ -119,38 +119,51 @@ func compileFunctions() {
})
}
// We queue up a goroutine per function that needs to be
// compiled, but require them to grab an available worker ID
// before doing any substantial work to limit parallelism.
workerIDs := make(chan int, base.Flag.LowerC)
for i := 0; i < base.Flag.LowerC; i++ {
workerIDs <- i
// By default, we perform work right away on the current goroutine
// as the solo worker.
queue := func(work func(int)) {
work(0)
}
if nWorkers := base.Flag.LowerC; nWorkers > 1 {
// For concurrent builds, we create a goroutine per task, but
// require them to hold a unique worker ID while performing work
// to limit parallelism.
workerIDs := make(chan int, nWorkers)
for i := 0; i < nWorkers; i++ {
workerIDs <- i
}
queue = func(work func(int)) {
go func() {
worker := <-workerIDs
work(worker)
workerIDs <- worker
}()
}
}
var wg sync.WaitGroup
var asyncCompile func(*ir.Func)
asyncCompile = func(fn *ir.Func) {
wg.Add(1)
go func() {
worker := <-workerIDs
ssagen.Compile(fn, worker)
workerIDs <- worker
// Done compiling fn. Schedule it's closures for compilation.
for _, closure := range fn.Closures {
asyncCompile(closure)
}
wg.Done()
}()
var compile func([]*ir.Func)
compile = func(fns []*ir.Func) {
wg.Add(len(fns))
for _, fn := range fns {
fn := fn
queue(func(worker int) {
ssagen.Compile(fn, worker)
compile(fn.Closures)
wg.Done()
})
}
}
types.CalcSizeDisabled = true // not safe to calculate sizes concurrently
base.Ctxt.InParallel = true
for _, fn := range compilequeue {
asyncCompile(fn)
}
compile(compilequeue)
compilequeue = nil
wg.Wait()
base.Ctxt.InParallel = false
types.CalcSizeDisabled = false
}

View File

@ -148,6 +148,7 @@ func dumpdata() {
if reflectdata.ZeroSize > 0 {
zero := base.PkgLinksym("go.map", "zero", obj.ABI0)
objw.Global(zero, int32(reflectdata.ZeroSize), obj.DUPOK|obj.RODATA)
zero.Set(obj.AttrStatic, true)
}
staticdata.WriteFuncSyms()

View File

@ -1124,6 +1124,10 @@ type inlsubst struct {
newclofn *ir.Func
fn *ir.Func // For debug -- the func that is being inlined
// If true, then don't update source positions during substitution
// (retain old source positions).
noPosUpdate bool
}
// list inlines a list of nodes.
@ -1219,7 +1223,14 @@ func (subst *inlsubst) clovar(n *ir.Name) *ir.Name {
// closure node.
func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
m := ir.Copy(n)
m.SetPos(subst.updatedPos(m.Pos()))
// Prior to the subst edit, set a flag in the inlsubst to
// indicated that we don't want to update the source positions in
// the new closure. If we do this, it will appear that the closure
// itself has things inlined into it, which is not the case. See
// issue #46234 for more details.
defer func(prev bool) { subst.noPosUpdate = prev }(subst.noPosUpdate)
subst.noPosUpdate = true
ir.EditChildren(m, subst.edit)
//fmt.Printf("Inlining func %v with closure into %v\n", subst.fn, ir.FuncName(ir.CurFunc))
@ -1445,6 +1456,9 @@ func (subst *inlsubst) node(n ir.Node) ir.Node {
}
func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos {
if subst.noPosUpdate {
return xpos
}
pos := base.Ctxt.PosTable.Pos(xpos)
oldbase := pos.Base() // can be nil
newbase := subst.bases[oldbase]

View File

@ -882,9 +882,6 @@ func (p *noder) typeExpr(typ syntax.Expr) ir.Ntype {
if n == nil {
return nil
}
if _, ok := n.(ir.Ntype); !ok {
ir.Dump("NOT NTYPE", n)
}
return n.(ir.Ntype)
}

View File

@ -6,6 +6,7 @@ package reflectdata
import (
"fmt"
"math/bits"
"sort"
"cmd/compile/internal/base"
@ -47,6 +48,11 @@ func eqCanPanic(t *types.Type) bool {
func AlgType(t *types.Type) types.AlgKind {
a, _ := types.AlgType(t)
if a == types.AMEM {
if t.Alignment() < int64(base.Ctxt.Arch.Alignment) && t.Alignment() < t.Width {
// For example, we can't treat [2]int16 as an int32 if int32s require
// 4-byte alignment. See issue 46283.
return a
}
switch t.Width {
case 0:
return types.AMEM0
@ -769,6 +775,20 @@ func memrun(t *types.Type, start int) (size int64, next int) {
if f := t.Field(next); f.Sym.IsBlank() || !isRegularMemory(f.Type) {
break
}
// For issue 46283, don't combine fields if the resulting load would
// require a larger alignment than the component fields.
if base.Ctxt.Arch.Alignment > 1 {
align := t.Alignment()
if off := t.Field(start).Offset; off&(align-1) != 0 {
// Offset is less aligned than the containing type.
// Use offset to determine alignment.
align = 1 << uint(bits.TrailingZeros64(uint64(off)))
}
size := t.Field(next).End() - t.Field(start).Offset
if size > align {
break
}
}
}
return t.Field(next-1).End() - t.Field(start).Offset, next
}

View File

@ -5,6 +5,7 @@
package reflectdata
import (
"encoding/binary"
"fmt"
"internal/buildcfg"
"os"
@ -473,21 +474,25 @@ func dnameField(lsym *obj.LSym, ot int, spkg *types.Pkg, ft *types.Field) int {
// dnameData writes the contents of a reflect.name into s at offset ot.
func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported bool) int {
if len(name) > 1<<16-1 {
base.Fatalf("name too long: %s", name)
if len(name) >= 1<<29 {
base.Fatalf("name too long: %d %s...", len(name), name[:1024])
}
if len(tag) > 1<<16-1 {
base.Fatalf("tag too long: %s", tag)
if len(tag) >= 1<<29 {
base.Fatalf("tag too long: %d %s...", len(tag), tag[:1024])
}
var nameLen [binary.MaxVarintLen64]byte
nameLenLen := binary.PutUvarint(nameLen[:], uint64(len(name)))
var tagLen [binary.MaxVarintLen64]byte
tagLenLen := binary.PutUvarint(tagLen[:], uint64(len(tag)))
// Encode name and tag. See reflect/type.go for details.
var bits byte
l := 1 + 2 + len(name)
l := 1 + nameLenLen + len(name)
if exported {
bits |= 1 << 0
}
if len(tag) > 0 {
l += 2 + len(tag)
l += tagLenLen + len(tag)
bits |= 1 << 1
}
if pkg != nil {
@ -495,14 +500,12 @@ func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported b
}
b := make([]byte, l)
b[0] = bits
b[1] = uint8(len(name) >> 8)
b[2] = uint8(len(name))
copy(b[3:], name)
copy(b[1:], nameLen[:nameLenLen])
copy(b[1+nameLenLen:], name)
if len(tag) > 0 {
tb := b[3+len(name):]
tb[0] = uint8(len(tag) >> 8)
tb[1] = uint8(len(tag))
copy(tb[2:], tag)
tb := b[1+nameLenLen+len(name):]
copy(tb, tagLen[:tagLenLen])
copy(tb[tagLenLen:], tag)
}
ot = int(s.WriteBytes(base.Ctxt, int64(ot), b))
@ -666,7 +669,7 @@ var kinds = []int{
// tflag is documented in reflect/type.go.
//
// tflag values must be kept in sync with copies in:
// cmd/compile/internal/gc/reflect.go
// cmd/compile/internal/reflectdata/reflect.go
// cmd/link/internal/ld/decodesym.go
// reflect/type.go
// runtime/type.go
@ -1109,6 +1112,15 @@ func writeType(t *types.Type) *obj.LSym {
}
ot = objw.Uint32(lsym, ot, flags)
ot = dextratype(lsym, ot, t, 0)
if u := t.Underlying(); u != t {
// If t is a named map type, also keep the underlying map
// type live in the binary. This is important to make sure that
// a named map and that same map cast to its underlying type via
// reflection, use the same hash function. See issue 37716.
r := obj.Addrel(lsym)
r.Sym = writeType(u)
r.Type = objabi.R_KEEP
}
case types.TPTR:
if t.Elem().Kind() == types.TANY {
@ -1490,8 +1502,8 @@ func (a typesByString) Less(i, j int) bool {
// will be equal for the above checks, but different in DWARF output.
// Sort by source position to ensure deterministic order.
// See issues 27013 and 30202.
if a[i].t.Kind() == types.TINTER && a[i].t.Methods().Len() > 0 {
return a[i].t.Methods().Index(0).Pos.Before(a[j].t.Methods().Index(0).Pos)
if a[i].t.Kind() == types.TINTER && a[i].t.AllMethods().Len() > 0 {
return a[i].t.AllMethods().Index(0).Pos.Before(a[j].t.AllMethods().Index(0).Pos)
}
return false
}

View File

@ -147,13 +147,6 @@ type Frontend interface {
// Given the name for a compound type, returns the name we should use
// for the parts of that compound type.
SplitString(LocalSlot) (LocalSlot, LocalSlot)
SplitInterface(LocalSlot) (LocalSlot, LocalSlot)
SplitSlice(LocalSlot) (LocalSlot, LocalSlot, LocalSlot)
SplitComplex(LocalSlot) (LocalSlot, LocalSlot)
SplitStruct(LocalSlot, int) LocalSlot
SplitArray(LocalSlot) LocalSlot // array must be length 1
SplitInt64(LocalSlot) (LocalSlot, LocalSlot) // returns (hi, lo)
SplitSlot(parent *LocalSlot, suffix string, offset int64, t *types.Type) LocalSlot
// DerefItab dereferences an itab function

View File

@ -26,7 +26,7 @@ func copyelim(f *Func) {
// Update named values.
for _, name := range f.Names {
values := f.NamedValues[name]
values := f.NamedValues[*name]
for i, v := range values {
if v.Op == OpCopy {
values[i] = v.Args[0]

View File

@ -223,7 +223,7 @@ func deadcode(f *Func) {
for _, name := range f.Names {
j := 0
s.clear()
values := f.NamedValues[name]
values := f.NamedValues[*name]
for _, v := range values {
if live[v.ID] && !s.contains(v.ID) {
values[j] = v
@ -232,19 +232,19 @@ func deadcode(f *Func) {
}
}
if j == 0 {
delete(f.NamedValues, name)
delete(f.NamedValues, *name)
} else {
f.Names[i] = name
i++
for k := len(values) - 1; k >= j; k-- {
values[k] = nil
}
f.NamedValues[name] = values[:j]
f.NamedValues[*name] = values[:j]
}
}
clearNames := f.Names[i:]
for j := range clearNames {
clearNames[j] = LocalSlot{}
clearNames[j] = nil
}
f.Names = f.Names[:i]

View File

@ -7,10 +7,13 @@ package ssa
import (
"cmd/compile/internal/abi"
"cmd/compile/internal/ir"
"cmd/compile/internal/types"
"cmd/internal/dwarf"
"cmd/internal/obj"
"cmd/internal/src"
"encoding/hex"
"fmt"
"internal/buildcfg"
"math/bits"
"sort"
"strings"
@ -335,6 +338,216 @@ func (s *debugState) stateString(state stateAtPC) string {
return strings.Join(strs, "")
}
// slotCanonicalizer is a table used to lookup and canonicalize
// LocalSlot's in a type insensitive way (e.g. taking into account the
// base name, offset, and width of the slot, but ignoring the slot
// type).
type slotCanonicalizer struct {
slmap map[slotKey]SlKeyIdx
slkeys []LocalSlot
}
func newSlotCanonicalizer() *slotCanonicalizer {
return &slotCanonicalizer{
slmap: make(map[slotKey]SlKeyIdx),
slkeys: []LocalSlot{LocalSlot{N: nil}},
}
}
type SlKeyIdx uint32
const noSlot = SlKeyIdx(0)
// slotKey is a type-insensitive encapsulation of a LocalSlot; it
// is used to key a map within slotCanonicalizer.
type slotKey struct {
name *ir.Name
offset int64
width int64
splitOf SlKeyIdx // idx in slkeys slice in slotCanonicalizer
splitOffset int64
}
// lookup looks up a LocalSlot in the slot canonicalizer "sc", returning
// a canonical index for the slot, and adding it to the table if need
// be. Return value is the canonical slot index, and a boolean indicating
// whether the slot was found in the table already (TRUE => found).
func (sc *slotCanonicalizer) lookup(ls LocalSlot) (SlKeyIdx, bool) {
split := noSlot
if ls.SplitOf != nil {
split, _ = sc.lookup(*ls.SplitOf)
}
k := slotKey{
name: ls.N, offset: ls.Off, width: ls.Type.Width,
splitOf: split, splitOffset: ls.SplitOffset,
}
if idx, ok := sc.slmap[k]; ok {
return idx, true
}
rv := SlKeyIdx(len(sc.slkeys))
sc.slkeys = append(sc.slkeys, ls)
sc.slmap[k] = rv
return rv, false
}
func (sc *slotCanonicalizer) canonSlot(idx SlKeyIdx) LocalSlot {
return sc.slkeys[idx]
}
// PopulateABIInRegArgOps examines the entry block of the function
// and looks for incoming parameters that have missing or partial
// OpArg{Int,Float}Reg values, inserting additional values in
// cases where they are missing. Example:
//
// func foo(s string, used int, notused int) int {
// return len(s) + used
// }
//
// In the function above, the incoming parameter "used" is fully live,
// "notused" is not live, and "s" is partially live (only the length
// field of the string is used). At the point where debug value
// analysis runs, we might expect to see an entry block with:
//
// b1:
// v4 = ArgIntReg <uintptr> {s+8} [0] : BX
// v5 = ArgIntReg <int> {used} [0] : CX
//
// While this is an accurate picture of the live incoming params,
// we also want to have debug locations for non-live params (or
// their non-live pieces), e.g. something like
//
// b1:
// v9 = ArgIntReg <*uint8> {s+0} [0] : AX
// v4 = ArgIntReg <uintptr> {s+8} [0] : BX
// v5 = ArgIntReg <int> {used} [0] : CX
// v10 = ArgIntReg <int> {unused} [0] : DI
//
// This function examines the live OpArg{Int,Float}Reg values and
// synthesizes new (dead) values for the non-live params or the
// non-live pieces of partially live params.
//
func PopulateABIInRegArgOps(f *Func) {
pri := f.ABISelf.ABIAnalyzeFuncType(f.Type.FuncType())
// When manufacturing new slots that correspond to splits of
// composite parameters, we want to avoid creating a new sub-slot
// that differs from some existing sub-slot only by type, since
// the debug location analysis will treat that slot as a separate
// entity. To achieve this, create a lookup table of existing
// slots that is type-insenstitive.
sc := newSlotCanonicalizer()
for _, sl := range f.Names {
sc.lookup(*sl)
}
// Add slot -> value entry to f.NamedValues if not already present.
addToNV := func(v *Value, sl LocalSlot) {
values, ok := f.NamedValues[sl]
if !ok {
// Haven't seen this slot yet.
sla := f.localSlotAddr(sl)
f.Names = append(f.Names, sla)
} else {
for _, ev := range values {
if v == ev {
return
}
}
}
values = append(values, v)
f.NamedValues[sl] = values
}
newValues := []*Value{}
abiRegIndexToRegister := func(reg abi.RegIndex) int8 {
i := f.ABISelf.FloatIndexFor(reg)
if i >= 0 { // float PR
return f.Config.floatParamRegs[i]
} else {
return f.Config.intParamRegs[reg]
}
}
// Helper to construct a new OpArg{Float,Int}Reg op value.
var pos src.XPos
if len(f.Entry.Values) != 0 {
pos = f.Entry.Values[0].Pos
}
synthesizeOpIntFloatArg := func(n *ir.Name, t *types.Type, reg abi.RegIndex, sl LocalSlot) *Value {
aux := &AuxNameOffset{n, sl.Off}
op, auxInt := ArgOpAndRegisterFor(reg, f.ABISelf)
v := f.newValueNoBlock(op, t, pos)
v.AuxInt = auxInt
v.Aux = aux
v.Args = nil
v.Block = f.Entry
newValues = append(newValues, v)
addToNV(v, sl)
f.setHome(v, &f.Config.registers[abiRegIndexToRegister(reg)])
return v
}
// Make a pass through the entry block looking for
// OpArg{Int,Float}Reg ops. Record the slots they use in a table
// ("sc"). We use a type-insensitive lookup for the slot table,
// since the type we get from the ABI analyzer won't always match
// what the compiler uses when creating OpArg{Int,Float}Reg ops.
for _, v := range f.Entry.Values {
if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
aux := v.Aux.(*AuxNameOffset)
sl := LocalSlot{N: aux.Name, Type: v.Type, Off: aux.Offset}
// install slot in lookup table
idx, _ := sc.lookup(sl)
// add to f.NamedValues if not already present
addToNV(v, sc.canonSlot(idx))
} else if v.Op.IsCall() {
// if we hit a call, we've gone too far.
break
}
}
// Now make a pass through the ABI in-params, looking for params
// or pieces of params that we didn't encounter in the loop above.
for _, inp := range pri.InParams() {
if !isNamedRegParam(inp) {
continue
}
n := inp.Name.(*ir.Name)
// Param is spread across one or more registers. Walk through
// each piece to see whether we've seen an arg reg op for it.
types, offsets := inp.RegisterTypesAndOffsets()
for k, t := range types {
// Note: this recipe for creating a LocalSlot is designed
// to be compatible with the one used in expand_calls.go
// as opposed to decompose.go. The expand calls code just
// takes the base name and creates an offset into it,
// without using the SplitOf/SplitOffset fields. The code
// in decompose.go does the opposite -- it creates a
// LocalSlot object with "Off" set to zero, but with
// SplitOf pointing to a parent slot, and SplitOffset
// holding the offset into the parent object.
pieceSlot := LocalSlot{N: n, Type: t, Off: offsets[k]}
// Look up this piece to see if we've seen a reg op
// for it. If not, create one.
_, found := sc.lookup(pieceSlot)
if !found {
// This slot doesn't appear in the map, meaning it
// corresponds to an in-param that is not live, or
// a portion of an in-param that is not live/used.
// Add a new dummy OpArg{Int,Float}Reg for it.
synthesizeOpIntFloatArg(n, t, inp.Registers[k],
pieceSlot)
}
}
}
// Insert the new values into the head of the block.
f.Entry.Values = append(newValues, f.Entry.Values...)
}
// BuildFuncDebug returns debug information for f.
// f must be fully processed, so that each Value is where it will be when
// machine code is emitted.
@ -349,6 +562,10 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu
state.stackOffset = stackOffset
state.ctxt = ctxt
if buildcfg.Experiment.RegabiArgs {
PopulateABIInRegArgOps(f)
}
if state.loggingEnabled {
state.logf("Generating location lists for function %q\n", f.Name)
}
@ -367,12 +584,12 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu
state.slots = state.slots[:0]
state.vars = state.vars[:0]
for i, slot := range f.Names {
state.slots = append(state.slots, slot)
state.slots = append(state.slots, *slot)
if ir.IsSynthetic(slot.N) {
continue
}
topSlot := &slot
topSlot := slot
for topSlot.SplitOf != nil {
topSlot = topSlot.SplitOf
}
@ -436,7 +653,7 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu
if ir.IsSynthetic(slot.N) {
continue
}
for _, value := range f.NamedValues[slot] {
for _, value := range f.NamedValues[*slot] {
state.valueNames[value.ID] = append(state.valueNames[value.ID], SlotID(i))
}
}
@ -898,8 +1115,14 @@ func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) {
continue
}
mustBeFirst := func(v *Value) bool {
return v.Op == OpPhi || v.Op.isLoweredGetClosurePtr() ||
v.Op == OpArgIntReg || v.Op == OpArgFloatReg
}
zeroWidthPending := false
apcChangedSize := 0 // size of changedVars for leading Args, Phi, ClosurePtr
blockPrologComplete := false // set to true at first non-zero-width op
apcChangedSize := 0 // size of changedVars for leading Args, Phi, ClosurePtr
// expect to see values in pattern (apc)* (zerowidth|real)*
for _, v := range b.Values {
slots := state.valueNames[v.ID]
@ -908,16 +1131,16 @@ func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) {
if opcodeTable[v.Op].zeroWidth {
if changed {
if hasAnyArgOp(v) || v.Op == OpPhi || v.Op.isLoweredGetClosurePtr() {
if mustBeFirst(v) || v.Op == OpArg {
// These ranges begin at true beginning of block, not after first instruction
if zeroWidthPending {
panic(fmt.Errorf("Unexpected op '%s' mixed with OpArg/OpPhi/OpLoweredGetClosurePtr at beginning of block %s in %s\n%s", v.LongString(), b, b.Func.Name, b.Func))
if blockPrologComplete && mustBeFirst(v) {
panic(fmt.Errorf("Unexpected placement of op '%s' appearing after non-pseudo-op at beginning of block %s in %s\n%s", v.LongString(), b, b.Func.Name, b.Func))
}
apcChangedSize = len(state.changedVars.contents())
// Other zero-width ops must wait on a "real" op.
zeroWidthPending = true
continue
}
// Other zero-width ops must wait on a "real" op.
zeroWidthPending = true
}
continue
}
@ -928,6 +1151,7 @@ func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) {
// Not zero-width; i.e., a "real" instruction.
zeroWidthPending = false
blockPrologComplete = true
for i, varID := range state.changedVars.contents() {
if i < apcChangedSize { // buffered true start-of-block changes
state.updateVar(VarID(varID), v.Block, BlockStart)

View File

@ -36,64 +36,65 @@ func decomposeBuiltIn(f *Func) {
// accumulate new LocalSlots in newNames for addition after the iteration. This decomposition is for
// builtin types with leaf components, and thus there is no need to reprocess the newly create LocalSlots.
var toDelete []namedVal
var newNames []LocalSlot
var newNames []*LocalSlot
for i, name := range f.Names {
t := name.Type
switch {
case t.IsInteger() && t.Size() > f.Config.RegSize:
hiName, loName := f.fe.SplitInt64(name)
newNames = append(newNames, hiName, loName)
for j, v := range f.NamedValues[name] {
hiName, loName := f.SplitInt64(name)
newNames = maybeAppend2(f, newNames, hiName, loName)
for j, v := range f.NamedValues[*name] {
if v.Op != OpInt64Make {
continue
}
f.NamedValues[hiName] = append(f.NamedValues[hiName], v.Args[0])
f.NamedValues[loName] = append(f.NamedValues[loName], v.Args[1])
f.NamedValues[*hiName] = append(f.NamedValues[*hiName], v.Args[0])
f.NamedValues[*loName] = append(f.NamedValues[*loName], v.Args[1])
toDelete = append(toDelete, namedVal{i, j})
}
case t.IsComplex():
rName, iName := f.fe.SplitComplex(name)
newNames = append(newNames, rName, iName)
for j, v := range f.NamedValues[name] {
rName, iName := f.SplitComplex(name)
newNames = maybeAppend2(f, newNames, rName, iName)
for j, v := range f.NamedValues[*name] {
if v.Op != OpComplexMake {
continue
}
f.NamedValues[rName] = append(f.NamedValues[rName], v.Args[0])
f.NamedValues[iName] = append(f.NamedValues[iName], v.Args[1])
f.NamedValues[*rName] = append(f.NamedValues[*rName], v.Args[0])
f.NamedValues[*iName] = append(f.NamedValues[*iName], v.Args[1])
toDelete = append(toDelete, namedVal{i, j})
}
case t.IsString():
ptrName, lenName := f.fe.SplitString(name)
newNames = append(newNames, ptrName, lenName)
for j, v := range f.NamedValues[name] {
ptrName, lenName := f.SplitString(name)
newNames = maybeAppend2(f, newNames, ptrName, lenName)
for j, v := range f.NamedValues[*name] {
if v.Op != OpStringMake {
continue
}
f.NamedValues[ptrName] = append(f.NamedValues[ptrName], v.Args[0])
f.NamedValues[lenName] = append(f.NamedValues[lenName], v.Args[1])
f.NamedValues[*ptrName] = append(f.NamedValues[*ptrName], v.Args[0])
f.NamedValues[*lenName] = append(f.NamedValues[*lenName], v.Args[1])
toDelete = append(toDelete, namedVal{i, j})
}
case t.IsSlice():
ptrName, lenName, capName := f.fe.SplitSlice(name)
newNames = append(newNames, ptrName, lenName, capName)
for j, v := range f.NamedValues[name] {
ptrName, lenName, capName := f.SplitSlice(name)
newNames = maybeAppend2(f, newNames, ptrName, lenName)
newNames = maybeAppend(f, newNames, capName)
for j, v := range f.NamedValues[*name] {
if v.Op != OpSliceMake {
continue
}
f.NamedValues[ptrName] = append(f.NamedValues[ptrName], v.Args[0])
f.NamedValues[lenName] = append(f.NamedValues[lenName], v.Args[1])
f.NamedValues[capName] = append(f.NamedValues[capName], v.Args[2])
f.NamedValues[*ptrName] = append(f.NamedValues[*ptrName], v.Args[0])
f.NamedValues[*lenName] = append(f.NamedValues[*lenName], v.Args[1])
f.NamedValues[*capName] = append(f.NamedValues[*capName], v.Args[2])
toDelete = append(toDelete, namedVal{i, j})
}
case t.IsInterface():
typeName, dataName := f.fe.SplitInterface(name)
newNames = append(newNames, typeName, dataName)
for j, v := range f.NamedValues[name] {
typeName, dataName := f.SplitInterface(name)
newNames = maybeAppend2(f, newNames, typeName, dataName)
for j, v := range f.NamedValues[*name] {
if v.Op != OpIMake {
continue
}
f.NamedValues[typeName] = append(f.NamedValues[typeName], v.Args[0])
f.NamedValues[dataName] = append(f.NamedValues[dataName], v.Args[1])
f.NamedValues[*typeName] = append(f.NamedValues[*typeName], v.Args[0])
f.NamedValues[*dataName] = append(f.NamedValues[*dataName], v.Args[1])
toDelete = append(toDelete, namedVal{i, j})
}
case t.IsFloat():
@ -107,6 +108,18 @@ func decomposeBuiltIn(f *Func) {
f.Names = append(f.Names, newNames...)
}
func maybeAppend(f *Func, ss []*LocalSlot, s *LocalSlot) []*LocalSlot {
if _, ok := f.NamedValues[*s]; !ok {
f.NamedValues[*s] = nil
return append(ss, s)
}
return ss
}
func maybeAppend2(f *Func, ss []*LocalSlot, s1, s2 *LocalSlot) []*LocalSlot {
return maybeAppend(f, maybeAppend(f, ss, s1), s2)
}
func decomposeBuiltInPhi(v *Value) {
switch {
case v.Type.IsInteger() && v.Type.Size() > v.Block.Func.Config.RegSize:
@ -230,7 +243,7 @@ func decomposeUser(f *Func) {
}
// Split up named values into their components.
i := 0
var newNames []LocalSlot
var newNames []*LocalSlot
for _, name := range f.Names {
t := name.Type
switch {
@ -250,7 +263,7 @@ func decomposeUser(f *Func) {
// decomposeUserArrayInto creates names for the element(s) of arrays referenced
// by name where possible, and appends those new names to slots, which is then
// returned.
func decomposeUserArrayInto(f *Func, name LocalSlot, slots []LocalSlot) []LocalSlot {
func decomposeUserArrayInto(f *Func, name *LocalSlot, slots []*LocalSlot) []*LocalSlot {
t := name.Type
if t.NumElem() == 0 {
// TODO(khr): Not sure what to do here. Probably nothing.
@ -261,20 +274,20 @@ func decomposeUserArrayInto(f *Func, name LocalSlot, slots []LocalSlot) []LocalS
// shouldn't get here due to CanSSA
f.Fatalf("array not of size 1")
}
elemName := f.fe.SplitArray(name)
elemName := f.SplitArray(name)
var keep []*Value
for _, v := range f.NamedValues[name] {
for _, v := range f.NamedValues[*name] {
if v.Op != OpArrayMake1 {
keep = append(keep, v)
continue
}
f.NamedValues[elemName] = append(f.NamedValues[elemName], v.Args[0])
f.NamedValues[*elemName] = append(f.NamedValues[*elemName], v.Args[0])
}
if len(keep) == 0 {
// delete the name for the array as a whole
delete(f.NamedValues, name)
delete(f.NamedValues, *name)
} else {
f.NamedValues[name] = keep
f.NamedValues[*name] = keep
}
if t.Elem().IsArray() {
@ -289,38 +302,38 @@ func decomposeUserArrayInto(f *Func, name LocalSlot, slots []LocalSlot) []LocalS
// decomposeUserStructInto creates names for the fields(s) of structs referenced
// by name where possible, and appends those new names to slots, which is then
// returned.
func decomposeUserStructInto(f *Func, name LocalSlot, slots []LocalSlot) []LocalSlot {
fnames := []LocalSlot{} // slots for struct in name
func decomposeUserStructInto(f *Func, name *LocalSlot, slots []*LocalSlot) []*LocalSlot {
fnames := []*LocalSlot{} // slots for struct in name
t := name.Type
n := t.NumFields()
for i := 0; i < n; i++ {
fs := f.fe.SplitStruct(name, i)
fs := f.SplitStruct(name, i)
fnames = append(fnames, fs)
// arrays and structs will be decomposed further, so
// there's no need to record a name
if !fs.Type.IsArray() && !fs.Type.IsStruct() {
slots = append(slots, fs)
slots = maybeAppend(f, slots, fs)
}
}
makeOp := StructMakeOp(n)
var keep []*Value
// create named values for each struct field
for _, v := range f.NamedValues[name] {
for _, v := range f.NamedValues[*name] {
if v.Op != makeOp {
keep = append(keep, v)
continue
}
for i := 0; i < len(fnames); i++ {
f.NamedValues[fnames[i]] = append(f.NamedValues[fnames[i]], v.Args[i])
f.NamedValues[*fnames[i]] = append(f.NamedValues[*fnames[i]], v.Args[i])
}
}
if len(keep) == 0 {
// delete the name for the struct as a whole
delete(f.NamedValues, name)
delete(f.NamedValues, *name)
} else {
f.NamedValues[name] = keep
f.NamedValues[*name] = keep
}
// now that this f.NamedValues contains values for the struct
@ -328,10 +341,10 @@ func decomposeUserStructInto(f *Func, name LocalSlot, slots []LocalSlot) []Local
for i := 0; i < n; i++ {
if name.Type.FieldType(i).IsStruct() {
slots = decomposeUserStructInto(f, fnames[i], slots)
delete(f.NamedValues, fnames[i])
delete(f.NamedValues, *fnames[i])
} else if name.Type.FieldType(i).IsArray() {
slots = decomposeUserArrayInto(f, fnames[i], slots)
delete(f.NamedValues, fnames[i])
delete(f.NamedValues, *fnames[i])
}
}
return slots
@ -416,9 +429,10 @@ type namedVal struct {
locIndex, valIndex int // f.NamedValues[f.Names[locIndex]][valIndex] = key
}
// deleteNamedVals removes particular values with debugger names from f's naming data structures
// deleteNamedVals removes particular values with debugger names from f's naming data structures,
// removes all values with OpInvalid, and re-sorts the list of Names.
func deleteNamedVals(f *Func, toDelete []namedVal) {
// Arrange to delete from larger indices to smaller, to ensure swap-with-end deletion does not invalid pending indices.
// Arrange to delete from larger indices to smaller, to ensure swap-with-end deletion does not invalidate pending indices.
sort.Slice(toDelete, func(i, j int) bool {
if toDelete[i].locIndex != toDelete[j].locIndex {
return toDelete[i].locIndex > toDelete[j].locIndex
@ -430,16 +444,36 @@ func deleteNamedVals(f *Func, toDelete []namedVal) {
// Get rid of obsolete names
for _, d := range toDelete {
loc := f.Names[d.locIndex]
vals := f.NamedValues[loc]
vals := f.NamedValues[*loc]
l := len(vals) - 1
if l > 0 {
vals[d.valIndex] = vals[l]
f.NamedValues[loc] = vals[:l]
} else {
delete(f.NamedValues, loc)
l = len(f.Names) - 1
f.Names[d.locIndex] = f.Names[l]
f.Names = f.Names[:l]
}
vals[l] = nil
f.NamedValues[*loc] = vals[:l]
}
// Delete locations with no values attached.
end := len(f.Names)
for i := len(f.Names) - 1; i >= 0; i-- {
loc := f.Names[i]
vals := f.NamedValues[*loc]
last := len(vals)
for j := len(vals) - 1; j >= 0; j-- {
if vals[j].Op == OpInvalid {
last--
vals[j] = vals[last]
vals[last] = nil
}
}
if last < len(vals) {
f.NamedValues[*loc] = vals[:last]
}
if len(vals) == 0 {
delete(f.NamedValues, *loc)
end--
f.Names[i] = f.Names[end]
f.Names[end] = nil
}
}
f.Names = f.Names[:end]
}

View File

@ -243,10 +243,10 @@ func (x *expandState) offsetFrom(b *Block, from *Value, offset int64, pt *types.
}
// splitSlots splits one "field" (specified by sfx, offset, and ty) out of the LocalSlots in ls and returns the new LocalSlots this generates.
func (x *expandState) splitSlots(ls []LocalSlot, sfx string, offset int64, ty *types.Type) []LocalSlot {
var locs []LocalSlot
func (x *expandState) splitSlots(ls []*LocalSlot, sfx string, offset int64, ty *types.Type) []*LocalSlot {
var locs []*LocalSlot
for i := range ls {
locs = append(locs, x.f.fe.SplitSlot(&ls[i], sfx, offset, ty))
locs = append(locs, x.f.SplitSlot(ls[i], sfx, offset, ty))
}
return locs
}
@ -301,13 +301,13 @@ func (x *expandState) Printf(format string, a ...interface{}) (n int, err error)
// It emits the code necessary to implement the leaf select operation that leads to the root.
//
// TODO when registers really arrive, must also decompose anything split across two registers or registers and memory.
func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, regOffset Abi1RO) []LocalSlot {
func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, regOffset Abi1RO) []*LocalSlot {
if x.debug {
x.indent(3)
defer x.indent(-3)
x.Printf("rewriteSelect(%s; %s; memOff=%d; regOff=%d)\n", leaf.LongString(), selector.LongString(), offset, regOffset)
}
var locs []LocalSlot
var locs []*LocalSlot
leafType := leaf.Type
if len(selector.Args) > 0 {
w := selector.Args[0]
@ -477,7 +477,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
case OpStructSelect:
w := selector.Args[0]
var ls []LocalSlot
var ls []*LocalSlot
if w.Type.Kind() != types.TSTRUCT { // IData artifact
ls = x.rewriteSelect(leaf, w, offset, regOffset)
} else {
@ -485,7 +485,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
ls = x.rewriteSelect(leaf, w, offset+w.Type.FieldOff(fldi), regOffset+x.regOffset(w.Type, fldi))
if w.Op != OpIData {
for _, l := range ls {
locs = append(locs, x.f.fe.SplitStruct(l, int(selector.AuxInt)))
locs = append(locs, x.f.SplitStruct(l, int(selector.AuxInt)))
}
}
}
@ -662,7 +662,7 @@ outer:
func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t *types.Type, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
pa := x.prAssignForArg(source)
var locs []LocalSlot
var locs []*LocalSlot
for _, s := range x.namedSelects[source] {
locs = append(locs, x.f.Names[s.locIndex])
}
@ -756,12 +756,15 @@ func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t
return nil
}
func (x *expandState) splitSlotsIntoNames(locs []LocalSlot, suffix string, off int64, rt *types.Type, w *Value) {
func (x *expandState) splitSlotsIntoNames(locs []*LocalSlot, suffix string, off int64, rt *types.Type, w *Value) {
wlocs := x.splitSlots(locs, suffix, off, rt)
for _, l := range wlocs {
x.f.NamedValues[l] = append(x.f.NamedValues[l], w)
old, ok := x.f.NamedValues[*l]
x.f.NamedValues[*l] = append(old, w)
if !ok {
x.f.Names = append(x.f.Names, l)
}
}
x.f.Names = append(x.f.Names, wlocs...)
}
// decomposeLoad is a helper for storeArgOrLoad.
@ -826,7 +829,7 @@ func (x *expandState) decomposeLoad(pos src.XPos, b *Block, source, mem *Value,
// storeOneArg creates a decomposed (one step) arg that is then stored.
// pos and b locate the store instruction, source is the "base" of the value input,
// mem is the input mem, t is the type in question, and offArg and offStore are the offsets from the respective bases.
func storeOneArg(x *expandState, pos src.XPos, b *Block, locs []LocalSlot, suffix string, source, mem *Value, t *types.Type, argOffset, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
func storeOneArg(x *expandState, pos src.XPos, b *Block, locs []*LocalSlot, suffix string, source, mem *Value, t *types.Type, argOffset, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
if x.debug {
x.indent(3)
defer x.indent(-3)
@ -848,7 +851,7 @@ func storeOneLoad(x *expandState, pos src.XPos, b *Block, source, mem *Value, t
return x.storeArgOrLoad(pos, b, w, mem, t, offStore, loadRegOffset, storeRc)
}
func storeTwoArg(x *expandState, pos src.XPos, b *Block, locs []LocalSlot, suffix1 string, suffix2 string, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
func storeTwoArg(x *expandState, pos src.XPos, b *Block, locs []*LocalSlot, suffix1 string, suffix2 string, source, mem *Value, t1, t2 *types.Type, offArg, offStore int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
mem = storeOneArg(x, pos, b, locs, suffix1, source, mem, t1, offArg, offStore, loadRegOffset, storeRc.next(t1))
pos = pos.WithNotStmt()
t1Size := t1.Size()
@ -1168,7 +1171,7 @@ func expandCalls(f *Func) {
for i, name := range f.Names {
t := name.Type
if x.isAlreadyExpandedAggregateType(t) {
for j, v := range f.NamedValues[name] {
for j, v := range f.NamedValues[*name] {
if v.Op == OpSelectN || v.Op == OpArg && x.isAlreadyExpandedAggregateType(v.Type) {
ns := x.namedSelects[v]
x.namedSelects[v] = append(ns, namedVal{locIndex: i, valIndex: j})
@ -1477,10 +1480,10 @@ func expandCalls(f *Func) {
// Leaf types may have debug locations
if !x.isAlreadyExpandedAggregateType(v.Type) {
for _, l := range locs {
if _, ok := f.NamedValues[l]; !ok {
if _, ok := f.NamedValues[*l]; !ok {
f.Names = append(f.Names, l)
}
f.NamedValues[l] = append(f.NamedValues[l], v)
f.NamedValues[*l] = append(f.NamedValues[*l], v)
}
continue
}
@ -1553,7 +1556,7 @@ func expandCalls(f *Func) {
// Step 6: elide any copies introduced.
// Update named values.
for _, name := range f.Names {
values := f.NamedValues[name]
values := f.NamedValues[*name]
for i, v := range values {
if v.Op == OpCopy {
a := v.Args[0]
@ -1714,21 +1717,6 @@ func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64,
} else {
w = baseArg.Block.NewValue0IA(pos, op, t, auxInt, aux)
}
// If we are creating an OpArgIntReg/OpArgFloatReg that
// corresponds to an in-param that fits entirely in a register,
// then enter it into the name/value table. The LocalSlot
// is somewhat fictitious, since there is no incoming live
// memory version of the parameter, but we need an entry in
// NamedValues in order for ssa debug tracking to include
// the value in the tracking analysis.
if len(pa.Registers) == 1 {
loc := LocalSlot{N: aux.Name, Type: t, Off: 0}
values, ok := x.f.NamedValues[loc]
if !ok {
x.f.Names = append(x.f.Names, loc)
}
x.f.NamedValues[loc] = append(values, w)
}
x.commonArgs[key] = w
if toReplace != nil {
toReplace.copyOf(w)

View File

@ -73,36 +73,6 @@ func (TestFrontend) Auto(pos src.XPos, t *types.Type) *ir.Name {
n.Class = ir.PAUTO
return n
}
func (d TestFrontend) SplitString(s LocalSlot) (LocalSlot, LocalSlot) {
return LocalSlot{N: s.N, Type: testTypes.BytePtr, Off: s.Off}, LocalSlot{N: s.N, Type: testTypes.Int, Off: s.Off + 8}
}
func (d TestFrontend) SplitInterface(s LocalSlot) (LocalSlot, LocalSlot) {
return LocalSlot{N: s.N, Type: testTypes.BytePtr, Off: s.Off}, LocalSlot{N: s.N, Type: testTypes.BytePtr, Off: s.Off + 8}
}
func (d TestFrontend) SplitSlice(s LocalSlot) (LocalSlot, LocalSlot, LocalSlot) {
return LocalSlot{N: s.N, Type: s.Type.Elem().PtrTo(), Off: s.Off},
LocalSlot{N: s.N, Type: testTypes.Int, Off: s.Off + 8},
LocalSlot{N: s.N, Type: testTypes.Int, Off: s.Off + 16}
}
func (d TestFrontend) SplitComplex(s LocalSlot) (LocalSlot, LocalSlot) {
if s.Type.Size() == 16 {
return LocalSlot{N: s.N, Type: testTypes.Float64, Off: s.Off}, LocalSlot{N: s.N, Type: testTypes.Float64, Off: s.Off + 8}
}
return LocalSlot{N: s.N, Type: testTypes.Float32, Off: s.Off}, LocalSlot{N: s.N, Type: testTypes.Float32, Off: s.Off + 4}
}
func (d TestFrontend) SplitInt64(s LocalSlot) (LocalSlot, LocalSlot) {
if s.Type.IsSigned() {
return LocalSlot{N: s.N, Type: testTypes.Int32, Off: s.Off + 4}, LocalSlot{N: s.N, Type: testTypes.UInt32, Off: s.Off}
}
return LocalSlot{N: s.N, Type: testTypes.UInt32, Off: s.Off + 4}, LocalSlot{N: s.N, Type: testTypes.UInt32, Off: s.Off}
}
func (d TestFrontend) SplitStruct(s LocalSlot, i int) LocalSlot {
return LocalSlot{N: s.N, Type: s.Type.FieldType(i), Off: s.Off + s.Type.FieldOff(i)}
}
func (d TestFrontend) SplitArray(s LocalSlot) LocalSlot {
return LocalSlot{N: s.N, Type: s.Type.Elem(), Off: s.Off}
}
func (d TestFrontend) SplitSlot(parent *LocalSlot, suffix string, offset int64, t *types.Type) LocalSlot {
return LocalSlot{N: parent.N, Type: t, Off: offset}
}

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build amd64
#include "textflag.h"
TEXT ·asmAddFlags(SB),NOSPLIT,$0-24

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build arm64
#include "textflag.h"
TEXT ·asmAddFlags(SB),NOSPLIT,$0-24

View File

@ -6,6 +6,7 @@ package ssa
import (
"cmd/compile/internal/abi"
"cmd/compile/internal/base"
"cmd/compile/internal/types"
"cmd/internal/src"
"crypto/sha1"
@ -61,7 +62,11 @@ type Func struct {
NamedValues map[LocalSlot][]*Value
// Names is a copy of NamedValues.Keys. We keep a separate list
// of keys to make iteration order deterministic.
Names []LocalSlot
Names []*LocalSlot
// Canonicalize root/top-level local slots, and canonicalize their pieces.
// Because LocalSlot pieces refer to their parents with a pointer, this ensures that equivalent slots really are equal.
CanonicalLocalSlots map[LocalSlot]*LocalSlot
CanonicalLocalSplits map[LocalSlotSplitKey]*LocalSlot
// RegArgs is a slice of register-memory pairs that must be spilled and unspilled in the uncommon path of function entry.
RegArgs []Spill
@ -87,10 +92,16 @@ type Func struct {
constants map[int64][]*Value // constants cache, keyed by constant value; users must check value's Op and Type
}
type LocalSlotSplitKey struct {
parent *LocalSlot
Off int64 // offset of slot in N
Type *types.Type // type of slot
}
// NewFunc returns a new, empty function object.
// Caller must set f.Config and f.Cache before using f.
func NewFunc(fe Frontend) *Func {
return &Func{fe: fe, NamedValues: make(map[LocalSlot][]*Value)}
return &Func{fe: fe, NamedValues: make(map[LocalSlot][]*Value), CanonicalLocalSlots: make(map[LocalSlot]*LocalSlot), CanonicalLocalSplits: make(map[LocalSlotSplitKey]*LocalSlot)}
}
// NumBlocks returns an integer larger than the id of any Block in the Func.
@ -193,6 +204,101 @@ func (f *Func) retDeadcodeLiveOrderStmts(liveOrderStmts []*Value) {
f.Cache.deadcode.liveOrderStmts = liveOrderStmts
}
func (f *Func) localSlotAddr(slot LocalSlot) *LocalSlot {
a, ok := f.CanonicalLocalSlots[slot]
if !ok {
a = new(LocalSlot)
*a = slot // don't escape slot
f.CanonicalLocalSlots[slot] = a
}
return a
}
func (f *Func) SplitString(name *LocalSlot) (*LocalSlot, *LocalSlot) {
ptrType := types.NewPtr(types.Types[types.TUINT8])
lenType := types.Types[types.TINT]
// Split this string up into two separate variables.
p := f.SplitSlot(name, ".ptr", 0, ptrType)
l := f.SplitSlot(name, ".len", ptrType.Size(), lenType)
return p, l
}
func (f *Func) SplitInterface(name *LocalSlot) (*LocalSlot, *LocalSlot) {
n := name.N
u := types.Types[types.TUINTPTR]
t := types.NewPtr(types.Types[types.TUINT8])
// Split this interface up into two separate variables.
sfx := ".itab"
if n.Type().IsEmptyInterface() {
sfx = ".type"
}
c := f.SplitSlot(name, sfx, 0, u) // see comment in typebits.Set
d := f.SplitSlot(name, ".data", u.Size(), t)
return c, d
}
func (f *Func) SplitSlice(name *LocalSlot) (*LocalSlot, *LocalSlot, *LocalSlot) {
ptrType := types.NewPtr(name.Type.Elem())
lenType := types.Types[types.TINT]
p := f.SplitSlot(name, ".ptr", 0, ptrType)
l := f.SplitSlot(name, ".len", ptrType.Size(), lenType)
c := f.SplitSlot(name, ".cap", ptrType.Size()+lenType.Size(), lenType)
return p, l, c
}
func (f *Func) SplitComplex(name *LocalSlot) (*LocalSlot, *LocalSlot) {
s := name.Type.Size() / 2
var t *types.Type
if s == 8 {
t = types.Types[types.TFLOAT64]
} else {
t = types.Types[types.TFLOAT32]
}
r := f.SplitSlot(name, ".real", 0, t)
i := f.SplitSlot(name, ".imag", t.Size(), t)
return r, i
}
func (f *Func) SplitInt64(name *LocalSlot) (*LocalSlot, *LocalSlot) {
var t *types.Type
if name.Type.IsSigned() {
t = types.Types[types.TINT32]
} else {
t = types.Types[types.TUINT32]
}
if f.Config.BigEndian {
return f.SplitSlot(name, ".hi", 0, t), f.SplitSlot(name, ".lo", t.Size(), types.Types[types.TUINT32])
}
return f.SplitSlot(name, ".hi", t.Size(), t), f.SplitSlot(name, ".lo", 0, types.Types[types.TUINT32])
}
func (f *Func) SplitStruct(name *LocalSlot, i int) *LocalSlot {
st := name.Type
return f.SplitSlot(name, st.FieldName(i), st.FieldOff(i), st.FieldType(i))
}
func (f *Func) SplitArray(name *LocalSlot) *LocalSlot {
n := name.N
at := name.Type
if at.NumElem() != 1 {
base.FatalfAt(n.Pos(), "bad array size")
}
et := at.Elem()
return f.SplitSlot(name, "[0]", 0, et)
}
func (f *Func) SplitSlot(name *LocalSlot, sfx string, offset int64, t *types.Type) *LocalSlot {
lssk := LocalSlotSplitKey{name, offset, t}
if als, ok := f.CanonicalLocalSplits[lssk]; ok {
return als
}
// Note: the _ field may appear several times. But
// have no fear, identically-named but distinct Autos are
// ok, albeit maybe confusing for a debugger.
ls := f.fe.SplitSlot(name, sfx, offset, t)
f.CanonicalLocalSplits[lssk] = &ls
return &ls
}
// newValue allocates a new Value with the given fields and places it at the end of b.Values.
func (f *Func) newValue(op Op, t *types.Type, b *Block, pos src.XPos) *Value {
var v *Value

View File

@ -2216,3 +2216,22 @@
(MOVOstore [dstOff] {dstSym} ptr (MOVOload [srcOff] {srcSym} (SB) _) mem) && symIsRO(srcSym) =>
(MOVQstore [dstOff+8] {dstSym} ptr (MOVQconst [int64(read64(srcSym, int64(srcOff)+8, config.ctxt.Arch.ByteOrder))])
(MOVQstore [dstOff] {dstSym} ptr (MOVQconst [int64(read64(srcSym, int64(srcOff), config.ctxt.Arch.ByteOrder))]) mem))
// Arch-specific inlining for small or disjoint runtime.memmove
// Match post-lowering calls, memory version.
(SelectN [0] call:(CALLstatic {sym} s1:(MOVQstoreconst _ [sc] s2:(MOVQstore _ src s3:(MOVQstore _ dst mem)))))
&& sc.Val64() >= 0
&& isSameCall(sym, "runtime.memmove")
&& s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
&& isInlinableMemmove(dst, src, sc.Val64(), config)
&& clobber(s1, s2, s3, call)
=> (Move [sc.Val64()] dst src mem)
// Match post-lowering calls, register version.
(SelectN [0] call:(CALLstatic {sym} dst src (MOVQconst [sz]) mem))
&& sz >= 0
&& isSameCall(sym, "runtime.memmove")
&& call.Uses == 1
&& isInlinableMemmove(dst, src, sz, config)
&& clobber(call)
=> (Move [sz] dst src mem)

View File

@ -2859,3 +2859,12 @@
(MOVHUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))])
(MOVWUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))])
(MOVDload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(read64(sym, int64(off), config.ctxt.Arch.ByteOrder))])
// Arch-specific inlining for small or disjoint runtime.memmove
(SelectN [0] call:(CALLstatic {sym} s1:(MOVDstore _ (MOVDconst [sz]) s2:(MOVDstore _ src s3:(MOVDstore {t} _ dst mem)))))
&& sz >= 0
&& isSameCall(sym, "runtime.memmove")
&& s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
&& isInlinableMemmove(dst, src, sz, config)
&& clobber(s1, s2, s3, call)
=> (Move [sz] dst src mem)

View File

@ -2065,7 +2065,7 @@
(SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const(64|32) [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
&& sz >= 0
&& isSameCall(sym, "runtime.memmove")
&& t.IsPtr() // avoids TUINTPTR, see issue 30061
&& t.IsPtr() // avoids TUNSAFEPTR, see issue 30061
&& s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
&& isInlinableMemmove(dst, src, int64(sz), config)
&& clobber(s1, s2, s3, call)
@ -2076,7 +2076,7 @@
&& sz >= 0
&& call.Uses == 1 // this will exclude all calls with results
&& isSameCall(sym, "runtime.memmove")
&& dst.Type.IsPtr() // avoids TUINTPTR, see issue 30061
&& dst.Type.IsPtr() // avoids TUNSAFEPTR, see issue 30061
&& isInlinableMemmove(dst, src, int64(sz), config)
&& clobber(call)
=> (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
@ -2086,7 +2086,7 @@
&& sz >= 0
&& call.Uses == 1 // this will exclude all calls with results
&& isSameCall(sym, "runtime.memmove")
&& dst.Type.IsPtr() // avoids TUINTPTR, see issue 30061
&& dst.Type.IsPtr() // avoids TUNSAFEPTR, see issue 30061
&& isInlinableMemmove(dst, src, int64(sz), config)
&& clobber(call)
=> (Move {dst.Type.Elem()} [int64(sz)] dst src mem)

View File

@ -12,26 +12,10 @@ func layout(f *Func) {
}
// Register allocation may use a different order which has constraints
// imposed by the linear-scan algorithm. Note that f.pass here is
// regalloc, so the switch is conditional on -d=ssa/regalloc/test=N
// imposed by the linear-scan algorithm.
func layoutRegallocOrder(f *Func) []*Block {
switch f.pass.test {
case 0: // layout order
return layoutOrder(f)
case 1: // existing block order
return f.Blocks
case 2: // reverse of postorder; legal, but usually not good.
po := f.postorder()
visitOrder := make([]*Block, len(po))
for i, b := range po {
j := len(po) - i - 1
visitOrder[j] = b
}
return visitOrder
}
return nil
// remnant of an experiment; perhaps there will be another.
return layoutOrder(f)
}
func layoutOrder(f *Func) []*Block {

View File

@ -154,6 +154,6 @@ func fprintFunc(p funcPrinter, f *Func) {
p.endBlock(b)
}
for _, name := range f.Names {
p.named(name, f.NamedValues[name])
p.named(*name, f.NamedValues[*name])
}
}

View File

@ -1882,6 +1882,10 @@ func (s *regAllocState) placeSpills() {
phiRegs[b.ID] = m
}
mustBeFirst := func(op Op) bool {
return op.isLoweredGetClosurePtr() || op == OpPhi || op == OpArgIntReg || op == OpArgFloatReg
}
// Start maps block IDs to the list of spills
// that go at the start of the block (but after any phis).
start := map[ID][]*Value{}
@ -1971,7 +1975,7 @@ func (s *regAllocState) placeSpills() {
// Put the spill in the best block we found.
spill.Block = best
spill.AddArg(bestArg)
if best == v.Block && v.Op != OpPhi {
if best == v.Block && !mustBeFirst(v.Op) {
// Place immediately after v.
after[v.ID] = append(after[v.ID], spill)
} else {
@ -1983,15 +1987,15 @@ func (s *regAllocState) placeSpills() {
// Insert spill instructions into the block schedules.
var oldSched []*Value
for _, b := range s.visitOrder {
nphi := 0
nfirst := 0
for _, v := range b.Values {
if v.Op != OpPhi {
if !mustBeFirst(v.Op) {
break
}
nphi++
nfirst++
}
oldSched = append(oldSched[:0], b.Values[nphi:]...)
b.Values = b.Values[:nphi]
oldSched = append(oldSched[:0], b.Values[nfirst:]...)
b.Values = b.Values[:nfirst]
b.Values = append(b.Values, start[b.ID]...)
for _, v := range oldSched {
b.Values = append(b.Values, v)

View File

@ -1038,6 +1038,8 @@ func rewriteValueAMD64(v *Value) bool {
return rewriteValueAMD64_OpSelect0(v)
case OpSelect1:
return rewriteValueAMD64_OpSelect1(v)
case OpSelectN:
return rewriteValueAMD64_OpSelectN(v)
case OpSignExt16to32:
v.Op = OpAMD64MOVWQSX
return true
@ -32981,6 +32983,78 @@ func rewriteValueAMD64_OpSelect1(v *Value) bool {
}
return false
}
func rewriteValueAMD64_OpSelectN(v *Value) bool {
v_0 := v.Args[0]
b := v.Block
config := b.Func.Config
// match: (SelectN [0] call:(CALLstatic {sym} s1:(MOVQstoreconst _ [sc] s2:(MOVQstore _ src s3:(MOVQstore _ dst mem)))))
// cond: sc.Val64() >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, sc.Val64(), config) && clobber(s1, s2, s3, call)
// result: (Move [sc.Val64()] dst src mem)
for {
if auxIntToInt64(v.AuxInt) != 0 {
break
}
call := v_0
if call.Op != OpAMD64CALLstatic || len(call.Args) != 1 {
break
}
sym := auxToCall(call.Aux)
s1 := call.Args[0]
if s1.Op != OpAMD64MOVQstoreconst {
break
}
sc := auxIntToValAndOff(s1.AuxInt)
_ = s1.Args[1]
s2 := s1.Args[1]
if s2.Op != OpAMD64MOVQstore {
break
}
_ = s2.Args[2]
src := s2.Args[1]
s3 := s2.Args[2]
if s3.Op != OpAMD64MOVQstore {
break
}
mem := s3.Args[2]
dst := s3.Args[1]
if !(sc.Val64() >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, sc.Val64(), config) && clobber(s1, s2, s3, call)) {
break
}
v.reset(OpMove)
v.AuxInt = int64ToAuxInt(sc.Val64())
v.AddArg3(dst, src, mem)
return true
}
// match: (SelectN [0] call:(CALLstatic {sym} dst src (MOVQconst [sz]) mem))
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && call.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(call)
// result: (Move [sz] dst src mem)
for {
if auxIntToInt64(v.AuxInt) != 0 {
break
}
call := v_0
if call.Op != OpAMD64CALLstatic || len(call.Args) != 4 {
break
}
sym := auxToCall(call.Aux)
mem := call.Args[3]
dst := call.Args[0]
src := call.Args[1]
call_2 := call.Args[2]
if call_2.Op != OpAMD64MOVQconst {
break
}
sz := auxIntToInt64(call_2.AuxInt)
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && call.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(call)) {
break
}
v.reset(OpMove)
v.AuxInt = int64ToAuxInt(sz)
v.AddArg3(dst, src, mem)
return true
}
return false
}
func rewriteValueAMD64_OpSlicemask(v *Value) bool {
v_0 := v.Args[0]
b := v.Block

View File

@ -984,6 +984,8 @@ func rewriteValueARM64(v *Value) bool {
return rewriteValueARM64_OpSelect0(v)
case OpSelect1:
return rewriteValueARM64_OpSelect1(v)
case OpSelectN:
return rewriteValueARM64_OpSelectN(v)
case OpSignExt16to32:
v.Op = OpARM64MOVHreg
return true
@ -25983,6 +25985,54 @@ func rewriteValueARM64_OpSelect1(v *Value) bool {
}
return false
}
func rewriteValueARM64_OpSelectN(v *Value) bool {
v_0 := v.Args[0]
b := v.Block
config := b.Func.Config
// match: (SelectN [0] call:(CALLstatic {sym} s1:(MOVDstore _ (MOVDconst [sz]) s2:(MOVDstore _ src s3:(MOVDstore {t} _ dst mem)))))
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(s1, s2, s3, call)
// result: (Move [sz] dst src mem)
for {
if auxIntToInt64(v.AuxInt) != 0 {
break
}
call := v_0
if call.Op != OpARM64CALLstatic {
break
}
sym := auxToCall(call.Aux)
s1 := call.Args[0]
if s1.Op != OpARM64MOVDstore {
break
}
_ = s1.Args[2]
s1_1 := s1.Args[1]
if s1_1.Op != OpARM64MOVDconst {
break
}
sz := auxIntToInt64(s1_1.AuxInt)
s2 := s1.Args[2]
if s2.Op != OpARM64MOVDstore {
break
}
_ = s2.Args[2]
src := s2.Args[1]
s3 := s2.Args[2]
if s3.Op != OpARM64MOVDstore {
break
}
mem := s3.Args[2]
dst := s3.Args[1]
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(s1, s2, s3, call)) {
break
}
v.reset(OpMove)
v.AuxInt = int64ToAuxInt(sz)
v.AddArg3(dst, src, mem)
return true
}
return false
}
func rewriteValueARM64_OpSlicemask(v *Value) bool {
v_0 := v.Args[0]
b := v.Block

View File

@ -141,10 +141,11 @@ func (s *stackAllocState) stackalloc() {
s.names = make([]LocalSlot, n)
}
names := s.names
empty := LocalSlot{}
for _, name := range f.Names {
// Note: not "range f.NamedValues" above, because
// that would be nondeterministic.
for _, v := range f.NamedValues[name] {
for _, v := range f.NamedValues[*name] {
if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
aux := v.Aux.(*AuxNameOffset)
// Never let an arg be bound to a differently named thing.
@ -162,10 +163,12 @@ func (s *stackAllocState) stackalloc() {
continue
}
if f.pass.debug > stackDebug {
fmt.Printf("stackalloc value %s to name %s\n", v, name)
if names[v.ID] == empty {
if f.pass.debug > stackDebug {
fmt.Printf("stackalloc value %s to name %s\n", v, *name)
}
names[v.ID] = *name
}
names[v.ID] = name
}
}

View File

@ -8,7 +8,6 @@ import (
"bufio"
"bytes"
"cmd/compile/internal/abi"
"encoding/binary"
"fmt"
"go/constant"
"html"
@ -1297,7 +1296,7 @@ func (s *state) instrumentFields(t *types.Type, addr *ssa.Value, kind instrument
if f.Sym.IsBlank() {
continue
}
offptr := s.newValue1I(ssa.OpOffPtr, types.NewPtr(f.Type), abi.FieldOffsetOf(f), addr)
offptr := s.newValue1I(ssa.OpOffPtr, types.NewPtr(f.Type), f.Offset, addr)
s.instrumentFields(f.Type, offptr, kind)
}
}
@ -3175,7 +3174,7 @@ func (s *state) expr(n ir.Node) *ssa.Value {
arrlen := s.constInt(types.Types[types.TINT], n.Type().Elem().NumElem())
cap := s.newValue1(ssa.OpSliceLen, types.Types[types.TINT], v)
s.boundsCheck(arrlen, cap, ssa.BoundsConvert, false)
return s.newValue1(ssa.OpSlicePtrUnchecked, types.Types[types.TINT], v)
return s.newValue1(ssa.OpSlicePtrUnchecked, n.Type(), v)
case ir.OCALLFUNC:
n := n.(*ir.CallExpr)
@ -5054,19 +5053,23 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
ft := fn.Type()
off := t.FieldOff(12) // TODO register args: be sure this isn't a hardcoded param stack offset.
args := n.Args
i0 := 0
// Set receiver (for interface calls). Always a pointer.
if rcvr != nil {
p := s.newValue1I(ssa.OpOffPtr, ft.Recv().Type.PtrTo(), off, addr)
s.store(types.Types[types.TUINTPTR], p, rcvr)
i0 = 1
}
// Set receiver (for method calls).
if n.Op() == ir.OCALLMETH {
base.Fatalf("OCALLMETH missed by walkCall")
}
// Set other args.
for _, f := range ft.Params().Fields().Slice() {
s.storeArgWithBase(args[0], f.Type, addr, off+abi.FieldOffsetOf(f))
// This code is only used when RegabiDefer is not enabled, and arguments are always
// passed on stack.
for i, f := range ft.Params().Fields().Slice() {
s.storeArgWithBase(args[0], f.Type, addr, off+params.InParam(i+i0).FrameOffset(params))
args = args[1:]
}
@ -5079,7 +5082,6 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
if stksize < int64(types.PtrSize) {
// We need room for both the call to deferprocStack and the call to
// the deferred function.
// TODO(register args) Revisit this if/when we pass args in registers.
stksize = int64(types.PtrSize)
}
call.AuxInt = stksize
@ -6463,7 +6465,8 @@ func (s *state) addNamedValue(n *ir.Name, v *ssa.Value) {
loc := ssa.LocalSlot{N: n, Type: n.Type(), Off: 0}
values, ok := s.f.NamedValues[loc]
if !ok {
s.f.Names = append(s.f.Names, loc)
s.f.Names = append(s.f.Names, &loc)
s.f.CanonicalLocalSlots[loc] = &loc
}
s.f.NamedValues[loc] = append(values, v)
}
@ -6598,6 +6601,7 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
x := base.Ctxt.Lookup(fmt.Sprintf("%s.arginfo%d", f.LSym.Name, f.ABI))
PtrSize := int64(types.PtrSize)
uintptrTyp := types.Types[types.TUINTPTR]
isAggregate := func(t *types.Type) bool {
return t.IsStruct() || t.IsArray() || t.IsComplex() || t.IsInterface() || t.IsString() || t.IsSlice()
@ -6641,12 +6645,8 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
n := 0
writebyte := func(o uint8) { wOff = objw.Uint8(x, wOff, o) }
// Write one non-aggrgate arg/field/element if there is room.
// Returns whether to continue.
write1 := func(sz, offset int64) bool {
if n >= limit {
return false
}
// Write one non-aggrgate arg/field/element.
write1 := func(sz, offset int64) {
if offset >= _special {
writebyte(_offsetTooLarge)
} else {
@ -6654,7 +6654,6 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
writebyte(uint8(sz))
}
n++
return true
}
// Visit t recursively and write it out.
@ -6662,10 +6661,12 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
var visitType func(baseOffset int64, t *types.Type, depth int) bool
visitType = func(baseOffset int64, t *types.Type, depth int) bool {
if n >= limit {
writebyte(_dotdotdot)
return false
}
if !isAggregate(t) {
return write1(t.Size(), baseOffset)
write1(t.Size(), baseOffset)
return true
}
writebyte(_startAgg)
depth++
@ -6675,58 +6676,47 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
n++
return true
}
var r bool
switch {
case t.IsInterface(), t.IsString():
r = write1(PtrSize, baseOffset) &&
write1(PtrSize, baseOffset+PtrSize)
_ = visitType(baseOffset, uintptrTyp, depth) &&
visitType(baseOffset+PtrSize, uintptrTyp, depth)
case t.IsSlice():
r = write1(PtrSize, baseOffset) &&
write1(PtrSize, baseOffset+PtrSize) &&
write1(PtrSize, baseOffset+PtrSize*2)
_ = visitType(baseOffset, uintptrTyp, depth) &&
visitType(baseOffset+PtrSize, uintptrTyp, depth) &&
visitType(baseOffset+PtrSize*2, uintptrTyp, depth)
case t.IsComplex():
r = write1(t.Size()/2, baseOffset) &&
write1(t.Size()/2, baseOffset+t.Size()/2)
_ = visitType(baseOffset, types.FloatForComplex(t), depth) &&
visitType(baseOffset+t.Size()/2, types.FloatForComplex(t), depth)
case t.IsArray():
r = true
if t.NumElem() == 0 {
n++ // {} counts as a component
break
}
for i := int64(0); i < t.NumElem(); i++ {
if !visitType(baseOffset, t.Elem(), depth) {
r = false
break
}
baseOffset += t.Elem().Size()
}
case t.IsStruct():
r = true
if t.NumFields() == 0 {
n++ // {} counts as a component
break
}
for _, field := range t.Fields().Slice() {
if !visitType(baseOffset+field.Offset, field.Type, depth) {
r = false
break
}
}
}
if !r {
writebyte(_dotdotdot)
}
writebyte(_endAgg)
return r
return true
}
c := true
for _, a := range abiInfo.InParams() {
if !c {
writebyte(_dotdotdot)
if !visitType(a.FrameOffset(abiInfo), a.Type, 0) {
break
}
c = visitType(a.FrameOffset(abiInfo), a.Type, 0)
}
writebyte(_endSeq)
if wOff > maxLen {
@ -7552,82 +7542,6 @@ func (e *ssafn) Auto(pos src.XPos, t *types.Type) *ir.Name {
return typecheck.TempAt(pos, e.curfn, t) // Note: adds new auto to e.curfn.Func.Dcl list
}
func (e *ssafn) SplitString(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
ptrType := types.NewPtr(types.Types[types.TUINT8])
lenType := types.Types[types.TINT]
// Split this string up into two separate variables.
p := e.SplitSlot(&name, ".ptr", 0, ptrType)
l := e.SplitSlot(&name, ".len", ptrType.Size(), lenType)
return p, l
}
func (e *ssafn) SplitInterface(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
n := name.N
u := types.Types[types.TUINTPTR]
t := types.NewPtr(types.Types[types.TUINT8])
// Split this interface up into two separate variables.
f := ".itab"
if n.Type().IsEmptyInterface() {
f = ".type"
}
c := e.SplitSlot(&name, f, 0, u) // see comment in typebits.Set
d := e.SplitSlot(&name, ".data", u.Size(), t)
return c, d
}
func (e *ssafn) SplitSlice(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot, ssa.LocalSlot) {
ptrType := types.NewPtr(name.Type.Elem())
lenType := types.Types[types.TINT]
p := e.SplitSlot(&name, ".ptr", 0, ptrType)
l := e.SplitSlot(&name, ".len", ptrType.Size(), lenType)
c := e.SplitSlot(&name, ".cap", ptrType.Size()+lenType.Size(), lenType)
return p, l, c
}
func (e *ssafn) SplitComplex(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
s := name.Type.Size() / 2
var t *types.Type
if s == 8 {
t = types.Types[types.TFLOAT64]
} else {
t = types.Types[types.TFLOAT32]
}
r := e.SplitSlot(&name, ".real", 0, t)
i := e.SplitSlot(&name, ".imag", t.Size(), t)
return r, i
}
func (e *ssafn) SplitInt64(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
var t *types.Type
if name.Type.IsSigned() {
t = types.Types[types.TINT32]
} else {
t = types.Types[types.TUINT32]
}
if Arch.LinkArch.ByteOrder == binary.BigEndian {
return e.SplitSlot(&name, ".hi", 0, t), e.SplitSlot(&name, ".lo", t.Size(), types.Types[types.TUINT32])
}
return e.SplitSlot(&name, ".hi", t.Size(), t), e.SplitSlot(&name, ".lo", 0, types.Types[types.TUINT32])
}
func (e *ssafn) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot {
st := name.Type
// Note: the _ field may appear several times. But
// have no fear, identically-named but distinct Autos are
// ok, albeit maybe confusing for a debugger.
return e.SplitSlot(&name, "."+st.FieldName(i), st.FieldOff(i), st.FieldType(i))
}
func (e *ssafn) SplitArray(name ssa.LocalSlot) ssa.LocalSlot {
n := name.N
at := name.Type
if at.NumElem() != 1 {
e.Fatalf(n.Pos(), "bad array size")
}
et := at.Elem()
return e.SplitSlot(&name, "[0]", 0, et)
}
func (e *ssafn) DerefItab(it *obj.LSym, offset int64) *obj.LSym {
return reflectdata.ITabSym(it, offset)
}

View File

@ -0,0 +1,96 @@
// 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 to make sure that equality functions (and hash
// functions) don't do unaligned reads on architectures
// that can't do unaligned reads. See issue 46283.
package test
import "testing"
type T1 struct {
x float32
a, b, c, d int16 // memequal64
}
type T2 struct {
x float32
a, b, c, d int32 // memequal128
}
type A2 [2]byte // eq uses a 2-byte load
type A4 [4]byte // eq uses a 4-byte load
type A8 [8]byte // eq uses an 8-byte load
//go:noinline
func cmpT1(p, q *T1) {
if *p != *q {
panic("comparison test wrong")
}
}
//go:noinline
func cmpT2(p, q *T2) {
if *p != *q {
panic("comparison test wrong")
}
}
//go:noinline
func cmpA2(p, q *A2) {
if *p != *q {
panic("comparison test wrong")
}
}
//go:noinline
func cmpA4(p, q *A4) {
if *p != *q {
panic("comparison test wrong")
}
}
//go:noinline
func cmpA8(p, q *A8) {
if *p != *q {
panic("comparison test wrong")
}
}
func TestAlignEqual(t *testing.T) {
cmpT1(&T1{}, &T1{})
cmpT2(&T2{}, &T2{})
m1 := map[T1]bool{}
m1[T1{}] = true
m1[T1{}] = false
if len(m1) != 1 {
t.Fatalf("len(m1)=%d, want 1", len(m1))
}
m2 := map[T2]bool{}
m2[T2{}] = true
m2[T2{}] = false
if len(m2) != 1 {
t.Fatalf("len(m2)=%d, want 1", len(m2))
}
type X2 struct {
y byte
z A2
}
var x2 X2
cmpA2(&x2.z, &A2{})
type X4 struct {
y byte
z A4
}
var x4 X4
cmpA4(&x4.z, &A4{})
type X8 struct {
y byte
z A8
}
var x8 X8
cmpA8(&x8.z, &A8{})
}

View File

@ -138,6 +138,7 @@ var runtimeDecls = [...]struct {
{"growslice", funcTag, 116},
{"unsafeslice", funcTag, 117},
{"unsafeslice64", funcTag, 118},
{"unsafeslicecheckptr", funcTag, 118},
{"memmove", funcTag, 119},
{"memclrNoHeapPointers", funcTag, 120},
{"memclrHasPointers", funcTag, 120},
@ -341,8 +342,8 @@ func runtimeTypes() []*types.Type {
typs[114] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7]))
typs[115] = types.NewSlice(typs[2])
typs[116] = newSig(params(typs[1], typs[115], typs[15]), params(typs[115]))
typs[117] = newSig(params(typs[1], typs[15]), nil)
typs[118] = newSig(params(typs[1], typs[22]), nil)
typs[117] = newSig(params(typs[1], typs[7], typs[15]), nil)
typs[118] = newSig(params(typs[1], typs[7], typs[22]), nil)
typs[119] = newSig(params(typs[3], typs[3], typs[5]), nil)
typs[120] = newSig(params(typs[7], typs[5]), nil)
typs[121] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6]))

View File

@ -183,8 +183,9 @@ func makeslice(typ *byte, len int, cap int) unsafe.Pointer
func makeslice64(typ *byte, len int64, cap int64) unsafe.Pointer
func makeslicecopy(typ *byte, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer
func growslice(typ *byte, old []any, cap int) (ary []any)
func unsafeslice(typ *byte, len int)
func unsafeslice64(typ *byte, len int64)
func unsafeslice(typ *byte, ptr unsafe.Pointer, len int)
func unsafeslice64(typ *byte, ptr unsafe.Pointer, len int64)
func unsafeslicecheckptr(typ *byte, ptr unsafe.Pointer, len int64)
func memmove(to *any, frm *any, length uintptr)
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)

View File

@ -633,6 +633,17 @@ func defaultlit2(l ir.Node, r ir.Node, force bool) (ir.Node, ir.Node) {
if l.Type() == nil || r.Type() == nil {
return l, r
}
if !l.Type().IsInterface() && !r.Type().IsInterface() {
// Can't mix bool with non-bool, string with non-string.
if l.Type().IsBoolean() != r.Type().IsBoolean() {
return l, r
}
if l.Type().IsString() != r.Type().IsString() {
return l, r
}
}
if !l.Type().IsUntyped() {
r = convlit(r, l.Type())
return l, r
@ -647,17 +658,10 @@ func defaultlit2(l ir.Node, r ir.Node, force bool) (ir.Node, ir.Node) {
return l, r
}
// Can't mix bool with non-bool, string with non-string, or nil with anything (untyped).
if l.Type().IsBoolean() != r.Type().IsBoolean() {
return l, r
}
if l.Type().IsString() != r.Type().IsString() {
return l, r
}
// Can't mix nil with anything untyped.
if ir.IsNil(l) || ir.IsNil(r) {
return l, r
}
t := defaultType(mixUntyped(l.Type(), r.Type()))
l = convlit(l, t)
r = convlit(r, t)

View File

@ -106,7 +106,17 @@ func Export(n *ir.Name) {
// Redeclared emits a diagnostic about symbol s being redeclared at pos.
func Redeclared(pos src.XPos, s *types.Sym, where string) {
if !s.Lastlineno.IsKnown() {
pkgName := DotImportRefs[s.Def.(*ir.Ident)]
var pkgName *ir.PkgName
if s.Def == nil {
for id, pkg := range DotImportRefs {
if id.Sym().Name == s.Name {
pkgName = pkg
break
}
}
} else {
pkgName = DotImportRefs[s.Def.(*ir.Ident)]
}
base.ErrorfAt(pos, "%v redeclared %s\n"+
"\t%v: previous declaration during import %q", s, where, base.FmtPos(pkgName.Pos()), pkgName.Pkg.Path)
} else {

View File

@ -981,6 +981,12 @@ func tcRecover(n *ir.CallExpr) ir.Node {
// tcUnsafeAdd typechecks an OUNSAFEADD node.
func tcUnsafeAdd(n *ir.BinaryExpr) *ir.BinaryExpr {
if !types.AllowsGoVersion(curpkg(), 1, 17) {
base.ErrorfVers("go1.17", "unsafe.Add")
n.SetType(nil)
return n
}
n.X = AssignConv(Expr(n.X), types.Types[types.TUNSAFEPTR], "argument to unsafe.Add")
n.Y = DefaultLit(Expr(n.Y), types.Types[types.TINT])
if n.X.Type() == nil || n.Y.Type() == nil {
@ -997,6 +1003,12 @@ func tcUnsafeAdd(n *ir.BinaryExpr) *ir.BinaryExpr {
// tcUnsafeSlice typechecks an OUNSAFESLICE node.
func tcUnsafeSlice(n *ir.BinaryExpr) *ir.BinaryExpr {
if !types.AllowsGoVersion(curpkg(), 1, 17) {
base.ErrorfVers("go1.17", "unsafe.Slice")
n.SetType(nil)
return n
}
n.X = Expr(n.X)
n.Y = Expr(n.Y)
if n.X.Type() == nil || n.Y.Type() == nil {
@ -1006,7 +1018,14 @@ func tcUnsafeSlice(n *ir.BinaryExpr) *ir.BinaryExpr {
t := n.X.Type()
if !t.IsPtr() {
base.Errorf("first argument to unsafe.Slice must be pointer; have %L", t)
} else if t.Elem().NotInHeap() {
// TODO(mdempsky): This can be relaxed, but should only affect the
// Go runtime itself. End users should only see //go:notinheap
// types due to incomplete C structs in cgo, and those types don't
// have a meaningful size anyway.
base.Errorf("unsafe.Slice of incomplete (or unallocatable) type not allowed")
}
if !checkunsafeslice(&n.Y) {
n.SetType(nil)
return n

View File

@ -1540,7 +1540,7 @@ func (r *importReader) exprsOrNil() (a, b ir.Node) {
func builtinCall(pos src.XPos, op ir.Op) *ir.CallExpr {
if go117ExportTypes {
// These should all be encoded as direct ops, not OCALL.
base.Fatalf("builtinCall should not be invoked when types are included in inport/export")
base.Fatalf("builtinCall should not be invoked when types are included in import/export")
}
return ir.NewCallExpr(pos, ir.OCALL, ir.NewIdent(base.Pos, types.BuiltinPkg.Lookup(ir.OpNames[op])), nil)
}

View File

@ -204,8 +204,20 @@ assignOK:
r.Use = ir.CallUseList
rtyp := r.Type()
mismatched := false
failed := false
for i := range lhs {
assignType(i, rtyp.Field(i).Type)
result := rtyp.Field(i).Type
assignType(i, result)
if lhs[i].Type() == nil || result == nil {
failed = true
} else if lhs[i] != ir.BlankNode && !types.Identical(lhs[i].Type(), result) {
mismatched = true
}
}
if mismatched && !failed {
rewriteMultiValueCall(stmt, r)
}
return
}

View File

@ -945,16 +945,18 @@ func typecheckargs(n ir.InitNode) {
return
}
// Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...).
// Save n as n.Orig for fmt.go.
if ir.Orig(n) == n {
n.(ir.OrigNode).SetOrig(ir.SepCopy(n))
}
as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
as.Rhs.Append(list...)
// Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...).
rewriteMultiValueCall(n, list[0])
}
// rewriteMultiValueCall rewrites multi-valued f() to use temporaries,
// so the backend wouldn't need to worry about tuple-valued expressions.
func rewriteMultiValueCall(n ir.InitNode, call ir.Node) {
// If we're outside of function context, then this call will
// be executed during the generated init function. However,
// init.go hasn't yet created it. Instead, associate the
@ -964,25 +966,40 @@ func typecheckargs(n ir.InitNode) {
if static {
ir.CurFunc = InitTodoFunc
}
list = nil
for _, f := range t.FieldSlice() {
t := Temp(f.Type)
as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, t))
as.Lhs.Append(t)
list = append(list, t)
as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, []ir.Node{call})
results := call.Type().FieldSlice()
list := make([]ir.Node, len(results))
for i, result := range results {
tmp := Temp(result.Type)
as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, tmp))
as.Lhs.Append(tmp)
list[i] = tmp
}
if static {
ir.CurFunc = nil
}
n.PtrInit().Append(Stmt(as))
switch n := n.(type) {
default:
base.Fatalf("rewriteMultiValueCall %+v", n.Op())
case *ir.CallExpr:
n.Args = list
case *ir.ReturnStmt:
n.Results = list
case *ir.AssignListStmt:
if n.Op() != ir.OAS2FUNC {
base.Fatalf("rewriteMultiValueCall: invalid op %v", n.Op())
}
as.SetOp(ir.OAS2FUNC)
n.SetOp(ir.OAS2)
n.Rhs = make([]ir.Node, len(list))
for i, tmp := range list {
n.Rhs[i] = AssignConv(tmp, n.Lhs[i].Type(), "assignment")
}
}
n.PtrInit().Append(Stmt(as))
}
func checksliceindex(l ir.Node, r ir.Node, tp *types.Type) bool {
@ -1443,15 +1460,22 @@ toomany:
}
func errorDetails(nl ir.Nodes, tstruct *types.Type, isddd bool) string {
// If we don't know any type at a call site, let's suppress any return
// message signatures. See Issue https://golang.org/issues/19012.
// Suppress any return message signatures if:
//
// (1) We don't know any type at a call site (see #19012).
// (2) Any node has an unknown type.
// (3) Invalid type for variadic parameter (see #46957).
if tstruct == nil {
return ""
return "" // case 1
}
// If any node has an unknown type, suppress it as well
if isddd && !nl[len(nl)-1].Type().IsSlice() {
return "" // case 3
}
for _, n := range nl {
if n.Type() == nil {
return ""
return "" // case 2
}
}
return fmt.Sprintf("\n\thave %s\n\twant %v", fmtSignature(nl, isddd), tstruct)

View File

@ -109,14 +109,18 @@ func sconv(s *Sym, verb rune, mode fmtMode) string {
return "<S>"
}
if s.Name == "_" {
return "_"
q := pkgqual(s.Pkg, verb, mode)
if q == "" {
return s.Name
}
buf := fmtBufferPool.Get().(*bytes.Buffer)
buf.Reset()
defer fmtBufferPool.Put(buf)
symfmt(buf, s, verb, mode)
buf.WriteString(q)
buf.WriteByte('.')
buf.WriteString(s.Name)
return InternString(buf.Bytes())
}
@ -128,56 +132,49 @@ func sconv2(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
b.WriteString("<S>")
return
}
if s.Name == "_" {
b.WriteString("_")
return
}
symfmt(b, s, verb, mode)
}
func symfmt(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
if q := pkgqual(s.Pkg, verb, mode); q != "" {
b.WriteString(q)
b.WriteByte('.')
}
b.WriteString(s.Name)
}
// pkgqual returns the qualifier that should be used for printing
// symbols from the given package in the given mode.
// If it returns the empty string, no qualification is needed.
func pkgqual(pkg *Pkg, verb rune, mode fmtMode) string {
if verb != 'S' {
switch mode {
case fmtGo: // This is for the user
if s.Pkg == BuiltinPkg || s.Pkg == LocalPkg {
b.WriteString(s.Name)
return
if pkg == BuiltinPkg || pkg == LocalPkg {
return ""
}
// If the name was used by multiple packages, display the full path,
if s.Pkg.Name != "" && NumImport[s.Pkg.Name] > 1 {
fmt.Fprintf(b, "%q.%s", s.Pkg.Path, s.Name)
return
if pkg.Name != "" && NumImport[pkg.Name] > 1 {
return strconv.Quote(pkg.Path)
}
b.WriteString(s.Pkg.Name)
b.WriteByte('.')
b.WriteString(s.Name)
return
return pkg.Name
case fmtDebug:
b.WriteString(s.Pkg.Name)
b.WriteByte('.')
b.WriteString(s.Name)
return
return pkg.Name
case fmtTypeIDName:
// dcommontype, typehash
b.WriteString(s.Pkg.Name)
b.WriteByte('.')
b.WriteString(s.Name)
return
return pkg.Name
case fmtTypeID:
// (methodsym), typesym, weaksym
b.WriteString(s.Pkg.Prefix)
b.WriteByte('.')
b.WriteString(s.Name)
return
return pkg.Prefix
}
}
b.WriteString(s.Name)
return ""
}
// Type

View File

@ -579,6 +579,11 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
case _Add:
// unsafe.Add(ptr unsafe.Pointer, len IntegerType) unsafe.Pointer
if !check.allowVersion(check.pkg, 1, 17) {
check.error(call.Fun, "unsafe.Add requires go1.17 or later")
return
}
check.assignment(x, Typ[UnsafePointer], "argument to unsafe.Add")
if x.mode == invalid {
return
@ -675,6 +680,11 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
case _Slice:
// unsafe.Slice(ptr *T, len IntegerType) []T
if !check.allowVersion(check.pkg, 1, 17) {
check.error(call.Fun, "unsafe.Slice requires go1.17 or later")
return
}
typ := asPointer(x.typ)
if typ == nil {
check.errorf(x, invalidArg+"%s is not a pointer", x)

View File

@ -489,7 +489,7 @@ func walkNew(n *ir.UnaryExpr, init *ir.Nodes) ir.Node {
base.Errorf("%v can't be allocated in Go; it is incomplete (or unallocatable)", n.Type().Elem())
}
if n.Esc() == ir.EscNone {
if t.Size() >= ir.MaxImplicitStackVarSize {
if t.Size() > ir.MaxImplicitStackVarSize {
base.Fatalf("large ONEW with EscNone: %v", n)
}
return stackTempAddr(init, t)
@ -654,36 +654,28 @@ func walkRecover(nn *ir.CallExpr, init *ir.Nodes) ir.Node {
}
func walkUnsafeSlice(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
ptr := safeExpr(n.X, init)
len := safeExpr(n.Y, init)
fnname := "unsafeslice64"
argtype := types.Types[types.TINT64]
lenType := types.Types[types.TINT64]
// Type checking guarantees that TIDEAL len/cap are positive and fit in an int.
// The case of len or cap overflow when converting TUINT or TUINTPTR to TINT
// will be handled by the negative range checks in unsafeslice during runtime.
if len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size() {
if ir.ShouldCheckPtr(ir.CurFunc, 1) {
fnname = "unsafeslicecheckptr"
// for simplicity, unsafeslicecheckptr always uses int64
} else if len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size() {
fnname = "unsafeslice"
argtype = types.Types[types.TINT]
lenType = types.Types[types.TINT]
}
t := n.Type()
// Call runtime.unsafeslice[64] to check that the length argument is
// non-negative and smaller than the max length allowed for the
// element type.
// Call runtime.unsafeslice{,64,checkptr} to check ptr and len.
fn := typecheck.LookupRuntime(fnname)
init.Append(mkcall1(fn, nil, init, reflectdata.TypePtr(t.Elem()), typecheck.Conv(len, argtype)))
ptr := walkExpr(n.X, init)
c := ir.NewUnaryExpr(n.Pos(), ir.OCHECKNIL, ptr)
c.SetTypecheck(1)
init.Append(c)
// TODO(mdempsky): checkptr instrumentation. Maybe merge into length
// check above, along with nil check? Need to be careful about
// notinheap pointers though: can't pass them as unsafe.Pointer.
init.Append(mkcall1(fn, nil, init, reflectdata.TypePtr(t.Elem()), typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]), typecheck.Conv(len, lenType)))
h := ir.NewSliceHeaderExpr(n.Pos(), t,
typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]),

View File

@ -313,7 +313,7 @@ func mayCall(n ir.Node) bool {
return true
case ir.OINDEX, ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR,
ir.ODEREF, ir.ODOTPTR, ir.ODOTTYPE, ir.ODIV, ir.OMOD:
ir.ODEREF, ir.ODOTPTR, ir.ODOTTYPE, ir.ODIV, ir.OMOD, ir.OSLICE2ARRPTR:
// These ops might panic, make sure they are done
// before we start marshaling args for a call. See issue 16760.
return true

16
src/cmd/dist/build.go vendored
View File

@ -1607,6 +1607,18 @@ var incomplete = map[string]bool{
"linux/sparc64": true,
}
// List of platforms which are first class ports. See golang.org/issue/38874.
var firstClass = map[string]bool{
"darwin/amd64": true,
"darwin/arm64": true,
"linux/386": true,
"linux/amd64": true,
"linux/arm": true,
"linux/arm64": true,
"windows/386": true,
"windows/amd64": true,
}
func needCC() bool {
switch os.Getenv("CGO_ENABLED") {
case "1":
@ -1743,6 +1755,7 @@ func cmdlist() {
GOOS string
GOARCH string
CgoSupported bool
FirstClass bool
}
var results []jsonResult
for _, p := range plats {
@ -1750,7 +1763,8 @@ func cmdlist() {
results = append(results, jsonResult{
GOOS: fields[0],
GOARCH: fields[1],
CgoSupported: cgoEnabled[p]})
CgoSupported: cgoEnabled[p],
FirstClass: firstClass[p]})
}
out, err := json.MarshalIndent(results, "", "\t")
if err != nil {

33
src/cmd/dist/test.go vendored
View File

@ -722,14 +722,29 @@ func (t *tester) registerTests() {
},
})
if t.hasCxx() {
t.tests = append(t.tests, distTest{
name: "swig_callback",
heading: "../misc/swig/callback",
fn: func(dt *distTest) error {
t.addCmd(dt, "misc/swig/callback", t.goTest())
return nil
t.tests = append(t.tests,
distTest{
name: "swig_callback",
heading: "../misc/swig/callback",
fn: func(dt *distTest) error {
t.addCmd(dt, "misc/swig/callback", t.goTest())
return nil
},
},
})
distTest{
name: "swig_callback_lto",
heading: "../misc/swig/callback",
fn: func(dt *distTest) error {
cmd := t.addCmd(dt, "misc/swig/callback", t.goTest())
cmd.Env = append(os.Environ(),
"CGO_CFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option",
"CGO_CXXFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option",
"CGO_LDFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option",
)
return nil
},
},
)
}
}
}
@ -766,7 +781,7 @@ func (t *tester) registerTests() {
t.registerTest("testasan", "../misc/cgo/testasan", "go", "run", ".")
}
if goos == "linux" && goarch != "ppc64le" {
// because syscall.SysProcAttri struct used in misc/cgo/testsanitizers is only built on linux.
// because syscall.SysProcAttr struct used in misc/cgo/testsanitizers is only built on linux.
// Some inconsistent failures happen on ppc64le so disable for now.
t.registerHostTest("testsanitizers", "../misc/cgo/testsanitizers", "misc/cgo/testsanitizers", ".")
}
@ -1042,7 +1057,7 @@ func (t *tester) supportedBuildmode(mode string) bool {
"darwin-amd64", "darwin-arm64",
"freebsd-amd64",
"android-arm", "android-arm64", "android-386",
"windows-amd64", "windows-386":
"windows-amd64", "windows-386", "windows-arm64":
return true
}
return false

View File

@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build gc,arm
//go:build gc
// +build gc
#include "textflag.h"

View File

@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !arm,gc
//go:build gc && !arm
// +build gc,!arm
#include "textflag.h"

View File

@ -3,13 +3,13 @@ module cmd
go 1.17
require (
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5
github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 // indirect
golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e
golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e // indirect
golang.org/x/mod v0.4.3-0.20210504181020-67f1c1edc27a
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 // indirect
golang.org/x/mod v0.4.3-0.20210608190319-0f08993efd8a
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 // indirect
golang.org/x/term v0.0.0-20210503060354-a79de5458b56
golang.org/x/tools v0.1.1-0.20210505014545-7cab0ef2e9a5
golang.org/x/tools v0.1.2-0.20210519160823-49064d2332f9
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
)

View File

@ -1,22 +1,45 @@
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5 h1:zIaiqGYDQwa4HVx5wGRTXbx38Pqxjemn4BP98wpzpXo=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a h1:jmAp/2PZAScNd62lTD3Mcb0Ey9FvIIJtLohPhtxZJ+Q=
github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e h1:pv3V0NlNSh5Q6AX/StwGLBjcLS7UN4m4Gq+V+uSecqM=
golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e h1:8foAy0aoO5GkqCvAEJ4VC4P3zksTg4X4aJCDpZzmgQI=
golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/mod v0.4.3-0.20210504181020-67f1c1edc27a h1:wbpC/7Wbo5WFVox32n+KjhRRLmTLq8YW/wRlL2iVAhk=
golang.org/x/mod v0.4.3-0.20210504181020-67f1c1edc27a/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.3-0.20210608190319-0f08993efd8a h1:e8qnjKz4EE6OjRki9wTadWSIogINvq10sMcuBRORxMY=
golang.org/x/mod v0.4.3-0.20210608190319-0f08993efd8a/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 h1:cdsMqa2nXzqlgs183pHxtvoVwU7CyzaCTAUOg94af4c=
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 h1:yhBbb4IRs2HS9PPlAg6DMC6mUOKexJBNsLf4Z+6En1Q=
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
golang.org/x/tools v0.1.1-0.20210505014545-7cab0ef2e9a5 h1:ImcI7RFHWLu2QWpFDXaReu0j+sQAHIy65vUFZImXiqY=
golang.org/x/tools v0.1.1-0.20210505014545-7cab0ef2e9a5/go.mod h1:sH/Eidr0EddymY8HZSakBo32zU3fG5ovDq874hJLjVg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.2-0.20210519160823-49064d2332f9 h1:2XlR/j4I4xz5GQZI7zBjqTfezYyRIE2jD5IMousB2rg=
golang.org/x/tools v0.1.2-0.20210519160823-49064d2332f9/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View File

@ -174,8 +174,8 @@
// a build will run as if the disk file path exists with the contents
// given by the backing file paths, or as if the disk file path does not
// exist if its backing file path is empty. Support for the -overlay flag
// has some limitations:importantly, cgo files included from outside the
// include path must be in the same directory as the Go package they are
// has some limitations: importantly, cgo files included from outside the
// include path must be in the same directory as the Go package they are
// included from, and overlays will not appear when binaries and tests are
// run through go run and go test respectively.
// -pkgdir dir
@ -293,7 +293,7 @@
//
// Usage:
//
// go doc [-u] [-c] [package|[package.]symbol[.methodOrField]]
// go doc [doc flags] [package|[package.]symbol[.methodOrField]]
//
// Doc prints the documentation comments associated with the item identified by its
// arguments (a package, const, func, type, var, method, or struct field)
@ -1078,7 +1078,7 @@
//
// Usage:
//
// go mod edit [editing flags] [go.mod]
// go mod edit [editing flags] [-fmt|-print|-json] [go.mod]
//
// Edit provides a command-line interface for editing go.mod,
// for use primarily by tools or scripts. It reads only go.mod;
@ -1186,13 +1186,17 @@
//
// Usage:
//
// go mod graph
// go mod graph [-go=version]
//
// Graph prints the module requirement graph (with replacements applied)
// in text form. Each line in the output has two space-separated fields: a module
// and one of its requirements. Each module is identified as a string of the form
// path@version, except for the main module, which has no @version suffix.
//
// The -go flag causes graph to report the module graph as loaded by the
// given Go version, instead of the version indicated by the 'go' directive
// in the go.mod file.
//
// See https://golang.org/ref/mod#go-mod-graph for more about 'go mod graph'.
//
//
@ -1200,7 +1204,7 @@
//
// Usage:
//
// go mod init [module]
// go mod init [module-path]
//
// Init initializes and writes a new go.mod file in the current directory, in
// effect creating a new module rooted at the current directory. The go.mod file
@ -1221,7 +1225,7 @@
//
// Usage:
//
// go mod tidy [-e] [-v] [-go=version]
// go mod tidy [-e] [-v] [-go=version] [-compat=version]
//
// Tidy makes sure go.mod matches the source code in the module.
// It adds any missing modules necessary to build the current module's
@ -1241,6 +1245,14 @@
// (Go versions 1.17 and higher retain more requirements in order to
// support lazy module loading.)
//
// The -compat flag preserves any additional checksums needed for the
// 'go' command from the indicated major Go release to successfully load
// the module graph, and causes tidy to error out if that version of the
// 'go' command would load any imported package from a different module
// version. By default, tidy acts as if the -compat flag were set to the
// version prior to the one indicated by the 'go' directive in the go.mod
// file.
//
// See https://golang.org/ref/mod#go-mod-tidy for more about 'go mod tidy'.
//
//
@ -1560,7 +1572,7 @@
//
// A build constraint, also known as a build tag, is a line comment that begins
//
// // +build
// //go:build
//
// that lists the conditions under which a file should be included in the package.
// Constraints may appear in any kind of source file (not just Go), but
@ -1568,30 +1580,20 @@
// only by blank lines and other line comments. These rules mean that in Go
// files a build constraint must appear before the package clause.
//
// To distinguish build constraints from package documentation, a series of
// build constraints must be followed by a blank line.
// To distinguish build constraints from package documentation,
// a build constraint should be followed by a blank line.
//
// A build constraint is evaluated as the OR of space-separated options.
// Each option evaluates as the AND of its comma-separated terms.
// Each term consists of letters, digits, underscores, and dots.
// A term may be negated with a preceding !.
// For example, the build constraint:
// A build constraint is evaluated as an expression containing options
// combined by ||, &&, and ! operators and parentheses. Operators have
// the same meaning as in Go.
//
// // +build linux,386 darwin,!cgo
// For example, the following build constraint constrains a file to
// build when the "linux" and "386" constraints are satisfied, or when
// "darwin" is satisfied and "cgo" is not:
//
// corresponds to the boolean formula:
// //go:build (linux && 386) || (darwin && !cgo)
//
// (linux AND 386) OR (darwin AND (NOT cgo))
//
// A file may have multiple build constraints. The overall constraint is the AND
// of the individual constraints. That is, the build constraints:
//
// // +build linux darwin
// // +build amd64
//
// corresponds to the boolean formula:
//
// (linux OR darwin) AND amd64
// It is an error for a file to have more than one //go:build line.
//
// During a particular build, the following words are satisfied:
//
@ -1629,24 +1631,28 @@
//
// To keep a file from being considered for the build:
//
// // +build ignore
// //go:build ignore
//
// (any other unsatisfied word will work as well, but "ignore" is conventional.)
//
// To build a file only when using cgo, and only on Linux and OS X:
//
// // +build linux,cgo darwin,cgo
// //go:build cgo && (linux || darwin)
//
// Such a file is usually paired with another file implementing the
// default functionality for other systems, which in this case would
// carry the constraint:
//
// // +build !linux,!darwin !cgo
// //go:build !(cgo && (linux || darwin))
//
// Naming a file dns_windows.go will cause it to be included only when
// building the package for Windows; similarly, math_386.s will be included
// only when building the package for 32-bit x86.
//
// Go versions 1.16 and earlier used a different syntax for build constraints,
// with a "// +build" prefix. The gofmt command will add an equivalent //go:build
// constraint when encountering the older syntax.
//
//
// Build modes
//
@ -1885,6 +1891,9 @@
// GOMIPS64
// For GOARCH=mips64{,le}, whether to use floating point instructions.
// Valid values are hardfloat (default), softfloat.
// GOPPC64
// For GOARCH=ppc64{,le}, the target ISA (Instruction Set Architecture).
// Valid values are power8 (default), power9.
// GOWASM
// For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use.
// Valid values are satconv, signext.
@ -1894,6 +1903,12 @@
// GCCGOTOOLDIR
// If set, where to find gccgo tools, such as cgo.
// The default is based on how gccgo was configured.
// GOEXPERIMENT
// Comma-separated list of toolchain experiments to enable or disable.
// The list of available experiments may change arbitrarily over time.
// See src/internal/goexperiment/flags.go for currently valid values.
// Warning: This variable is provided for the development and testing
// of the Go toolchain itself. Use beyond that purpose is unsupported.
// GOROOT_FINAL
// The root of the installed Go tree, when it is
// installed in a location other than where it is built.

View File

@ -72,7 +72,6 @@ func tooSlow(t *testing.T) {
// (temp) directory.
var testGOROOT string
var testCC string
var testGOCACHE string
var testGo string
@ -179,13 +178,6 @@ func TestMain(m *testing.M) {
os.Exit(2)
}
out, err = exec.Command(gotool, "env", "CC").CombinedOutput()
if err != nil {
fmt.Fprintf(os.Stderr, "could not find testing CC: %v\n%s", err, out)
os.Exit(2)
}
testCC = strings.TrimSpace(string(out))
cmd := exec.Command(testGo, "env", "CGO_ENABLED")
cmd.Stderr = new(strings.Builder)
if out, err := cmd.Output(); err != nil {
@ -2193,7 +2185,7 @@ func testBuildmodePIE(t *testing.T, useCgo, setBuildmodeToPIE bool) {
// See https://sourceware.org/bugzilla/show_bug.cgi?id=19011
section := f.Section(".edata")
if section == nil {
t.Fatalf(".edata section is not present")
t.Skip(".edata section is not present")
}
// TODO: deduplicate this struct from cmd/link/internal/ld/pe.go
type IMAGE_EXPORT_DIRECTORY struct {
@ -2864,3 +2856,35 @@ func TestExecInDeletedDir(t *testing.T) {
// `go version` should not fail
tg.run("version")
}
// A missing C compiler should not force the net package to be stale.
// Issue 47215.
func TestMissingCC(t *testing.T) {
if !canCgo {
t.Skip("test is only meaningful on systems with cgo")
}
cc := os.Getenv("CC")
if cc == "" {
cc = "gcc"
}
if filepath.IsAbs(cc) {
t.Skipf(`"CC" (%s) is an absolute path`, cc)
}
_, err := exec.LookPath(cc)
if err != nil {
t.Skipf(`"CC" (%s) not on PATH`, cc)
}
tg := testgo(t)
defer tg.cleanup()
netStale, _ := tg.isStale("net")
if netStale {
t.Skip(`skipping test because "net" package is currently stale`)
}
tg.setenv("PATH", "") // No C compiler on PATH.
netStale, _ = tg.isStale("net")
if netStale {
t.Error(`clearing "PATH" causes "net" to be stale`)
}
}

View File

@ -77,6 +77,14 @@ func defaultContext() build.Context {
ctxt.GOOS = envOr("GOOS", ctxt.GOOS)
ctxt.GOARCH = envOr("GOARCH", ctxt.GOARCH)
// The experiments flags are based on GOARCH, so they may
// need to change. TODO: This should be cleaned up.
buildcfg.UpdateExperiments(ctxt.GOOS, ctxt.GOARCH, envOr("GOEXPERIMENT", buildcfg.DefaultGOEXPERIMENT))
ctxt.ToolTags = nil
for _, exp := range buildcfg.EnabledExperiments() {
ctxt.ToolTags = append(ctxt.ToolTags, "goexperiment."+exp)
}
// The go/build rule for whether cgo is enabled is:
// 1. If $CGO_ENABLED is set, respect it.
// 2. Otherwise, if this is a cross-compile, disable cgo.

View File

@ -13,7 +13,7 @@ import (
var CmdDoc = &base.Command{
Run: runDoc,
UsageLine: "go doc [-u] [-c] [package|[package.]symbol[.methodOrField]]",
UsageLine: "go doc [doc flags] [package|[package.]symbol[.methodOrField]]",
CustomFlags: true,
Short: "show documentation for package or symbol",
Long: `

View File

@ -10,6 +10,7 @@ import (
"encoding/json"
"fmt"
"go/build"
"internal/buildcfg"
"io"
"os"
"path/filepath"
@ -72,6 +73,7 @@ func MkEnv() []cfg.EnvVar {
{Name: "GOCACHE", Value: cache.DefaultDir()},
{Name: "GOENV", Value: envFile},
{Name: "GOEXE", Value: cfg.ExeSuffix},
{Name: "GOEXPERIMENT", Value: buildcfg.GOEXPERIMENT()},
{Name: "GOFLAGS", Value: cfg.Getenv("GOFLAGS")},
{Name: "GOHOSTARCH", Value: runtime.GOARCH},
{Name: "GOHOSTOS", Value: runtime.GOOS},
@ -197,6 +199,21 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
if *envU && *envW {
base.Fatalf("go env: cannot use -u with -w")
}
// Handle 'go env -w' and 'go env -u' before calling buildcfg.Check,
// so they can be used to recover from an invalid configuration.
if *envW {
runEnvW(args)
return
}
if *envU {
runEnvU(args)
return
}
buildcfg.Check()
env := cfg.CmdEnv
env = append(env, ExtraEnvVars()...)
@ -206,14 +223,7 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
// Do we need to call ExtraEnvVarsCostly, which is a bit expensive?
needCostly := false
if *envU || *envW {
// We're overwriting or removing default settings,
// so it doesn't really matter what the existing settings are.
//
// Moreover, we haven't validated the new settings yet, so it is
// important that we NOT perform any actions based on them,
// such as initializing the builder to compute other variables.
} else if len(args) == 0 {
if len(args) == 0 {
// We're listing all environment variables ("go env"),
// including the expensive ones.
needCostly = true
@ -238,95 +248,6 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
env = append(env, ExtraEnvVarsCostly()...)
}
if *envW {
// Process and sanity-check command line.
if len(args) == 0 {
base.Fatalf("go env -w: no KEY=VALUE arguments given")
}
osEnv := make(map[string]string)
for _, e := range cfg.OrigEnv {
if i := strings.Index(e, "="); i >= 0 {
osEnv[e[:i]] = e[i+1:]
}
}
add := make(map[string]string)
for _, arg := range args {
i := strings.Index(arg, "=")
if i < 0 {
base.Fatalf("go env -w: arguments must be KEY=VALUE: invalid argument: %s", arg)
}
key, val := arg[:i], arg[i+1:]
if err := checkEnvWrite(key, val); err != nil {
base.Fatalf("go env -w: %v", err)
}
if _, ok := add[key]; ok {
base.Fatalf("go env -w: multiple values for key: %s", key)
}
add[key] = val
if osVal := osEnv[key]; osVal != "" && osVal != val {
fmt.Fprintf(os.Stderr, "warning: go env -w %s=... does not override conflicting OS environment variable\n", key)
}
}
goos, okGOOS := add["GOOS"]
goarch, okGOARCH := add["GOARCH"]
if okGOOS || okGOARCH {
if !okGOOS {
goos = cfg.Goos
}
if !okGOARCH {
goarch = cfg.Goarch
}
if err := work.CheckGOOSARCHPair(goos, goarch); err != nil {
base.Fatalf("go env -w: %v", err)
}
}
gotmp, okGOTMP := add["GOTMPDIR"]
if okGOTMP {
if !filepath.IsAbs(gotmp) && gotmp != "" {
base.Fatalf("go env -w: GOTMPDIR must be an absolute path")
}
}
updateEnvFile(add, nil)
return
}
if *envU {
// Process and sanity-check command line.
if len(args) == 0 {
base.Fatalf("go env -u: no arguments given")
}
del := make(map[string]bool)
for _, arg := range args {
if err := checkEnvWrite(arg, ""); err != nil {
base.Fatalf("go env -u: %v", err)
}
del[arg] = true
}
if del["GOOS"] || del["GOARCH"] {
goos, goarch := cfg.Goos, cfg.Goarch
if del["GOOS"] {
goos = getOrigEnv("GOOS")
if goos == "" {
goos = build.Default.GOOS
}
}
if del["GOARCH"] {
goarch = getOrigEnv("GOARCH")
if goarch == "" {
goarch = build.Default.GOARCH
}
}
if err := work.CheckGOOSARCHPair(goos, goarch); err != nil {
base.Fatalf("go env -u: %v", err)
}
}
updateEnvFile(nil, del)
return
}
if len(args) > 0 {
if *envJson {
var es []cfg.EnvVar
@ -351,6 +272,109 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
PrintEnv(os.Stdout, env)
}
func runEnvW(args []string) {
// Process and sanity-check command line.
if len(args) == 0 {
base.Fatalf("go env -w: no KEY=VALUE arguments given")
}
osEnv := make(map[string]string)
for _, e := range cfg.OrigEnv {
if i := strings.Index(e, "="); i >= 0 {
osEnv[e[:i]] = e[i+1:]
}
}
add := make(map[string]string)
for _, arg := range args {
i := strings.Index(arg, "=")
if i < 0 {
base.Fatalf("go env -w: arguments must be KEY=VALUE: invalid argument: %s", arg)
}
key, val := arg[:i], arg[i+1:]
if err := checkEnvWrite(key, val); err != nil {
base.Fatalf("go env -w: %v", err)
}
if _, ok := add[key]; ok {
base.Fatalf("go env -w: multiple values for key: %s", key)
}
add[key] = val
if osVal := osEnv[key]; osVal != "" && osVal != val {
fmt.Fprintf(os.Stderr, "warning: go env -w %s=... does not override conflicting OS environment variable\n", key)
}
}
if err := checkBuildConfig(add, nil); err != nil {
base.Fatalf("go env -w: %v", err)
}
gotmp, okGOTMP := add["GOTMPDIR"]
if okGOTMP {
if !filepath.IsAbs(gotmp) && gotmp != "" {
base.Fatalf("go env -w: GOTMPDIR must be an absolute path")
}
}
updateEnvFile(add, nil)
}
func runEnvU(args []string) {
// Process and sanity-check command line.
if len(args) == 0 {
base.Fatalf("go env -u: no arguments given")
}
del := make(map[string]bool)
for _, arg := range args {
if err := checkEnvWrite(arg, ""); err != nil {
base.Fatalf("go env -u: %v", err)
}
del[arg] = true
}
if err := checkBuildConfig(nil, del); err != nil {
base.Fatalf("go env -u: %v", err)
}
updateEnvFile(nil, del)
}
// checkBuildConfig checks whether the build configuration is valid
// after the specified configuration environment changes are applied.
func checkBuildConfig(add map[string]string, del map[string]bool) error {
// get returns the value for key after applying add and del and
// reports whether it changed. cur should be the current value
// (i.e., before applying changes) and def should be the default
// value (i.e., when no environment variables are provided at all).
get := func(key, cur, def string) (string, bool) {
if val, ok := add[key]; ok {
return val, true
}
if del[key] {
val := getOrigEnv(key)
if val == "" {
val = def
}
return val, true
}
return cur, false
}
goos, okGOOS := get("GOOS", cfg.Goos, build.Default.GOOS)
goarch, okGOARCH := get("GOARCH", cfg.Goarch, build.Default.GOARCH)
if okGOOS || okGOARCH {
if err := work.CheckGOOSARCHPair(goos, goarch); err != nil {
return err
}
}
goexperiment, okGOEXPERIMENT := get("GOEXPERIMENT", buildcfg.GOEXPERIMENT(), "")
if okGOEXPERIMENT {
if _, _, err := buildcfg.ParseGOEXPERIMENT(goos, goarch, goexperiment); err != nil {
return err
}
}
return nil
}
// PrintEnv prints the environment variables to w.
func PrintEnv(w io.Writer, env []cfg.EnvVar) {
for _, e := range env {

View File

@ -598,6 +598,9 @@ Architecture-specific environment variables:
GOMIPS64
For GOARCH=mips64{,le}, whether to use floating point instructions.
Valid values are hardfloat (default), softfloat.
GOPPC64
For GOARCH=ppc64{,le}, the target ISA (Instruction Set Architecture).
Valid values are power8 (default), power9.
GOWASM
For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use.
Valid values are satconv, signext.
@ -607,6 +610,12 @@ Special-purpose environment variables:
GCCGOTOOLDIR
If set, where to find gccgo tools, such as cgo.
The default is based on how gccgo was configured.
GOEXPERIMENT
Comma-separated list of toolchain experiments to enable or disable.
The list of available experiments may change arbitrarily over time.
See src/internal/goexperiment/flags.go for currently valid values.
Warning: This variable is provided for the development and testing
of the Go toolchain itself. Use beyond that purpose is unsupported.
GOROOT_FINAL
The root of the installed Go tree, when it is
installed in a location other than where it is built.
@ -784,7 +793,7 @@ var HelpBuildConstraint = &base.Command{
Long: `
A build constraint, also known as a build tag, is a line comment that begins
// +build
//go:build
that lists the conditions under which a file should be included in the package.
Constraints may appear in any kind of source file (not just Go), but
@ -792,30 +801,20 @@ they must appear near the top of the file, preceded
only by blank lines and other line comments. These rules mean that in Go
files a build constraint must appear before the package clause.
To distinguish build constraints from package documentation, a series of
build constraints must be followed by a blank line.
To distinguish build constraints from package documentation,
a build constraint should be followed by a blank line.
A build constraint is evaluated as the OR of space-separated options.
Each option evaluates as the AND of its comma-separated terms.
Each term consists of letters, digits, underscores, and dots.
A term may be negated with a preceding !.
For example, the build constraint:
A build constraint is evaluated as an expression containing options
combined by ||, &&, and ! operators and parentheses. Operators have
the same meaning as in Go.
// +build linux,386 darwin,!cgo
For example, the following build constraint constrains a file to
build when the "linux" and "386" constraints are satisfied, or when
"darwin" is satisfied and "cgo" is not:
corresponds to the boolean formula:
//go:build (linux && 386) || (darwin && !cgo)
(linux AND 386) OR (darwin AND (NOT cgo))
A file may have multiple build constraints. The overall constraint is the AND
of the individual constraints. That is, the build constraints:
// +build linux darwin
// +build amd64
corresponds to the boolean formula:
(linux OR darwin) AND amd64
It is an error for a file to have more than one //go:build line.
During a particular build, the following words are satisfied:
@ -853,22 +852,26 @@ in addition to ios tags and files.
To keep a file from being considered for the build:
// +build ignore
//go:build ignore
(any other unsatisfied word will work as well, but "ignore" is conventional.)
To build a file only when using cgo, and only on Linux and OS X:
// +build linux,cgo darwin,cgo
//go:build cgo && (linux || darwin)
Such a file is usually paired with another file implementing the
default functionality for other systems, which in this case would
carry the constraint:
// +build !linux,!darwin !cgo
//go:build !(cgo && (linux || darwin))
Naming a file dns_windows.go will cause it to be included only when
building the package for Windows; similarly, math_386.s will be included
only when building the package for 32-bit x86.
Go versions 1.16 and earlier used a different syntax for build constraints,
with a "// +build" prefix. The gofmt command will add an equivalent //go:build
constraint when encountering the older syntax.
`,
}

View File

@ -8,6 +8,7 @@ package imports
import (
"bufio"
"bytes"
"errors"
"io"
"unicode/utf8"
@ -22,6 +23,19 @@ type importReader struct {
nerr int
}
var bom = []byte{0xef, 0xbb, 0xbf}
func newImportReader(b *bufio.Reader) *importReader {
// Remove leading UTF-8 BOM.
// Per https://golang.org/ref/spec#Source_code_representation:
// a compiler may ignore a UTF-8-encoded byte order mark (U+FEFF)
// if it is the first Unicode code point in the source text.
if leadingBytes, err := b.Peek(3); err == nil && bytes.Equal(leadingBytes, bom) {
b.Discard(3)
}
return &importReader{b: b}
}
func isIdent(c byte) bool {
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf
}
@ -201,7 +215,7 @@ func (r *importReader) readImport(imports *[]string) {
// ReadComments is like io.ReadAll, except that it only reads the leading
// block of comments in the file.
func ReadComments(f io.Reader) ([]byte, error) {
r := &importReader{b: bufio.NewReader(f)}
r := newImportReader(bufio.NewReader(f))
r.peekByte(true)
if r.err == nil && !r.eof {
// Didn't reach EOF, so must have found a non-space byte. Remove it.
@ -213,7 +227,7 @@ func ReadComments(f io.Reader) ([]byte, error) {
// ReadImports is like io.ReadAll, except that it expects a Go file as input
// and stops reading the input once the imports have completed.
func ReadImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) {
r := &importReader{b: bufio.NewReader(f)}
r := newImportReader(bufio.NewReader(f))
r.readKeyword("package")
r.readIdent()

View File

@ -66,6 +66,10 @@ var readImportsTests = []readTest{
`,
"",
},
{
"\ufeff𝔻" + `package p; import "x";var x = 1`,
"",
},
}
var readCommentsTests = []readTest{
@ -81,6 +85,10 @@ var readCommentsTests = []readTest{
`package p; import . "x"`,
"",
},
{
"\ufeff𝔻" + `package p; import . "x"`,
"",
},
{
`// foo
@ -90,6 +98,19 @@ var readCommentsTests = []readTest{
/*/ zot */
// asdf
Hello, world`,
"",
},
{
"\ufeff𝔻" + `// foo
/* bar */
/* quux */ // baz
/*/ zot */
// asdf
Hello, world`,
"",
@ -107,6 +128,11 @@ func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, erro
in = tt.in[:j] + tt.in[j+len(""):]
testOut = tt.in[:j]
}
d := strings.Index(tt.in, "𝔻")
if d >= 0 {
in = in[:d] + in[d+len("𝔻"):]
testOut = testOut[d+len("𝔻"):]
}
r := strings.NewReader(in)
buf, err := read(r)
if err != nil {

View File

@ -724,8 +724,18 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
// Record non-identity import mappings in p.ImportMap.
for _, p := range pkgs {
for i, srcPath := range p.Internal.RawImports {
path := p.Imports[i]
nRaw := len(p.Internal.RawImports)
for i, path := range p.Imports {
var srcPath string
if i < nRaw {
srcPath = p.Internal.RawImports[i]
} else {
// This path is not within the raw imports, so it must be an import
// found only within CompiledGoFiles. Those paths are found in
// CompiledImports.
srcPath = p.Internal.CompiledImports[i-nRaw]
}
if path != srcPath {
if p.ImportMap == nil {
p.ImportMap = make(map[string]string)

View File

@ -87,6 +87,7 @@ type PackagePublic struct {
CgoFiles []string `json:",omitempty"` // .go source files that import "C"
CompiledGoFiles []string `json:",omitempty"` // .go output from running cgo on CgoFiles
IgnoredGoFiles []string `json:",omitempty"` // .go source files ignored due to build constraints
InvalidGoFiles []string `json:",omitempty"` // .go source files with detected problems (parse error, wrong package name, and so on)
IgnoredOtherFiles []string `json:",omitempty"` // non-.go source files ignored due to build constraints
CFiles []string `json:",omitempty"` // .c source files
CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files
@ -144,6 +145,7 @@ func (p *Package) AllFiles() []string {
p.CgoFiles,
// no p.CompiledGoFiles, because they are from GoFiles or generated by us
p.IgnoredGoFiles,
// no p.InvalidGoFiles, because they are from GoFiles
p.IgnoredOtherFiles,
p.CFiles,
p.CXXFiles,
@ -192,8 +194,8 @@ type PackageInternal struct {
// Unexported fields are not part of the public API.
Build *build.Package
Imports []*Package // this package's direct imports
CompiledImports []string // additional Imports necessary when using CompiledGoFiles (all from standard library)
RawImports []string // this package's original imports as they appear in the text of the program
CompiledImports []string // additional Imports necessary when using CompiledGoFiles (all from standard library); 1:1 with the end of PackagePublic.Imports
RawImports []string // this package's original imports as they appear in the text of the program; 1:1 with the end of PackagePublic.Imports
ForceLibrary bool // this package is a library (even if named "main")
CmdlineFiles bool // package built from files listed on command line
CmdlinePkg bool // package listed on command line
@ -371,6 +373,7 @@ func (p *Package) copyBuild(opts PackageOpts, pp *build.Package) {
p.GoFiles = pp.GoFiles
p.CgoFiles = pp.CgoFiles
p.IgnoredGoFiles = pp.IgnoredGoFiles
p.InvalidGoFiles = pp.InvalidGoFiles
p.IgnoredOtherFiles = pp.IgnoredOtherFiles
p.CFiles = pp.CFiles
p.CXXFiles = pp.CXXFiles
@ -852,7 +855,9 @@ func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoo
buildMode = build.ImportComment
}
data.p, data.err = cfg.BuildContext.ImportDir(r.dir, buildMode)
if data.p.Root == "" && cfg.ModulesEnabled {
if cfg.ModulesEnabled {
// Override data.p.Root, since ImportDir sets it to $GOPATH, if
// the module is inside $GOPATH/src.
if info := modload.PackageModuleInfo(ctx, path); info != nil {
data.p.Root = info.Dir
}
@ -1800,35 +1805,37 @@ func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk *
}
}
// Cgo translation adds imports of "unsafe", "runtime/cgo" and "syscall",
// except for certain packages, to avoid circular dependencies.
if p.UsesCgo() {
addImport("unsafe", true)
}
if p.UsesCgo() && (!p.Standard || !cgoExclude[p.ImportPath]) && cfg.BuildContext.Compiler != "gccgo" {
addImport("runtime/cgo", true)
}
if p.UsesCgo() && (!p.Standard || !cgoSyscallExclude[p.ImportPath]) {
addImport("syscall", true)
}
// SWIG adds imports of some standard packages.
if p.UsesSwig() {
addImport("unsafe", true)
if cfg.BuildContext.Compiler != "gccgo" {
if !opts.IgnoreImports {
// Cgo translation adds imports of "unsafe", "runtime/cgo" and "syscall",
// except for certain packages, to avoid circular dependencies.
if p.UsesCgo() {
addImport("unsafe", true)
}
if p.UsesCgo() && (!p.Standard || !cgoExclude[p.ImportPath]) && cfg.BuildContext.Compiler != "gccgo" {
addImport("runtime/cgo", true)
}
addImport("syscall", true)
addImport("sync", true)
if p.UsesCgo() && (!p.Standard || !cgoSyscallExclude[p.ImportPath]) {
addImport("syscall", true)
}
// TODO: The .swig and .swigcxx files can use
// %go_import directives to import other packages.
}
// SWIG adds imports of some standard packages.
if p.UsesSwig() {
addImport("unsafe", true)
if cfg.BuildContext.Compiler != "gccgo" {
addImport("runtime/cgo", true)
}
addImport("syscall", true)
addImport("sync", true)
// The linker loads implicit dependencies.
if p.Name == "main" && !p.Internal.ForceLibrary {
for _, dep := range LinkerDeps(p) {
addImport(dep, false)
// TODO: The .swig and .swigcxx files can use
// %go_import directives to import other packages.
}
// The linker loads implicit dependencies.
if p.Name == "main" && !p.Internal.ForceLibrary {
for _, dep := range LinkerDeps(p) {
addImport(dep, false)
}
}
}
@ -2390,7 +2397,9 @@ func LoadImportWithFlags(path, srcDir string, parent *Package, stk *ImportStack,
// PackageOpts control the behavior of PackagesAndErrors and other package
// loading functions.
type PackageOpts struct {
// IgnoreImports controls whether we ignore imports when loading packages.
// IgnoreImports controls whether we ignore explicit and implicit imports
// when loading packages. Implicit imports are added when supporting Cgo
// or SWIG and when linking main packages.
IgnoreImports bool
// ModResolveTests indicates whether calls to the module loader should also
@ -2499,7 +2508,7 @@ func PackagesAndErrors(ctx context.Context, opts PackageOpts, patterns []string)
}
if opts.MainOnly {
pkgs = mainPackagesOnly(pkgs, patterns)
pkgs = mainPackagesOnly(pkgs, matches)
}
// Now that CmdlinePkg is set correctly,
@ -2553,50 +2562,63 @@ func CheckPackageErrors(pkgs []*Package) {
// mainPackagesOnly filters out non-main packages matched only by arguments
// containing "..." and returns the remaining main packages.
//
// Packages with missing, invalid, or ambiguous names may be treated as
// possibly-main packages.
//
// mainPackagesOnly sets a non-main package's Error field and returns it if it
// is named by a literal argument.
//
// mainPackagesOnly prints warnings for non-literal arguments that only match
// non-main packages.
func mainPackagesOnly(pkgs []*Package, patterns []string) []*Package {
matchers := make([]func(string) bool, len(patterns))
for i, p := range patterns {
if strings.Contains(p, "...") {
matchers[i] = search.MatchPattern(p)
func mainPackagesOnly(pkgs []*Package, matches []*search.Match) []*Package {
treatAsMain := map[string]bool{}
for _, m := range matches {
if m.IsLiteral() {
for _, path := range m.Pkgs {
treatAsMain[path] = true
}
}
}
matchedPkgs := make([]*Package, 0, len(pkgs))
mainCount := make([]int, len(patterns))
nonMainCount := make([]int, len(patterns))
var mains []*Package
for _, pkg := range pkgs {
if pkg.Name == "main" || (pkg.Incomplete && pkg.Name == "") {
matchedPkgs = append(matchedPkgs, pkg)
for i := range patterns {
if matchers[i] != nil && matchers[i](pkg.ImportPath) {
mainCount[i]++
}
}
} else {
for i := range patterns {
if matchers[i] == nil && patterns[i] == pkg.ImportPath {
if pkg.Error == nil {
pkg.Error = &PackageError{Err: &mainPackageError{importPath: pkg.ImportPath}}
}
matchedPkgs = append(matchedPkgs, pkg)
} else if matchers[i] != nil && matchers[i](pkg.ImportPath) {
nonMainCount[i]++
}
}
if pkg.Name == "main" {
treatAsMain[pkg.ImportPath] = true
mains = append(mains, pkg)
continue
}
}
for i, p := range patterns {
if matchers[i] != nil && mainCount[i] == 0 && nonMainCount[i] > 0 {
fmt.Fprintf(os.Stderr, "go: warning: %q matched no main packages\n", p)
if len(pkg.InvalidGoFiles) > 0 { // TODO(#45999): && pkg.Name == "", but currently go/build sets pkg.Name arbitrarily if it is ambiguous.
// The package has (or may have) conflicting names, and we can't easily
// tell whether one of them is "main". So assume that it could be, and
// report an error for the package.
treatAsMain[pkg.ImportPath] = true
}
if treatAsMain[pkg.ImportPath] {
if pkg.Error == nil {
pkg.Error = &PackageError{Err: &mainPackageError{importPath: pkg.ImportPath}}
}
mains = append(mains, pkg)
}
}
return matchedPkgs
for _, m := range matches {
if m.IsLiteral() || len(m.Pkgs) == 0 {
continue
}
foundMain := false
for _, path := range m.Pkgs {
if treatAsMain[path] {
foundMain = true
break
}
}
if !foundMain {
fmt.Fprintf(os.Stderr, "go: warning: %q matched only non-main packages\n", m.Pattern())
}
}
return mains
}
type mainPackageError struct {

View File

@ -116,7 +116,7 @@ func TestPackagesAndErrors(ctx context.Context, opts PackageOpts, p *Package, co
// Can't change that code, because that code is only for loading the
// non-test copy of a package.
ptestErr = &PackageError{
ImportStack: testImportStack(stk[0], p1, p.ImportPath),
ImportStack: importCycleStack(p1, p.ImportPath),
Err: errors.New("import cycle not allowed in test"),
IsImportCycle: true,
}
@ -375,22 +375,44 @@ func TestPackagesAndErrors(ctx context.Context, opts PackageOpts, p *Package, co
return pmain, ptest, pxtest
}
func testImportStack(top string, p *Package, target string) []string {
stk := []string{top, p.ImportPath}
Search:
for p.ImportPath != target {
for _, p1 := range p.Internal.Imports {
if p1.ImportPath == target || str.Contains(p1.Deps, target) {
stk = append(stk, p1.ImportPath)
p = p1
continue Search
// importCycleStack returns an import stack from p to the package whose import
// path is target.
func importCycleStack(p *Package, target string) []string {
// importerOf maps each import path to its importer nearest to p.
importerOf := map[string]string{p.ImportPath: ""}
// q is a breadth-first queue of packages to search for target.
// Every package added to q has a corresponding entry in pathTo.
//
// We search breadth-first for two reasons:
//
// 1. We want to report the shortest cycle.
//
// 2. If p contains multiple cycles, the first cycle we encounter might not
// contain target. To ensure termination, we have to break all cycles
// other than the first.
q := []*Package{p}
for len(q) > 0 {
p := q[0]
q = q[1:]
if path := p.ImportPath; path == target {
var stk []string
for path != "" {
stk = append(stk, path)
path = importerOf[path]
}
return stk
}
for _, dep := range p.Internal.Imports {
if _, ok := importerOf[dep.ImportPath]; !ok {
importerOf[dep.ImportPath] = p.ImportPath
q = append(q, dep)
}
}
// Can't happen, but in case it does...
stk = append(stk, "<lost path to cycle>")
break
}
return stk
panic("lost path to cycle")
}
// recompileForTest copies and replaces certain packages in pmain's dependency

View File

@ -11,7 +11,6 @@ import (
"io/fs"
"os"
"cmd/go/internal/fsys"
"cmd/go/internal/lockedfile/internal/filelock"
)
@ -21,7 +20,7 @@ func openFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
// calls for Linux and Windows anyway, so it's simpler to use that approach
// consistently.
f, err := fsys.OpenFile(name, flag&^os.O_TRUNC, perm)
f, err := os.OpenFile(name, flag&^os.O_TRUNC, perm)
if err != nil {
return nil, err
}

View File

@ -13,8 +13,6 @@ import (
"os"
"strings"
"time"
"cmd/go/internal/fsys"
)
// Opening an exclusive-use file returns an error.
@ -59,7 +57,7 @@ func openFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
// If the file was unpacked or created by some other program, it might not
// have the ModeExclusive bit set. Set it before we call OpenFile, so that we
// can be confident that a successful OpenFile implies exclusive use.
if fi, err := fsys.Stat(name); err == nil {
if fi, err := os.Stat(name); err == nil {
if fi.Mode()&fs.ModeExclusive == 0 {
if err := os.Chmod(name, fi.Mode()|fs.ModeExclusive); err != nil {
return nil, err
@ -72,7 +70,7 @@ func openFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
nextSleep := 1 * time.Millisecond
const maxSleep = 500 * time.Millisecond
for {
f, err := fsys.OpenFile(name, flag, perm|fs.ModeExclusive)
f, err := os.OpenFile(name, flag, perm|fs.ModeExclusive)
if err == nil {
return f, nil
}

View File

@ -86,9 +86,11 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
if !modload.HasModRoot() && len(args) == 0 {
base.Fatalf("go mod download: no modules specified (see 'go help mod download')")
}
if len(args) == 0 {
haveExplicitArgs := len(args) > 0
if !haveExplicitArgs {
args = []string{"all"}
} else if modload.HasModRoot() {
}
if modload.HasModRoot() {
modload.LoadModFile(ctx) // to fill Target
targetAtUpgrade := modload.Target.Path + "@upgrade"
targetAtPatch := modload.Target.Path + "@patch"
@ -135,6 +137,18 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
type token struct{}
sem := make(chan token, runtime.GOMAXPROCS(0))
infos, infosErr := modload.ListModules(ctx, args, 0)
if !haveExplicitArgs {
// 'go mod download' is sometimes run without arguments to pre-populate the
// module cache. It may fetch modules that aren't needed to build packages
// in the main mdoule. This is usually not intended, so don't save sums for
// downloaded modules (golang.org/issue/45332).
// TODO(golang.org/issue/45551): For now, in ListModules, save sums needed
// to load the build list (same as 1.15 behavior). In the future, report an
// error if go.mod or go.sum need to be updated after loading the build
// list.
modload.DisallowWriteGoMod()
}
for _, info := range infos {
if info.Replace != nil {
info = info.Replace
@ -185,8 +199,15 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
base.ExitIfErrors()
}
// Update go.mod and especially go.sum if needed.
modload.WriteGoMod(ctx)
// If there were explicit arguments, update go.mod and especially go.sum.
// 'go mod download mod@version' is a useful way to add a sum without using
// 'go get mod@version', which may have other side effects. We print this in
// some error message hints.
//
// Don't save sums for 'go mod download' without arguments; see comment above.
if haveExplicitArgs {
modload.WriteGoMod(ctx)
}
// If there was an error matching some of the requested packages, emit it now
// (after we've written the checksums for the modules that were downloaded

View File

@ -25,7 +25,7 @@ import (
)
var cmdEdit = &base.Command{
UsageLine: "go mod edit [editing flags] [go.mod]",
UsageLine: "go mod edit [editing flags] [-fmt|-print|-json] [go.mod]",
Short: "edit go.mod from tools or scripts",
Long: `
Edit provides a command-line interface for editing go.mod,
@ -196,7 +196,7 @@ func runEdit(ctx context.Context, cmd *base.Command, args []string) {
if *editGo != "" {
if !modfile.GoVersionRE.MatchString(*editGo) {
base.Fatalf(`go mod: invalid -go option; expecting something like "-go 1.12"`)
base.Fatalf(`go mod: invalid -go option; expecting something like "-go %s"`, modload.LatestGoVersion())
}
}

View File

@ -18,7 +18,7 @@ import (
)
var cmdGraph = &base.Command{
UsageLine: "go mod graph",
UsageLine: "go mod graph [-go=version]",
Short: "print module requirement graph",
Long: `
Graph prints the module requirement graph (with replacements applied)
@ -26,12 +26,21 @@ in text form. Each line in the output has two space-separated fields: a module
and one of its requirements. Each module is identified as a string of the form
path@version, except for the main module, which has no @version suffix.
The -go flag causes graph to report the module graph as loaded by the
given Go version, instead of the version indicated by the 'go' directive
in the go.mod file.
See https://golang.org/ref/mod#go-mod-graph for more about 'go mod graph'.
`,
Run: runGraph,
}
var (
graphGo goVersionFlag
)
func init() {
cmdGraph.Flag.Var(&graphGo, "go", "")
base.AddModCommonFlags(&cmdGraph.Flag)
}
@ -41,7 +50,7 @@ func runGraph(ctx context.Context, cmd *base.Command, args []string) {
}
modload.ForceUseModules = true
modload.RootMode = modload.NeedRoot
mg := modload.LoadModGraph(ctx)
mg := modload.LoadModGraph(ctx, graphGo.String())
w := bufio.NewWriter(os.Stdout)
defer w.Flush()

View File

@ -13,7 +13,7 @@ import (
)
var cmdInit = &base.Command{
UsageLine: "go mod init [module]",
UsageLine: "go mod init [module-path]",
Short: "initialize new module in current directory",
Long: `
Init initializes and writes a new go.mod file in the current directory, in

View File

@ -12,12 +12,14 @@ import (
"cmd/go/internal/imports"
"cmd/go/internal/modload"
"context"
"fmt"
"golang.org/x/mod/modfile"
"golang.org/x/mod/semver"
)
var cmdTidy = &base.Command{
UsageLine: "go mod tidy [-e] [-v] [-go=version]",
UsageLine: "go mod tidy [-e] [-v] [-go=version] [-compat=version]",
Short: "add missing and remove unused modules",
Long: `
Tidy makes sure go.mod matches the source code in the module.
@ -38,34 +40,64 @@ are retained as explicit requirements in the go.mod file.
(Go versions 1.17 and higher retain more requirements in order to
support lazy module loading.)
The -compat flag preserves any additional checksums needed for the
'go' command from the indicated major Go release to successfully load
the module graph, and causes tidy to error out if that version of the
'go' command would load any imported package from a different module
version. By default, tidy acts as if the -compat flag were set to the
version prior to the one indicated by the 'go' directive in the go.mod
file.
See https://golang.org/ref/mod#go-mod-tidy for more about 'go mod tidy'.
`,
Run: runTidy,
}
var (
tidyE bool // if true, report errors but proceed anyway.
tidyGo string // go version to write to the tidied go.mod file (toggles lazy loading)
tidyE bool // if true, report errors but proceed anyway.
tidyGo goVersionFlag // go version to write to the tidied go.mod file (toggles lazy loading)
tidyCompat goVersionFlag // go version for which the tidied go.mod and go.sum files should be “compatible”
)
func init() {
cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "")
cmdTidy.Flag.BoolVar(&tidyE, "e", false, "")
cmdTidy.Flag.StringVar(&tidyGo, "go", "", "")
cmdTidy.Flag.Var(&tidyGo, "go", "")
cmdTidy.Flag.Var(&tidyCompat, "compat", "")
base.AddModCommonFlags(&cmdTidy.Flag)
}
// A goVersionFlag is a flag.Value representing a supported Go version.
//
// (Note that the -go argument to 'go mod edit' is *not* a goVersionFlag.
// It intentionally allows newer-than-supported versions as arguments.)
type goVersionFlag struct {
v string
}
func (f *goVersionFlag) String() string { return f.v }
func (f *goVersionFlag) Get() interface{} { return f.v }
func (f *goVersionFlag) Set(s string) error {
if s != "" {
latest := modload.LatestGoVersion()
if !modfile.GoVersionRE.MatchString(s) {
return fmt.Errorf("expecting a Go version like %q", latest)
}
if semver.Compare("v"+s, "v"+latest) > 0 {
return fmt.Errorf("maximum supported Go version is %s", latest)
}
}
f.v = s
return nil
}
func runTidy(ctx context.Context, cmd *base.Command, args []string) {
if len(args) > 0 {
base.Fatalf("go mod tidy: no arguments allowed")
}
if tidyGo != "" {
if !modfile.GoVersionRE.MatchString(tidyGo) {
base.Fatalf(`go mod: invalid -go option %q; expecting something like "-go 1.17"`, tidyGo)
}
}
// Tidy aims to make 'go test' reproducible for any package in 'all', so we
// need to include test dependencies. For modules that specify go 1.15 or
// earlier this is a no-op (because 'all' saturates transitive test
@ -80,9 +112,10 @@ func runTidy(ctx context.Context, cmd *base.Command, args []string) {
modload.RootMode = modload.NeedRoot
modload.LoadPackages(ctx, modload.PackageOpts{
GoVersion: tidyGo,
GoVersion: tidyGo.String(),
Tags: imports.AnyTags(),
Tidy: true,
TidyCompatibleVersion: tidyCompat.String(),
VendorModulesInGOROOTSrc: true,
ResolveMissingImports: true,
LoadTests: true,

View File

@ -13,6 +13,7 @@ import (
"io"
"io/fs"
"os"
"path"
"path/filepath"
"sort"
"strings"
@ -299,7 +300,7 @@ func copyMetadata(modPath, pkg, dst, src string, copiedFiles map[string]bool) {
if modPath == pkg {
break
}
pkg = filepath.Dir(pkg)
pkg = path.Dir(pkg)
dst = filepath.Dir(dst)
src = filepath.Dir(src)
}

View File

@ -54,7 +54,8 @@ func runVerify(ctx context.Context, cmd *base.Command, args []string) {
sem := make(chan token, runtime.GOMAXPROCS(0))
// Use a slice of result channels, so that the output is deterministic.
mods := modload.LoadModGraph(ctx).BuildList()[1:]
const defaultGoVersion = ""
mods := modload.LoadModGraph(ctx, defaultGoVersion).BuildList()[1:]
errsChans := make([]<-chan []error, len(mods))
for i, mod := range mods {

View File

@ -152,7 +152,7 @@ func lockVersion(mod module.Version) (unlock func(), err error) {
// If err is nil, the caller MUST eventually call the unlock function.
func SideLock() (unlock func(), err error) {
if err := checkCacheDir(); err != nil {
base.Fatalf("go: %v", err)
return nil, err
}
path := filepath.Join(cfg.GOMODCACHE, "cache", "lock")

View File

@ -8,7 +8,6 @@ import (
"archive/zip"
"bytes"
"flag"
"fmt"
"internal/testenv"
"io"
"io/fs"
@ -47,12 +46,6 @@ var altRepos = []string{
var localGitRepo string
func testMain(m *testing.M) int {
if _, err := exec.LookPath("git"); err != nil {
fmt.Fprintln(os.Stderr, "skipping because git binary not found")
fmt.Println("PASS")
return 0
}
dir, err := os.MkdirTemp("", "gitrepo-test-")
if err != nil {
log.Fatal(err)
@ -60,23 +53,25 @@ func testMain(m *testing.M) int {
defer os.RemoveAll(dir)
if testenv.HasExternalNetwork() && testenv.HasExec() {
// Clone gitrepo1 into a local directory.
// If we use a file:// URL to access the local directory,
// then git starts up all the usual protocol machinery,
// which will let us test remote git archive invocations.
localGitRepo = filepath.Join(dir, "gitrepo2")
if _, err := Run("", "git", "clone", "--mirror", gitrepo1, localGitRepo); err != nil {
log.Fatal(err)
}
if _, err := Run(localGitRepo, "git", "config", "daemon.uploadarch", "true"); err != nil {
log.Fatal(err)
if _, err := exec.LookPath("git"); err == nil {
// Clone gitrepo1 into a local directory.
// If we use a file:// URL to access the local directory,
// then git starts up all the usual protocol machinery,
// which will let us test remote git archive invocations.
localGitRepo = filepath.Join(dir, "gitrepo2")
if _, err := Run("", "git", "clone", "--mirror", gitrepo1, localGitRepo); err != nil {
log.Fatal(err)
}
if _, err := Run(localGitRepo, "git", "config", "daemon.uploadarch", "true"); err != nil {
log.Fatal(err)
}
}
}
return m.Run()
}
func testRepo(remote string) (Repo, error) {
func testRepo(t *testing.T, remote string) (Repo, error) {
if remote == "localGitRepo" {
// Convert absolute path to file URL. LocalGitRepo will not accept
// Windows absolute paths because they look like a host:path remote.
@ -87,15 +82,17 @@ func testRepo(remote string) (Repo, error) {
} else {
url = "file:///" + filepath.ToSlash(localGitRepo)
}
testenv.MustHaveExecPath(t, "git")
return LocalGitRepo(url)
}
kind := "git"
vcs := "git"
for _, k := range []string{"hg"} {
if strings.Contains(remote, "/"+k+"/") {
kind = k
vcs = k
}
}
return NewRepo(kind, remote)
testenv.MustHaveExecPath(t, vcs)
return NewRepo(vcs, remote)
}
var tagsTests = []struct {
@ -116,7 +113,7 @@ func TestTags(t *testing.T) {
for _, tt := range tagsTests {
f := func(t *testing.T) {
r, err := testRepo(tt.repo)
r, err := testRepo(t, tt.repo)
if err != nil {
t.Fatal(err)
}
@ -168,7 +165,7 @@ func TestLatest(t *testing.T) {
for _, tt := range latestTests {
f := func(t *testing.T) {
r, err := testRepo(tt.repo)
r, err := testRepo(t, tt.repo)
if err != nil {
t.Fatal(err)
}
@ -221,7 +218,7 @@ func TestReadFile(t *testing.T) {
for _, tt := range readFileTests {
f := func(t *testing.T) {
r, err := testRepo(tt.repo)
r, err := testRepo(t, tt.repo)
if err != nil {
t.Fatal(err)
}
@ -412,7 +409,7 @@ func TestReadZip(t *testing.T) {
for _, tt := range readZipTests {
f := func(t *testing.T) {
r, err := testRepo(tt.repo)
r, err := testRepo(t, tt.repo)
if err != nil {
t.Fatal(err)
}
@ -581,7 +578,7 @@ func TestStat(t *testing.T) {
for _, tt := range statTests {
f := func(t *testing.T) {
r, err := testRepo(tt.repo)
r, err := testRepo(t, tt.repo)
if err != nil {
t.Fatal(err)
}

View File

@ -864,22 +864,25 @@ func (r *codeRepo) GoMod(version string) (data []byte, err error) {
data, err = r.code.ReadFile(rev, path.Join(dir, "go.mod"), codehost.MaxGoMod)
if err != nil {
if os.IsNotExist(err) {
return r.legacyGoMod(rev, dir), nil
return LegacyGoMod(r.modPath), nil
}
return nil, err
}
return data, nil
}
func (r *codeRepo) legacyGoMod(rev, dir string) []byte {
// We used to try to build a go.mod reflecting pre-existing
// package management metadata files, but the conversion
// was inherently imperfect (because those files don't have
// exactly the same semantics as go.mod) and, when done
// for dependencies in the middle of a build, impossible to
// correct. So we stopped.
// Return a fake go.mod that simply declares the module path.
return []byte(fmt.Sprintf("module %s\n", modfile.AutoQuote(r.modPath)))
// LegacyGoMod generates a fake go.mod file for a module that doesn't have one.
// The go.mod file contains a module directive and nothing else: no go version,
// no requirements.
//
// We used to try to build a go.mod reflecting pre-existing
// package management metadata files, but the conversion
// was inherently imperfect (because those files don't have
// exactly the same semantics as go.mod) and, when done
// for dependencies in the middle of a build, impossible to
// correct. So we stopped.
func LegacyGoMod(modPath string) []byte {
return []byte(fmt.Sprintf("module %s\n", modfile.AutoQuote(modPath)))
}
func (r *codeRepo) modPrefix(rev string) string {

View File

@ -22,6 +22,7 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/fsys"
"cmd/go/internal/lockedfile"
"cmd/go/internal/par"
"cmd/go/internal/robustio"
@ -416,7 +417,18 @@ func initGoSum() (bool, error) {
goSum.m = make(map[module.Version][]string)
goSum.status = make(map[modSum]modSumStatus)
data, err := lockedfile.Read(GoSumFile)
var (
data []byte
err error
)
if actualSumFile, ok := fsys.OverlayPath(GoSumFile); ok {
// Don't lock go.sum if it's part of the overlay.
// On Plan 9, locking requires chmod, and we don't want to modify any file
// in the overlay. See #44700.
data, err = os.ReadFile(actualSumFile)
} else {
data, err = lockedfile.Read(GoSumFile)
}
if err != nil && !os.IsNotExist(err) {
return false, err
}
@ -716,6 +728,9 @@ Outer:
if cfg.BuildMod == "readonly" {
base.Fatalf("go: updates to go.sum needed, disabled by -mod=readonly")
}
if _, ok := fsys.OverlayPath(GoSumFile); ok {
base.Fatalf("go: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay")
}
// Make a best-effort attempt to acquire the side lock, only to exclude
// previous versions of the 'go' command from making simultaneous edits.

View File

@ -38,6 +38,7 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/imports"
"cmd/go/internal/load"
"cmd/go/internal/modfetch"
"cmd/go/internal/modload"
"cmd/go/internal/par"
"cmd/go/internal/search"
@ -386,14 +387,14 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
}
load.CheckPackageErrors(pkgs)
haveExe := false
haveExternalExe := false
for _, pkg := range pkgs {
if pkg.Name == "main" {
haveExe = true
if pkg.Name == "main" && pkg.Module != nil && pkg.Module.Path != modload.Target.Path {
haveExternalExe = true
break
}
}
if haveExe {
if haveExternalExe {
fmt.Fprint(os.Stderr, "go get: installing executables with 'go get' in module mode is deprecated.")
var altMsg string
if modload.HasModRoot() {
@ -505,7 +506,8 @@ type versionReason struct {
func newResolver(ctx context.Context, queries []*query) *resolver {
// LoadModGraph also sets modload.Target, which is needed by various resolver
// methods.
mg := modload.LoadModGraph(ctx)
const defaultGoVersion = ""
mg := modload.LoadModGraph(ctx, defaultGoVersion)
buildList := mg.BuildList()
initialVersion := make(map[string]string, len(buildList))
@ -1152,6 +1154,7 @@ func (r *resolver) loadPackages(ctx context.Context, patterns []string, findPack
Tags: imports.AnyTags(),
VendorModulesInGOROOTSrc: true,
LoadTests: *getT,
AssumeRootsImported: true, // After 'go get foo', imports of foo should build.
SilencePackageErrors: true, // May be fixed by subsequent upgrades or downgrades.
}
@ -1466,6 +1469,8 @@ func (r *resolver) chooseArbitrarily(cs pathSet) (isPackage bool, m module.Versi
// checkPackageProblems reloads packages for the given patterns and reports
// missing and ambiguous package errors. It also reports retractions and
// deprecations for resolved modules and modules needed to build named packages.
// It also adds a sum for each updated module in the build list if we had one
// before and didn't get one while loading packages.
//
// We skip missing-package errors earlier in the process, since we want to
// resolve pathSets ourselves, but at that point, we don't have enough context
@ -1593,12 +1598,55 @@ func (r *resolver) checkPackageProblems(ctx context.Context, pkgPatterns []strin
})
}
// Load sums for updated modules that had sums before. When we update a
// module, we may update another module in the build list that provides a
// package in 'all' that wasn't loaded as part of this 'go get' command.
// If we don't add a sum for that module, builds may fail later.
// Note that an incidentally updated package could still import packages
// from unknown modules or from modules in the build list that we didn't
// need previously. We can't handle that case without loading 'all'.
sumErrs := make([]error, len(r.buildList))
for i := range r.buildList {
i := i
m := r.buildList[i]
mActual := m
if mRepl := modload.Replacement(m); mRepl.Path != "" {
mActual = mRepl
}
old := module.Version{Path: m.Path, Version: r.initialVersion[m.Path]}
if old.Version == "" {
continue
}
oldActual := old
if oldRepl := modload.Replacement(old); oldRepl.Path != "" {
oldActual = oldRepl
}
if mActual == oldActual || mActual.Version == "" || !modfetch.HaveSum(oldActual) {
continue
}
r.work.Add(func() {
if _, err := modfetch.DownloadZip(ctx, mActual); err != nil {
verb := "upgraded"
if semver.Compare(m.Version, old.Version) < 0 {
verb = "downgraded"
}
replaced := ""
if mActual != m {
replaced = fmt.Sprintf(" (replaced by %s)", mActual)
}
err = fmt.Errorf("%s %s %s => %s%s: error finding sum for %s: %v", verb, m.Path, old.Version, m.Version, replaced, mActual, err)
sumErrs[i] = err
}
})
}
<-r.work.Idle()
// Report deprecations, then retractions.
// Report deprecations, then retractions, then errors fetching sums.
// Only errors fetching sums are hard errors.
for _, mm := range deprecations {
if mm.message != "" {
fmt.Fprintf(os.Stderr, "go: warning: module %s is deprecated: %s\n", mm.m.Path, mm.message)
fmt.Fprintf(os.Stderr, "go: module %s is deprecated: %s\n", mm.m.Path, mm.message)
}
}
var retractPath string
@ -1615,6 +1663,12 @@ func (r *resolver) checkPackageProblems(ctx context.Context, pkgPatterns []strin
if retractPath != "" {
fmt.Fprintf(os.Stderr, "go: to switch to the latest unretracted version, run:\n\tgo get %s@latest\n", retractPath)
}
for _, err := range sumErrs {
if err != nil {
base.Errorf("go: %v", err)
}
}
base.ExitIfErrors()
}
// reportChanges logs version changes to os.Stderr.
@ -1750,7 +1804,8 @@ func (r *resolver) updateBuildList(ctx context.Context, additions []module.Versi
return false
}
r.buildList = modload.LoadModGraph(ctx).BuildList()
const defaultGoVersion = ""
r.buildList = modload.LoadModGraph(ctx, defaultGoVersion).BuildList()
r.buildListVersion = make(map[string]string, len(r.buildList))
for _, m := range r.buildList {
r.buildListVersion[m.Path] = m.Version

View File

@ -403,11 +403,33 @@ func (mg *ModuleGraph) allRootsSelected() bool {
// LoadModGraph loads and returns the graph of module dependencies of the main module,
// without loading any packages.
//
// If the goVersion string is non-empty, the returned graph is the graph
// as interpreted by the given Go version (instead of the version indicated
// in the go.mod file).
//
// Modules are loaded automatically (and lazily) in LoadPackages:
// LoadModGraph need only be called if LoadPackages is not,
// typically in commands that care about modules but no particular package.
func LoadModGraph(ctx context.Context) *ModuleGraph {
rs, mg, err := expandGraph(ctx, LoadModFile(ctx))
func LoadModGraph(ctx context.Context, goVersion string) *ModuleGraph {
rs := LoadModFile(ctx)
if goVersion != "" {
depth := modDepthFromGoVersion(goVersion)
if depth == eager && rs.depth != eager {
// Use newRequirements instead of convertDepth because convertDepth
// also updates roots; here, we want to report the unmodified roots
// even though they may seem inconsistent.
rs = newRequirements(eager, rs.rootModules, rs.direct)
}
mg, err := rs.Graph(ctx)
if err != nil {
base.Fatalf("go: %v", err)
}
return mg
}
rs, mg, err := expandGraph(ctx, rs)
if err != nil {
base.Fatalf("go: %v", err)
}
@ -443,7 +465,7 @@ func expandGraph(ctx context.Context, rs *Requirements) (*Requirements, *ModuleG
// roots — but in a lazy module it may pull in previously-irrelevant
// transitive dependencies.
newRS, rsErr := updateRoots(ctx, rs.direct, rs, nil, nil)
newRS, rsErr := updateRoots(ctx, rs.direct, rs, nil, nil, false)
if rsErr != nil {
// Failed to update roots, perhaps because of an error in a transitive
// dependency needed for the update. Return the original Requirements
@ -517,11 +539,11 @@ func tidyRoots(ctx context.Context, rs *Requirements, pkgs []*loadPkg) (*Require
return tidyLazyRoots(ctx, rs.direct, pkgs)
}
func updateRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version) (*Requirements, error) {
func updateRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) {
if rs.depth == eager {
return updateEagerRoots(ctx, direct, rs, add)
}
return updateLazyRoots(ctx, direct, rs, pkgs, add)
return updateLazyRoots(ctx, direct, rs, pkgs, add, rootsImported)
}
// tidyLazyRoots returns a minimal set of root requirements that maintains the
@ -661,7 +683,7 @@ func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg)
//
// (See https://golang.org/design/36460-lazy-module-loading#invariants for more
// detail.)
func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version) (*Requirements, error) {
func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) {
roots := rs.rootModules
rootsUpgraded := false
@ -688,6 +710,10 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen
//
// (This is the “import invariant” that makes lazy loading possible.)
case rootsImported && pkg.flags.has(pkgFromRoot):
// pkg is a transitive dependency of some root, and we are treating the
// roots as if they are imported by the main module (as in 'go get').
case pkg.flags.has(pkgIsRoot):
// pkg is a root of the package-import graph. (Generally this means that
// it matches a command-line argument.) We want future invocations of the
@ -815,7 +841,8 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen
roots = make([]module.Version, 0, len(rs.rootModules))
rootsUpgraded = false
inRootPaths := make(map[string]bool, len(rs.rootModules))
inRootPaths := make(map[string]bool, len(rs.rootModules)+1)
inRootPaths[Target.Path] = true
for _, m := range rs.rootModules {
if inRootPaths[m.Path] {
// This root specifies a redundant path. We already retained the

View File

@ -178,11 +178,13 @@ func (e *ImportMissingSumError) Error() string {
// Importing package is unknown, or the missing package was named on the
// command line. Recommend 'go mod download' for the modules that could
// provide the package, since that shouldn't change go.mod.
args := make([]string, len(e.mods))
for i, mod := range e.mods {
args[i] = mod.Path
if len(e.mods) > 0 {
args := make([]string, len(e.mods))
for i, mod := range e.mods {
args[i] = mod.Path
}
hint = fmt.Sprintf("; to add:\n\tgo mod download %s", strings.Join(args, " "))
}
hint = fmt.Sprintf("; to add:\n\tgo mod download %s", strings.Join(args, " "))
} else {
// Importing package is known (common case). Recommend 'go get' on the
// current version of the importing package.
@ -426,6 +428,15 @@ func queryImport(ctx context.Context, path string, rs *Requirements) (module.Ver
mv = module.ZeroPseudoVersion("v0")
}
}
mg, err := rs.Graph(ctx)
if err != nil {
return module.Version{}, err
}
if cmpVersion(mg.Selected(mp), mv) >= 0 {
// We can't resolve the import by adding mp@mv to the module graph,
// because the selected version of mp is already at least mv.
continue
}
mods = append(mods, module.Version{Path: mp, Version: mv})
}

View File

@ -71,7 +71,7 @@ type Root int
const (
// AutoRoot is the default for most commands. modload.Init will look for
// a go.mod file in the current directory or any parent. If none is found,
// modules may be disabled (GO111MODULE=on) or commands may run in a
// modules may be disabled (GO111MODULE=auto) or commands may run in a
// limited module mode.
AutoRoot Root = iota
@ -405,14 +405,23 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
if modRoot == "" {
Target = module.Version{Path: "command-line-arguments"}
targetPrefix = "command-line-arguments"
goVersion := latestGoVersion()
goVersion := LatestGoVersion()
rawGoVersion.Store(Target, goVersion)
requirements = newRequirements(modDepthFromGoVersion(goVersion), nil, nil)
return requirements, false
}
gomod := ModFilePath()
data, err := lockedfile.Read(gomod)
var data []byte
var err error
if gomodActual, ok := fsys.OverlayPath(gomod); ok {
// Don't lock go.mod if it's part of the overlay.
// On Plan 9, locking requires chmod, and we don't want to modify any file
// in the overlay. See #44700.
data, err = os.ReadFile(gomodActual)
} else {
data, err = lockedfile.Read(gomodActual)
}
if err != nil {
base.Fatalf("go: %v", err)
}
@ -432,7 +441,10 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
initTarget(f.Module.Mod)
index = indexModFile(data, f, fixed)
if err := checkModulePathLax(f.Module.Mod.Path); err != nil {
if err := module.CheckImportPath(f.Module.Mod.Path); err != nil {
if pathErr, ok := err.(*module.InvalidPathError); ok {
pathErr.Kind = "module"
}
base.Fatalf("go: %v", err)
}
@ -448,7 +460,7 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
// TODO(#45551): Do something more principled instead of checking
// cfg.CmdName directly here.
if cfg.BuildMod == "mod" && cfg.CmdName != "mod graph" && cfg.CmdName != "mod why" {
addGoStmt(latestGoVersion())
addGoStmt(LatestGoVersion())
if go117EnableLazyLoading {
// We need to add a 'go' version to the go.mod file, but we must assume
// that its existing contents match something between Go 1.11 and 1.16.
@ -492,7 +504,15 @@ func CreateModFile(ctx context.Context, modPath string) {
if err != nil {
base.Fatalf("go: %v", err)
}
} else if err := checkModulePathLax(modPath); err != nil {
} else if err := module.CheckImportPath(modPath); err != nil {
if pathErr, ok := err.(*module.InvalidPathError); ok {
pathErr.Kind = "module"
// Same as build.IsLocalPath()
if pathErr.Path == "." || pathErr.Path == ".." ||
strings.HasPrefix(pathErr.Path, "./") || strings.HasPrefix(pathErr.Path, "../") {
pathErr.Err = errors.New("is a local import path")
}
}
base.Fatalf("go: %v", err)
}
@ -500,7 +520,7 @@ func CreateModFile(ctx context.Context, modPath string) {
modFile = new(modfile.File)
modFile.AddModuleStmt(modPath)
initTarget(modFile.Module.Mod)
addGoStmt(latestGoVersion()) // Add the go directive before converted module requirements.
addGoStmt(LatestGoVersion()) // Add the go directive before converted module requirements.
convertedFrom, err := convertLegacyConfig(modPath)
if convertedFrom != "" {
@ -536,49 +556,6 @@ func CreateModFile(ctx context.Context, modPath string) {
}
}
// checkModulePathLax checks that the path meets some minimum requirements
// to avoid confusing users or the module cache. The requirements are weaker
// than those of module.CheckPath to allow room for weakening module path
// requirements in the future, but strong enough to help users avoid significant
// problems.
func checkModulePathLax(p string) error {
// TODO(matloob): Replace calls of this function in this CL with calls
// to module.CheckImportPath once it's been laxened, if it becomes laxened.
// See golang.org/issue/29101 for a discussion about whether to make CheckImportPath
// more lax or more strict.
errorf := func(format string, args ...interface{}) error {
return fmt.Errorf("invalid module path %q: %s", p, fmt.Sprintf(format, args...))
}
// Disallow shell characters " ' * < > ? ` | to avoid triggering bugs
// with file systems and subcommands. Disallow file path separators : and \
// because path separators other than / will confuse the module cache.
// See fileNameOK in golang.org/x/mod/module/module.go.
shellChars := "`" + `"'*<>?|`
fsChars := `\:`
if i := strings.IndexAny(p, shellChars); i >= 0 {
return errorf("contains disallowed shell character %q", p[i])
}
if i := strings.IndexAny(p, fsChars); i >= 0 {
return errorf("contains disallowed path separator character %q", p[i])
}
// Ensure path.IsAbs and build.IsLocalImport are false, and that the path is
// invariant under path.Clean, also to avoid confusing the module cache.
if path.IsAbs(p) {
return errorf("is an absolute path")
}
if build.IsLocalImport(p) {
return errorf("is a local import path")
}
if path.Clean(p) != p {
return errorf("is not clean")
}
return nil
}
// fixVersion returns a modfile.VersionFixer implemented using the Query function.
//
// It resolves commit hashes and branch names to versions,
@ -693,7 +670,7 @@ func requirementsFromModFile(ctx context.Context) *Requirements {
for _, n := range mPathCount {
if n > 1 {
var err error
rs, err = updateRoots(ctx, rs.direct, rs, nil, nil)
rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false)
if err != nil {
base.Fatalf("go: %v", err)
}
@ -793,17 +770,39 @@ func addGoStmt(v string) {
rawGoVersion.Store(Target, v)
}
// latestGoVersion returns the latest version of the Go language supported by
// LatestGoVersion returns the latest version of the Go language supported by
// this toolchain, like "1.17".
func latestGoVersion() string {
func LatestGoVersion() string {
tags := build.Default.ReleaseTags
version := tags[len(tags)-1]
if !strings.HasPrefix(version, "go") || !modfile.GoVersionRE.MatchString(version[2:]) {
base.Fatalf("go: unrecognized default version %q", version)
base.Fatalf("go: internal error: unrecognized default version %q", version)
}
return version[2:]
}
// priorGoVersion returns the Go major release immediately preceding v,
// or v itself if v is the first Go major release (1.0) or not a supported
// Go version.
func priorGoVersion(v string) string {
vTag := "go" + v
tags := build.Default.ReleaseTags
for i, tag := range tags {
if tag == vTag {
if i == 0 {
return v
}
version := tags[i-1]
if !strings.HasPrefix(version, "go") || !modfile.GoVersionRE.MatchString(version[2:]) {
base.Fatalf("go: internal error: unrecognized version %q", version)
}
return version[2:]
}
}
return v
}
var altConfigs = []string{
"Gopkg.lock",
@ -918,14 +917,8 @@ func findModulePath(dir string) (string, error) {
}
if rel := search.InDir(dir, filepath.Join(gpdir, "src")); rel != "" && rel != "." {
path := filepath.ToSlash(rel)
// TODO(matloob): replace this with module.CheckImportPath
// once it's been laxened.
// Only checkModulePathLax here. There are some unpublishable
// module names that are compatible with checkModulePathLax
// but they already work in GOPATH so don't break users
// trying to do a build with modules. gorelease will alert users
// publishing their modules to fix their paths.
if err := checkModulePathLax(path); err != nil {
// gorelease will alert users publishing their modules to fix their paths.
if err := module.CheckImportPath(path); err != nil {
badPathErr = err
break
}
@ -1015,10 +1008,14 @@ func commitRequirements(ctx context.Context, goVersion string, rs *Requirements)
Indirect: !rs.direct[m.Path],
})
}
modFile.SetRequire(list)
if goVersion != "" {
modFile.AddGoStmt(goVersion)
}
if semver.Compare("v"+modFileGoVersion(), separateIndirectVersionV) < 0 {
modFile.SetRequire(list)
} else {
modFile.SetRequireSeparateIndirect(list)
}
modFile.Cleanup()
dirty := index.modFileIsDirty(modFile)
@ -1038,6 +1035,13 @@ func commitRequirements(ctx context.Context, goVersion string, rs *Requirements)
}
return
}
gomod := ModFilePath()
if _, ok := fsys.OverlayPath(gomod); ok {
if dirty {
base.Fatalf("go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay")
}
return
}
new, err := modFile.Format()
if err != nil {
@ -1138,12 +1142,11 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums
}
}
if rs.depth == lazy && rs.graph.Load() == nil {
// The main module is lazy and we haven't needed to load the module graph so
// far. Don't incur the cost of loading it now — since we haven't loaded the
// graph, we probably don't have any checksums to contribute to the distant
// parts of the graph anyway. Instead, just request sums for the roots that
// we know about.
if rs.graph.Load() == nil {
// The module graph was not loaded, possibly because the main module is lazy
// or possibly because we haven't needed to load the graph yet.
// Save sums for the root modules (or their replacements), but don't
// incur the cost of loading the graph just to find and retain the sums.
for _, m := range rs.rootModules {
r := resolveReplacement(m)
keep[modkey(r)] = true

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