diff --git a/api/go1.25.txt b/api/go1.25.txt
new file mode 100644
index 0000000000..d50d19545f
--- /dev/null
+++ b/api/go1.25.txt
@@ -0,0 +1,111 @@
+pkg crypto, func SignMessage(Signer, io.Reader, []uint8, SignerOpts) ([]uint8, error) #63405
+pkg crypto, type MessageSigner interface { Public, Sign, SignMessage } #63405
+pkg crypto, type MessageSigner interface, Public() PublicKey #63405
+pkg crypto, type MessageSigner interface, Sign(io.Reader, []uint8, SignerOpts) ([]uint8, error) #63405
+pkg crypto, type MessageSigner interface, SignMessage(io.Reader, []uint8, SignerOpts) ([]uint8, error) #63405
+pkg crypto/ecdsa, func ParseRawPrivateKey(elliptic.Curve, []uint8) (*PrivateKey, error) #63963
+pkg crypto/ecdsa, func ParseUncompressedPublicKey(elliptic.Curve, []uint8) (*PublicKey, error) #63963
+pkg crypto/ecdsa, method (*PrivateKey) Bytes() ([]uint8, error) #63963
+pkg crypto/ecdsa, method (*PublicKey) Bytes() ([]uint8, error) #63963
+pkg crypto/sha3, method (*SHA3) Clone() (hash.Cloner, error) #69521
+pkg crypto/tls, type Config struct, GetEncryptedClientHelloKeys func(*ClientHelloInfo) ([]EncryptedClientHelloKey, error) #71920
+pkg crypto/tls, type ConnectionState struct, CurveID CurveID #67516
+pkg debug/elf, const PT_RISCV_ATTRIBUTES = 1879048195 #72843
+pkg debug/elf, const PT_RISCV_ATTRIBUTES ProgType #72843
+pkg debug/elf, const SHT_RISCV_ATTRIBUTES = 1879048195 #72843
+pkg debug/elf, const SHT_RISCV_ATTRIBUTES SectionType #72843
+pkg go/ast, const FilterFuncDuplicates //deprecated #73088
+pkg go/ast, const FilterImportDuplicates //deprecated #73088
+pkg go/ast, const FilterUnassociatedComments //deprecated #73088
+pkg go/ast, func FilterPackage //deprecated #73088
+pkg go/ast, func MergePackageFiles //deprecated #73088
+pkg go/ast, func PackageExports //deprecated #73088
+pkg go/ast, func PreorderStack(Node, []Node, func(Node, []Node) bool) #73319
+pkg go/ast, type MergeMode //deprecated #73088
+pkg go/parser, func ParseDir //deprecated #71122
+pkg go/token, method (*FileSet) AddExistingFiles(...*File) #73205
+pkg go/types, const FieldVar = 6 #70250
+pkg go/types, const FieldVar VarKind #70250
+pkg go/types, const LocalVar = 2 #70250
+pkg go/types, const LocalVar VarKind #70250
+pkg go/types, const PackageVar = 1 #70250
+pkg go/types, const PackageVar VarKind #70250
+pkg go/types, const ParamVar = 4 #70250
+pkg go/types, const ParamVar VarKind #70250
+pkg go/types, const RecvVar = 3 #70250
+pkg go/types, const RecvVar VarKind #70250
+pkg go/types, const ResultVar = 5 #70250
+pkg go/types, const ResultVar VarKind #70250
+pkg go/types, func LookupSelection(Type, bool, *Package, string) (Selection, bool) #70737
+pkg go/types, method (*Var) Kind() VarKind #70250
+pkg go/types, method (*Var) SetKind(VarKind) #70250
+pkg go/types, method (VarKind) String() string #70250
+pkg go/types, type VarKind uint8 #70250
+pkg hash, type Cloner interface { BlockSize, Clone, Reset, Size, Sum, Write } #69521
+pkg hash, type Cloner interface, BlockSize() int #69521
+pkg hash, type Cloner interface, Clone() (Cloner, error) #69521
+pkg hash, type Cloner interface, Reset() #69521
+pkg hash, type Cloner interface, Size() int #69521
+pkg hash, type Cloner interface, Sum([]uint8) []uint8 #69521
+pkg hash, type Cloner interface, Write([]uint8) (int, error) #69521
+pkg hash, type XOF interface { BlockSize, Read, Reset, Write } #69518
+pkg hash, type XOF interface, BlockSize() int #69518
+pkg hash, type XOF interface, Read([]uint8) (int, error) #69518
+pkg hash, type XOF interface, Reset() #69518
+pkg hash, type XOF interface, Write([]uint8) (int, error) #69518
+pkg hash/maphash, method (*Hash) Clone() (hash.Cloner, error) #69521
+pkg io/fs, func Lstat(FS, string) (FileInfo, error) #49580
+pkg io/fs, func ReadLink(FS, string) (string, error) #49580
+pkg io/fs, type ReadLinkFS interface { Lstat, Open, ReadLink } #49580
+pkg io/fs, type ReadLinkFS interface, Lstat(string) (FileInfo, error) #49580
+pkg io/fs, type ReadLinkFS interface, Open(string) (File, error) #49580
+pkg io/fs, type ReadLinkFS interface, ReadLink(string) (string, error) #49580
+pkg log/slog, func GroupAttrs(string, ...Attr) Attr #66365
+pkg log/slog, method (Record) Source() *Source #70280
+pkg mime/multipart, func FileContentDisposition(string, string) string #46771
+pkg net/http, func NewCrossOriginProtection() *CrossOriginProtection #73626
+pkg net/http, method (*CrossOriginProtection) AddInsecureBypassPattern(string) #73626
+pkg net/http, method (*CrossOriginProtection) AddTrustedOrigin(string) error #73626
+pkg net/http, method (*CrossOriginProtection) Check(*Request) error #73626
+pkg net/http, method (*CrossOriginProtection) Handler(Handler) Handler #73626
+pkg net/http, method (*CrossOriginProtection) SetDenyHandler(Handler) #73626
+pkg net/http, type CrossOriginProtection struct #73626
+pkg os, method (*Root) Chmod(string, fs.FileMode) error #67002
+pkg os, method (*Root) Chown(string, int, int) error #67002
+pkg os, method (*Root) Chtimes(string, time.Time, time.Time) error #67002
+pkg os, method (*Root) Lchown(string, int, int) error #67002
+pkg os, method (*Root) Link(string, string) error #67002
+pkg os, method (*Root) MkdirAll(string, fs.FileMode) error #67002
+pkg os, method (*Root) ReadFile(string) ([]uint8, error) #73126
+pkg os, method (*Root) Readlink(string) (string, error) #67002
+pkg os, method (*Root) RemoveAll(string) error #67002
+pkg os, method (*Root) Rename(string, string) error #67002
+pkg os, method (*Root) Symlink(string, string) error #67002
+pkg os, method (*Root) WriteFile(string, []uint8, fs.FileMode) error #73126
+pkg reflect, func TypeAssert[$0 interface{}](Value) ($0, bool) #62121
+pkg runtime, func SetDefaultGOMAXPROCS() #73193
+pkg runtime/trace, func NewFlightRecorder(FlightRecorderConfig) *FlightRecorder #63185
+pkg runtime/trace, method (*FlightRecorder) Enabled() bool #63185
+pkg runtime/trace, method (*FlightRecorder) Start() error #63185
+pkg runtime/trace, method (*FlightRecorder) Stop() #63185
+pkg runtime/trace, method (*FlightRecorder) WriteTo(io.Writer) (int64, error) #63185
+pkg runtime/trace, type FlightRecorder struct #63185
+pkg runtime/trace, type FlightRecorderConfig struct #63185
+pkg runtime/trace, type FlightRecorderConfig struct, MaxBytes uint64 #63185
+pkg runtime/trace, type FlightRecorderConfig struct, MinAge time.Duration #63185
+pkg sync, method (*WaitGroup) Go(func()) #63796
+pkg testing, method (*B) Attr(string, string) #43936
+pkg testing, method (*B) Output() io.Writer #59928
+pkg testing, method (*F) Attr(string, string) #43936
+pkg testing, method (*F) Output() io.Writer #59928
+pkg testing, method (*T) Attr(string, string) #43936
+pkg testing, method (*T) Output() io.Writer #59928
+pkg testing, type TB interface, Attr(string, string) #43936
+pkg testing, type TB interface, Output() io.Writer #59928
+pkg testing/fstest, method (MapFS) Lstat(string) (fs.FileInfo, error) #49580
+pkg testing/fstest, method (MapFS) ReadLink(string) (string, error) #49580
+pkg testing/synctest, func Test(*testing.T, func(*testing.T)) #67434
+pkg testing/synctest, func Wait() #67434
+pkg unicode, var CategoryAliases map[string]string #70780
+pkg unicode, var Cn *RangeTable #70780
+pkg unicode, var LC *RangeTable #70780
diff --git a/api/next/49097.txt b/api/next/49097.txt
new file mode 100644
index 0000000000..f7240954c6
--- /dev/null
+++ b/api/next/49097.txt
@@ -0,0 +1,4 @@
+pkg net, method (*Dialer) DialIP(context.Context, string, netip.Addr, netip.Addr) (*IPConn, error) #49097
+pkg net, method (*Dialer) DialTCP(context.Context, string, netip.AddrPort, netip.AddrPort) (*TCPConn, error) #49097
+pkg net, method (*Dialer) DialUDP(context.Context, string, netip.AddrPort, netip.AddrPort) (*UDPConn, error) #49097
+pkg net, method (*Dialer) DialUnix(context.Context, string, *UnixAddr, *UnixAddr) (*UnixConn, error) #49097
diff --git a/api/next/51945.txt b/api/next/51945.txt
new file mode 100644
index 0000000000..7db1f093e5
--- /dev/null
+++ b/api/next/51945.txt
@@ -0,0 +1 @@
+pkg errors, func AsType[$0 error](error) ($0, bool) #51945
diff --git a/api/next/61642.txt b/api/next/61642.txt
new file mode 100644
index 0000000000..dd67874ab9
--- /dev/null
+++ b/api/next/61642.txt
@@ -0,0 +1 @@
+pkg net/netip, method (Prefix) Compare(Prefix) int #61642
diff --git a/api/next/63963.txt b/api/next/63963.txt
new file mode 100644
index 0000000000..c1aaae5044
--- /dev/null
+++ b/api/next/63963.txt
@@ -0,0 +1,3 @@
+pkg crypto/ecdsa, type PrivateKey struct, D //deprecated #63963
+pkg crypto/ecdsa, type PublicKey struct, X //deprecated #63963
+pkg crypto/ecdsa, type PublicKey struct, Y //deprecated #63963
diff --git a/api/next/65954.txt b/api/next/65954.txt
new file mode 100644
index 0000000000..88d7c2668f
--- /dev/null
+++ b/api/next/65954.txt
@@ -0,0 +1,6 @@
+pkg log/slog, func NewMultiHandler(...Handler) *MultiHandler #65954
+pkg log/slog, method (*MultiHandler) Enabled(context.Context, Level) bool #65954
+pkg log/slog, method (*MultiHandler) Handle(context.Context, Record) error #65954
+pkg log/slog, method (*MultiHandler) WithAttrs([]Attr) Handler #65954
+pkg log/slog, method (*MultiHandler) WithGroup(string) Handler #65954
+pkg log/slog, type MultiHandler struct #65954
diff --git a/api/next/67546.txt b/api/next/67546.txt
new file mode 100644
index 0000000000..0b5b4b981c
--- /dev/null
+++ b/api/next/67546.txt
@@ -0,0 +1,5 @@
+pkg database/sql/driver, type RowsColumnScanner interface { Close, Columns, Next, ScanColumn } #67546
+pkg database/sql/driver, type RowsColumnScanner interface, Close() error #67546
+pkg database/sql/driver, type RowsColumnScanner interface, Columns() []string #67546
+pkg database/sql/driver, type RowsColumnScanner interface, Next([]Value) error #67546
+pkg database/sql/driver, type RowsColumnScanner interface, ScanColumn(interface{}, int) error #67546
diff --git a/api/next/67813.txt b/api/next/67813.txt
new file mode 100644
index 0000000000..b420a141e1
--- /dev/null
+++ b/api/next/67813.txt
@@ -0,0 +1 @@
+pkg net/http, type HTTP2Config struct, StrictMaxConcurrentRequests bool #67813
diff --git a/api/next/70352.txt b/api/next/70352.txt
new file mode 100644
index 0000000000..cd264046c3
--- /dev/null
+++ b/api/next/70352.txt
@@ -0,0 +1,2 @@
+pkg os, method (*Process) WithHandle(func(uintptr)) error #70352
+pkg os, var ErrNoHandle error #70352
diff --git a/api/next/71287.txt b/api/next/71287.txt
new file mode 100644
index 0000000000..c1e09a1f52
--- /dev/null
+++ b/api/next/71287.txt
@@ -0,0 +1,4 @@
+pkg testing, method (*B) ArtifactDir() string #71287
+pkg testing, method (*F) ArtifactDir() string #71287
+pkg testing, method (*T) ArtifactDir() string #71287
+pkg testing, type TB interface, ArtifactDir() string #71287
diff --git a/api/next/73161.txt b/api/next/73161.txt
new file mode 100644
index 0000000000..86526b597a
--- /dev/null
+++ b/api/next/73161.txt
@@ -0,0 +1 @@
+pkg net/http/httputil, type ReverseProxy struct, Director //deprecated #73161
diff --git a/doc/go1.17_spec.html b/doc/go1.17_spec.html
deleted file mode 100644
index dbff3598b5..0000000000
--- a/doc/go1.17_spec.html
+++ /dev/null
@@ -1,6864 +0,0 @@
-
-
-
Introduction
-
-
-This is the reference manual for the Go programming language as it was for
-language version 1.17, in October 2021, before the introduction of generics.
-It is provided for historical interest.
-The current reference manual can be found here.
-For more information and other documents, see go.dev.
-
-
-
-Go is a general-purpose language designed with systems programming
-in mind. It is strongly typed and garbage-collected and has explicit
-support for concurrent programming. Programs are constructed from
-packages, whose properties allow efficient management of
-dependencies.
-
-
-
-The grammar is compact and simple to parse, allowing for easy analysis
-by automatic tools such as integrated development environments.
-
-
-Notation
-
-The syntax is specified using Extended Backus-Naur Form (EBNF):
-
-
-
-Production = production_name "=" [ Expression ] "." .
-Expression = Alternative { "|" Alternative } .
-Alternative = Term { Term } .
-Term = production_name | token [ "…" token ] | Group | Option | Repetition .
-Group = "(" Expression ")" .
-Option = "[" Expression "]" .
-Repetition = "{" Expression "}" .
-
-
-
-Productions are expressions constructed from terms and the following
-operators, in increasing precedence:
-
-
-| alternation
-() grouping
-[] option (0 or 1 times)
-{} repetition (0 to n times)
-
-
-
-Lower-case production names are used to identify lexical tokens.
-Non-terminals are in CamelCase. Lexical tokens are enclosed in
-double quotes ""
or back quotes ``
.
-
-
-
-The form a … b
represents the set of characters from
-a
through b
as alternatives. The horizontal
-ellipsis …
is also used elsewhere in the spec to informally denote various
-enumerations or code snippets that are not further specified. The character …
-(as opposed to the three characters ...
) is not a token of the Go
-language.
-
-
-Source code representation
-
-
-Source code is Unicode text encoded in
-UTF-8. The text is not
-canonicalized, so a single accented code point is distinct from the
-same character constructed from combining an accent and a letter;
-those are treated as two code points. For simplicity, this document
-will use the unqualified term character to refer to a Unicode code point
-in the source text.
-
-
-Each code point is distinct; for instance, upper and lower case letters
-are different characters.
-
-
-Implementation restriction: For compatibility with other tools, a
-compiler may disallow the NUL character (U+0000) in the source text.
-
-
-Implementation restriction: For compatibility with other tools, 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.
-A byte order mark may be disallowed anywhere else in the source.
-
-
-Characters
-
-
-The following terms are used to denote specific Unicode character classes:
-
-
-newline = /* the Unicode code point U+000A */ .
-unicode_char = /* an arbitrary Unicode code point except newline */ .
-unicode_letter = /* a Unicode code point classified as "Letter" */ .
-unicode_digit = /* a Unicode code point classified as "Number, decimal digit" */ .
-
-
-
-In The Unicode Standard 8.0,
-Section 4.5 "General Category" defines a set of character categories.
-Go treats all characters in any of the Letter categories Lu, Ll, Lt, Lm, or Lo
-as Unicode letters, and those in the Number category Nd as Unicode digits.
-
-
-Letters and digits
-
-
-The underscore character _
(U+005F) is considered a letter.
-
-
-letter = unicode_letter | "_" .
-decimal_digit = "0" … "9" .
-binary_digit = "0" | "1" .
-octal_digit = "0" … "7" .
-hex_digit = "0" … "9" | "A" … "F" | "a" … "f" .
-
-
-Lexical elements
-
-
-
-
-Comments serve as program documentation. There are two forms:
-
-
-
--
-Line comments start with the character sequence
//
-and stop at the end of the line.
-
--
-General comments start with the character sequence
/*
-and stop with the first subsequent character sequence */
.
-
-
-
-
-A comment cannot start inside a rune or
-string literal, or inside a comment.
-A general comment containing no newlines acts like a space.
-Any other comment acts like a newline.
-
-
-Tokens
-
-
-Tokens form the vocabulary of the Go language.
-There are four classes: identifiers, keywords, operators
-and punctuation, and literals. White space, formed from
-spaces (U+0020), horizontal tabs (U+0009),
-carriage returns (U+000D), and newlines (U+000A),
-is ignored except as it separates tokens
-that would otherwise combine into a single token. Also, a newline or end of file
-may trigger the insertion of a semicolon.
-While breaking the input into tokens,
-the next token is the longest sequence of characters that form a
-valid token.
-
-
-Semicolons
-
-
-The formal grammar uses semicolons ";"
as terminators in
-a number of productions. Go programs may omit most of these semicolons
-using the following two rules:
-
-
-
--
-When the input is broken into tokens, a semicolon is automatically inserted
-into the token stream immediately after a line's final token if that token is
-
- - an
- identifier
-
-
- - an
- integer,
- floating-point,
- imaginary,
- rune, or
- string literal
-
-
- - one of the keywords
-
break
,
- continue
,
- fallthrough
, or
- return
-
-
- - one of the operators and punctuation
-
++
,
- --
,
- )
,
- ]
, or
- }
-
-
-
-
--
-To allow complex statements to occupy a single line, a semicolon
-may be omitted before a closing
")"
or "}"
.
-
-
-
-
-To reflect idiomatic use, code examples in this document elide semicolons
-using these rules.
-
-
-
-Identifiers
-
-
-Identifiers name program entities such as variables and types.
-An identifier is a sequence of one or more letters and digits.
-The first character in an identifier must be a letter.
-
-
-identifier = letter { letter | unicode_digit } .
-
-
-a
-_x9
-ThisVariableIsExported
-αβ
-
-
-
-Some identifiers are predeclared.
-
-
-
-Keywords
-
-
-The following keywords are reserved and may not be used as identifiers.
-
-
-break default func interface select
-case defer go map struct
-chan else goto package switch
-const fallthrough if range type
-continue for import return var
-
-
-Operators and punctuation
-
-
-The following character sequences represent operators
-(including assignment operators) and punctuation:
-
-
-+ & += &= && == != ( )
-- | -= |= || < <= [ ]
-* ^ *= ^= <- > >= { }
-/ << /= <<= ++ = := , ;
-% >> %= >>= -- ! ... . :
- &^ &^=
-
-
-Integer literals
-
-
-An integer literal is a sequence of digits representing an
-integer constant.
-An optional prefix sets a non-decimal base: 0b
or 0B
-for binary, 0
, 0o
, or 0O
for octal,
-and 0x
or 0X
for hexadecimal.
-A single 0
is considered a decimal zero.
-In hexadecimal literals, letters a
through f
-and A
through F
represent values 10 through 15.
-
-
-
-For readability, an underscore character _
may appear after
-a base prefix or between successive digits; such underscores do not change
-the literal's value.
-
-
-int_lit = decimal_lit | binary_lit | octal_lit | hex_lit .
-decimal_lit = "0" | ( "1" … "9" ) [ [ "_" ] decimal_digits ] .
-binary_lit = "0" ( "b" | "B" ) [ "_" ] binary_digits .
-octal_lit = "0" [ "o" | "O" ] [ "_" ] octal_digits .
-hex_lit = "0" ( "x" | "X" ) [ "_" ] hex_digits .
-
-decimal_digits = decimal_digit { [ "_" ] decimal_digit } .
-binary_digits = binary_digit { [ "_" ] binary_digit } .
-octal_digits = octal_digit { [ "_" ] octal_digit } .
-hex_digits = hex_digit { [ "_" ] hex_digit } .
-
-
-
-42
-4_2
-0600
-0_600
-0o600
-0O600 // second character is capital letter 'O'
-0xBadFace
-0xBad_Face
-0x_67_7a_2f_cc_40_c6
-170141183460469231731687303715884105727
-170_141183_460469_231731_687303_715884_105727
-
-_42 // an identifier, not an integer literal
-42_ // invalid: _ must separate successive digits
-4__2 // invalid: only one _ at a time
-0_xBadFace // invalid: _ must separate successive digits
-
-
-
-Floating-point literals
-
-
-A floating-point literal is a decimal or hexadecimal representation of a
-floating-point constant.
-
-
-
-A decimal floating-point literal consists of an integer part (decimal digits),
-a decimal point, a fractional part (decimal digits), and an exponent part
-(e
or E
followed by an optional sign and decimal digits).
-One of the integer part or the fractional part may be elided; one of the decimal point
-or the exponent part may be elided.
-An exponent value exp scales the mantissa (integer and fractional part) by 10exp.
-
-
-
-A hexadecimal floating-point literal consists of a 0x
or 0X
-prefix, an integer part (hexadecimal digits), a radix point, a fractional part (hexadecimal digits),
-and an exponent part (p
or P
followed by an optional sign and decimal digits).
-One of the integer part or the fractional part may be elided; the radix point may be elided as well,
-but the exponent part is required. (This syntax matches the one given in IEEE 754-2008 §5.12.3.)
-An exponent value exp scales the mantissa (integer and fractional part) by 2exp.
-
-
-
-For readability, an underscore character _
may appear after
-a base prefix or between successive digits; such underscores do not change
-the literal value.
-
-
-
-float_lit = decimal_float_lit | hex_float_lit .
-
-decimal_float_lit = decimal_digits "." [ decimal_digits ] [ decimal_exponent ] |
- decimal_digits decimal_exponent |
- "." decimal_digits [ decimal_exponent ] .
-decimal_exponent = ( "e" | "E" ) [ "+" | "-" ] decimal_digits .
-
-hex_float_lit = "0" ( "x" | "X" ) hex_mantissa hex_exponent .
-hex_mantissa = [ "_" ] hex_digits "." [ hex_digits ] |
- [ "_" ] hex_digits |
- "." hex_digits .
-hex_exponent = ( "p" | "P" ) [ "+" | "-" ] decimal_digits .
-
-
-
-0.
-72.40
-072.40 // == 72.40
-2.71828
-1.e+0
-6.67428e-11
-1E6
-.25
-.12345E+5
-1_5. // == 15.0
-0.15e+0_2 // == 15.0
-
-0x1p-2 // == 0.25
-0x2.p10 // == 2048.0
-0x1.Fp+0 // == 1.9375
-0X.8p-0 // == 0.5
-0X_1FFFP-16 // == 0.1249847412109375
-0x15e-2 // == 0x15e - 2 (integer subtraction)
-
-0x.p1 // invalid: mantissa has no digits
-1p-2 // invalid: p exponent requires hexadecimal mantissa
-0x1.5e-2 // invalid: hexadecimal mantissa requires p exponent
-1_.5 // invalid: _ must separate successive digits
-1._5 // invalid: _ must separate successive digits
-1.5_e1 // invalid: _ must separate successive digits
-1.5e_1 // invalid: _ must separate successive digits
-1.5e1_ // invalid: _ must separate successive digits
-
-
-
-Imaginary literals
-
-
-An imaginary literal represents the imaginary part of a
-complex constant.
-It consists of an integer or
-floating-point literal
-followed by the lower-case letter i
.
-The value of an imaginary literal is the value of the respective
-integer or floating-point literal multiplied by the imaginary unit i.
-
-
-
-imaginary_lit = (decimal_digits | int_lit | float_lit) "i" .
-
-
-
-For backward compatibility, an imaginary literal's integer part consisting
-entirely of decimal digits (and possibly underscores) is considered a decimal
-integer, even if it starts with a leading 0
.
-
-
-
-0i
-0123i // == 123i for backward-compatibility
-0o123i // == 0o123 * 1i == 83i
-0xabci // == 0xabc * 1i == 2748i
-0.i
-2.71828i
-1.e+0i
-6.67428e-11i
-1E6i
-.25i
-.12345E+5i
-0x1p-2i // == 0x1p-2 * 1i == 0.25i
-
-
-
-Rune literals
-
-
-A rune literal represents a rune constant,
-an integer value identifying a Unicode code point.
-A rune literal is expressed as one or more characters enclosed in single quotes,
-as in 'x'
or '\n'
.
-Within the quotes, any character may appear except newline and unescaped single
-quote. A single quoted character represents the Unicode value
-of the character itself,
-while multi-character sequences beginning with a backslash encode
-values in various formats.
-
-
-
-The simplest form represents the single character within the quotes;
-since Go source text is Unicode characters encoded in UTF-8, multiple
-UTF-8-encoded bytes may represent a single integer value. For
-instance, the literal 'a'
holds a single byte representing
-a literal a
, Unicode U+0061, value 0x61
, while
-'ä'
holds two bytes (0xc3
0xa4
) representing
-a literal a
-dieresis, U+00E4, value 0xe4
.
-
-
-
-Several backslash escapes allow arbitrary values to be encoded as
-ASCII text. There are four ways to represent the integer value
-as a numeric constant: \x
followed by exactly two hexadecimal
-digits; \u
followed by exactly four hexadecimal digits;
-\U
followed by exactly eight hexadecimal digits, and a
-plain backslash \
followed by exactly three octal digits.
-In each case the value of the literal is the value represented by
-the digits in the corresponding base.
-
-
-
-Although these representations all result in an integer, they have
-different valid ranges. Octal escapes must represent a value between
-0 and 255 inclusive. Hexadecimal escapes satisfy this condition
-by construction. The escapes \u
and \U
-represent Unicode code points so within them some values are illegal,
-in particular those above 0x10FFFF
and surrogate halves.
-
-
-
-After a backslash, certain single-character escapes represent special values:
-
-
-
-\a U+0007 alert or bell
-\b U+0008 backspace
-\f U+000C form feed
-\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
-\' U+0027 single quote (valid escape only within rune literals)
-\" U+0022 double quote (valid escape only within string literals)
-
-
-
-All other sequences starting with a backslash are illegal inside rune literals.
-
-
-rune_lit = "'" ( unicode_value | byte_value ) "'" .
-unicode_value = unicode_char | little_u_value | big_u_value | escaped_char .
-byte_value = octal_byte_value | hex_byte_value .
-octal_byte_value = `\` octal_digit octal_digit octal_digit .
-hex_byte_value = `\` "x" hex_digit hex_digit .
-little_u_value = `\` "u" hex_digit hex_digit hex_digit hex_digit .
-big_u_value = `\` "U" hex_digit hex_digit hex_digit hex_digit
- hex_digit hex_digit hex_digit hex_digit .
-escaped_char = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | "'" | `"` ) .
-
-
-
-'a'
-'ä'
-'本'
-'\t'
-'\000'
-'\007'
-'\377'
-'\x07'
-'\xff'
-'\u12e4'
-'\U00101234'
-'\'' // rune literal containing single quote character
-'aa' // illegal: too many characters
-'\xa' // illegal: too few hexadecimal digits
-'\0' // illegal: too few octal digits
-'\uDFFF' // illegal: surrogate half
-'\U00110000' // illegal: invalid Unicode code point
-
-
-
-String literals
-
-
-A string literal represents a string constant
-obtained from concatenating a sequence of characters. There are two forms:
-raw string literals and interpreted string literals.
-
-
-
-Raw string literals are character sequences between back quotes, as in
-`foo`
. Within the quotes, any character may appear except
-back quote. The value of a raw string literal is the
-string composed of the uninterpreted (implicitly UTF-8-encoded) characters
-between the quotes;
-in particular, backslashes have no special meaning and the string may
-contain newlines.
-Carriage return characters ('\r') inside raw string literals
-are discarded from the raw string value.
-
-
-
-Interpreted string literals are character sequences between double
-quotes, as in "bar"
.
-Within the quotes, any character may appear except newline and unescaped double quote.
-The text between the quotes forms the
-value of the literal, with backslash escapes interpreted as they
-are in rune literals (except that \'
is illegal and
-\"
is legal), with the same restrictions.
-The three-digit octal (\
nnn)
-and two-digit hexadecimal (\x
nn) escapes represent individual
-bytes of the resulting string; all other escapes represent
-the (possibly multi-byte) UTF-8 encoding of individual characters.
-Thus inside a string literal \377
and \xFF
represent
-a single byte of value 0xFF
=255, while ÿ
,
-\u00FF
, \U000000FF
and \xc3\xbf
represent
-the two bytes 0xc3
0xbf
of the UTF-8 encoding of character
-U+00FF.
-
-
-
-string_lit = raw_string_lit | interpreted_string_lit .
-raw_string_lit = "`" { unicode_char | newline } "`" .
-interpreted_string_lit = `"` { unicode_value | byte_value } `"` .
-
-
-
-`abc` // same as "abc"
-`\n
-\n` // same as "\\n\n\\n"
-"\n"
-"\"" // same as `"`
-"Hello, world!\n"
-"日本語"
-"\u65e5本\U00008a9e"
-"\xff\u00FF"
-"\uD800" // illegal: surrogate half
-"\U00110000" // illegal: invalid Unicode code point
-
-
-
-These examples all represent the same string:
-
-
-
-"日本語" // UTF-8 input text
-`日本語` // UTF-8 input text as a raw literal
-"\u65e5\u672c\u8a9e" // the explicit Unicode code points
-"\U000065e5\U0000672c\U00008a9e" // the explicit Unicode code points
-"\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e" // the explicit UTF-8 bytes
-
-
-
-If the source code represents a character as two code points, such as
-a combining form involving an accent and a letter, the result will be
-an error if placed in a rune literal (it is not a single code
-point), and will appear as two code points if placed in a string
-literal.
-
-
-
-Constants
-
-There are boolean constants,
-rune constants,
-integer constants,
-floating-point constants, complex constants,
-and string constants. Rune, integer, floating-point,
-and complex constants are
-collectively called numeric constants.
-
-
-
-A constant value is represented by a
-rune,
-integer,
-floating-point,
-imaginary,
-or
-string literal,
-an identifier denoting a constant,
-a constant expression,
-a conversion with a result that is a constant, or
-the result value of some built-in functions such as
-unsafe.Sizeof
applied to any value,
-cap
or len
applied to
-some expressions,
-real
and imag
applied to a complex constant
-and complex
applied to numeric constants.
-The boolean truth values are represented by the predeclared constants
-true
and false
. The predeclared identifier
-iota denotes an integer constant.
-
-
-
-In general, complex constants are a form of
-constant expression
-and are discussed in that section.
-
-
-
-Numeric constants represent exact values of arbitrary precision and do not overflow.
-Consequently, there are no constants denoting the IEEE 754 negative zero, infinity,
-and not-a-number values.
-
-
-
-Constants may be typed or untyped.
-Literal constants, true
, false
, iota
,
-and certain constant expressions
-containing only untyped constant operands are untyped.
-
-
-
-A constant may be given a type explicitly by a constant declaration
-or conversion, or implicitly when used in a
-variable declaration or an
-assignment or as an
-operand in an expression.
-It is an error if the constant value
-cannot be represented as a value of the respective type.
-
-
-
-An untyped constant has a default type which is the type to which the
-constant is implicitly converted in contexts where a typed value is required,
-for instance, in a short variable declaration
-such as i := 0
where there is no explicit type.
-The default type of an untyped constant is bool
, rune
,
-int
, float64
, complex128
or string
-respectively, depending on whether it is a boolean, rune, integer, floating-point,
-complex, or string constant.
-
-
-
-Implementation restriction: Although numeric constants have arbitrary
-precision in the language, a compiler may implement them using an
-internal representation with limited precision. That said, every
-implementation must:
-
-
-
- - Represent integer constants with at least 256 bits.
-
- - Represent floating-point constants, including the parts of
- a complex constant, with a mantissa of at least 256 bits
- and a signed binary exponent of at least 16 bits.
-
- - Give an error if unable to represent an integer constant
- precisely.
-
- - Give an error if unable to represent a floating-point or
- complex constant due to overflow.
-
- - Round to the nearest representable constant if unable to
- represent a floating-point or complex constant due to limits
- on precision.
-
-
-
-These requirements apply both to literal constants and to the result
-of evaluating constant
-expressions.
-
-
-
-Variables
-
-
-A variable is a storage location for holding a value.
-The set of permissible values is determined by the
-variable's type.
-
-
-
-A variable declaration
-or, for function parameters and results, the signature
-of a function declaration
-or function literal reserves
-storage for a named variable.
-
-Calling the built-in function new
-or taking the address of a composite literal
-allocates storage for a variable at run time.
-Such an anonymous variable is referred to via a (possibly implicit)
-pointer indirection.
-
-
-
-Structured variables of array, slice,
-and struct types have elements and fields that may
-be addressed individually. Each such element
-acts like a variable.
-
-
-
-The static type (or just type) of a variable is the
-type given in its declaration, the type provided in the
-new
call or composite literal, or the type of
-an element of a structured variable.
-Variables of interface type also have a distinct dynamic type,
-which is the concrete type of the value assigned to the variable at run time
-(unless the value is the predeclared identifier nil
,
-which has no type).
-The dynamic type may vary during execution but values stored in interface
-variables are always assignable
-to the static type of the variable.
-
-
-
-var x interface{} // x is nil and has static type interface{}
-var v *T // v has value nil, static type *T
-x = 42 // x has value 42 and dynamic type int
-x = v // x has value (*T)(nil) and dynamic type *T
-
-
-
-A variable's value is retrieved by referring to the variable in an
-expression; it is the most recent value
-assigned to the variable.
-If a variable has not yet been assigned a value, its value is the
-zero value for its type.
-
-
-
-Types
-
-
-A type determines a set of values together with operations and methods specific
-to those values. A type may be denoted by a type name, if it has one,
-or specified using a type literal, which composes a type from existing types.
-
-
-
-Type = TypeName | TypeLit | "(" Type ")" .
-TypeName = identifier | QualifiedIdent .
-TypeLit = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
- SliceType | MapType | ChannelType .
-
-
-
-The language predeclares certain type names.
-Others are introduced with type declarations.
-Composite types—array, struct, pointer, function,
-interface, slice, map, and channel types—may be constructed using
-type literals.
-
-
-
-Each type T
has an underlying type: If T
-is one of the predeclared boolean, numeric, or string types, or a type literal,
-the corresponding underlying
-type is T
itself. Otherwise, T
's underlying type
-is the underlying type of the type to which T
refers in its
-type declaration.
-
-
-
-type (
- A1 = string
- A2 = A1
-)
-
-type (
- B1 string
- B2 B1
- B3 []B1
- B4 B3
-)
-
-
-
-The underlying type of string
, A1
, A2
, B1
,
-and B2
is string
.
-The underlying type of []B1
, B3
, and B4
is []B1
.
-
-
-Method sets
-
-A type has a (possibly empty) method set associated with it.
-The method set of an interface type is its interface.
-The method set of any other type T
consists of all
-methods declared with receiver type T
.
-The method set of the corresponding pointer type *T
-is the set of all methods declared with receiver *T
or T
-(that is, it also contains the method set of T
).
-Further rules apply to structs containing embedded fields, as described
-in the section on struct types.
-Any other type has an empty method set.
-In a method set, each method must have a
-unique
-non-blank method name.
-
-
-
-The method set of a type determines the interfaces that the
-type implements
-and the methods that can be called
-using a receiver of that type.
-
-
-Boolean types
-
-
-A boolean type represents the set of Boolean truth values
-denoted by the predeclared constants true
-and false
. The predeclared boolean type is bool
;
-it is a defined type.
-
-
-Numeric types
-
-
-A numeric type represents sets of integer or floating-point values.
-The predeclared architecture-independent numeric types are:
-
-
-
-uint8 the set of all unsigned 8-bit integers (0 to 255)
-uint16 the set of all unsigned 16-bit integers (0 to 65535)
-uint32 the set of all unsigned 32-bit integers (0 to 4294967295)
-uint64 the set of all unsigned 64-bit integers (0 to 18446744073709551615)
-
-int8 the set of all signed 8-bit integers (-128 to 127)
-int16 the set of all signed 16-bit integers (-32768 to 32767)
-int32 the set of all signed 32-bit integers (-2147483648 to 2147483647)
-int64 the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)
-
-float32 the set of all IEEE 754 32-bit floating-point numbers
-float64 the set of all IEEE 754 64-bit floating-point numbers
-
-complex64 the set of all complex numbers with float32 real and imaginary parts
-complex128 the set of all complex numbers with float64 real and imaginary parts
-
-byte alias for uint8
-rune alias for int32
-
-
-
-The value of an n-bit integer is n bits wide and represented using
-two's complement arithmetic.
-
-
-
-There is also a set of predeclared numeric types with implementation-specific sizes:
-
-
-
-uint either 32 or 64 bits
-int same size as uint
-uintptr an unsigned integer large enough to store the uninterpreted bits of a pointer value
-
-
-
-To avoid portability issues all numeric types are defined
-types and thus distinct except
-byte
, which is an alias for uint8
, and
-rune
, which is an alias for int32
.
-Explicit conversions
-are required when different numeric types are mixed in an expression
-or assignment. For instance, int32
and int
-are not the same type even though they may have the same size on a
-particular architecture.
-
-
-String types
-
-
-A string type represents the set of string values.
-A string value is a (possibly empty) sequence of bytes.
-The number of bytes is called the length of the string and is never negative.
-Strings are immutable: once created,
-it is impossible to change the contents of a string.
-The predeclared string type is string
;
-it is a defined type.
-
-
-
-The length of a string s
can be discovered using
-the built-in function len
.
-The length is a compile-time constant if the string is a constant.
-A string's bytes can be accessed by integer indices
-0 through len(s)-1
.
-It is illegal to take the address of such an element; if
-s[i]
is the i
'th byte of a
-string, &s[i]
is invalid.
-
-
-
-Array types
-
-
-An array is a numbered sequence of elements of a single
-type, called the element type.
-The number of elements is called the length of the array and is never negative.
-
-
-
-ArrayType = "[" ArrayLength "]" ElementType .
-ArrayLength = Expression .
-ElementType = Type .
-
-
-
-The length is part of the array's type; it must evaluate to a
-non-negative constant
-representable by a value
-of type int
.
-The length of array a
can be discovered
-using the built-in function len
.
-The elements can be addressed by integer indices
-0 through len(a)-1
.
-Array types are always one-dimensional but may be composed to form
-multi-dimensional types.
-
-
-
-[32]byte
-[2*N] struct { x, y int32 }
-[1000]*float64
-[3][5]int
-[2][2][2]float64 // same as [2]([2]([2]float64))
-
-
-Slice types
-
-
-A slice is a descriptor for a contiguous segment of an underlying array and
-provides access to a numbered sequence of elements from that array.
-A slice type denotes the set of all slices of arrays of its element type.
-The number of elements is called the length of the slice and is never negative.
-The value of an uninitialized slice is nil
.
-
-
-
-SliceType = "[" "]" ElementType .
-
-
-
-The length of a slice s
can be discovered by the built-in function
-len
; unlike with arrays it may change during
-execution. The elements can be addressed by integer indices
-0 through len(s)-1
. The slice index of a
-given element may be less than the index of the same element in the
-underlying array.
-
-
-A slice, once initialized, is always associated with an underlying
-array that holds its elements. A slice therefore shares storage
-with its array and with other slices of the same array; by contrast,
-distinct arrays always represent distinct storage.
-
-
-The array underlying a slice may extend past the end of the slice.
-The capacity is a measure of that extent: it is the sum of
-the length of the slice and the length of the array beyond the slice;
-a slice of length up to that capacity can be created by
-slicing a new one from the original slice.
-The capacity of a slice a
can be discovered using the
-built-in function cap(a)
.
-
-
-
-A new, initialized slice value for a given element type T
is
-made using the built-in function
-make
,
-which takes a slice type
-and parameters specifying the length and optionally the capacity.
-A slice created with make
always allocates a new, hidden array
-to which the returned slice value refers. That is, executing
-
-
-
-make([]T, length, capacity)
-
-
-
-produces the same slice as allocating an array and slicing
-it, so these two expressions are equivalent:
-
-
-
-make([]int, 50, 100)
-new([100]int)[0:50]
-
-
-
-Like arrays, slices are always one-dimensional but may be composed to construct
-higher-dimensional objects.
-With arrays of arrays, the inner arrays are, by construction, always the same length;
-however with slices of slices (or arrays of slices), the inner lengths may vary dynamically.
-Moreover, the inner slices must be initialized individually.
-
-
-Struct types
-
-
-A struct is a sequence of named elements, called fields, each of which has a
-name and a type. Field names may be specified explicitly (IdentifierList) or
-implicitly (EmbeddedField).
-Within a struct, non-blank field names must
-be unique.
-
-
-
-StructType = "struct" "{" { FieldDecl ";" } "}" .
-FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] .
-EmbeddedField = [ "*" ] TypeName .
-Tag = string_lit .
-
-
-
-// An empty struct.
-struct {}
-
-// A struct with 6 fields.
-struct {
- x, y int
- u float32
- _ float32 // padding
- A *[]int
- F func()
-}
-
-
-
-A field declared with a type but no explicit field name is called an embedded field.
-An embedded field must be specified as
-a type name T
or as a pointer to a non-interface type name *T
,
-and T
itself may not be
-a pointer type. The unqualified type name acts as the field name.
-
-
-
-// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4
-struct {
- T1 // field name is T1
- *T2 // field name is T2
- P.T3 // field name is T3
- *P.T4 // field name is T4
- x, y int // field names are x and y
-}
-
-
-
-The following declaration is illegal because field names must be unique
-in a struct type:
-
-
-
-struct {
- T // conflicts with embedded field *T and *P.T
- *T // conflicts with embedded field T and *P.T
- *P.T // conflicts with embedded field T and *T
-}
-
-
-
-A field or method f
of an
-embedded field in a struct x
is called promoted if
-x.f
is a legal selector that denotes
-that field or method f
.
-
-
-
-Promoted fields act like ordinary fields
-of a struct except that they cannot be used as field names in
-composite literals of the struct.
-
-
-
-Given a struct type S
and a defined type
-T
, promoted methods are included in the method set of the struct as follows:
-
-
- -
- If
S
contains an embedded field T
,
- the method sets of S
- and *S
both include promoted methods with receiver
- T
. The method set of *S
also
- includes promoted methods with receiver *T
.
-
-
- -
- If
S
contains an embedded field *T
,
- the method sets of S
and *S
both
- include promoted methods with receiver T
or
- *T
.
-
-
-
-
-A field declaration may be followed by an optional string literal tag,
-which becomes an attribute for all the fields in the corresponding
-field declaration. An empty tag string is equivalent to an absent tag.
-The tags are made visible through a reflection interface
-and take part in type identity for structs
-but are otherwise ignored.
-
-
-
-struct {
- x, y float64 "" // an empty tag string is like an absent tag
- name string "any string is permitted as a tag"
- _ [4]byte "ceci n'est pas un champ de structure"
-}
-
-// A struct corresponding to a TimeStamp protocol buffer.
-// The tag strings define the protocol buffer field numbers;
-// they follow the convention outlined by the reflect package.
-struct {
- microsec uint64 `protobuf:"1"`
- serverIP6 uint64 `protobuf:"2"`
-}
-
-
-Pointer types
-
-
-A pointer type denotes the set of all pointers to variables of a given
-type, called the base type of the pointer.
-The value of an uninitialized pointer is nil
.
-
-
-
-PointerType = "*" BaseType .
-BaseType = Type .
-
-
-
-*Point
-*[4]int
-
-
-Function types
-
-
-A function type denotes the set of all functions with the same parameter
-and result types. The value of an uninitialized variable of function type
-is nil
.
-
-
-
-FunctionType = "func" Signature .
-Signature = Parameters [ Result ] .
-Result = Parameters | Type .
-Parameters = "(" [ ParameterList [ "," ] ] ")" .
-ParameterList = ParameterDecl { "," ParameterDecl } .
-ParameterDecl = [ IdentifierList ] [ "..." ] Type .
-
-
-
-Within a list of parameters or results, the names (IdentifierList)
-must either all be present or all be absent. If present, each name
-stands for one item (parameter or result) of the specified type and
-all non-blank names in the signature
-must be unique.
-If absent, each type stands for one item of that type.
-Parameter and result
-lists are always parenthesized except that if there is exactly
-one unnamed result it may be written as an unparenthesized type.
-
-
-
-The final incoming parameter in a function signature may have
-a type prefixed with ...
.
-A function with such a parameter is called variadic and
-may be invoked with zero or more arguments for that parameter.
-
-
-
-func()
-func(x int) int
-func(a, _ int, z float32) bool
-func(a, b int, z float32) (bool)
-func(prefix string, values ...int)
-func(a, b int, z float64, opt ...interface{}) (success bool)
-func(int, int, float64) (float64, *[]int)
-func(n int) func(p *T)
-
-
-
-Interface types
-
-
-An interface type specifies a method set called its interface.
-A variable of interface type can store a value of any type with a method set
-that is any superset of the interface. Such a type is said to
-implement the interface.
-The value of an uninitialized variable of interface type is nil
.
-
-
-
-InterfaceType = "interface" "{" { ( MethodSpec | InterfaceTypeName ) ";" } "}" .
-MethodSpec = MethodName Signature .
-MethodName = identifier .
-InterfaceTypeName = TypeName .
-
-
-
-An interface type may specify methods explicitly through method specifications,
-or it may embed methods of other interfaces through interface type names.
-
-
-
-// A simple File interface.
-interface {
- Read([]byte) (int, error)
- Write([]byte) (int, error)
- Close() error
-}
-
-
-
-The name of each explicitly specified method must be unique
-and not blank.
-
-
-
-interface {
- String() string
- String() string // illegal: String not unique
- _(x int) // illegal: method must have non-blank name
-}
-
-
-
-More than one type may implement an interface.
-For instance, if two types S1
and S2
-have the method set
-
-
-
-func (p T) Read(p []byte) (n int, err error)
-func (p T) Write(p []byte) (n int, err error)
-func (p T) Close() error
-
-
-
-(where T
stands for either S1
or S2
)
-then the File
interface is implemented by both S1
and
-S2
, regardless of what other methods
-S1
and S2
may have or share.
-
-
-
-A type implements any interface comprising any subset of its methods
-and may therefore implement several distinct interfaces. For
-instance, all types implement the empty interface:
-
-
-
-interface{}
-
-
-
-Similarly, consider this interface specification,
-which appears within a type declaration
-to define an interface called Locker
:
-
-
-
-type Locker interface {
- Lock()
- Unlock()
-}
-
-
-
-If S1
and S2
also implement
-
-
-
-func (p T) Lock() { … }
-func (p T) Unlock() { … }
-
-
-
-they implement the Locker
interface as well
-as the File
interface.
-
-
-
-An interface T
may use a (possibly qualified) interface type
-name E
in place of a method specification. This is called
-embedding interface E
in T
.
-The method set of T
is the union
-of the method sets of T
’s explicitly declared methods and of
-T
’s embedded interfaces.
-
-
-
-type Reader interface {
- Read(p []byte) (n int, err error)
- Close() error
-}
-
-type Writer interface {
- Write(p []byte) (n int, err error)
- Close() error
-}
-
-// ReadWriter's methods are Read, Write, and Close.
-type ReadWriter interface {
- Reader // includes methods of Reader in ReadWriter's method set
- Writer // includes methods of Writer in ReadWriter's method set
-}
-
-
-
-A union of method sets contains the (exported and non-exported)
-methods of each method set exactly once, and methods with the
-same names must
-have identical signatures.
-
-
-
-type ReadCloser interface {
- Reader // includes methods of Reader in ReadCloser's method set
- Close() // illegal: signatures of Reader.Close and Close are different
-}
-
-
-
-An interface type T
may not embed itself
-or any interface type that embeds T
, recursively.
-
-
-
-// illegal: Bad cannot embed itself
-type Bad interface {
- Bad
-}
-
-// illegal: Bad1 cannot embed itself using Bad2
-type Bad1 interface {
- Bad2
-}
-type Bad2 interface {
- Bad1
-}
-
-
-Map types
-
-
-A map is an unordered group of elements of one type, called the
-element type, indexed by a set of unique keys of another type,
-called the key type.
-The value of an uninitialized map is nil
.
-
-
-
-MapType = "map" "[" KeyType "]" ElementType .
-KeyType = Type .
-
-
-
-The comparison operators
-==
and !=
must be fully defined
-for operands of the key type; thus the key type must not be a function, map, or
-slice.
-If the key type is an interface type, these
-comparison operators must be defined for the dynamic key values;
-failure will cause a run-time panic.
-
-
-
-
-map[string]int
-map[*T]struct{ x, y float64 }
-map[string]interface{}
-
-
-
-The number of map elements is called its length.
-For a map m
, it can be discovered using the
-built-in function len
-and may change during execution. Elements may be added during execution
-using assignments and retrieved with
-index expressions; they may be removed with the
-delete
built-in function.
-
-
-A new, empty map value is made using the built-in
-function make
,
-which takes the map type and an optional capacity hint as arguments:
-
-
-
-make(map[string]int)
-make(map[string]int, 100)
-
-
-
-The initial capacity does not bound its size:
-maps grow to accommodate the number of items
-stored in them, with the exception of nil
maps.
-A nil
map is equivalent to an empty map except that no elements
-may be added.
-
-
-Channel types
-
-
-A channel provides a mechanism for
-concurrently executing functions
-to communicate by
-sending and
-receiving
-values of a specified element type.
-The value of an uninitialized channel is nil
.
-
-
-
-ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .
-
-
-
-The optional <-
operator specifies the channel direction,
-send or receive. If no direction is given, the channel is
-bidirectional.
-A channel may be constrained only to send or only to receive by
-assignment or
-explicit conversion.
-
-
-
-chan T // can be used to send and receive values of type T
-chan<- float64 // can only be used to send float64s
-<-chan int // can only be used to receive ints
-
-
-
-The <-
operator associates with the leftmost chan
-possible:
-
-
-
-chan<- chan int // same as chan<- (chan int)
-chan<- <-chan int // same as chan<- (<-chan int)
-<-chan <-chan int // same as <-chan (<-chan int)
-chan (<-chan int)
-
-
-
-A new, initialized channel
-value can be made using the built-in function
-make
,
-which takes the channel type and an optional capacity as arguments:
-
-
-
-make(chan int, 100)
-
-
-
-The capacity, in number of elements, sets the size of the buffer in the channel.
-If the capacity is zero or absent, the channel is unbuffered and communication
-succeeds only when both a sender and receiver are ready. Otherwise, the channel
-is buffered and communication succeeds without blocking if the buffer
-is not full (sends) or not empty (receives).
-A nil
channel is never ready for communication.
-
-
-
-A channel may be closed with the built-in function
-close
.
-The multi-valued assignment form of the
-receive operator
-reports whether a received value was sent before
-the channel was closed.
-
-
-
-A single channel may be used in
-send statements,
-receive operations,
-and calls to the built-in functions
-cap
and
-len
-by any number of goroutines without further synchronization.
-Channels act as first-in-first-out queues.
-For example, if one goroutine sends values on a channel
-and a second goroutine receives them, the values are
-received in the order sent.
-
-
-Properties of types and values
-
-Type identity
-
-
-Two types are either identical or different.
-
-
-
-A defined type is always different from any other type.
-Otherwise, two types are identical if their underlying type literals are
-structurally equivalent; that is, they have the same literal structure and corresponding
-components have identical types. In detail:
-
-
-
- - Two array types are identical if they have identical element types and
- the same array length.
-
- - Two slice types are identical if they have identical element types.
-
- - Two struct types are identical if they have the same sequence of fields,
- and if corresponding fields have the same names, and identical types,
- and identical tags.
- Non-exported field names from different
- packages are always different.
-
- - Two pointer types are identical if they have identical base types.
-
- - Two function types are identical if they have the same number of parameters
- and result values, corresponding parameter and result types are
- identical, and either both functions are variadic or neither is.
- Parameter and result names are not required to match.
-
- - Two interface types are identical if they have the same set of methods
- with the same names and identical function types.
- Non-exported method names from different
- packages are always different. The order of the methods is irrelevant.
-
- - Two map types are identical if they have identical key and element types.
-
- - Two channel types are identical if they have identical element types and
- the same direction.
-
-
-
-Given the declarations
-
-
-
-type (
- A0 = []string
- A1 = A0
- A2 = struct{ a, b int }
- A3 = int
- A4 = func(A3, float64) *A0
- A5 = func(x int, _ float64) *[]string
-)
-
-type (
- B0 A0
- B1 []string
- B2 struct{ a, b int }
- B3 struct{ a, c int }
- B4 func(int, float64) *B0
- B5 func(x int, y float64) *A1
-)
-
-type C0 = B0
-
-
-
-these types are identical:
-
-
-
-A0, A1, and []string
-A2 and struct{ a, b int }
-A3 and int
-A4, func(int, float64) *[]string, and A5
-
-B0 and C0
-[]int and []int
-struct{ a, b *T5 } and struct{ a, b *T5 }
-func(x int, y float64) *[]string, func(int, float64) (result *[]string), and A5
-
-
-
-B0
and B1
are different because they are new types
-created by distinct type definitions;
-func(int, float64) *B0
and func(x int, y float64) *[]string
-are different because B0
is different from []string
.
-
-
-
-Assignability
-
-
-A value x
is assignable to a variable of type T
-("x
is assignable to T
") if one of the following conditions applies:
-
-
-
--
-
x
's type is identical to T
.
-
--
-
x
's type V
and T
have identical
-underlying types and at least one of V
-or T
is not a defined type.
-
--
-
T
is an interface type and
-x
implements T
.
-
--
-
x
is a bidirectional channel value, T
is a channel type,
-x
's type V
and T
have identical element types,
-and at least one of V
or T
is not a defined type.
-
--
-
x
is the predeclared identifier nil
and T
-is a pointer, function, slice, map, channel, or interface type.
-
--
-
x
is an untyped constant
-representable
-by a value of type T
.
-
-
-
-
-Representability
-
-
-A constant x
is representable
-by a value of type T
if one of the following conditions applies:
-
-
-
--
-
x
is in the set of values determined by T
.
-
-
--
-
T
is a floating-point type and x
can be rounded to T
's
-precision without overflow. Rounding uses IEEE 754 round-to-even rules but with an IEEE
-negative zero further simplified to an unsigned zero. Note that constant values never result
-in an IEEE negative zero, NaN, or infinity.
-
-
--
-
T
is a complex type, and x
's
-components real(x)
and imag(x)
-are representable by values of T
's component type (float32
or
-float64
).
-
-
-
-
-x T x is representable by a value of T because
-
-'a' byte 97 is in the set of byte values
-97 rune rune is an alias for int32, and 97 is in the set of 32-bit integers
-"foo" string "foo" is in the set of string values
-1024 int16 1024 is in the set of 16-bit integers
-42.0 byte 42 is in the set of unsigned 8-bit integers
-1e10 uint64 10000000000 is in the set of unsigned 64-bit integers
-2.718281828459045 float32 2.718281828459045 rounds to 2.7182817 which is in the set of float32 values
--1e-1000 float64 -1e-1000 rounds to IEEE -0.0 which is further simplified to 0.0
-0i int 0 is an integer value
-(42 + 0i) float32 42.0 (with zero imaginary part) is in the set of float32 values
-
-
-
-x T x is not representable by a value of T because
-
-0 bool 0 is not in the set of boolean values
-'a' string 'a' is a rune, it is not in the set of string values
-1024 byte 1024 is not in the set of unsigned 8-bit integers
--1 uint16 -1 is not in the set of unsigned 16-bit integers
-1.1 int 1.1 is not an integer value
-42i float32 (0 + 42i) is not in the set of float32 values
-1e1000 float64 1e1000 overflows to IEEE +Inf after rounding
-
-
-
-Blocks
-
-
-A block is a possibly empty sequence of declarations and statements
-within matching brace brackets.
-
-
-
-Block = "{" StatementList "}" .
-StatementList = { Statement ";" } .
-
-
-
-In addition to explicit blocks in the source code, there are implicit blocks:
-
-
-
- - The universe block encompasses all Go source text.
-
- - Each package has a package block containing all
- Go source text for that package.
-
- - Each file has a file block containing all Go source text
- in that file.
-
- - Each "if",
- "for", and
- "switch"
- statement is considered to be in its own implicit block.
-
- - Each clause in a "switch"
- or "select" statement
- acts as an implicit block.
-
-
-
-Blocks nest and influence scoping.
-
-
-
-Declarations and scope
-
-
-A declaration binds a non-blank identifier to a
-constant,
-type,
-variable,
-function,
-label, or
-package.
-Every identifier in a program must be declared.
-No identifier may be declared twice in the same block, and
-no identifier may be declared in both the file and package block.
-
-
-
-The blank identifier may be used like any other identifier
-in a declaration, but it does not introduce a binding and thus is not declared.
-In the package block, the identifier init
may only be used for
-init
function declarations,
-and like the blank identifier it does not introduce a new binding.
-
-
-
-Declaration = ConstDecl | TypeDecl | VarDecl .
-TopLevelDecl = Declaration | FunctionDecl | MethodDecl .
-
-
-
-The scope of a declared identifier is the extent of source text in which
-the identifier denotes the specified constant, type, variable, function, label, or package.
-
-
-
-Go is lexically scoped using blocks:
-
-
-
- - The scope of a predeclared identifier is the universe block.
-
- - The scope of an identifier denoting a constant, type, variable,
- or function (but not method) declared at top level (outside any
- function) is the package block.
-
- - The scope of the package name of an imported package is the file block
- of the file containing the import declaration.
-
- - The scope of an identifier denoting a method receiver, function parameter,
- or result variable is the function body.
-
- - The scope of a constant or variable identifier declared
- inside a function begins at the end of the ConstSpec or VarSpec
- (ShortVarDecl for short variable declarations)
- and ends at the end of the innermost containing block.
-
- - The scope of a type identifier declared inside a function
- begins at the identifier in the TypeSpec
- and ends at the end of the innermost containing block.
-
-
-
-An identifier declared in a block may be redeclared in an inner block.
-While the identifier of the inner declaration is in scope, it denotes
-the entity declared by the inner declaration.
-
-
-
-The package clause is not a declaration; the package name
-does not appear in any scope. Its purpose is to identify the files belonging
-to the same package and to specify the default package name for import
-declarations.
-
-
-
-Label scopes
-
-
-Labels are declared by labeled statements and are
-used in the "break",
-"continue", and
-"goto" statements.
-It is illegal to define a label that is never used.
-In contrast to other identifiers, labels are not block scoped and do
-not conflict with identifiers that are not labels. The scope of a label
-is the body of the function in which it is declared and excludes
-the body of any nested function.
-
-
-
-Blank identifier
-
-
-The blank identifier is represented by the underscore character _
.
-It serves as an anonymous placeholder instead of a regular (non-blank)
-identifier and has special meaning in declarations,
-as an operand, and in assignments.
-
-
-
-Predeclared identifiers
-
-
-The following identifiers are implicitly declared in the
-universe block:
-
-
-Types:
- bool byte complex64 complex128 error float32 float64
- int int8 int16 int32 int64 rune string
- uint uint8 uint16 uint32 uint64 uintptr
-
-Constants:
- true false iota
-
-Zero value:
- nil
-
-Functions:
- append cap close complex copy delete imag len
- make new panic print println real recover
-
-
-
-Exported identifiers
-
-
-An identifier may be exported to permit access to it from another package.
-An identifier is exported if both:
-
-
- - the first character of the identifier's name is a Unicode upper case
- letter (Unicode class "Lu"); and
- - the identifier is declared in the package block
- or it is a field name or
- method name.
-
-
-All other identifiers are not exported.
-
-
-
-Uniqueness of identifiers
-
-
-Given a set of identifiers, an identifier is called unique if it is
-different from every other in the set.
-Two identifiers are different if they are spelled differently, or if they
-appear in different packages and are not
-exported. Otherwise, they are the same.
-
-
-Constant declarations
-
-
-A constant declaration binds a list of identifiers (the names of
-the constants) to the values of a list of constant expressions.
-The number of identifiers must be equal
-to the number of expressions, and the nth identifier on
-the left is bound to the value of the nth expression on the
-right.
-
-
-
-ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
-ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] .
-
-IdentifierList = identifier { "," identifier } .
-ExpressionList = Expression { "," Expression } .
-
-
-
-If the type is present, all constants take the type specified, and
-the expressions must be assignable to that type.
-If the type is omitted, the constants take the
-individual types of the corresponding expressions.
-If the expression values are untyped constants,
-the declared constants remain untyped and the constant identifiers
-denote the constant values. For instance, if the expression is a
-floating-point literal, the constant identifier denotes a floating-point
-constant, even if the literal's fractional part is zero.
-
-
-
-const Pi float64 = 3.14159265358979323846
-const zero = 0.0 // untyped floating-point constant
-const (
- size int64 = 1024
- eof = -1 // untyped integer constant
-)
-const a, b, c = 3, 4, "foo" // a = 3, b = 4, c = "foo", untyped integer and string constants
-const u, v float32 = 0, 3 // u = 0.0, v = 3.0
-
-
-
-Within a parenthesized const
declaration list the
-expression list may be omitted from any but the first ConstSpec.
-Such an empty list is equivalent to the textual substitution of the
-first preceding non-empty expression list and its type if any.
-Omitting the list of expressions is therefore equivalent to
-repeating the previous list. The number of identifiers must be equal
-to the number of expressions in the previous list.
-Together with the iota
constant generator
-this mechanism permits light-weight declaration of sequential values:
-
-
-
-const (
- Sunday = iota
- Monday
- Tuesday
- Wednesday
- Thursday
- Friday
- Partyday
- numberOfDays // this constant is not exported
-)
-
-
-
-Iota
-
-
-Within a constant declaration, the predeclared identifier
-iota
represents successive untyped integer
-constants. Its value is the index of the respective ConstSpec
-in that constant declaration, starting at zero.
-It can be used to construct a set of related constants:
-
-
-
-const (
- c0 = iota // c0 == 0
- c1 = iota // c1 == 1
- c2 = iota // c2 == 2
-)
-
-const (
- a = 1 << iota // a == 1 (iota == 0)
- b = 1 << iota // b == 2 (iota == 1)
- c = 3 // c == 3 (iota == 2, unused)
- d = 1 << iota // d == 8 (iota == 3)
-)
-
-const (
- u = iota * 42 // u == 0 (untyped integer constant)
- v float64 = iota * 42 // v == 42.0 (float64 constant)
- w = iota * 42 // w == 84 (untyped integer constant)
-)
-
-const x = iota // x == 0
-const y = iota // y == 0
-
-
-
-By definition, multiple uses of iota
in the same ConstSpec all have the same value:
-
-
-
-const (
- bit0, mask0 = 1 << iota, 1<<iota - 1 // bit0 == 1, mask0 == 0 (iota == 0)
- bit1, mask1 // bit1 == 2, mask1 == 1 (iota == 1)
- _, _ // (iota == 2, unused)
- bit3, mask3 // bit3 == 8, mask3 == 7 (iota == 3)
-)
-
-
-
-This last example exploits the implicit repetition
-of the last non-empty expression list.
-
-
-
-Type declarations
-
-
-A type declaration binds an identifier, the type name, to a type.
-Type declarations come in two forms: alias declarations and type definitions.
-
-
-
-TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
-TypeSpec = AliasDecl | TypeDef .
-
-
-Alias declarations
-
-
-An alias declaration binds an identifier to the given type.
-
-
-
-AliasDecl = identifier "=" Type .
-
-
-
-Within the scope of
-the identifier, it serves as an alias for the type.
-
-
-
-type (
- nodeList = []*Node // nodeList and []*Node are identical types
- Polar = polar // Polar and polar denote identical types
-)
-
-
-
-Type definitions
-
-
-A type definition creates a new, distinct type with the same
-underlying type and operations as the given type,
-and binds an identifier to it.
-
-
-
-TypeDef = identifier Type .
-
-
-
-The new type is called a defined type.
-It is different from any other type,
-including the type it is created from.
-
-
-
-type (
- Point struct{ x, y float64 } // Point and struct{ x, y float64 } are different types
- polar Point // polar and Point denote different types
-)
-
-type TreeNode struct {
- left, right *TreeNode
- value *Comparable
-}
-
-type Block interface {
- BlockSize() int
- Encrypt(src, dst []byte)
- Decrypt(src, dst []byte)
-}
-
-
-
-A defined type may have methods associated with it.
-It does not inherit any methods bound to the given type,
-but the method set
-of an interface type or of elements of a composite type remains unchanged:
-
-
-
-// A Mutex is a data type with two methods, Lock and Unlock.
-type Mutex struct { /* Mutex fields */ }
-func (m *Mutex) Lock() { /* Lock implementation */ }
-func (m *Mutex) Unlock() { /* Unlock implementation */ }
-
-// NewMutex has the same composition as Mutex but its method set is empty.
-type NewMutex Mutex
-
-// The method set of PtrMutex's underlying type *Mutex remains unchanged,
-// but the method set of PtrMutex is empty.
-type PtrMutex *Mutex
-
-// The method set of *PrintableMutex contains the methods
-// Lock and Unlock bound to its embedded field Mutex.
-type PrintableMutex struct {
- Mutex
-}
-
-// MyBlock is an interface type that has the same method set as Block.
-type MyBlock Block
-
-
-
-Type definitions may be used to define different boolean, numeric,
-or string types and associate methods with them:
-
-
-
-type TimeZone int
-
-const (
- EST TimeZone = -(5 + iota)
- CST
- MST
- PST
-)
-
-func (tz TimeZone) String() string {
- return fmt.Sprintf("GMT%+dh", tz)
-}
-
-
-
-Variable declarations
-
-
-A variable declaration creates one or more variables,
-binds corresponding identifiers to them, and gives each a type and an initial value.
-
-
-
-VarDecl = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
-VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
-
-
-
-var i int
-var U, V, W float64
-var k = 0
-var x, y float32 = -1, -2
-var (
- i int
- u, v, s = 2.0, 3.0, "bar"
-)
-var re, im = complexSqrt(-1)
-var _, found = entries[name] // map lookup; only interested in "found"
-
-
-
-If a list of expressions is given, the variables are initialized
-with the expressions following the rules for assignments.
-Otherwise, each variable is initialized to its zero value.
-
-
-
-If a type is present, each variable is given that type.
-Otherwise, each variable is given the type of the corresponding
-initialization value in the assignment.
-If that value is an untyped constant, it is first implicitly
-converted to its default type;
-if it is an untyped boolean value, it is first implicitly converted to type bool
.
-The predeclared value nil
cannot be used to initialize a variable
-with no explicit type.
-
-
-
-var d = math.Sin(0.5) // d is float64
-var i = 42 // i is int
-var t, ok = x.(T) // t is T, ok is bool
-var n = nil // illegal
-
-
-
-Implementation restriction: A compiler may make it illegal to declare a variable
-inside a function body if the variable is
-never used.
-
-
-Short variable declarations
-
-
-A short variable declaration uses the syntax:
-
-
-
-ShortVarDecl = IdentifierList ":=" ExpressionList .
-
-
-
-It is shorthand for a regular variable declaration
-with initializer expressions but no types:
-
-
-
-"var" IdentifierList = ExpressionList .
-
-
-
-i, j := 0, 10
-f := func() int { return 7 }
-ch := make(chan int)
-r, w, _ := os.Pipe() // os.Pipe() returns a connected pair of Files and an error, if any
-_, y, _ := coord(p) // coord() returns three values; only interested in y coordinate
-
-
-
-Unlike regular variable declarations, a short variable declaration may redeclare
-variables provided they were originally declared earlier in the same block
-(or the parameter lists if the block is the function body) with the same type,
-and at least one of the non-blank variables is new.
-As a consequence, redeclaration can only appear in a multi-variable short declaration.
-Redeclaration does not introduce a new variable; it just assigns a new value to the original.
-
-
-
-field1, offset := nextField(str, 0)
-field2, offset := nextField(str, offset) // redeclares offset
-a, a := 1, 2 // illegal: double declaration of a or no new variable if a was declared elsewhere
-
-
-
-Short variable declarations may appear only inside functions.
-In some contexts such as the initializers for
-"if",
-"for", or
-"switch" statements,
-they can be used to declare local temporary variables.
-
-
-Function declarations
-
-
-A function declaration binds an identifier, the function name,
-to a function.
-
-
-
-FunctionDecl = "func" FunctionName Signature [ FunctionBody ] .
-FunctionName = identifier .
-FunctionBody = Block .
-
-
-
-If the function's signature declares
-result parameters, the function body's statement list must end in
-a terminating statement.
-
-
-
-func IndexRune(s string, r rune) int {
- for i, c := range s {
- if c == r {
- return i
- }
- }
- // invalid: missing return statement
-}
-
-
-
-A function declaration may omit the body. Such a declaration provides the
-signature for a function implemented outside Go, such as an assembly routine.
-
-
-
-func min(x int, y int) int {
- if x < y {
- return x
- }
- return y
-}
-
-func flushICache(begin, end uintptr) // implemented externally
-
-
-Method declarations
-
-
-A method is a function with a receiver.
-A method declaration binds an identifier, the method name, to a method,
-and associates the method with the receiver's base type.
-
-
-
-MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] .
-Receiver = Parameters .
-
-
-
-The receiver is specified via an extra parameter section preceding the method
-name. That parameter section must declare a single non-variadic parameter, the receiver.
-Its type must be a defined type T
or a
-pointer to a defined type T
. T
is called the receiver
-base type. A receiver base type cannot be a pointer or interface type and
-it must be defined in the same package as the method.
-The method is said to be bound to its receiver base type and the method name
-is visible only within selectors for type T
-or *T
.
-
-
-
-A non-blank receiver identifier must be
-unique in the method signature.
-If the receiver's value is not referenced inside the body of the method,
-its identifier may be omitted in the declaration. The same applies in
-general to parameters of functions and methods.
-
-
-
-For a base type, the non-blank names of methods bound to it must be unique.
-If the base type is a struct type,
-the non-blank method and field names must be distinct.
-
-
-
-Given defined type Point
, the declarations
-
-
-
-func (p *Point) Length() float64 {
- return math.Sqrt(p.x * p.x + p.y * p.y)
-}
-
-func (p *Point) Scale(factor float64) {
- p.x *= factor
- p.y *= factor
-}
-
-
-
-bind the methods Length
and Scale
,
-with receiver type *Point
,
-to the base type Point
.
-
-
-
-The type of a method is the type of a function with the receiver as first
-argument. For instance, the method Scale
has type
-
-
-
-func(p *Point, factor float64)
-
-
-
-However, a function declared this way is not a method.
-
-
-
-Expressions
-
-
-An expression specifies the computation of a value by applying
-operators and functions to operands.
-
-
-Operands
-
-
-Operands denote the elementary values in an expression. An operand may be a
-literal, a (possibly qualified)
-non-blank identifier denoting a
-constant,
-variable, or
-function,
-or a parenthesized expression.
-
-
-
-The blank identifier may appear as an
-operand only on the left-hand side of an assignment.
-
-
-
-Operand = Literal | OperandName | "(" Expression ")" .
-Literal = BasicLit | CompositeLit | FunctionLit .
-BasicLit = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
-OperandName = identifier | QualifiedIdent .
-
-
-Qualified identifiers
-
-
-A qualified identifier is an identifier qualified with a package name prefix.
-Both the package name and the identifier must not be
-blank.
-
-
-
-QualifiedIdent = PackageName "." identifier .
-
-
-
-A qualified identifier accesses an identifier in a different package, which
-must be imported.
-The identifier must be exported and
-declared in the package block of that package.
-
-
-
-math.Sin // denotes the Sin function in package math
-
-
-Composite literals
-
-
-Composite literals construct values for structs, arrays, slices, and maps
-and create a new value each time they are evaluated.
-They consist of the type of the literal followed by a brace-bound list of elements.
-Each element may optionally be preceded by a corresponding key.
-
-
-
-CompositeLit = LiteralType LiteralValue .
-LiteralType = StructType | ArrayType | "[" "..." "]" ElementType |
- SliceType | MapType | TypeName .
-LiteralValue = "{" [ ElementList [ "," ] ] "}" .
-ElementList = KeyedElement { "," KeyedElement } .
-KeyedElement = [ Key ":" ] Element .
-Key = FieldName | Expression | LiteralValue .
-FieldName = identifier .
-Element = Expression | LiteralValue .
-
-
-
-The LiteralType's underlying type must be a struct, array, slice, or map type
-(the grammar enforces this constraint except when the type is given
-as a TypeName).
-The types of the elements and keys must be assignable
-to the respective field, element, and key types of the literal type;
-there is no additional conversion.
-The key is interpreted as a field name for struct literals,
-an index for array and slice literals, and a key for map literals.
-For map literals, all elements must have a key. It is an error
-to specify multiple elements with the same field name or
-constant key value. For non-constant map keys, see the section on
-evaluation order.
-
-
-
-For struct literals the following rules apply:
-
-
- - A key must be a field name declared in the struct type.
-
- - An element list that does not contain any keys must
- list an element for each struct field in the
- order in which the fields are declared.
-
- - If any element has a key, every element must have a key.
-
- - An element list that contains keys does not need to
- have an element for each struct field. Omitted fields
- get the zero value for that field.
-
- - A literal may omit the element list; such a literal evaluates
- to the zero value for its type.
-
- - It is an error to specify an element for a non-exported
- field of a struct belonging to a different package.
-
-
-
-
-Given the declarations
-
-
-type Point3D struct { x, y, z float64 }
-type Line struct { p, q Point3D }
-
-
-
-one may write
-
-
-
-origin := Point3D{} // zero value for Point3D
-line := Line{origin, Point3D{y: -4, z: 12.3}} // zero value for line.q.x
-
-
-
-For array and slice literals the following rules apply:
-
-
- - Each element has an associated integer index marking
- its position in the array.
-
- - An element with a key uses the key as its index. The
- key must be a non-negative constant
- representable by
- a value of type
int
; and if it is typed
- it must be of integer type.
-
- - An element without a key uses the previous element's index plus one.
- If the first element has no key, its index is zero.
-
-
-
-
-Taking the address of a composite literal
-generates a pointer to a unique variable initialized
-with the literal's value.
-
-
-
-var pointer *Point3D = &Point3D{y: 1000}
-
-
-
-Note that the zero value for a slice or map
-type is not the same as an initialized but empty value of the same type.
-Consequently, taking the address of an empty slice or map composite literal
-does not have the same effect as allocating a new slice or map value with
-new.
-
-
-
-p1 := &[]int{} // p1 points to an initialized, empty slice with value []int{} and length 0
-p2 := new([]int) // p2 points to an uninitialized slice with value nil and length 0
-
-
-
-The length of an array literal is the length specified in the literal type.
-If fewer elements than the length are provided in the literal, the missing
-elements are set to the zero value for the array element type.
-It is an error to provide elements with index values outside the index range
-of the array. The notation ...
specifies an array length equal
-to the maximum element index plus one.
-
-
-
-buffer := [10]string{} // len(buffer) == 10
-intSet := [6]int{1, 2, 3, 5} // len(intSet) == 6
-days := [...]string{"Sat", "Sun"} // len(days) == 2
-
-
-
-A slice literal describes the entire underlying array literal.
-Thus the length and capacity of a slice literal are the maximum
-element index plus one. A slice literal has the form
-
-
-
-[]T{x1, x2, … xn}
-
-
-
-and is shorthand for a slice operation applied to an array:
-
-
-
-tmp := [n]T{x1, x2, … xn}
-tmp[0 : n]
-
-
-
-Within a composite literal of array, slice, or map type T
,
-elements or map keys that are themselves composite literals may elide the respective
-literal type if it is identical to the element or key type of T
.
-Similarly, elements or keys that are addresses of composite literals may elide
-the &T
when the element or key type is *T
.
-
-
-
-[...]Point{{1.5, -3.5}, {0, 0}} // same as [...]Point{Point{1.5, -3.5}, Point{0, 0}}
-[][]int{{1, 2, 3}, {4, 5}} // same as [][]int{[]int{1, 2, 3}, []int{4, 5}}
-[][]Point{{{0, 1}, {1, 2}}} // same as [][]Point{[]Point{Point{0, 1}, Point{1, 2}}}
-map[string]Point{"orig": {0, 0}} // same as map[string]Point{"orig": Point{0, 0}}
-map[Point]string{{0, 0}: "orig"} // same as map[Point]string{Point{0, 0}: "orig"}
-
-type PPoint *Point
-[2]*Point{{1.5, -3.5}, {}} // same as [2]*Point{&Point{1.5, -3.5}, &Point{}}
-[2]PPoint{{1.5, -3.5}, {}} // same as [2]PPoint{PPoint(&Point{1.5, -3.5}), PPoint(&Point{})}
-
-
-
-A parsing ambiguity arises when a composite literal using the
-TypeName form of the LiteralType appears as an operand between the
-keyword and the opening brace of the block
-of an "if", "for", or "switch" statement, and the composite literal
-is not enclosed in parentheses, square brackets, or curly braces.
-In this rare case, the opening brace of the literal is erroneously parsed
-as the one introducing the block of statements. To resolve the ambiguity,
-the composite literal must appear within parentheses.
-
-
-
-if x == (T{a,b,c}[i]) { … }
-if (x == T{a,b,c}[i]) { … }
-
-
-
-Examples of valid array, slice, and map literals:
-
-
-
-// list of prime numbers
-primes := []int{2, 3, 5, 7, 9, 2147483647}
-
-// vowels[ch] is true if ch is a vowel
-vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}
-
-// the array [10]float32{-1, 0, 0, 0, -0.1, -0.1, 0, 0, 0, -1}
-filter := [10]float32{-1, 4: -0.1, -0.1, 9: -1}
-
-// frequencies in Hz for equal-tempered scale (A4 = 440Hz)
-noteFrequency := map[string]float32{
- "C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83,
- "G0": 24.50, "A0": 27.50, "B0": 30.87,
-}
-
-
-
-Function literals
-
-
-A function literal represents an anonymous function.
-
-
-
-FunctionLit = "func" Signature FunctionBody .
-
-
-
-func(a, b int, z float64) bool { return a*b < int(z) }
-
-
-
-A function literal can be assigned to a variable or invoked directly.
-
-
-
-f := func(x, y int) int { return x + y }
-func(ch chan int) { ch <- ACK }(replyChan)
-
-
-
-Function literals are closures: they may refer to variables
-defined in a surrounding function. Those variables are then shared between
-the surrounding function and the function literal, and they survive as long
-as they are accessible.
-
-
-
-Primary expressions
-
-
-Primary expressions are the operands for unary and binary expressions.
-
-
-
-PrimaryExpr =
- Operand |
- Conversion |
- MethodExpr |
- PrimaryExpr Selector |
- PrimaryExpr Index |
- PrimaryExpr Slice |
- PrimaryExpr TypeAssertion |
- PrimaryExpr Arguments .
-
-Selector = "." identifier .
-Index = "[" Expression "]" .
-Slice = "[" [ Expression ] ":" [ Expression ] "]" |
- "[" [ Expression ] ":" Expression ":" Expression "]" .
-TypeAssertion = "." "(" Type ")" .
-Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
-
-
-
-
-x
-2
-(s + ".txt")
-f(3.1415, true)
-Point{1, 2}
-m["foo"]
-s[i : j + 1]
-obj.color
-f.p[i].x()
-
-
-
-Selectors
-
-
-For a primary expression x
-that is not a package name, the
-selector expression
-
-
-
-x.f
-
-
-
-denotes the field or method f
of the value x
-(or sometimes *x
; see below).
-The identifier f
is called the (field or method) selector;
-it must not be the blank identifier.
-The type of the selector expression is the type of f
.
-If x
is a package name, see the section on
-qualified identifiers.
-
-
-
-A selector f
may denote a field or method f
of
-a type T
, or it may refer
-to a field or method f
of a nested
-embedded field of T
.
-The number of embedded fields traversed
-to reach f
is called its depth in T
.
-The depth of a field or method f
-declared in T
is zero.
-The depth of a field or method f
declared in
-an embedded field A
in T
is the
-depth of f
in A
plus one.
-
-
-
-The following rules apply to selectors:
-
-
-
--
-For a value
x
of type T
or *T
-where T
is not a pointer or interface type,
-x.f
denotes the field or method at the shallowest depth
-in T
where there
-is such an f
.
-If there is not exactly one f
-with shallowest depth, the selector expression is illegal.
-
-
--
-For a value
x
of type I
where I
-is an interface type, x.f
denotes the actual method with name
-f
of the dynamic value of x
.
-If there is no method with name f
in the
-method set of I
, the selector
-expression is illegal.
-
-
--
-As an exception, if the type of
x
is a defined
-pointer type and (*x).f
is a valid selector expression denoting a field
-(but not a method), x.f
is shorthand for (*x).f
.
-
-
--
-In all other cases,
x.f
is illegal.
-
-
--
-If
x
is of pointer type and has the value
-nil
and x.f
denotes a struct field,
-assigning to or evaluating x.f
-causes a run-time panic.
-
-
--
-If
x
is of interface type and has the value
-nil
, calling or
-evaluating the method x.f
-causes a run-time panic.
-
-
-
-
-For example, given the declarations:
-
-
-
-type T0 struct {
- x int
-}
-
-func (*T0) M0()
-
-type T1 struct {
- y int
-}
-
-func (T1) M1()
-
-type T2 struct {
- z int
- T1
- *T0
-}
-
-func (*T2) M2()
-
-type Q *T2
-
-var t T2 // with t.T0 != nil
-var p *T2 // with p != nil and (*p).T0 != nil
-var q Q = p
-
-
-
-one may write:
-
-
-
-t.z // t.z
-t.y // t.T1.y
-t.x // (*t.T0).x
-
-p.z // (*p).z
-p.y // (*p).T1.y
-p.x // (*(*p).T0).x
-
-q.x // (*(*q).T0).x (*q).x is a valid field selector
-
-p.M0() // ((*p).T0).M0() M0 expects *T0 receiver
-p.M1() // ((*p).T1).M1() M1 expects T1 receiver
-p.M2() // p.M2() M2 expects *T2 receiver
-t.M2() // (&t).M2() M2 expects *T2 receiver, see section on Calls
-
-
-
-but the following is invalid:
-
-
-
-q.M0() // (*q).M0 is valid but not a field selector
-
-
-
-Method expressions
-
-
-If M
is in the method set of type T
,
-T.M
is a function that is callable as a regular function
-with the same arguments as M
prefixed by an additional
-argument that is the receiver of the method.
-
-
-
-MethodExpr = ReceiverType "." MethodName .
-ReceiverType = Type .
-
-
-
-Consider a struct type T
with two methods,
-Mv
, whose receiver is of type T
, and
-Mp
, whose receiver is of type *T
.
-
-
-
-type T struct {
- a int
-}
-func (tv T) Mv(a int) int { return 0 } // value receiver
-func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver
-
-var t T
-
-
-
-The expression
-
-
-
-T.Mv
-
-
-
-yields a function equivalent to Mv
but
-with an explicit receiver as its first argument; it has signature
-
-
-
-func(tv T, a int) int
-
-
-
-That function may be called normally with an explicit receiver, so
-these five invocations are equivalent:
-
-
-
-t.Mv(7)
-T.Mv(t, 7)
-(T).Mv(t, 7)
-f1 := T.Mv; f1(t, 7)
-f2 := (T).Mv; f2(t, 7)
-
-
-
-Similarly, the expression
-
-
-
-(*T).Mp
-
-
-
-yields a function value representing Mp
with signature
-
-
-
-func(tp *T, f float32) float32
-
-
-
-For a method with a value receiver, one can derive a function
-with an explicit pointer receiver, so
-
-
-
-(*T).Mv
-
-
-
-yields a function value representing Mv
with signature
-
-
-
-func(tv *T, a int) int
-
-
-
-Such a function indirects through the receiver to create a value
-to pass as the receiver to the underlying method;
-the method does not overwrite the value whose address is passed in
-the function call.
-
-
-
-The final case, a value-receiver function for a pointer-receiver method,
-is illegal because pointer-receiver methods are not in the method set
-of the value type.
-
-
-
-Function values derived from methods are called with function call syntax;
-the receiver is provided as the first argument to the call.
-That is, given f := T.Mv
, f
is invoked
-as f(t, 7)
not t.f(7)
.
-To construct a function that binds the receiver, use a
-function literal or
-method value.
-
-
-
-It is legal to derive a function value from a method of an interface type.
-The resulting function takes an explicit receiver of that interface type.
-
-
-Method values
-
-
-If the expression x
has static type T
and
-M
is in the method set of type T
,
-x.M
is called a method value.
-The method value x.M
is a function value that is callable
-with the same arguments as a method call of x.M
.
-The expression x
is evaluated and saved during the evaluation of the
-method value; the saved copy is then used as the receiver in any calls,
-which may be executed later.
-
-
-
-type S struct { *T }
-type T int
-func (t T) M() { print(t) }
-
-t := new(T)
-s := S{T: t}
-f := t.M // receiver *t is evaluated and stored in f
-g := s.M // receiver *(s.T) is evaluated and stored in g
-*t = 42 // does not affect stored receivers in f and g
-
-
-
-The type T
may be an interface or non-interface type.
-
-
-
-As in the discussion of method expressions above,
-consider a struct type T
with two methods,
-Mv
, whose receiver is of type T
, and
-Mp
, whose receiver is of type *T
.
-
-
-
-type T struct {
- a int
-}
-func (tv T) Mv(a int) int { return 0 } // value receiver
-func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver
-
-var t T
-var pt *T
-func makeT() T
-
-
-
-The expression
-
-
-
-t.Mv
-
-
-
-yields a function value of type
-
-
-
-func(int) int
-
-
-
-These two invocations are equivalent:
-
-
-
-t.Mv(7)
-f := t.Mv; f(7)
-
-
-
-Similarly, the expression
-
-
-
-pt.Mp
-
-
-
-yields a function value of type
-
-
-
-func(float32) float32
-
-
-
-As with selectors, a reference to a non-interface method with a value receiver
-using a pointer will automatically dereference that pointer: pt.Mv
is equivalent to (*pt).Mv
.
-
-
-
-As with method calls, a reference to a non-interface method with a pointer receiver
-using an addressable value will automatically take the address of that value: t.Mp
is equivalent to (&t).Mp
.
-
-
-
-f := t.Mv; f(7) // like t.Mv(7)
-f := pt.Mp; f(7) // like pt.Mp(7)
-f := pt.Mv; f(7) // like (*pt).Mv(7)
-f := t.Mp; f(7) // like (&t).Mp(7)
-f := makeT().Mp // invalid: result of makeT() is not addressable
-
-
-
-Although the examples above use non-interface types, it is also legal to create a method value
-from a value of interface type.
-
-
-
-var i interface { M(int) } = myVal
-f := i.M; f(7) // like i.M(7)
-
-
-
-Index expressions
-
-
-A primary expression of the form
-
-
-
-a[x]
-
-
-
-denotes the element of the array, pointer to array, slice, string or map a
indexed by x
.
-The value x
is called the index or map key, respectively.
-The following rules apply:
-
-
-
-If a
is not a map:
-
-
- - the index
x
must be of integer type or an untyped constant
- - a constant index must be non-negative and
- representable by a value of type
int
- - a constant index that is untyped is given type
int
- - the index
x
is in range if 0 <= x < len(a)
,
- otherwise it is out of range
-
-
-
-For a
of array type A
:
-
-
- - a constant index must be in range
- - if
x
is out of range at run time,
- a run-time panic occurs
- a[x]
is the array element at index x
and the type of
- a[x]
is the element type of A
-
-
-
-For a
of pointer to array type:
-
-
- a[x]
is shorthand for (*a)[x]
-
-
-
-For a
of slice type S
:
-
-
- - if
x
is out of range at run time,
- a run-time panic occurs
- a[x]
is the slice element at index x
and the type of
- a[x]
is the element type of S
-
-
-
-For a
of string type:
-
-
- - a constant index must be in range
- if the string
a
is also constant
- - if
x
is out of range at run time,
- a run-time panic occurs
- a[x]
is the non-constant byte value at index x
and the type of
- a[x]
is byte
- a[x]
may not be assigned to
-
-
-
-For a
of map type M
:
-
-
- x
's type must be
- assignable
- to the key type of M
- - if the map contains an entry with key
x
,
- a[x]
is the map element with key x
- and the type of a[x]
is the element type of M
- - if the map is
nil
or does not contain such an entry,
- a[x]
is the zero value
- for the element type of M
-
-
-
-Otherwise a[x]
is illegal.
-
-
-
-An index expression on a map a
of type map[K]V
-used in an assignment or initialization of the special form
-
-
-
-v, ok = a[x]
-v, ok := a[x]
-var v, ok = a[x]
-
-
-
-yields an additional untyped boolean value. The value of ok
is
-true
if the key x
is present in the map, and
-false
otherwise.
-
-
-
-Assigning to an element of a nil
map causes a
-run-time panic.
-
-
-
-Slice expressions
-
-
-Slice expressions construct a substring or slice from a string, array, pointer
-to array, or slice. There are two variants: a simple form that specifies a low
-and high bound, and a full form that also specifies a bound on the capacity.
-
-
-Simple slice expressions
-
-
-For a string, array, pointer to array, or slice a
, the primary expression
-
-
-
-a[low : high]
-
-
-
-constructs a substring or slice. The indices low
and
-high
select which elements of operand a
appear
-in the result. The result has indices starting at 0 and length equal to
-high
- low
.
-After slicing the array a
-
-
-
-a := [5]int{1, 2, 3, 4, 5}
-s := a[1:4]
-
-
-
-the slice s
has type []int
, length 3, capacity 4, and elements
-
-
-
-s[0] == 2
-s[1] == 3
-s[2] == 4
-
-
-
-For convenience, any of the indices may be omitted. A missing low
-index defaults to zero; a missing high
index defaults to the length of the
-sliced operand:
-
-
-
-a[2:] // same as a[2 : len(a)]
-a[:3] // same as a[0 : 3]
-a[:] // same as a[0 : len(a)]
-
-
-
-If a
is a pointer to an array, a[low : high]
is shorthand for
-(*a)[low : high]
.
-
-
-
-For arrays or strings, the indices are in range if
-0
<= low
<= high
<= len(a)
,
-otherwise they are out of range.
-For slices, the upper index bound is the slice capacity cap(a)
rather than the length.
-A constant index must be non-negative and
-representable by a value of type
-int
; for arrays or constant strings, constant indices must also be in range.
-If both indices are constant, they must satisfy low <= high
.
-If the indices are out of range at run time, a run-time panic occurs.
-
-
-
-Except for untyped strings, if the sliced operand is a string or slice,
-the result of the slice operation is a non-constant value of the same type as the operand.
-For untyped string operands the result is a non-constant value of type string
.
-If the sliced operand is an array, it must be addressable
-and the result of the slice operation is a slice with the same element type as the array.
-
-
-
-If the sliced operand of a valid slice expression is a nil
slice, the result
-is a nil
slice. Otherwise, if the result is a slice, it shares its underlying
-array with the operand.
-
-
-
-var a [10]int
-s1 := a[3:7] // underlying array of s1 is array a; &s1[2] == &a[5]
-s2 := s1[1:4] // underlying array of s2 is underlying array of s1 which is array a; &s2[1] == &a[5]
-s2[1] = 42 // s2[1] == s1[2] == a[5] == 42; they all refer to the same underlying array element
-
-
-
-Full slice expressions
-
-
-For an array, pointer to array, or slice a
(but not a string), the primary expression
-
-
-
-a[low : high : max]
-
-
-
-constructs a slice of the same type, and with the same length and elements as the simple slice
-expression a[low : high]
. Additionally, it controls the resulting slice's capacity
-by setting it to max - low
. Only the first index may be omitted; it defaults to 0.
-After slicing the array a
-
-
-
-a := [5]int{1, 2, 3, 4, 5}
-t := a[1:3:5]
-
-
-
-the slice t
has type []int
, length 2, capacity 4, and elements
-
-
-
-t[0] == 2
-t[1] == 3
-
-
-
-As for simple slice expressions, if a
is a pointer to an array,
-a[low : high : max]
is shorthand for (*a)[low : high : max]
.
-If the sliced operand is an array, it must be addressable.
-
-
-
-The indices are in range if 0 <= low <= high <= max <= cap(a)
,
-otherwise they are out of range.
-A constant index must be non-negative and
-representable by a value of type
-int
; for arrays, constant indices must also be in range.
-If multiple indices are constant, the constants that are present must be in range relative to each
-other.
-If the indices are out of range at run time, a run-time panic occurs.
-
-
-Type assertions
-
-
-For an expression x
of interface type
-and a type T
, the primary expression
-
-
-
-x.(T)
-
-
-
-asserts that x
is not nil
-and that the value stored in x
is of type T
.
-The notation x.(T)
is called a type assertion.
-
-
-More precisely, if T
is not an interface type, x.(T)
asserts
-that the dynamic type of x
is identical
-to the type T
.
-In this case, T
must implement the (interface) type of x
;
-otherwise the type assertion is invalid since it is not possible for x
-to store a value of type T
.
-If T
is an interface type, x.(T)
asserts that the dynamic type
-of x
implements the interface T
.
-
-
-If the type assertion holds, the value of the expression is the value
-stored in x
and its type is T
. If the type assertion is false,
-a run-time panic occurs.
-In other words, even though the dynamic type of x
-is known only at run time, the type of x.(T)
is
-known to be T
in a correct program.
-
-
-
-var x interface{} = 7 // x has dynamic type int and value 7
-i := x.(int) // i has type int and value 7
-
-type I interface { m() }
-
-func f(y I) {
- s := y.(string) // illegal: string does not implement I (missing method m)
- r := y.(io.Reader) // r has type io.Reader and the dynamic type of y must implement both I and io.Reader
- …
-}
-
-
-
-A type assertion used in an assignment or initialization of the special form
-
-
-
-v, ok = x.(T)
-v, ok := x.(T)
-var v, ok = x.(T)
-var v, ok interface{} = x.(T) // dynamic types of v and ok are T and bool
-
-
-
-yields an additional untyped boolean value. The value of ok
is true
-if the assertion holds. Otherwise it is false
and the value of v
is
-the zero value for type T
.
-No run-time panic occurs in this case.
-
-
-
-Calls
-
-
-Given an expression f
of function type
-F
,
-
-
-
-f(a1, a2, … an)
-
-
-
-calls f
with arguments a1, a2, … an
.
-Except for one special case, arguments must be single-valued expressions
-assignable to the parameter types of
-F
and are evaluated before the function is called.
-The type of the expression is the result type
-of F
.
-A method invocation is similar but the method itself
-is specified as a selector upon a value of the receiver type for
-the method.
-
-
-
-math.Atan2(x, y) // function call
-var pt *Point
-pt.Scale(3.5) // method call with receiver pt
-
-
-
-In a function call, the function value and arguments are evaluated in
-the usual order.
-After they are evaluated, the parameters of the call are passed by value to the function
-and the called function begins execution.
-The return parameters of the function are passed by value
-back to the caller when the function returns.
-
-
-
-Calling a nil
function value
-causes a run-time panic.
-
-
-
-As a special case, if the return values of a function or method
-g
are equal in number and individually
-assignable to the parameters of another function or method
-f
, then the call f(g(parameters_of_g))
-will invoke f
after binding the return values of
-g
to the parameters of f
in order. The call
-of f
must contain no parameters other than the call of g
,
-and g
must have at least one return value.
-If f
has a final ...
parameter, it is
-assigned the return values of g
that remain after
-assignment of regular parameters.
-
-
-
-func Split(s string, pos int) (string, string) {
- return s[0:pos], s[pos:]
-}
-
-func Join(s, t string) string {
- return s + t
-}
-
-if Join(Split(value, len(value)/2)) != value {
- log.Panic("test fails")
-}
-
-
-
-A method call x.m()
is valid if the method set
-of (the type of) x
contains m
and the
-argument list can be assigned to the parameter list of m
.
-If x
is addressable and &x
's method
-set contains m
, x.m()
is shorthand
-for (&x).m()
:
-
-
-
-var p Point
-p.Scale(3.5)
-
-
-
-There is no distinct method type and there are no method literals.
-
-
-Passing arguments to ...
parameters
-
-
-If f
is variadic with a final
-parameter p
of type ...T
, then within f
-the type of p
is equivalent to type []T
.
-If f
is invoked with no actual arguments for p
,
-the value passed to p
is nil
.
-Otherwise, the value passed is a new slice
-of type []T
with a new underlying array whose successive elements
-are the actual arguments, which all must be assignable
-to T
. The length and capacity of the slice is therefore
-the number of arguments bound to p
and may differ for each
-call site.
-
-
-
-Given the function and calls
-
-
-func Greeting(prefix string, who ...string)
-Greeting("nobody")
-Greeting("hello:", "Joe", "Anna", "Eileen")
-
-
-
-within Greeting
, who
will have the value
-nil
in the first call, and
-[]string{"Joe", "Anna", "Eileen"}
in the second.
-
-
-
-If the final argument is assignable to a slice type []T
and
-is followed by ...
, it is passed unchanged as the value
-for a ...T
parameter. In this case no new slice is created.
-
-
-
-Given the slice s
and call
-
-
-
-s := []string{"James", "Jasmine"}
-Greeting("goodbye:", s...)
-
-
-
-within Greeting
, who
will have the same value as s
-with the same underlying array.
-
-
-
-Operators
-
-
-Operators combine operands into expressions.
-
-
-
-Expression = UnaryExpr | Expression binary_op Expression .
-UnaryExpr = PrimaryExpr | unary_op UnaryExpr .
-
-binary_op = "||" | "&&" | rel_op | add_op | mul_op .
-rel_op = "==" | "!=" | "<" | "<=" | ">" | ">=" .
-add_op = "+" | "-" | "|" | "^" .
-mul_op = "*" | "/" | "%" | "<<" | ">>" | "&" | "&^" .
-
-unary_op = "+" | "-" | "!" | "^" | "*" | "&" | "<-" .
-
-
-
-Comparisons are discussed elsewhere.
-For other binary operators, the operand types must be identical
-unless the operation involves shifts or untyped constants.
-For operations involving constants only, see the section on
-constant expressions.
-
-
-
-Except for shift operations, if one operand is an untyped constant
-and the other operand is not, the constant is implicitly converted
-to the type of the other operand.
-
-
-
-The right operand in a shift expression must have integer type
-or be an untyped constant representable by a
-value of type uint
.
-If the left operand of a non-constant shift expression is an untyped constant,
-it is first implicitly converted to the type it would assume if the shift expression were
-replaced by its left operand alone.
-
-
-
-var a [1024]byte
-var s uint = 33
-
-// The results of the following examples are given for 64-bit ints.
-var i = 1<<s // 1 has type int
-var j int32 = 1<<s // 1 has type int32; j == 0
-var k = uint64(1<<s) // 1 has type uint64; k == 1<<33
-var m int = 1.0<<s // 1.0 has type int; m == 1<<33
-var n = 1.0<<s == j // 1.0 has type int32; n == true
-var o = 1<<s == 2<<s // 1 and 2 have type int; o == false
-var p = 1<<s == 1<<33 // 1 has type int; p == true
-var u = 1.0<<s // illegal: 1.0 has type float64, cannot shift
-var u1 = 1.0<<s != 0 // illegal: 1.0 has type float64, cannot shift
-var u2 = 1<<s != 1.0 // illegal: 1 has type float64, cannot shift
-var v float32 = 1<<s // illegal: 1 has type float32, cannot shift
-var w int64 = 1.0<<33 // 1.0<<33 is a constant shift expression; w == 1<<33
-var x = a[1.0<<s] // panics: 1.0 has type int, but 1<<33 overflows array bounds
-var b = make([]byte, 1.0<<s) // 1.0 has type int; len(b) == 1<<33
-
-// The results of the following examples are given for 32-bit ints,
-// which means the shifts will overflow.
-var mm int = 1.0<<s // 1.0 has type int; mm == 0
-var oo = 1<<s == 2<<s // 1 and 2 have type int; oo == true
-var pp = 1<<s == 1<<33 // illegal: 1 has type int, but 1<<33 overflows int
-var xx = a[1.0<<s] // 1.0 has type int; xx == a[0]
-var bb = make([]byte, 1.0<<s) // 1.0 has type int; len(bb) == 0
-
-
-Operator precedence
-
-Unary operators have the highest precedence.
-As the ++
and --
operators form
-statements, not expressions, they fall
-outside the operator hierarchy.
-As a consequence, statement *p++
is the same as (*p)++
.
-
-
-
-There are five precedence levels for binary operators.
-Multiplication operators bind strongest, followed by addition
-operators, comparison operators, &&
(logical AND),
-and finally ||
(logical OR):
-
-
-
-Precedence Operator
- 5 * / % << >> & &^
- 4 + - | ^
- 3 == != < <= > >=
- 2 &&
- 1 ||
-
-
-
-Binary operators of the same precedence associate from left to right.
-For instance, x / y * z
is the same as (x / y) * z
.
-
-
-
-+x
-23 + 3*x[i]
-x <= f()
-^a >> b
-f() || g()
-x == y+1 && <-chanInt > 0
-
-
-
-Arithmetic operators
-
-Arithmetic operators apply to numeric values and yield a result of the same
-type as the first operand. The four standard arithmetic operators (+
,
--
, *
, /
) apply to integer,
-floating-point, and complex types; +
also applies to strings.
-The bitwise logical and shift operators apply to integers only.
-
-
-
-+ sum integers, floats, complex values, strings
-- difference integers, floats, complex values
-* product integers, floats, complex values
-/ quotient integers, floats, complex values
-% remainder integers
-
-& bitwise AND integers
-| bitwise OR integers
-^ bitwise XOR integers
-&^ bit clear (AND NOT) integers
-
-<< left shift integer << integer >= 0
->> right shift integer >> integer >= 0
-
-
-
-Integer operators
-
-
-For two integer values x
and y
, the integer quotient
-q = x / y
and remainder r = x % y
satisfy the following
-relationships:
-
-
-
-x = q*y + r and |r| < |y|
-
-
-
-with x / y
truncated towards zero
-("truncated division").
-
-
-
- x y x / y x % y
- 5 3 1 2
--5 3 -1 -2
- 5 -3 -1 2
--5 -3 1 -2
-
-
-
-The one exception to this rule is that if the dividend x
is
-the most negative value for the int type of x
, the quotient
-q = x / -1
is equal to x
(and r = 0
)
-due to two's-complement integer overflow:
-
-
-
- x, q
-int8 -128
-int16 -32768
-int32 -2147483648
-int64 -9223372036854775808
-
-
-
-If the divisor is a constant, it must not be zero.
-If the divisor is zero at run time, a run-time panic occurs.
-If the dividend is non-negative and the divisor is a constant power of 2,
-the division may be replaced by a right shift, and computing the remainder may
-be replaced by a bitwise AND operation:
-
-
-
- x x / 4 x % 4 x >> 2 x & 3
- 11 2 3 2 3
--11 -2 -3 -3 1
-
-
-
-The shift operators shift the left operand by the shift count specified by the
-right operand, which must be non-negative. If the shift count is negative at run time,
-a run-time panic occurs.
-The shift operators implement arithmetic shifts if the left operand is a signed
-integer and logical shifts if it is an unsigned integer.
-There is no upper limit on the shift count. Shifts behave
-as if the left operand is shifted n
times by 1 for a shift
-count of n
.
-As a result, x << 1
is the same as x*2
-and x >> 1
is the same as
-x/2
but truncated towards negative infinity.
-
-
-
-For integer operands, the unary operators
-+
, -
, and ^
are defined as
-follows:
-
-
-
-+x is 0 + x
--x negation is 0 - x
-^x bitwise complement is m ^ x with m = "all bits set to 1" for unsigned x
- and m = -1 for signed x
-
-
-
-Integer overflow
-
-
-For unsigned integer values, the operations +
,
--
, *
, and <<
are
-computed modulo 2n, where n is the bit width of
-the unsigned integer's type.
-Loosely speaking, these unsigned integer operations
-discard high bits upon overflow, and programs may rely on "wrap around".
-
-
-For signed integers, the operations +
,
--
, *
, /
, and <<
may legally
-overflow and the resulting value exists and is deterministically defined
-by the signed integer representation, the operation, and its operands.
-Overflow does not cause a run-time panic.
-A compiler may not optimize code under the assumption that overflow does
-not occur. For instance, it may not assume that x < x + 1
is always true.
-
-
-
-Floating-point operators
-
-
-For floating-point and complex numbers,
-+x
is the same as x
,
-while -x
is the negation of x
.
-The result of a floating-point or complex division by zero is not specified beyond the
-IEEE 754 standard; whether a run-time panic
-occurs is implementation-specific.
-
-
-
-An implementation may combine multiple floating-point operations into a single
-fused operation, possibly across statements, and produce a result that differs
-from the value obtained by executing and rounding the instructions individually.
-An explicit floating-point type conversion rounds to
-the precision of the target type, preventing fusion that would discard that rounding.
-
-
-
-For instance, some architectures provide a "fused multiply and add" (FMA) instruction
-that computes x*y + z
without rounding the intermediate result x*y
.
-These examples show when a Go implementation can use that instruction:
-
-
-
-// FMA allowed for computing r, because x*y is not explicitly rounded:
-r = x*y + z
-r = z; r += x*y
-t = x*y; r = t + z
-*p = x*y; r = *p + z
-r = x*y + float64(z)
-
-// FMA disallowed for computing r, because it would omit rounding of x*y:
-r = float64(x*y) + z
-r = z; r += float64(x*y)
-t = float64(x*y); r = t + z
-
-
-String concatenation
-
-
-Strings can be concatenated using the +
operator
-or the +=
assignment operator:
-
-
-
-s := "hi" + string(c)
-s += " and good bye"
-
-
-
-String addition creates a new string by concatenating the operands.
-
-
-
-Comparison operators
-
-
-Comparison operators compare two operands and yield an untyped boolean value.
-
-
-
-== equal
-!= not equal
-< less
-<= less or equal
-> greater
->= greater or equal
-
-
-
-In any comparison, the first operand
-must be assignable
-to the type of the second operand, or vice versa.
-
-
-The equality operators ==
and !=
apply
-to operands that are comparable.
-The ordering operators <
, <=
, >
, and >=
-apply to operands that are ordered.
-These terms and the result of the comparisons are defined as follows:
-
-
-
- -
- Boolean values are comparable.
- Two boolean values are equal if they are either both
-
true
or both false
.
-
-
- -
- Integer values are comparable and ordered, in the usual way.
-
-
- -
- Floating-point values are comparable and ordered,
- as defined by the IEEE 754 standard.
-
-
- -
- Complex values are comparable.
- Two complex values
u
and v
are
- equal if both real(u) == real(v)
and
- imag(u) == imag(v)
.
-
-
- -
- String values are comparable and ordered, lexically byte-wise.
-
-
- -
- Pointer values are comparable.
- Two pointer values are equal if they point to the same variable or if both have value
nil
.
- Pointers to distinct zero-size variables may or may not be equal.
-
-
- -
- Channel values are comparable.
- Two channel values are equal if they were created by the same call to
-
make
- or if both have value nil
.
-
-
- -
- Interface values are comparable.
- Two interface values are equal if they have identical dynamic types
- and equal dynamic values or if both have value
nil
.
-
-
- -
- A value
x
of non-interface type X
and
- a value t
of interface type T
are comparable when values
- of type X
are comparable and
- X
implements T
.
- They are equal if t
's dynamic type is identical to X
- and t
's dynamic value is equal to x
.
-
-
- -
- Struct values are comparable if all their fields are comparable.
- Two struct values are equal if their corresponding
- non-blank fields are equal.
-
-
- -
- Array values are comparable if values of the array element type are comparable.
- Two array values are equal if their corresponding elements are equal.
-
-
-
-
-A comparison of two interface values with identical dynamic types
-causes a run-time panic if values
-of that type are not comparable. This behavior applies not only to direct interface
-value comparisons but also when comparing arrays of interface values
-or structs with interface-valued fields.
-
-
-
-Slice, map, and function values are not comparable.
-However, as a special case, a slice, map, or function value may
-be compared to the predeclared identifier nil
.
-Comparison of pointer, channel, and interface values to nil
-is also allowed and follows from the general rules above.
-
-
-
-const c = 3 < 4 // c is the untyped boolean constant true
-
-type MyBool bool
-var x, y int
-var (
- // The result of a comparison is an untyped boolean.
- // The usual assignment rules apply.
- b3 = x == y // b3 has type bool
- b4 bool = x == y // b4 has type bool
- b5 MyBool = x == y // b5 has type MyBool
-)
-
-
-Logical operators
-
-
-Logical operators apply to boolean values
-and yield a result of the same type as the operands.
-The right operand is evaluated conditionally.
-
-
-
-&& conditional AND p && q is "if p then q else false"
-|| conditional OR p || q is "if p then true else q"
-! NOT !p is "not p"
-
-
-
-Address operators
-
-
-For an operand x
of type T
, the address operation
-&x
generates a pointer of type *T
to x
.
-The operand must be addressable,
-that is, either a variable, pointer indirection, or slice indexing
-operation; or a field selector of an addressable struct operand;
-or an array indexing operation of an addressable array.
-As an exception to the addressability requirement, x
may also be a
-(possibly parenthesized)
-composite literal.
-If the evaluation of x
would cause a run-time panic,
-then the evaluation of &x
does too.
-
-
-
-For an operand x
of pointer type *T
, the pointer
-indirection *x
denotes the variable of type T
pointed
-to by x
.
-If x
is nil
, an attempt to evaluate *x
-will cause a run-time panic.
-
-
-
-&x
-&a[f(2)]
-&Point{2, 3}
-*p
-*pf(x)
-
-var x *int = nil
-*x // causes a run-time panic
-&*x // causes a run-time panic
-
-
-
-Receive operator
-
-
-For an operand ch
of channel type,
-the value of the receive operation <-ch
is the value received
-from the channel ch
. The channel direction must permit receive operations,
-and the type of the receive operation is the element type of the channel.
-The expression blocks until a value is available.
-Receiving from a nil
channel blocks forever.
-A receive operation on a closed channel can always proceed
-immediately, yielding the element type's zero value
-after any previously sent values have been received.
-
-
-
-v1 := <-ch
-v2 = <-ch
-f(<-ch)
-<-strobe // wait until clock pulse and discard received value
-
-
-
-A receive expression used in an assignment or initialization of the special form
-
-
-
-x, ok = <-ch
-x, ok := <-ch
-var x, ok = <-ch
-var x, ok T = <-ch
-
-
-
-yields an additional untyped boolean result reporting whether the
-communication succeeded. The value of ok
is true
-if the value received was delivered by a successful send operation to the
-channel, or false
if it is a zero value generated because the
-channel is closed and empty.
-
-
-
-Conversions
-
-
-A conversion changes the type of an expression
-to the type specified by the conversion.
-A conversion may appear literally in the source, or it may be implied
-by the context in which an expression appears.
-
-
-
-An explicit conversion is an expression of the form T(x)
-where T
is a type and x
is an expression
-that can be converted to type T
.
-
-
-
-Conversion = Type "(" Expression [ "," ] ")" .
-
-
-
-If the type starts with the operator *
or <-
,
-or if the type starts with the keyword func
-and has no result list, it must be parenthesized when
-necessary to avoid ambiguity:
-
-
-
-*Point(p) // same as *(Point(p))
-(*Point)(p) // p is converted to *Point
-<-chan int(c) // same as <-(chan int(c))
-(<-chan int)(c) // c is converted to <-chan int
-func()(x) // function signature func() x
-(func())(x) // x is converted to func()
-(func() int)(x) // x is converted to func() int
-func() int(x) // x is converted to func() int (unambiguous)
-
-
-
-A constant value x
can be converted to
-type T
if x
is representable
-by a value of T
.
-As a special case, an integer constant x
can be explicitly converted to a
-string type using the
-same rule
-as for non-constant x
.
-
-
-
-Converting a constant yields a typed constant as result.
-
-
-
-uint(iota) // iota value of type uint
-float32(2.718281828) // 2.718281828 of type float32
-complex128(1) // 1.0 + 0.0i of type complex128
-float32(0.49999999) // 0.5 of type float32
-float64(-1e-1000) // 0.0 of type float64
-string('x') // "x" of type string
-string(0x266c) // "♬" of type string
-MyString("foo" + "bar") // "foobar" of type MyString
-string([]byte{'a'}) // not a constant: []byte{'a'} is not a constant
-(*int)(nil) // not a constant: nil is not a constant, *int is not a boolean, numeric, or string type
-int(1.2) // illegal: 1.2 cannot be represented as an int
-string(65.0) // illegal: 65.0 is not an integer constant
-
-
-
-A non-constant value x
can be converted to type T
-in any of these cases:
-
-
-
- -
-
x
is assignable
- to T
.
-
- -
- ignoring struct tags (see below),
-
x
's type and T
have identical
- underlying types.
-
- -
- ignoring struct tags (see below),
-
x
's type and T
are pointer types
- that are not defined types,
- and their pointer base types have identical underlying types.
-
- -
-
x
's type and T
are both integer or floating
- point types.
-
- -
-
x
's type and T
are both complex types.
-
- -
-
x
is an integer or a slice of bytes or runes
- and T
is a string type.
-
- -
-
x
is a string and T
is a slice of bytes or runes.
-
- -
-
x
is a slice, T
is a pointer to an array,
- and the slice and array types have identical element types.
-
-
-
-
-Struct tags are ignored when comparing struct types
-for identity for the purpose of conversion:
-
-
-
-type Person struct {
- Name string
- Address *struct {
- Street string
- City string
- }
-}
-
-var data *struct {
- Name string `json:"name"`
- Address *struct {
- Street string `json:"street"`
- City string `json:"city"`
- } `json:"address"`
-}
-
-var person = (*Person)(data) // ignoring tags, the underlying types are identical
-
-
-
-Specific rules apply to (non-constant) conversions between numeric types or
-to and from a string type.
-These conversions may change the representation of x
-and incur a run-time cost.
-All other conversions only change the type but not the representation
-of x
.
-
-
-
-There is no linguistic mechanism to convert between pointers and integers.
-The package unsafe
-implements this functionality under
-restricted circumstances.
-
-
-Conversions between numeric types
-
-
-For the conversion of non-constant numeric values, the following rules apply:
-
-
-
--
-When converting between integer types, if the value is a signed integer, it is
-sign extended to implicit infinite precision; otherwise it is zero extended.
-It is then truncated to fit in the result type's size.
-For example, if
v := uint16(0x10F0)
, then uint32(int8(v)) == 0xFFFFFFF0
.
-The conversion always yields a valid value; there is no indication of overflow.
-
--
-When converting a floating-point number to an integer, the fraction is discarded
-(truncation towards zero).
-
--
-When converting an integer or floating-point number to a floating-point type,
-or a complex number to another complex type, the result value is rounded
-to the precision specified by the destination type.
-For instance, the value of a variable
x
of type float32
-may be stored using additional precision beyond that of an IEEE 754 32-bit number,
-but float32(x) represents the result of rounding x
's value to
-32-bit precision. Similarly, x + 0.1
may use more than 32 bits
-of precision, but float32(x + 0.1)
does not.
-
-
-
-
-In all non-constant conversions involving floating-point or complex values,
-if the result type cannot represent the value the conversion
-succeeds but the result value is implementation-dependent.
-
-
-Conversions to and from a string type
-
-
--
-Converting a signed or unsigned integer value to a string type yields a
-string containing the UTF-8 representation of the integer. Values outside
-the range of valid Unicode code points are converted to
"\uFFFD"
.
-
-
-string('a') // "a"
-string(-1) // "\ufffd" == "\xef\xbf\xbd"
-string(0xf8) // "\u00f8" == "ø" == "\xc3\xb8"
-type MyString string
-MyString(0x65e5) // "\u65e5" == "日" == "\xe6\x97\xa5"
-
-
-
--
-Converting a slice of bytes to a string type yields
-a string whose successive bytes are the elements of the slice.
-
-
-string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø"
-string([]byte{}) // ""
-string([]byte(nil)) // ""
-
-type MyBytes []byte
-string(MyBytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø"
-
-
-
--
-Converting a slice of runes to a string type yields
-a string that is the concatenation of the individual rune values
-converted to strings.
-
-
-string([]rune{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔"
-string([]rune{}) // ""
-string([]rune(nil)) // ""
-
-type MyRunes []rune
-string(MyRunes{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔"
-
-
-
--
-Converting a value of a string type to a slice of bytes type
-yields a slice whose successive elements are the bytes of the string.
-
-
-[]byte("hellø") // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
-[]byte("") // []byte{}
-
-MyBytes("hellø") // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
-
-
-
--
-Converting a value of a string type to a slice of runes type
-yields a slice containing the individual Unicode code points of the string.
-
-
-[]rune(MyString("白鵬翔")) // []rune{0x767d, 0x9d6c, 0x7fd4}
-[]rune("") // []rune{}
-
-MyRunes("白鵬翔") // []rune{0x767d, 0x9d6c, 0x7fd4}
-
-
-
-
-Conversions from slice to array pointer
-
-
-Converting a slice to an array pointer yields a pointer to the underlying array of the slice.
-If the length of the slice is less than the length of the array,
-a run-time panic occurs.
-
-
-
-s := make([]byte, 2, 4)
-s0 := (*[0]byte)(s) // s0 != nil
-s1 := (*[1]byte)(s[1:]) // &s1[0] == &s[1]
-s2 := (*[2]byte)(s) // &s2[0] == &s[0]
-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(t)
-
-u := make([]byte, 0)
-u0 := (*[0]byte)(u) // u0 != nil
-
-
-Constant expressions
-
-
-Constant expressions may contain only constant
-operands and are evaluated at compile time.
-
-
-
-Untyped boolean, numeric, and string constants may be used as operands
-wherever it is legal to use an operand of boolean, numeric, or string type,
-respectively.
-
-
-
-A constant comparison always yields
-an untyped boolean constant. If the left operand of a constant
-shift expression is an untyped constant, the
-result is an integer constant; otherwise it is a constant of the same
-type as the left operand, which must be of
-integer type.
-
-
-
-Any other operation on untyped constants results in an untyped constant of the
-same kind; that is, a boolean, integer, floating-point, complex, or string
-constant.
-If the untyped operands of a binary operation (other than a shift) are of
-different kinds, the result is of the operand's kind that appears later in this
-list: integer, rune, floating-point, complex.
-For example, an untyped integer constant divided by an
-untyped complex constant yields an untyped complex constant.
-
-
-
-const a = 2 + 3.0 // a == 5.0 (untyped floating-point constant)
-const b = 15 / 4 // b == 3 (untyped integer constant)
-const c = 15 / 4.0 // c == 3.75 (untyped floating-point constant)
-const Θ float64 = 3/2 // Θ == 1.0 (type float64, 3/2 is integer division)
-const Π float64 = 3/2. // Π == 1.5 (type float64, 3/2. is float division)
-const d = 1 << 3.0 // d == 8 (untyped integer constant)
-const e = 1.0 << 3 // e == 8 (untyped integer constant)
-const f = int32(1) << 33 // illegal (constant 8589934592 overflows int32)
-const g = float64(2) >> 1 // illegal (float64(2) is a typed floating-point constant)
-const h = "foo" > "bar" // h == true (untyped boolean constant)
-const j = true // j == true (untyped boolean constant)
-const k = 'w' + 1 // k == 'x' (untyped rune constant)
-const l = "hi" // l == "hi" (untyped string constant)
-const m = string(k) // m == "x" (type string)
-const Σ = 1 - 0.707i // (untyped complex constant)
-const Δ = Σ + 2.0e-4 // (untyped complex constant)
-const Φ = iota*1i - 1/1i // (untyped complex constant)
-
-
-
-Applying the built-in function complex
to untyped
-integer, rune, or floating-point constants yields
-an untyped complex constant.
-
-
-
-const ic = complex(0, c) // ic == 3.75i (untyped complex constant)
-const iΘ = complex(0, Θ) // iΘ == 1i (type complex128)
-
-
-
-Constant expressions are always evaluated exactly; intermediate values and the
-constants themselves may require precision significantly larger than supported
-by any predeclared type in the language. The following are legal declarations:
-
-
-
-const Huge = 1 << 100 // Huge == 1267650600228229401496703205376 (untyped integer constant)
-const Four int8 = Huge >> 98 // Four == 4 (type int8)
-
-
-
-The divisor of a constant division or remainder operation must not be zero:
-
-
-
-3.14 / 0.0 // illegal: division by zero
-
-
-
-The values of typed constants must always be accurately
-representable by values
-of the constant type. The following constant expressions are illegal:
-
-
-
-uint(-1) // -1 cannot be represented as a uint
-int(3.14) // 3.14 cannot be represented as an int
-int64(Huge) // 1267650600228229401496703205376 cannot be represented as an int64
-Four * 300 // operand 300 cannot be represented as an int8 (type of Four)
-Four * 100 // product 400 cannot be represented as an int8 (type of Four)
-
-
-
-The mask used by the unary bitwise complement operator ^
matches
-the rule for non-constants: the mask is all 1s for unsigned constants
-and -1 for signed and untyped constants.
-
-
-
-^1 // untyped integer constant, equal to -2
-uint8(^1) // illegal: same as uint8(-2), -2 cannot be represented as a uint8
-^uint8(1) // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)
-int8(^1) // same as int8(-2)
-^int8(1) // same as -1 ^ int8(1) = -2
-
-
-
-Implementation restriction: A compiler may use rounding while
-computing untyped floating-point or complex constant expressions; see
-the implementation restriction in the section
-on constants. This rounding may cause a
-floating-point constant expression to be invalid in an integer
-context, even if it would be integral when calculated using infinite
-precision, and vice versa.
-
-
-
-Order of evaluation
-
-
-At package level, initialization dependencies
-determine the evaluation order of individual initialization expressions in
-variable declarations.
-Otherwise, when evaluating the operands of an
-expression, assignment, or
-return statement,
-all function calls, method calls, and
-communication operations are evaluated in lexical left-to-right
-order.
-
-
-
-For example, in the (function-local) assignment
-
-
-y[f()], ok = g(h(), i()+x[j()], <-c), k()
-
-
-the function calls and communication happen in the order
-f()
, h()
, i()
, j()
,
-<-c
, g()
, and k()
.
-However, the order of those events compared to the evaluation
-and indexing of x
and the evaluation
-of y
is not specified.
-
-
-
-a := 1
-f := func() int { a++; return a }
-x := []int{a, f()} // x may be [1, 2] or [2, 2]: evaluation order between a and f() is not specified
-m := map[int]int{a: 1, a: 2} // m may be {2: 1} or {2: 2}: evaluation order between the two map assignments is not specified
-n := map[int]int{a: f()} // n may be {2: 3} or {3: 3}: evaluation order between the key and the value is not specified
-
-
-
-At package level, initialization dependencies override the left-to-right rule
-for individual initialization expressions, but not for operands within each
-expression:
-
-
-
-var a, b, c = f() + v(), g(), sqr(u()) + v()
-
-func f() int { return c }
-func g() int { return a }
-func sqr(x int) int { return x*x }
-
-// functions u and v are independent of all other variables and functions
-
-
-
-The function calls happen in the order
-u()
, sqr()
, v()
,
-f()
, v()
, and g()
.
-
-
-
-Floating-point operations within a single expression are evaluated according to
-the associativity of the operators. Explicit parentheses affect the evaluation
-by overriding the default associativity.
-In the expression x + (y + z)
the addition y + z
-is performed before adding x
.
-
-
-Statements
-
-
-Statements control execution.
-
-
-
-Statement =
- Declaration | LabeledStmt | SimpleStmt |
- GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
- FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
- DeferStmt .
-
-SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .
-
-
-Terminating statements
-
-
-A terminating statement interrupts the regular flow of control in
-a block. The following statements are terminating:
-
-
-
--
- A "return" or
- "goto" statement.
-
-
-
-
--
- A call to the built-in function
-
panic
.
-
-
-
-
--
- A block in which the statement list ends in a terminating statement.
-
-
-
-
--
- An "if" statement in which:
-
- - the "else" branch is present, and
- - both branches are terminating statements.
-
-
-
--
- A "for" statement in which:
-
- - there are no "break" statements referring to the "for" statement, and
- - the loop condition is absent, and
- - the "for" statement does not use a range clause.
-
-
-
--
- A "switch" statement in which:
-
- - there are no "break" statements referring to the "switch" statement,
- - there is a default case, and
- - the statement lists in each case, including the default, end in a terminating
- statement, or a possibly labeled "fallthrough"
- statement.
-
-
-
--
- A "select" statement in which:
-
- - there are no "break" statements referring to the "select" statement, and
- - the statement lists in each case, including the default if present,
- end in a terminating statement.
-
-
-
--
- A labeled statement labeling
- a terminating statement.
-
-
-
-
-All other statements are not terminating.
-
-
-
-A statement list ends in a terminating statement if the list
-is not empty and its final non-empty statement is terminating.
-
-
-
-Empty statements
-
-
-The empty statement does nothing.
-
-
-
-EmptyStmt = .
-
-
-
-Labeled statements
-
-
-A labeled statement may be the target of a goto
,
-break
or continue
statement.
-
-
-
-LabeledStmt = Label ":" Statement .
-Label = identifier .
-
-
-
-Error: log.Panic("error encountered")
-
-
-
-Expression statements
-
-
-With the exception of specific built-in functions,
-function and method calls and
-receive operations
-can appear in statement context. Such statements may be parenthesized.
-
-
-
-ExpressionStmt = Expression .
-
-
-
-The following built-in functions are not permitted in statement context:
-
-
-
-append cap complex imag len make new real
-unsafe.Add unsafe.Alignof unsafe.Offsetof unsafe.Sizeof unsafe.Slice
-
-
-
-h(x+y)
-f.Close()
-<-ch
-(<-ch)
-len("foo") // illegal if len is the built-in function
-
-
-
-Send statements
-
-
-A send statement sends a value on a channel.
-The channel expression must be of channel type,
-the channel direction must permit send operations,
-and the type of the value to be sent must be assignable
-to the channel's element type.
-
-
-
-SendStmt = Channel "<-" Expression .
-Channel = Expression .
-
-
-
-Both the channel and the value expression are evaluated before communication
-begins. Communication blocks until the send can proceed.
-A send on an unbuffered channel can proceed if a receiver is ready.
-A send on a buffered channel can proceed if there is room in the buffer.
-A send on a closed channel proceeds by causing a run-time panic.
-A send on a nil
channel blocks forever.
-
-
-
-ch <- 3 // send value 3 to channel ch
-
-
-
-IncDec statements
-
-
-The "++" and "--" statements increment or decrement their operands
-by the untyped constant 1
.
-As with an assignment, the operand must be addressable
-or a map index expression.
-
-
-
-IncDecStmt = Expression ( "++" | "--" ) .
-
-
-
-The following assignment statements are semantically
-equivalent:
-
-
-
-IncDec statement Assignment
-x++ x += 1
-x-- x -= 1
-
-
-
-Assignments
-
-
-Assignment = ExpressionList assign_op ExpressionList .
-
-assign_op = [ add_op | mul_op ] "=" .
-
-
-
-Each left-hand side operand must be addressable,
-a map index expression, or (for =
assignments only) the
-blank identifier.
-Operands may be parenthesized.
-
-
-
-x = 1
-*p = f()
-a[i] = 23
-(k) = <-ch // same as: k = <-ch
-
-
-
-An assignment operation x
op=
-y
where op is a binary arithmetic operator
-is equivalent to x
=
x
op
-(y)
but evaluates x
-only once. The op=
construct is a single token.
-In assignment operations, both the left- and right-hand expression lists
-must contain exactly one single-valued expression, and the left-hand
-expression must not be the blank identifier.
-
-
-
-a[i] <<= 2
-i &^= 1<<n
-
-
-
-A tuple assignment assigns the individual elements of a multi-valued
-operation to a list of variables. There are two forms. In the
-first, the right hand operand is a single multi-valued expression
-such as a function call, a channel or
-map operation, or a type assertion.
-The number of operands on the left
-hand side must match the number of values. For instance, if
-f
is a function returning two values,
-
-
-
-x, y = f()
-
-
-
-assigns the first value to x
and the second to y
.
-In the second form, the number of operands on the left must equal the number
-of expressions on the right, each of which must be single-valued, and the
-nth expression on the right is assigned to the nth
-operand on the left:
-
-
-
-one, two, three = '一', '二', '三'
-
-
-
-The blank identifier provides a way to
-ignore right-hand side values in an assignment:
-
-
-
-_ = x // evaluate x but ignore it
-x, _ = f() // evaluate f() but ignore second result value
-
-
-
-The assignment proceeds in two phases.
-First, the operands of index expressions
-and pointer indirections
-(including implicit pointer indirections in selectors)
-on the left and the expressions on the right are all
-evaluated in the usual order.
-Second, the assignments are carried out in left-to-right order.
-
-
-
-a, b = b, a // exchange a and b
-
-x := []int{1, 2, 3}
-i := 0
-i, x[i] = 1, 2 // set i = 1, x[0] = 2
-
-i = 0
-x[i], i = 2, 1 // set x[0] = 2, i = 1
-
-x[0], x[0] = 1, 2 // set x[0] = 1, then x[0] = 2 (so x[0] == 2 at end)
-
-x[1], x[3] = 4, 5 // set x[1] = 4, then panic setting x[3] = 5.
-
-type Point struct { x, y int }
-var p *Point
-x[2], p.x = 6, 7 // set x[2] = 6, then panic setting p.x = 7
-
-i = 2
-x = []int{3, 5, 7}
-for i, x[i] = range x { // set i, x[2] = 0, x[0]
- break
-}
-// after this loop, i == 0 and x == []int{3, 5, 3}
-
-
-
-In assignments, each value must be assignable
-to the type of the operand to which it is assigned, with the following special cases:
-
-
-
--
- Any typed value may be assigned to the blank identifier.
-
-
--
- If an untyped constant
- is assigned to a variable of interface type or the blank identifier,
- the constant is first implicitly converted to its
- default type.
-
-
--
- If an untyped boolean value is assigned to a variable of interface type or
- the blank identifier, it is first implicitly converted to type
bool
.
-
-
-
-If statements
-
-
-"If" statements specify the conditional execution of two branches
-according to the value of a boolean expression. If the expression
-evaluates to true, the "if" branch is executed, otherwise, if
-present, the "else" branch is executed.
-
-
-
-IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .
-
-
-
-if x > max {
- x = max
-}
-
-
-
-The expression may be preceded by a simple statement, which
-executes before the expression is evaluated.
-
-
-
-if x := f(); x < y {
- return x
-} else if x > z {
- return z
-} else {
- return y
-}
-
-
-
-Switch statements
-
-
-"Switch" statements provide multi-way execution.
-An expression or type is compared to the "cases"
-inside the "switch" to determine which branch
-to execute.
-
-
-
-SwitchStmt = ExprSwitchStmt | TypeSwitchStmt .
-
-
-
-There are two forms: expression switches and type switches.
-In an expression switch, the cases contain expressions that are compared
-against the value of the switch expression.
-In a type switch, the cases contain types that are compared against the
-type of a specially annotated switch expression.
-The switch expression is evaluated exactly once in a switch statement.
-
-
-Expression switches
-
-
-In an expression switch,
-the switch expression is evaluated and
-the case expressions, which need not be constants,
-are evaluated left-to-right and top-to-bottom; the first one that equals the
-switch expression
-triggers execution of the statements of the associated case;
-the other cases are skipped.
-If no case matches and there is a "default" case,
-its statements are executed.
-There can be at most one default case and it may appear anywhere in the
-"switch" statement.
-A missing switch expression is equivalent to the boolean value
-true
.
-
-
-
-ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
-ExprCaseClause = ExprSwitchCase ":" StatementList .
-ExprSwitchCase = "case" ExpressionList | "default" .
-
-
-
-If the switch expression evaluates to an untyped constant, it is first implicitly
-converted to its default type.
-The predeclared untyped value nil
cannot be used as a switch expression.
-The switch expression type must be comparable.
-
-
-
-If a case expression is untyped, it is first implicitly converted
-to the type of the switch expression.
-For each (possibly converted) case expression x
and the value t
-of the switch expression, x == t
must be a valid comparison.
-
-
-
-In other words, the switch expression is treated as if it were used to declare and
-initialize a temporary variable t
without explicit type; it is that
-value of t
against which each case expression x
is tested
-for equality.
-
-
-
-In a case or default clause, the last non-empty statement
-may be a (possibly labeled)
-"fallthrough" statement to
-indicate that control should flow from the end of this clause to
-the first statement of the next clause.
-Otherwise control flows to the end of the "switch" statement.
-A "fallthrough" statement may appear as the last statement of all
-but the last clause of an expression switch.
-
-
-
-The switch expression may be preceded by a simple statement, which
-executes before the expression is evaluated.
-
-
-
-switch tag {
-default: s3()
-case 0, 1, 2, 3: s1()
-case 4, 5, 6, 7: s2()
-}
-
-switch x := f(); { // missing switch expression means "true"
-case x < 0: return -x
-default: return x
-}
-
-switch {
-case x < y: f1()
-case x < z: f2()
-case x == 4: f3()
-}
-
-
-
-Implementation restriction: A compiler may disallow multiple case
-expressions evaluating to the same constant.
-For instance, the current compilers disallow duplicate integer,
-floating point, or string constants in case expressions.
-
-
-Type switches
-
-
-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 type assertion
-using the keyword type
rather than an actual type:
-
-
-
-switch x.(type) {
-// cases
-}
-
-
-
-Cases then match actual types T
against the dynamic type of the
-expression x
. As with type assertions, x
must be of
-interface type, and each non-interface type
-T
listed in a case must implement the type of x
.
-The types listed in the cases of a type switch must all be
-different.
-
-
-
-TypeSwitchStmt = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" .
-TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
-TypeCaseClause = TypeSwitchCase ":" StatementList .
-TypeSwitchCase = "case" TypeList | "default" .
-TypeList = Type { "," Type } .
-
-
-
-The TypeSwitchGuard may include a
-short variable declaration.
-When that form is used, the variable is declared at the end of the
-TypeSwitchCase in the implicit block of each clause.
-In clauses with a case listing exactly one type, the variable
-has that type; otherwise, the variable has the type of the expression
-in the TypeSwitchGuard.
-
-
-
-Instead of a type, a case may use the predeclared identifier
-nil
;
-that case is selected when the expression in the TypeSwitchGuard
-is a nil
interface value.
-There may be at most one nil
case.
-
-
-
-Given an expression x
of type interface{}
,
-the following type switch:
-
-
-
-switch i := x.(type) {
-case nil:
- printString("x is nil") // type of i is type of x (interface{})
-case int:
- printInt(i) // type of i is int
-case float64:
- printFloat64(i) // type of i is float64
-case func(int) float64:
- printFunction(i) // type of i is func(int) float64
-case bool, string:
- printString("type is bool or string") // type of i is type of x (interface{})
-default:
- printString("don't know the type") // type of i is type of x (interface{})
-}
-
-
-
-could be rewritten:
-
-
-
-v := x // x is evaluated exactly once
-if v == nil {
- i := v // type of i is type of x (interface{})
- printString("x is nil")
-} else if i, isInt := v.(int); isInt {
- printInt(i) // type of i is int
-} else if i, isFloat64 := v.(float64); isFloat64 {
- printFloat64(i) // type of i is float64
-} else if i, isFunc := v.(func(int) float64); isFunc {
- printFunction(i) // type of i is func(int) float64
-} else {
- _, isBool := v.(bool)
- _, isString := v.(string)
- if isBool || isString {
- i := v // type of i is type of x (interface{})
- printString("type is bool or string")
- } else {
- i := v // type of i is type of x (interface{})
- printString("don't know the type")
- }
-}
-
-
-
-The type switch guard may be preceded by a simple statement, which
-executes before the guard is evaluated.
-
-
-
-The "fallthrough" statement is not permitted in a type switch.
-
-
-For statements
-
-
-A "for" statement specifies repeated execution of a block. There are three forms:
-The iteration may be controlled by a single condition, a "for" clause, or a "range" clause.
-
-
-
-ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .
-Condition = Expression .
-
-
-For statements with single condition
-
-
-In its simplest form, a "for" statement specifies the repeated execution of
-a block as long as a boolean condition evaluates to true.
-The condition is evaluated before each iteration.
-If the condition is absent, it is equivalent to the boolean value
-true
.
-
-
-
-for a < b {
- a *= 2
-}
-
-
-For statements with for
clause
-
-
-A "for" statement with a ForClause is also controlled by its condition, but
-additionally it may specify an init
-and a post statement, such as an assignment,
-an increment or decrement statement. The init statement may be a
-short variable declaration, but the post statement must not.
-Variables declared by the init statement are re-used in each iteration.
-
-
-
-ForClause = [ InitStmt ] ";" [ Condition ] ";" [ PostStmt ] .
-InitStmt = SimpleStmt .
-PostStmt = SimpleStmt .
-
-
-
-for i := 0; i < 10; i++ {
- f(i)
-}
-
-
-
-If non-empty, the init statement is executed once before evaluating the
-condition for the first iteration;
-the post statement is executed after each execution of the block (and
-only if the block was executed).
-Any element of the ForClause may be empty but the
-semicolons are
-required unless there is only a condition.
-If the condition is absent, it is equivalent to the boolean value
-true
.
-
-
-
-for cond { S() } is the same as for ; cond ; { S() }
-for { S() } is the same as for true { S() }
-
-
-For statements with range
clause
-
-
-A "for" statement with a "range" clause
-iterates through all entries of an array, slice, string or map,
-or values received on a channel. For each entry it assigns iteration values
-to corresponding iteration variables if present and then executes the block.
-
-
-
-RangeClause = [ ExpressionList "=" | IdentifierList ":=" ] "range" Expression .
-
-
-
-The expression on the right in the "range" clause is called the range expression,
-which may be an array, pointer to an array, slice, string, map, or channel permitting
-receive operations.
-As with an assignment, if present the operands on the left must be
-addressable or map index expressions; they
-denote the iteration variables. If the range expression is a channel, at most
-one iteration variable is permitted, otherwise there may be up to two.
-If the last iteration variable is the blank identifier,
-the range clause is equivalent to the same clause without that identifier.
-
-
-
-The range expression x
is evaluated once before beginning the loop,
-with one exception: if at most one iteration variable is present and
-len(x)
is constant,
-the range expression is not evaluated.
-
-
-
-Function calls on the left are evaluated once per iteration.
-For each iteration, iteration values are produced as follows
-if the respective iteration variables are present:
-
-
-
-Range expression 1st value 2nd value
-
-array or slice a [n]E, *[n]E, or []E index i int a[i] E
-string s string type index i int see below rune
-map m map[K]V key k K m[k] V
-channel c chan E, <-chan E element e E
-
-
-
--
-For an array, pointer to array, or slice value
a
, the index iteration
-values are produced in increasing order, starting at element index 0.
-If at most one iteration variable is present, the range loop produces
-iteration values from 0 up to len(a)-1
and does not index into the array
-or slice itself. For a nil
slice, the number of iterations is 0.
-
-
--
-For a string value, the "range" clause iterates over the Unicode code points
-in the string starting at byte index 0. On successive iterations, the index value will be the
-index of the first byte of successive UTF-8-encoded code points in the string,
-and the second value, of type
rune
, will be the value of
-the corresponding code point. If the iteration encounters an invalid
-UTF-8 sequence, the second value will be 0xFFFD
,
-the Unicode replacement character, and the next iteration will advance
-a single byte in the string.
-
-
--
-The iteration order over maps is not specified
-and is not guaranteed to be the same from one iteration to the next.
-If a map entry that has not yet been reached is removed during iteration,
-the corresponding iteration value will not be produced. If a map entry is
-created during iteration, that entry may be produced during the iteration or
-may be skipped. The choice may vary for each entry created and from one
-iteration to the next.
-If the map is
nil
, the number of iterations is 0.
-
-
--
-For channels, the iteration values produced are the successive values sent on
-the channel until the channel is closed. If the channel
-is
nil
, the range expression blocks forever.
-
-
-
-
-The iteration values are assigned to the respective
-iteration variables as in an assignment statement.
-
-
-
-The iteration variables may be declared by the "range" clause using a form of
-short variable declaration
-(:=
).
-In this case their types are set to the types of the respective iteration values
-and their scope is the block of the "for"
-statement; they are re-used in each iteration.
-If the iteration variables are declared outside the "for" statement,
-after execution their values will be those of the last iteration.
-
-
-
-var testdata *struct {
- a *[7]int
-}
-for i, _ := range testdata.a {
- // testdata.a is never evaluated; len(testdata.a) is constant
- // i ranges from 0 to 6
- f(i)
-}
-
-var a [10]string
-for i, s := range a {
- // type of i is int
- // type of s is string
- // s == a[i]
- g(i, s)
-}
-
-var key string
-var val interface{} // element type of m is assignable to val
-m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6}
-for key, val = range m {
- h(key, val)
-}
-// key == last map key encountered in iteration
-// val == map[key]
-
-var ch chan Work = producer()
-for w := range ch {
- doWork(w)
-}
-
-// empty a channel
-for range ch {}
-
-
-
-Go statements
-
-
-A "go" statement starts the execution of a function call
-as an independent concurrent thread of control, or goroutine,
-within the same address space.
-
-
-
-GoStmt = "go" Expression .
-
-
-
-The expression must be a function or method call; it cannot be parenthesized.
-Calls of built-in functions are restricted as for
-expression statements.
-
-
-
-The function value and parameters are
-evaluated as usual
-in the calling goroutine, but
-unlike with a regular call, program execution does not wait
-for the invoked function to complete.
-Instead, the function begins executing independently
-in a new goroutine.
-When the function terminates, its goroutine also terminates.
-If the function has any return values, they are discarded when the
-function completes.
-
-
-
-go Server()
-go func(ch chan<- bool) { for { sleep(10); ch <- true }} (c)
-
-
-
-Select statements
-
-
-A "select" statement chooses which of a set of possible
-send or
-receive
-operations will proceed.
-It looks similar to a
-"switch" statement but with the
-cases all referring to communication operations.
-
-
-
-SelectStmt = "select" "{" { CommClause } "}" .
-CommClause = CommCase ":" StatementList .
-CommCase = "case" ( SendStmt | RecvStmt ) | "default" .
-RecvStmt = [ ExpressionList "=" | IdentifierList ":=" ] RecvExpr .
-RecvExpr = Expression .
-
-
-
-A case with a RecvStmt may assign the result of a RecvExpr to one or
-two variables, which may be declared using a
-short variable declaration.
-The RecvExpr must be a (possibly parenthesized) receive operation.
-There can be at most one default case and it may appear anywhere
-in the list of cases.
-
-
-
-Execution of a "select" statement proceeds in several steps:
-
-
-
--
-For all the cases in the statement, the channel operands of receive operations
-and the channel and right-hand-side expressions of send statements are
-evaluated exactly once, in source order, upon entering the "select" statement.
-The result is a set of channels to receive from or send to,
-and the corresponding values to send.
-Any side effects in that evaluation will occur irrespective of which (if any)
-communication operation is selected to proceed.
-Expressions on the left-hand side of a RecvStmt with a short variable declaration
-or assignment are not yet evaluated.
-
-
--
-If one or more of the communications can proceed,
-a single one that can proceed is chosen via a uniform pseudo-random selection.
-Otherwise, if there is a default case, that case is chosen.
-If there is no default case, the "select" statement blocks until
-at least one of the communications can proceed.
-
-
--
-Unless the selected case is the default case, the respective communication
-operation is executed.
-
-
--
-If the selected case is a RecvStmt with a short variable declaration or
-an assignment, the left-hand side expressions are evaluated and the
-received value (or values) are assigned.
-
-
--
-The statement list of the selected case is executed.
-
-
-
-
-Since communication on nil
channels can never proceed,
-a select with only nil
channels and no default case blocks forever.
-
-
-
-var a []int
-var c, c1, c2, c3, c4 chan int
-var i1, i2 int
-select {
-case i1 = <-c1:
- print("received ", i1, " from c1\n")
-case c2 <- i2:
- print("sent ", i2, " to c2\n")
-case i3, ok := (<-c3): // same as: i3, ok := <-c3
- if ok {
- print("received ", i3, " from c3\n")
- } else {
- print("c3 is closed\n")
- }
-case a[f()] = <-c4:
- // same as:
- // case t := <-c4
- // a[f()] = t
-default:
- print("no communication\n")
-}
-
-for { // send random sequence of bits to c
- select {
- case c <- 0: // note: no statement, no fallthrough, no folding of cases
- case c <- 1:
- }
-}
-
-select {} // block forever
-
-
-
-Return statements
-
-
-A "return" statement in a function F
terminates the execution
-of F
, and optionally provides one or more result values.
-Any functions deferred by F
-are executed before F
returns to its caller.
-
-
-
-ReturnStmt = "return" [ ExpressionList ] .
-
-
-
-In a function without a result type, a "return" statement must not
-specify any result values.
-
-
-func noResult() {
- return
-}
-
-
-
-There are three ways to return values from a function with a result
-type:
-
-
-
- - The return value or values may be explicitly listed
- in the "return" statement. Each expression must be single-valued
- and assignable
- to the corresponding element of the function's result type.
-
-func simpleF() int {
- return 2
-}
-
-func complexF1() (re float64, im float64) {
- return -7.0, -4.0
-}
-
-
- - The expression list in the "return" statement may be a single
- call to a multi-valued function. The effect is as if each value
- returned from that function were assigned to a temporary
- variable with the type of the respective value, followed by a
- "return" statement listing these variables, at which point the
- rules of the previous case apply.
-
-func complexF2() (re float64, im float64) {
- return complexF1()
-}
-
-
- - The expression list may be empty if the function's result
- type specifies names for its result parameters.
- The result parameters act as ordinary local variables
- and the function may assign values to them as necessary.
- The "return" statement returns the values of these variables.
-
-func complexF3() (re float64, im float64) {
- re = 7.0
- im = 4.0
- return
-}
-
-func (devnull) Write(p []byte) (n int, _ error) {
- n = len(p)
- return
-}
-
-
-
-
-
-Regardless of how they are declared, all the result values are initialized to
-the zero values for their type upon entry to the
-function. A "return" statement that specifies results sets the result parameters before
-any deferred functions are executed.
-
-
-
-Implementation restriction: A compiler may disallow an empty expression list
-in a "return" statement if a different entity (constant, type, or variable)
-with the same name as a result parameter is in
-scope at the place of the return.
-
-
-
-func f(n int) (res int, err error) {
- if _, err := f(n-1); err != nil {
- return // invalid return statement: err is shadowed
- }
- return
-}
-
-
-Break statements
-
-
-A "break" statement terminates execution of the innermost
-"for",
-"switch", or
-"select" statement
-within the same function.
-
-
-
-BreakStmt = "break" [ Label ] .
-
-
-
-If there is a label, it must be that of an enclosing
-"for", "switch", or "select" statement,
-and that is the one whose execution terminates.
-
-
-
-OuterLoop:
- for i = 0; i < n; i++ {
- for j = 0; j < m; j++ {
- switch a[i][j] {
- case nil:
- state = Error
- break OuterLoop
- case item:
- state = Found
- break OuterLoop
- }
- }
- }
-
-
-Continue statements
-
-
-A "continue" statement begins the next iteration of the
-innermost "for" loop at its post statement.
-The "for" loop must be within the same function.
-
-
-
-ContinueStmt = "continue" [ Label ] .
-
-
-
-If there is a label, it must be that of an enclosing
-"for" statement, and that is the one whose execution
-advances.
-
-
-
-RowLoop:
- for y, row := range rows {
- for x, data := range row {
- if data == endOfRow {
- continue RowLoop
- }
- row[x] = data + bias(x, y)
- }
- }
-
-
-Goto statements
-
-
-A "goto" statement transfers control to the statement with the corresponding label
-within the same function.
-
-
-
-GotoStmt = "goto" Label .
-
-
-
-goto Error
-
-
-
-Executing the "goto" statement must not cause any variables to come into
-scope that were not already in scope at the point of the goto.
-For instance, this example:
-
-
-
- goto L // BAD
- v := 3
-L:
-
-
-
-is erroneous because the jump to label L
skips
-the creation of v
.
-
-
-
-A "goto" statement outside a block cannot jump to a label inside that block.
-For instance, this example:
-
-
-
-if n%2 == 1 {
- goto L1
-}
-for n > 0 {
- f()
- n--
-L1:
- f()
- n--
-}
-
-
-
-is erroneous because the label L1
is inside
-the "for" statement's block but the goto
is not.
-
-
-Fallthrough statements
-
-
-A "fallthrough" statement transfers control to the first statement of the
-next case clause in an expression "switch" statement.
-It may be used only as the final non-empty statement in such a clause.
-
-
-
-FallthroughStmt = "fallthrough" .
-
-
-
-Defer statements
-
-
-A "defer" statement invokes a function whose execution is deferred
-to the moment the surrounding function returns, either because the
-surrounding function executed a return statement,
-reached the end of its function body,
-or because the corresponding goroutine is panicking.
-
-
-
-DeferStmt = "defer" Expression .
-
-
-
-The expression must be a function or method call; it cannot be parenthesized.
-Calls of built-in functions are restricted as for
-expression statements.
-
-
-
-Each time a "defer" statement
-executes, the function value and parameters to the call are
-evaluated as usual
-and saved anew but the actual function is not invoked.
-Instead, deferred functions are invoked immediately before
-the surrounding function returns, in the reverse order
-they were deferred. That is, if the surrounding function
-returns through an explicit return statement,
-deferred functions are executed after any result parameters are set
-by that return statement but before the function returns to its caller.
-If a deferred function value evaluates
-to nil
, execution panics
-when the function is invoked, not when the "defer" statement is executed.
-
-
-
-For instance, if the deferred function is
-a function literal and the surrounding
-function has named result parameters that
-are in scope within the literal, the deferred function may access and modify
-the result parameters before they are returned.
-If the deferred function has any return values, they are discarded when
-the function completes.
-(See also the section on handling panics.)
-
-
-
-lock(l)
-defer unlock(l) // unlocking happens before surrounding function returns
-
-// prints 3 2 1 0 before surrounding function returns
-for i := 0; i <= 3; i++ {
- defer fmt.Print(i)
-}
-
-// f returns 42
-func f() (result int) {
- defer func() {
- // result is accessed after it was set to 6 by the return statement
- result *= 7
- }()
- return 6
-}
-
-
-Built-in functions
-
-
-Built-in functions are
-predeclared.
-They are called like any other function but some of them
-accept a type instead of an expression as the first argument.
-
-
-
-The built-in functions do not have standard Go types,
-so they can only appear in call expressions;
-they cannot be used as function values.
-
-
-Close
-
-
-For a channel c
, the built-in function close(c)
-records that no more values will be sent on the channel.
-It is an error if c
is a receive-only channel.
-Sending to or closing a closed channel causes a run-time panic.
-Closing the nil channel also causes a run-time panic.
-After calling close
, and after any previously
-sent values have been received, receive operations will return
-the zero value for the channel's type without blocking.
-The multi-valued receive operation
-returns a received value along with an indication of whether the channel is closed.
-
-
-
-Length and capacity
-
-
-The built-in functions len
and cap
take arguments
-of various types and return a result of type int
.
-The implementation guarantees that the result always fits into an int
.
-
-
-
-Call Argument type Result
-
-len(s) string type string length in bytes
- [n]T, *[n]T array length (== n)
- []T slice length
- map[K]T map length (number of defined keys)
- chan T number of elements queued in channel buffer
-
-cap(s) [n]T, *[n]T array length (== n)
- []T slice capacity
- chan T channel buffer capacity
-
-
-
-The capacity of a slice is the number of elements for which there is
-space allocated in the underlying array.
-At any time the following relationship holds:
-
-
-
-0 <= len(s) <= cap(s)
-
-
-
-The length of a nil
slice, map or channel is 0.
-The capacity of a nil
slice or channel is 0.
-
-
-
-The expression len(s)
is constant if
-s
is a string constant. The expressions len(s)
and
-cap(s)
are constants if the type of s
is an array
-or pointer to an array and the expression s
does not contain
-channel receives or (non-constant)
-function calls; in this case s
is not evaluated.
-Otherwise, invocations of len
and cap
are not
-constant and s
is evaluated.
-
-
-
-const (
- c1 = imag(2i) // imag(2i) = 2.0 is a constant
- c2 = len([10]float64{2}) // [10]float64{2} contains no function calls
- c3 = len([10]float64{c1}) // [10]float64{c1} contains no function calls
- c4 = len([10]float64{imag(2i)}) // imag(2i) is a constant and no function call is issued
- c5 = len([10]float64{imag(z)}) // invalid: imag(z) is a (non-constant) function call
-)
-var z complex128
-
-
-Allocation
-
-
-The built-in function new
takes a type T
,
-allocates storage for a variable of that type
-at run time, and returns a value of type *T
-pointing to it.
-The variable is initialized as described in the section on
-initial values.
-
-
-
-new(T)
-
-
-
-For instance
-
-
-
-type S struct { a int; b float64 }
-new(S)
-
-
-
-allocates storage for a variable of type S
,
-initializes it (a=0
, b=0.0
),
-and returns a value of type *S
containing the address
-of the location.
-
-
-Making slices, maps and channels
-
-
-The built-in function make
takes a type T
,
-which must be a slice, map or channel type,
-optionally followed by a type-specific list of expressions.
-It returns a value of type T
(not *T
).
-The memory is initialized as described in the section on
-initial values.
-
-
-
-Call Type T Result
-
-make(T, n) slice slice of type T with length n and capacity n
-make(T, n, m) slice slice of type T with length n and capacity m
-
-make(T) map map of type T
-make(T, n) map map of type T with initial space for approximately n elements
-
-make(T) channel unbuffered channel of type T
-make(T, n) channel buffered channel of type T, buffer size n
-
-
-
-
-Each of the size arguments n
and m
must be of integer type
-or an untyped constant.
-A constant size argument must be non-negative and representable
-by a value of type int
; if it is an untyped constant it is given type int
.
-If both n
and m
are provided and are constant, then
-n
must be no larger than m
.
-If n
is negative or larger than m
at run time,
-a run-time panic occurs.
-
-
-
-s := make([]int, 10, 100) // slice with len(s) == 10, cap(s) == 100
-s := make([]int, 1e3) // slice with len(s) == cap(s) == 1000
-s := make([]int, 1<<63) // illegal: len(s) is not representable by a value of type int
-s := make([]int, 10, 0) // illegal: len(s) > cap(s)
-c := make(chan int, 10) // channel with a buffer size of 10
-m := make(map[string]int, 100) // map with initial space for approximately 100 elements
-
-
-
-Calling make
with a map type and size hint n
will
-create a map with initial space to hold n
map elements.
-The precise behavior is implementation-dependent.
-
-
-
-Appending to and copying slices
-
-
-The built-in functions append
and copy
assist in
-common slice operations.
-For both functions, the result is independent of whether the memory referenced
-by the arguments overlaps.
-
-
-
-The variadic function append
-appends zero or more values x
-to s
of type S
, which must be a slice type, and
-returns the resulting slice, also of type S
.
-The values x
are passed to a parameter of type ...T
-where T
is the element type of
-S
and the respective
-parameter passing rules apply.
-As a special case, append
also accepts a first argument
-assignable to type []byte
with a second argument of
-string type followed by ...
. This form appends the
-bytes of the string.
-
-
-
-append(s S, x ...T) S // T is the element type of S
-
-
-
-If the capacity of s
is not large enough to fit the additional
-values, append
allocates a new, sufficiently large underlying
-array that fits both the existing slice elements and the additional values.
-Otherwise, append
re-uses the underlying array.
-
-
-
-s0 := []int{0, 0}
-s1 := append(s0, 2) // append a single element s1 == []int{0, 0, 2}
-s2 := append(s1, 3, 5, 7) // append multiple elements s2 == []int{0, 0, 2, 3, 5, 7}
-s3 := append(s2, s0...) // append a slice s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}
-s4 := append(s3[3:6], s3[2:]...) // append overlapping slice s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0}
-
-var t []interface{}
-t = append(t, 42, 3.1415, "foo") // t == []interface{}{42, 3.1415, "foo"}
-
-var b []byte
-b = append(b, "bar"...) // append string contents b == []byte{'b', 'a', 'r' }
-
-
-
-The function copy
copies slice elements from
-a source src
to a destination dst
and returns the
-number of elements copied.
-Both arguments must have identical element type T
and must be
-assignable to a slice of type []T
.
-The number of elements copied is the minimum of
-len(src)
and len(dst)
.
-As a special case, copy
also accepts a destination argument assignable
-to type []byte
with a source argument of a string type.
-This form copies the bytes from the string into the byte slice.
-
-
-
-copy(dst, src []T) int
-copy(dst []byte, src string) int
-
-
-
-Examples:
-
-
-
-var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
-var s = make([]int, 6)
-var b = make([]byte, 5)
-n1 := copy(s, a[0:]) // n1 == 6, s == []int{0, 1, 2, 3, 4, 5}
-n2 := copy(s, s[2:]) // n2 == 4, s == []int{2, 3, 4, 5, 4, 5}
-n3 := copy(b, "Hello, World!") // n3 == 5, b == []byte("Hello")
-
-
-
-Deletion of map elements
-
-
-The built-in function delete
removes the element with key
-k
from a map m
. The
-type of k
must be assignable
-to the key type of m
.
-
-
-
-delete(m, k) // remove element m[k] from map m
-
-
-
-If the map m
is nil
or the element m[k]
-does not exist, delete
is a no-op.
-
-
-
-Manipulating complex numbers
-
-
-Three functions assemble and disassemble complex numbers.
-The built-in function complex
constructs a complex
-value from a floating-point real and imaginary part, while
-real
and imag
-extract the real and imaginary parts of a complex value.
-
-
-
-complex(realPart, imaginaryPart floatT) complexT
-real(complexT) floatT
-imag(complexT) floatT
-
-
-
-The type of the arguments and return value correspond.
-For complex
, the two arguments must be of the same
-floating-point type and the return type is the complex type
-with the corresponding floating-point constituents:
-complex64
for float32
arguments, and
-complex128
for float64
arguments.
-If one of the arguments evaluates to an untyped constant, it is first implicitly
-converted to the type of the other argument.
-If both arguments evaluate to untyped constants, they must be non-complex
-numbers or their imaginary parts must be zero, and the return value of
-the function is an untyped complex constant.
-
-
-
-For real
and imag
, the argument must be
-of complex type, and the return type is the corresponding floating-point
-type: float32
for a complex64
argument, and
-float64
for a complex128
argument.
-If the argument evaluates to an untyped constant, it must be a number,
-and the return value of the function is an untyped floating-point constant.
-
-
-
-The real
and imag
functions together form the inverse of
-complex
, so for a value z
of a complex type Z
,
-z == Z(complex(real(z), imag(z)))
.
-
-
-
-If the operands of these functions are all constants, the return
-value is a constant.
-
-
-
-var a = complex(2, -2) // complex128
-const b = complex(1.0, -1.4) // untyped complex constant 1 - 1.4i
-x := float32(math.Cos(math.Pi/2)) // float32
-var c64 = complex(5, -x) // complex64
-var s int = complex(1, 0) // untyped complex constant 1 + 0i can be converted to int
-_ = complex(1, 2<<s) // illegal: 2 assumes floating-point type, cannot shift
-var rl = real(c64) // float32
-var im = imag(a) // float64
-const c = imag(b) // untyped constant -1.4
-_ = imag(3 << s) // illegal: 3 assumes complex type, cannot shift
-
-
-Handling panics
-
- Two built-in functions, panic
and recover
,
-assist in reporting and handling run-time panics
-and program-defined error conditions.
-
-
-
-func panic(interface{})
-func recover() interface{}
-
-
-
-While executing a function F
,
-an explicit call to panic
or a run-time panic
-terminates the execution of F
.
-Any functions deferred by F
-are then executed as usual.
-Next, any deferred functions run by F's
caller are run,
-and so on up to any deferred by the top-level function in the executing goroutine.
-At that point, the program is terminated and the error
-condition is reported, including the value of the argument to panic
.
-This termination sequence is called panicking.
-
-
-
-panic(42)
-panic("unreachable")
-panic(Error("cannot parse"))
-
-
-
-The recover
function allows a program to manage behavior
-of a panicking goroutine.
-Suppose a function G
defers a function D
that calls
-recover
and a panic occurs in a function on the same goroutine in which G
-is executing.
-When the running of deferred functions reaches D
,
-the return value of D
's call to recover
will be the value passed to the call of panic
.
-If D
returns normally, without starting a new
-panic
, the panicking sequence stops. In that case,
-the state of functions called between G
and the call to panic
-is discarded, and normal execution resumes.
-Any functions deferred by G
before D
are then run and G
's
-execution terminates by returning to its caller.
-
-
-
-The return value of recover
is nil
if any of the following conditions holds:
-
-
--
-
panic
's argument was nil
;
-
--
-the goroutine is not panicking;
-
--
-
recover
was not called directly by a deferred function.
-
-
-
-
-The protect
function in the example below invokes
-the function argument g
and protects callers from
-run-time panics raised by g
.
-
-
-
-func protect(g func()) {
- defer func() {
- log.Println("done") // Println executes normally even if there is a panic
- if x := recover(); x != nil {
- log.Printf("run time panic: %v", x)
- }
- }()
- log.Println("start")
- g()
-}
-
-
-
-Bootstrapping
-
-
-Current implementations provide several built-in functions useful during
-bootstrapping. These functions are documented for completeness but are not
-guaranteed to stay in the language. They do not return a result.
-
-
-
-Function Behavior
-
-print prints all arguments; formatting of arguments is implementation-specific
-println like print but prints spaces between arguments and a newline at the end
-
-
-
-Implementation restriction: print
and println
need not
-accept arbitrary argument types, but printing of boolean, numeric, and string
-types must be supported.
-
-
-Packages
-
-
-Go programs are constructed by linking together packages.
-A package in turn is constructed from one or more source files
-that together declare constants, types, variables and functions
-belonging to the package and which are accessible in all files
-of the same package. Those elements may be
-exported and used in another package.
-
-
-Source file organization
-
-
-Each source file consists of a package clause defining the package
-to which it belongs, followed by a possibly empty set of import
-declarations that declare packages whose contents it wishes to use,
-followed by a possibly empty set of declarations of functions,
-types, variables, and constants.
-
-
-
-SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .
-
-
-Package clause
-
-
-A package clause begins each source file and defines the package
-to which the file belongs.
-
-
-
-PackageClause = "package" PackageName .
-PackageName = identifier .
-
-
-
-The PackageName must not be the blank identifier.
-
-
-
-package math
-
-
-
-A set of files sharing the same PackageName form the implementation of a package.
-An implementation may require that all source files for a package inhabit the same directory.
-
-
-Import declarations
-
-
-An import declaration states that the source file containing the declaration
-depends on functionality of the imported package
-(§Program initialization and execution)
-and enables access to exported identifiers
-of that package.
-The import names an identifier (PackageName) to be used for access and an ImportPath
-that specifies the package to be imported.
-
-
-
-ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
-ImportSpec = [ "." | PackageName ] ImportPath .
-ImportPath = string_lit .
-
-
-
-The PackageName is used in qualified identifiers
-to access exported identifiers of the package within the importing source file.
-It is declared in the file block.
-If the PackageName is omitted, it defaults to the identifier specified in the
-package clause of the imported package.
-If an explicit period (.
) appears instead of a name, all the
-package's exported identifiers declared in that package's
-package block will be declared in the importing source
-file's file block and must be accessed without a qualifier.
-
-
-
-The interpretation of the ImportPath is implementation-dependent but
-it is typically a substring of the full file name of the compiled
-package and may be relative to a repository of installed packages.
-
-
-
-Implementation restriction: A compiler may restrict ImportPaths to
-non-empty strings using only characters belonging to
-Unicode's
-L, M, N, P, and S general categories (the Graphic characters without
-spaces) and may also exclude the characters
-!"#$%&'()*,:;<=>?[\]^`{|}
-and the Unicode replacement character U+FFFD.
-
-
-
-Assume we have compiled a package containing the package clause
-package math
, which exports function Sin
, and
-installed the compiled package in the file identified by
-"lib/math"
.
-This table illustrates how Sin
is accessed in files
-that import the package after the
-various types of import declaration.
-
-
-
-Import declaration Local name of Sin
-
-import "lib/math" math.Sin
-import m "lib/math" m.Sin
-import . "lib/math" Sin
-
-
-
-An import declaration declares a dependency relation between
-the importing and imported package.
-It is illegal for a package to import itself, directly or indirectly,
-or to directly import a package without
-referring to any of its exported identifiers. To import a package solely for
-its side-effects (initialization), use the blank
-identifier as explicit package name:
-
-
-
-import _ "lib/math"
-
-
-
-An example package
-
-
-Here is a complete Go package that implements a concurrent prime sieve.
-
-
-
-package main
-
-import "fmt"
-
-// Send the sequence 2, 3, 4, … to channel 'ch'.
-func generate(ch chan<- int) {
- for i := 2; ; i++ {
- ch <- i // Send 'i' to channel 'ch'.
- }
-}
-
-// Copy the values from channel 'src' to channel 'dst',
-// removing those divisible by 'prime'.
-func filter(src <-chan int, dst chan<- int, prime int) {
- for i := range src { // Loop over values received from 'src'.
- if i%prime != 0 {
- dst <- i // Send 'i' to channel 'dst'.
- }
- }
-}
-
-// The prime sieve: Daisy-chain filter processes together.
-func sieve() {
- ch := make(chan int) // Create a new channel.
- go generate(ch) // Start generate() as a subprocess.
- for {
- prime := <-ch
- fmt.Print(prime, "\n")
- ch1 := make(chan int)
- go filter(ch, ch1, prime)
- ch = ch1
- }
-}
-
-func main() {
- sieve()
-}
-
-
-Program initialization and execution
-
-The zero value
-
-When storage is allocated for a variable,
-either through a declaration or a call of new
, or when
-a new value is created, either through a composite literal or a call
-of make
,
-and no explicit initialization is provided, the variable or value is
-given a default value. Each element of such a variable or value is
-set to the zero value for its type: false
for booleans,
-0
for numeric types, ""
-for strings, and nil
for pointers, functions, interfaces, slices, channels, and maps.
-This initialization is done recursively, so for instance each element of an
-array of structs will have its fields zeroed if no value is specified.
-
-
-These two simple declarations are equivalent:
-
-
-
-var i int
-var i int = 0
-
-
-
-After
-
-
-
-type T struct { i int; f float64; next *T }
-t := new(T)
-
-
-
-the following holds:
-
-
-
-t.i == 0
-t.f == 0.0
-t.next == nil
-
-
-
-The same would also be true after
-
-
-
-var t T
-
-
-Package initialization
-
-
-Within a package, package-level variable initialization proceeds stepwise,
-with each step selecting the variable earliest in declaration order
-which has no dependencies on uninitialized variables.
-
-
-
-More precisely, a package-level variable is considered ready for
-initialization if it is not yet initialized and either has
-no initialization expression or
-its initialization expression has no dependencies on uninitialized variables.
-Initialization proceeds by repeatedly initializing the next package-level
-variable that is earliest in declaration order and ready for initialization,
-until there are no variables ready for initialization.
-
-
-
-If any variables are still uninitialized when this
-process ends, those variables are part of one or more initialization cycles,
-and the program is not valid.
-
-
-
-Multiple variables on the left-hand side of a variable declaration initialized
-by single (multi-valued) expression on the right-hand side are initialized
-together: If any of the variables on the left-hand side is initialized, all
-those variables are initialized in the same step.
-
-
-
-var x = a
-var a, b = f() // a and b are initialized together, before x is initialized
-
-
-
-For the purpose of package initialization, blank
-variables are treated like any other variables in declarations.
-
-
-
-The declaration order of variables declared in multiple files is determined
-by the order in which the files are presented to the compiler: Variables
-declared in the first file are declared before any of the variables declared
-in the second file, and so on.
-
-
-
-Dependency analysis does not rely on the actual values of the
-variables, only on lexical references to them in the source,
-analyzed transitively. For instance, if a variable x
's
-initialization expression refers to a function whose body refers to
-variable y
then x
depends on y
.
-Specifically:
-
-
-
--
-A reference to a variable or function is an identifier denoting that
-variable or function.
-
-
--
-A reference to a method
m
is a
-method value or
-method expression of the form
-t.m
, where the (static) type of t
is
-not an interface type, and the method m
is in the
-method set of t
.
-It is immaterial whether the resulting function value
-t.m
is invoked.
-
-
--
-A variable, function, or method
x
depends on a variable
-y
if x
's initialization expression or body
-(for functions and methods) contains a reference to y
-or to a function or method that depends on y
.
-
-
-
-
-For example, given the declarations
-
-
-
-var (
- a = c + b // == 9
- b = f() // == 4
- c = f() // == 5
- d = 3 // == 5 after initialization has finished
-)
-
-func f() int {
- d++
- return d
-}
-
-
-
-the initialization order is d
, b
, c
, a
.
-Note that the order of subexpressions in initialization expressions is irrelevant:
-a = c + b
and a = b + c
result in the same initialization
-order in this example.
-
-
-
-Dependency analysis is performed per package; only references referring
-to variables, functions, and (non-interface) methods declared in the current
-package are considered. If other, hidden, data dependencies exists between
-variables, the initialization order between those variables is unspecified.
-
-
-
-For instance, given the declarations
-
-
-
-var x = I(T{}).ab() // x has an undetected, hidden dependency on a and b
-var _ = sideEffect() // unrelated to x, a, or b
-var a = b
-var b = 42
-
-type I interface { ab() []int }
-type T struct{}
-func (T) ab() []int { return []int{a, b} }
-
-
-
-the variable a
will be initialized after b
but
-whether x
is initialized before b
, between
-b
and a
, or after a
, and
-thus also the moment at which sideEffect()
is called (before
-or after x
is initialized) is not specified.
-
-
-
-Variables may also be initialized using functions named init
-declared in the package block, with no arguments and no result parameters.
-
-
-
-func init() { … }
-
-
-
-Multiple such functions may be defined per package, even within a single
-source file. In the package block, the init
identifier can
-be used only to declare init
functions, yet the identifier
-itself is not declared. Thus
-init
functions cannot be referred to from anywhere
-in a program.
-
-
-
-A package with no imports is initialized by assigning initial values
-to all its package-level variables followed by calling all init
-functions in the order they appear in the source, possibly in multiple files,
-as presented to the compiler.
-If a package has imports, the imported packages are initialized
-before initializing the package itself. If multiple packages import
-a package, the imported package will be initialized only once.
-The importing of packages, by construction, guarantees that there
-can be no cyclic initialization dependencies.
-
-
-
-Package initialization—variable initialization and the invocation of
-init
functions—happens in a single goroutine,
-sequentially, one package at a time.
-An init
function may launch other goroutines, which can run
-concurrently with the initialization code. However, initialization
-always sequences
-the init
functions: it will not invoke the next one
-until the previous one has returned.
-
-
-
-To ensure reproducible initialization behavior, build systems are encouraged
-to present multiple files belonging to the same package in lexical file name
-order to a compiler.
-
-
-
-Program execution
-
-A complete program is created by linking a single, unimported package
-called the main package with all the packages it imports, transitively.
-The main package must
-have package name main
and
-declare a function main
that takes no
-arguments and returns no value.
-
-
-
-func main() { … }
-
-
-
-Program execution begins by initializing the main package and then
-invoking the function main
.
-When that function invocation returns, the program exits.
-It does not wait for other (non-main
) goroutines to complete.
-
-
-Errors
-
-
-The predeclared type error
is defined as
-
-
-
-type error interface {
- Error() string
-}
-
-
-
-It is the conventional interface for representing an error condition,
-with the nil value representing no error.
-For instance, a function to read data from a file might be defined:
-
-
-
-func Read(f *File, b []byte) (n int, err error)
-
-
-Run-time panics
-
-
-Execution errors such as attempting to index an array out
-of bounds trigger a run-time panic equivalent to a call of
-the built-in function panic
-with a value of the implementation-defined interface type runtime.Error
.
-That type satisfies the predeclared interface type
-error
.
-The exact error values that
-represent distinct run-time error conditions are unspecified.
-
-
-
-package runtime
-
-type Error interface {
- error
- // and perhaps other methods
-}
-
-
-System considerations
-
-Package unsafe
-
-
-The built-in package unsafe
, known to the compiler
-and accessible through the import path "unsafe"
,
-provides facilities for low-level programming including operations
-that violate the type system. A package using unsafe
-must be vetted manually for type safety and may not be portable.
-The package provides the following interface:
-
-
-
-package unsafe
-
-type ArbitraryType int // shorthand for an arbitrary Go type; it is not a real type
-type Pointer *ArbitraryType
-
-func Alignof(variable ArbitraryType) uintptr
-func Offsetof(selector ArbitraryType) uintptr
-func Sizeof(variable ArbitraryType) uintptr
-
-type IntegerType int // shorthand for an integer type; it is not a real type
-func Add(ptr Pointer, len IntegerType) Pointer
-func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType
-
-
-
-A Pointer
is a pointer type but a Pointer
-value may not be dereferenced.
-Any pointer or value of underlying type uintptr
can be converted to
-a type of underlying type Pointer
and vice versa.
-The effect of converting between Pointer
and uintptr
is implementation-defined.
-
-
-
-var f float64
-bits = *(*uint64)(unsafe.Pointer(&f))
-
-type ptr unsafe.Pointer
-bits = *(*uint64)(ptr(&f))
-
-var p ptr = nil
-
-
-
-The functions Alignof
and Sizeof
take an expression x
-of any type and return the alignment or size, respectively, of a hypothetical variable v
-as if v
was declared via var v = x
.
-
-
-The function Offsetof
takes a (possibly parenthesized) selector
-s.f
, denoting a field f
of the struct denoted by s
-or *s
, and returns the field offset in bytes relative to the struct's address.
-If f
is an embedded field, it must be reachable
-without pointer indirections through fields of the struct.
-For a struct s
with field f
:
-
-
-
-uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f) == uintptr(unsafe.Pointer(&s.f))
-
-
-
-Computer architectures may require memory addresses to be aligned;
-that is, for addresses of a variable to be a multiple of a factor,
-the variable's type's alignment. The function Alignof
-takes an expression denoting a variable of any type and returns the
-alignment of the (type of the) variable in bytes. For a variable
-x
:
-
-
-
-uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0
-
-
-
-Calls to Alignof
, Offsetof
, and
-Sizeof
are compile-time constant expressions of type uintptr
.
-
-
-
-The function Add
adds len
to ptr
-and returns the updated pointer unsafe.Pointer(uintptr(ptr) + uintptr(len))
.
-The len
argument must be of integer type or an untyped constant.
-A constant len
argument must be representable by a value of type int
;
-if it is an untyped constant it is given type int
.
-The rules for valid uses of Pointer
still apply.
-
-
-
-The function Slice
returns a slice whose underlying array starts at ptr
-and whose length and capacity are len
.
-Slice(ptr, len)
is equivalent to
-
-
-
-(*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]
-
-
-
-except that, as a special case, if ptr
-is nil
and len
is zero,
-Slice
returns nil
.
-
-
-
-The len
argument must be of integer type or an untyped constant.
-A constant len
argument must be non-negative and representable by a value of type int
;
-if it is an untyped constant it is given type int
.
-At run time, if len
is negative,
-or if ptr
is nil
and len
is not zero,
-a run-time panic occurs.
-
-
-Size and alignment guarantees
-
-
-For the numeric types, the following sizes are guaranteed:
-
-
-
-type size in bytes
-
-byte, uint8, int8 1
-uint16, int16 2
-uint32, int32, float32 4
-uint64, int64, float64, complex64 8
-complex128 16
-
-
-
-The following minimal alignment properties are guaranteed:
-
-
-- For a variable
x
of any type: unsafe.Alignof(x)
is at least 1.
-
-
-- For a variable
x
of struct type: unsafe.Alignof(x)
is the largest of
- all the values unsafe.Alignof(x.f)
for each field f
of x
, but at least 1.
-
-
-- For a variable
x
of array type: unsafe.Alignof(x)
is the same as
- the alignment of a variable of the array's element type.
-
-
-
-
-A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero. Two distinct zero-size variables may have the same address in memory.
-
diff --git a/doc/go_mem.html b/doc/go_mem.html
index 633d35cd07..8db7d65e73 100644
--- a/doc/go_mem.html
+++ b/doc/go_mem.html
@@ -231,7 +231,7 @@ do exactly this.
A read of an array, struct, or complex number
-may by implemented as a read of each individual sub-value
+may be implemented as a read of each individual sub-value
(array element, struct field, or real/imaginary component),
in any order.
Similarly, a write of an array, struct, or complex number
@@ -453,7 +453,7 @@ crash, or do something else.)
-The kth receive on a channel with capacity C is synchronized before the completion of the k+Cth send from that channel completes.
+The kth receive from a channel with capacity C is synchronized before the completion of the k+Cth send on that channel.
diff --git a/doc/go_spec.html b/doc/go_spec.html
index db5fba45a5..d6c7962a96 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
@@ -8,8 +8,6 @@
This is the reference manual for the Go programming language.
-The pre-Go1.18 version, without generics, can be found
-here.
For more information and other documents, see go.dev.
@@ -1858,110 +1856,10 @@ The underlying type of []B1
, B3
, and B4
i
The underlying type of P
is interface{}
.
-Core types
-
-
-Each non-interface type T
has a core type, which is the same as the
-underlying type of T
.
-
-
-
-An interface T
has a core type if one of the following
-conditions is satisfied:
-
-
-
--
-There is a single type
U
which is the underlying type
-of all types in the type set of T
; or
-
--
-the type set of
T
contains only channel types
-with identical element type E
, and all directional channels have the same
-direction.
-
-
-
-
-No other interfaces have a core type.
-
-
-
-The core type of an interface is, depending on the condition that is satisfied, either:
-
-
-
--
-the type
U
; or
-
--
-the type
chan E
if T
contains only bidirectional
-channels, or the type chan<- E
or <-chan E
-depending on the direction of the directional channels present.
-
-
-
-
-By definition, a core type is never a defined type,
-type parameter, or
-interface type.
-
-
-
-Examples of interfaces with core types:
-
-
-
-type Celsius float32
-type Kelvin float32
-
-interface{ int } // int
-interface{ Celsius|Kelvin } // float32
-interface{ ~chan int } // chan int
-interface{ ~chan int|~chan<- int } // chan<- int
-interface{ ~[]*data; String() string } // []*data
-
-
-
-Examples of interfaces without core types:
-
-
-
-interface{} // no single underlying type
-interface{ Celsius|float64 } // no single underlying type
-interface{ chan int | chan<- string } // channels have different element types
-interface{ <-chan int | chan<- int } // directional channels have different directions
-
-
-
-Some operations (slice expressions,
-append
and copy
)
-rely on a slightly more loose form of core types which accept byte slices and strings.
-Specifically, if there are exactly two types, []byte
and string
,
-which are the underlying types of all types in the type set of interface T
,
-the core type of T
is called bytestring
.
-
-
-
-Examples of interfaces with bytestring
core types:
-
-
-
-interface{ int } // int (same as ordinary core type)
-interface{ []byte | string } // bytestring
-interface{ ~[]byte | myString } // bytestring
-
-
-
-Note that bytestring
is not a real type; it cannot be used to declare
-variables or compose other types. It exists solely to describe the behavior of some
-operations that read from a sequence of bytes, which may be a byte slice or a string.
-
-
Type identity
-Two types are either identical or different.
+Two types are either identical ("the same") or different.
@@ -3255,7 +3153,8 @@ math.Sin // denotes the Sin function in package math
Composite literals
-Composite literals construct new composite values each time they are evaluated.
+Composite literals construct new values for structs, arrays, slices, and maps
+each time they are evaluated.
They consist of the type of the literal followed by a brace-bound list of elements.
Each element may optionally be preceded by a corresponding key.
@@ -3273,10 +3172,14 @@ Element = Expression | LiteralValue .
-The LiteralType's core type T
+Unless the LiteralType is a type parameter,
+its underlying type
must be a struct, array, slice, or map type
(the syntax enforces this constraint except when the type is given
as a TypeName).
+If the LiteralType is a type parameter, all types in its type set
+must have the same underlying type which must be
+a valid composite literal type.
The types of the elements and keys must be assignable
to the respective field, element, and key types of type T
;
there is no additional conversion.
@@ -3461,7 +3364,6 @@ noteFrequency := map[string]float32{
}
-
Function literals
@@ -3934,11 +3836,12 @@ The following rules apply:
-If a
is neither a map nor a type parameter:
+If a
is neither a map nor a type parameter:
- - the index
x
must be an untyped constant or its
- core type must be an integer
+ - the index
x
must be an untyped constant, or its type must be
+ an integer or a type parameter whose type set
+ contains only integer types
- a constant index must be non-negative and
representable by a value of type
int
- a constant index that is untyped is given type
int
@@ -4052,14 +3955,26 @@ Assigning to an element of a nil
map causes a
Slice expressions construct a substring or slice from a string, array, pointer
-to array, or slice. There are two variants: a simple form that specifies a low
+to array, or slice operand.
+There are two variants: a simple form that specifies a low
and high bound, and a full form that also specifies a bound on the capacity.
+
+If the operand type is a type parameter,
+unless its type set contains string types,
+all types in the type set must have the same underlying type, and the slice expression
+must be valid for an operand of that type.
+If the type set contains string types it may also contain byte slices with underlying
+type []byte
.
+In this case, the slice expression must be valid for an operand of string
+type.
+
+
Simple slice expressions
-The primary expression
+For a string, array, pointer to array, or slice a
, the primary expression
@@ -4067,9 +3982,7 @@ a[low : high]
-constructs a substring or slice. The core type of
-a
must be a string, array, pointer to array, slice, or a
-bytestring
.
+constructs a substring or slice.
The indices low
and
high
select which elements of operand a
appear
in the result. The result has indices starting at 0 and length equal to
@@ -4149,7 +4062,7 @@ s3 := s[:0] // s3 == nil
Full slice expressions
-The primary expression
+For an array, pointer to array, or slice a
(but not a string), the primary expression
@@ -4160,8 +4073,6 @@ a[low : high : max]
constructs a slice of the same type, and with the same length and elements as the simple slice
expression a[low : high]
. Additionally, it controls the resulting slice's capacity
by setting it to max - low
. Only the first index may be omitted; it defaults to 0.
-The core type of a
must be an array, pointer to array,
-or slice (but not a string).
After slicing the array a
@@ -4267,8 +4178,8 @@ No run-time panic occurs in this case.
Calls
-Given an expression f
with a core type
-F
of function type,
+Given an expression f
of function type
+F
,
@@ -4298,6 +4209,12 @@ If f
denotes a generic function, it must be
or used as a function value.
+
+If the type of f
is a type parameter,
+all types in its type set must have the same underlying type, which must be a function type,
+and the function call must be valid for that type.
+
+
In a function call, the function value and arguments are evaluated in
the usual order.
@@ -4811,17 +4728,28 @@ more complicated:
-
- If
C
has a core type
- core(C)
+ If all types in C
's type set have the same
+ underlying type U
,
and P
has a known type argument A
,
- core(C)
and A
must unify loosely.
+ U
and A
must unify loosely.
+
+-
+ Similarly, if all types in
C
's type set are
+ channel types with the same element type and non-conflicting
+ channel directions,
+ and P
has a known type argument A
,
+ the most restrictive channel type in C
's type
+ set and A
must unify loosely.
+
+-
If
P
does not have a known type argument
and C
contains exactly one type term T
that is not an underlying (tilde) type, unification adds the
mapping P ➞ T
to the map.
-
- If
C
does not have a core type
+ If C
does not have a type U
+ as described above
and P
has a known type argument A
,
A
must have all methods of C
, if any,
and corresponding method types must unify exactly.
@@ -4945,7 +4873,7 @@ For instance, x / y * z
is the same as (x / y) * z
.
x <= f() // x <= f()
^a >> b // (^a) >> b
f() || g() // f() || g()
-x == y+1 && <-chanInt > 0 // (x == (y+1)) && ((<-chanInt) > 0)
+x == y+1 && <-chanInt > 0 // (x == (y+1)) && ((<-chanInt) > 0)
@@ -5372,10 +5300,10 @@ var x *int = nil
Receive operator
-For an operand ch
whose core type is a
-channel,
+For an operand ch
of channel type,
the value of the receive operation <-ch
is the value received
-from the channel ch
. The channel direction must permit receive operations,
+from the channel ch
.
+The channel direction must permit receive operations,
and the type of the receive operation is the element type of the channel.
The expression blocks until a value is available.
Receiving from a nil
channel blocks forever.
@@ -5391,6 +5319,12 @@ f(<-ch)
<-strobe // wait until clock pulse and discard received value
+
+If the operand type is a type parameter,
+all types in its type set must be channel types that permit receive operations, and
+they must all have the same element type, which is the type of the receive operation.
+
+
A receive expression used in an assignment statement or initialization of the special form
@@ -6126,8 +6060,7 @@ len("foo") // illegal if len is the built-in function
A send statement sends a value on a channel.
-The channel expression's core type
-must be a channel,
+The channel expression must be of channel type,
the channel direction must permit send operations,
and the type of the value to be sent must be assignable
to the channel's element type.
@@ -6151,6 +6084,13 @@ A send on a nil
channel blocks forever.
ch <- 3 // send value 3 to channel ch
+
+If the type of the channel expression is a
+type parameter,
+all types in its type set must be channel types that permit send operations,
+they must all have the same element type,
+and the type of the value to be sent must be assignable to that element type.
+
IncDec statements
@@ -6695,7 +6635,7 @@ iteration's variable at that moment.
var prints []func()
-for i := 0; i < 5; i++ {
+for i := 0; i < 5; i++ {
prints = append(prints, func() { println(i) })
i++
}
@@ -6743,8 +6683,7 @@ RangeClause = [ ExpressionList "=" | IdentifierList ":=" ] "range" Expression .
The expression on the right in the "range" clause is called the range expression,
-its core type must be
-an array, pointer to an array, slice, string, map, channel permitting
+which may be an array, pointer to an array, slice, string, map, channel permitting
receive operations, an integer, or
a function with specific signature (see below).
As with an assignment, if present the operands on the left must be
@@ -6833,7 +6772,7 @@ if the iteration variable is preexisting, the type of the iteration values is th
variable, which must be of integer type.
Otherwise, if the iteration variable is declared by the "range" clause or is absent,
the type of the iteration values is the default type for n
.
-If n
<= 0, the loop does not run any iterations.
+If n
<= 0, the loop does not run any iterations.
-
@@ -6958,6 +6897,12 @@ for k, v := range t.Walk {
}
+
+If the type of the range expression is a type parameter,
+all types in its type set must have the same underlying type and the range expression must be valid
+for that type, or, if the type set contains channel types, it must only contain channel types with
+identical element types, and all channel types must permit receive operations.
+
Go statements
@@ -7431,23 +7376,28 @@ by the arguments overlaps.
The variadic function append
-appends zero or more values x
to a slice s
-and returns the resulting slice of the same type as s
.
-The core type of s
must be a slice
-of type []E
.
+appends zero or more values x
to a slice s
of
+type S
and returns the resulting slice, also of type
+S
.
The values x
are passed to a parameter of type ...E
+where E
is the element type of S
and the respective parameter
passing rules apply.
-As a special case, if the core type of s
is []byte
,
-append
also accepts a second argument with core type
-bytestring
followed by ...
.
-This form appends the bytes of the byte slice or string.
+As a special case, append
also accepts a first argument assignable
+to type []byte
with a second argument of string type followed by
+...
.
+This form appends the bytes of the string.
-append(s S, x ...E) S // core type of S is []E
+append(s S, x ...E) S // E is the element type of S
+
+If S
is a type parameter,
+all types in its type set must have the same underlying slice type []E
.
+
+
If the capacity of s
is not large enough to fit the additional
values, append
allocates a new, sufficiently large underlying
@@ -7473,14 +7423,14 @@ b = append(b, "bar"...) // append string contents b is []byte{'b
The function copy
copies slice elements from
a source src
to a destination dst
and returns the
number of elements copied.
-The core types of both arguments must be slices
-with identical element type.
+Both arguments must have identical element type
+E
and must be assignable to a slice of type []E
.
The number of elements copied is the minimum of
len(src)
and len(dst)
.
-As a special case, if the destination's core type is []byte
,
-copy
also accepts a source argument with core type
-bytestring
.
-This form copies the bytes from the byte slice or string into the byte slice.
+As a special case, copy
also accepts a destination argument
+assignable to type []byte
with a source argument of a
+string
type.
+This form copies the bytes from the string into the byte slice.
@@ -7488,6 +7438,11 @@ copy(dst, src []T) int
copy(dst []byte, src string) int
+
+If the type of one or both arguments is a type parameter,
+all types in their respective type sets must have the same underlying slice type []E
.
+
+
Examples:
@@ -7538,8 +7493,7 @@ If the map or slice is nil
, clear
is a no-op.
Close
-For an argument ch
with a core type
-that is a channel, the built-in function close
+For a channel ch
, the built-in function close(ch)
records that no more values will be sent on the channel.
It is an error if ch
is a receive-only channel.
Sending to or closing a closed channel causes a run-time panic.
@@ -7551,6 +7505,12 @@ The multi-valued receive operation
returns a received value along with an indication of whether the channel is closed.
+
+If the type of the argument to close
is a
+type parameter,
+all types in its type set must be channels with the same element type.
+It is an error if any of those channels is a receive-only channel.
+
Manipulating complex numbers
@@ -7720,27 +7680,36 @@ var z complex128
The built-in function make
takes a type T
,
+which must be a slice, map or channel type, or a type parameter,
optionally followed by a type-specific list of expressions.
-The core type of T
must
-be a slice, map or channel.
It returns a value of type T
(not *T
).
The memory is initialized as described in the section on
initial values.
-Call Core type Result
+Call Type T Result
-make(T, n) slice slice of type T with length n and capacity n
-make(T, n, m) slice slice of type T with length n and capacity m
+make(T, n) slice slice of type T with length n and capacity n
+make(T, n, m) slice slice of type T with length n and capacity m
-make(T) map map of type T
-make(T, n) map map of type T with initial space for approximately n elements
+make(T) map map of type T
+make(T, n) map map of type T with initial space for approximately n elements
-make(T) channel unbuffered channel of type T
-make(T, n) channel buffered channel of type T, buffer size n
+make(T) channel unbuffered channel of type T
+make(T, n) channel buffered channel of type T, buffer size n
+
+make(T, n) type parameter see below
+make(T, n, m) type parameter see below
+
+If the first argument is a type parameter,
+all types in its type set must have the same underlying type, which must be a slice
+or map type, or, if there are channel types, there must only be channel types, they
+must all have the same element type, and the channel directions must not conflict.
+
+
Each of the size arguments n
and m
must be of integer type,
have a type set containing only integer types,
@@ -7830,19 +7799,39 @@ compared lexically byte-wise:
-min(x, y) == if x <= y then x else y
+min(x, y) == if x <= y then x else y
min(x, y, z) == min(min(x, y), z)
Allocation
-The built-in function new
takes a type T
,
-allocates storage for a variable of that type
-at run time, and returns a value of type *T
-pointing to it.
-The variable is initialized as described in the section on
-initial values.
+ The built-in function new
creates a new, initialized
+ variable and returns
+ a pointer to it.
+
+ It accepts a single argument, which may be either an expression or a type.
+
+
+ If the argument expr
is an expression of
+ type T
, or an untyped constant expression
+ whose default type is T
,
+ then new(expr)
allocates a variable of
+ type T
, initializes it to the value
+ of expr
, and returns its address, a value of
+ type *T
.
+
+
+ If the argument is a type T
, then new(T)
+ allocates a variable initialized to
+ the zero value of type T
.
+
+
+ For example, new(123)
and new(int)
each
+ return a pointer to a new variable of type int
.
+
+ The value of the first variable is 123
, and the value
+ of the second is 0
.
@@ -7925,7 +7914,7 @@ causes a run-time panic.
The protect
function in the example below invokes
the function argument g
and protects callers from
-run-time panics raised by g
.
+run-time panics caused by g
.
@@ -8483,17 +8472,14 @@ func String(ptr *byte, len IntegerType) string
func StringData(str string) *byte
-
-
A Pointer
is a pointer type but a Pointer
value may not be dereferenced.
-Any pointer or value of core type uintptr
can be
-converted to a type of core type Pointer
and vice versa.
+Any pointer or value of underlying type uintptr
can be
+converted to a type of underlying type Pointer
and vice versa.
+If the respective types are type parameters, all types in
+their respective type sets must have the same underlying type, which must be uintptr
and
+Pointer
, respectively.
The effect of converting between Pointer
and uintptr
is implementation-defined.
@@ -8847,9 +8833,9 @@ following conditions is true:
-
Exactly one type is an unbound
- type parameter with a core type,
- and that core type unifies with the other type per the
- unification rules for
≡A
+ type parameter, and all the types in its type set unify with
+ the other type
+ per the unification rules for ≡A
(loose unification at the top level and exact unification
for element types).
diff --git a/doc/godebug.md b/doc/godebug.md
index 1b5674f2cd..c12ce5311d 100644
--- a/doc/godebug.md
+++ b/doc/godebug.md
@@ -109,7 +109,9 @@ Only the work module's `go.mod` is consulted for `godebug` directives.
Any directives in required dependency modules are ignored.
It is an error to list a `godebug` with an unrecognized setting.
(Toolchains older than Go 1.23 reject all `godebug` lines, since they do not
-understand `godebug` at all.)
+understand `godebug` at all.) When a workspace is in use, `godebug`
+directives in `go.mod` files are ignored, and `go.work` will be consulted
+for `godebug` directives instead.
The defaults from the `go` and `godebug` lines apply to all main
packages that are built. For more fine-grained control,
@@ -151,8 +153,70 @@ for example,
see the [runtime documentation](/pkg/runtime#hdr-Environment_Variables)
and the [go command documentation](/cmd/go#hdr-Build_and_test_caching).
+### Go 1.26
+
+Go 1.26 added a new `httpcookiemaxnum` setting that controls the maximum number
+of cookies that net/http will accept when parsing HTTP headers. If the number of
+cookie in a header exceeds the number set in `httpcookiemaxnum`, cookie parsing
+will fail early. The default value is `httpcookiemaxnum=3000`. Setting
+`httpcookiemaxnum=0` will allow the cookie parsing to accept an indefinite
+number of cookies. To avoid denial of service attacks, this setting and default
+was backported to Go 1.25.2 and Go 1.24.8.
+
+### Go 1.25
+
+Go 1.25 added a new `decoratemappings` setting that controls whether the Go
+runtime annotates OS anonymous memory mappings with context about their
+purpose. These annotations appear in /proc/self/maps and /proc/self/smaps as
+"[anon: Go: ...]". This setting is only used on Linux. For Go 1.25, it defaults
+to `decoratemappings=1`, enabling annotations. Using `decoratemappings=0`
+reverts to the pre-Go 1.25 behavior. This setting is fixed at program startup
+time, and can't be modified by changing the `GODEBUG` environment variable
+after the program starts.
+
+Go 1.25 added a new `embedfollowsymlinks` setting that controls whether the
+Go command will follow symlinks to regular files embedding files.
+The default value `embedfollowsymlinks=0` does not allow following
+symlinks. `embedfollowsymlinks=1` will allow following symlinks.
+
+Go 1.25 added a new `containermaxprocs` setting that controls whether the Go
+runtime will consider cgroup CPU limits when setting the default GOMAXPROCS.
+The default value `containermaxprocs=1` will use cgroup limits in addition to
+the total logical CPU count and CPU affinity. `containermaxprocs=0` will
+disable consideration of cgroup limits. This setting only affects Linux.
+
+Go 1.25 added a new `updatemaxprocs` setting that controls whether the Go
+runtime will periodically update GOMAXPROCS for new CPU affinity or cgroup
+limits. The default value `updatemaxprocs=1` will enable periodic updates.
+`updatemaxprocs=0` will disable periodic updates.
+
+Go 1.25 disabled SHA-1 signature algorithms in TLS 1.2 according to RFC 9155.
+The default can be reverted using the `tlssha1=1` setting.
+
+Go 1.25 switched to SHA-256 to fill in missing SubjectKeyId in
+crypto/x509.CreateCertificate. The setting `x509sha256skid=0` reverts to SHA-1.
+
+Go 1.25 corrected the semantics of contention reports for runtime-internal locks,
+and so removed the [`runtimecontentionstacks` setting](/pkg/runtime#hdr-Environment_Variables).
+
+Go 1.25 (starting with Go 1.25 RC 2) disabled build information stamping when
+multiple VCS are detected due to concerns around VCS injection attacks. This
+behavior and setting was backported to Go 1.24.5 and Go 1.23.11. This behavior
+can be renabled with the setting `allowmultiplevcs=1`.
+
### Go 1.24
+Go 1.24 added a new `fips140` setting that controls whether the Go
+Cryptographic Module operates in FIPS 140-3 mode.
+The possible values are:
+- "off": no special support for FIPS 140-3 mode. This is the default.
+- "on": the Go Cryptographic Module operates in FIPS 140-3 mode.
+- "only": like "on", but cryptographic algorithms not approved by
+ FIPS 140-3 return an error or panic.
+For more information, see [FIPS 140-3 Compliance](/doc/security/fips140).
+This setting is fixed at program startup time, and can't be modified
+by changing the `GODEBUG` environment variable after the program starts.
+
Go 1.24 changed the global [`math/rand.Seed`](/pkg/math/rand/#Seed) to be a
no-op. This behavior is controlled by the `randseednop` setting.
For Go 1.24 it defaults to `randseednop=1`.
@@ -206,6 +270,8 @@ field by default.
Go 1.24 enabled the post-quantum key exchange mechanism
X25519MLKEM768 by default. The default can be reverted using the
[`tlsmlkem` setting](/pkg/crypto/tls/#Config.CurvePreferences).
+This can be useful when dealing with buggy TLS servers that do not handle large records correctly,
+causing a timeout during the handshake (see [TLS post-quantum TL;DR fail](https://tldr.fail/)).
Go 1.24 also removed X25519Kyber768Draft00 and the Go 1.23 `tlskyber` setting.
Go 1.24 made [`ParsePKCS1PrivateKey`](/pkg/crypto/x509/#ParsePKCS1PrivateKey)
@@ -242,6 +308,8 @@ Previous versions default to `winreadlinkvolume=0`.
Go 1.23 enabled the experimental post-quantum key exchange mechanism
X25519Kyber768Draft00 by default. The default can be reverted using the
[`tlskyber` setting](/pkg/crypto/tls/#Config.CurvePreferences).
+This can be useful when dealing with buggy TLS servers that do not handle large records correctly,
+causing a timeout during the handshake (see [TLS post-quantum TL;DR fail](https://tldr.fail/)).
Go 1.23 changed the behavior of
[crypto/x509.ParseCertificate](/pkg/crypto/x509/#ParseCertificate) to reject
@@ -316,7 +384,7 @@ In particular, a common default Linux kernel configuration can result in
significant memory overheads, and Go 1.22 no longer works around this default.
To work around this issue without adjusting kernel settings, transparent huge
pages can be disabled for Go memory with the
-[`disablethp` setting](/pkg/runtime#hdr-Environment_Variable).
+[`disablethp` setting](/pkg/runtime#hdr-Environment_Variables).
This behavior was backported to Go 1.21.1, but the setting is only available
starting with Go 1.21.6.
This setting may be removed in a future release, and users impacted by this issue
@@ -328,7 +396,7 @@ Go 1.22 added contention on runtime-internal locks to the [`mutex`
profile](/pkg/runtime/pprof#Profile). Contention on these locks is always
reported at `runtime._LostContendedRuntimeLock`. Complete stack traces of
runtime locks can be enabled with the [`runtimecontentionstacks`
-setting](/pkg/runtime#hdr-Environment_Variable). These stack traces have
+setting](/pkg/runtime#hdr-Environment_Variables). These stack traces have
non-standard semantics, see setting documentation for details.
Go 1.22 added a new [`crypto/x509.Certificate`](/pkg/crypto/x509/#Certificate)
@@ -337,7 +405,7 @@ certificate policy OIDs with components larger than 31 bits. By default this
field is only used during parsing, when it is populated with policy OIDs, but
not used during marshaling. It can be used to marshal these larger OIDs, instead
of the existing PolicyIdentifiers field, by using the
-[`x509usepolicies` setting.](/pkg/crypto/x509/#CreateCertificate).
+[`x509usepolicies` setting](/pkg/crypto/x509/#CreateCertificate).
### Go 1.21
diff --git a/doc/initial/6-stdlib/99-minor/0-heading.md b/doc/initial/6-stdlib/99-minor/0-heading.md
index a98105e8cc..e80ebc64c3 100644
--- a/doc/initial/6-stdlib/99-minor/0-heading.md
+++ b/doc/initial/6-stdlib/99-minor/0-heading.md
@@ -1,3 +1,10 @@
### Minor changes to the library {#minor_library_changes}
+#### go/types
+The `Var.Kind` method returns an enumeration of type `VarKind` that
+classifies the variable (package-level, local, receiver, parameter,
+result, or struct field). See issue #70250.
+
+Callers of `NewVar` or `NewParam` are encouraged to call `Var.SetKind`
+to ensure that this attribute is set correctly in all cases.
diff --git a/doc/next/1-intro.md b/doc/next/1-intro.md
new file mode 100644
index 0000000000..3cd0d66b5a
--- /dev/null
+++ b/doc/next/1-intro.md
@@ -0,0 +1,8 @@
+
+
+## DRAFT RELEASE NOTES — Introduction to Go 1.26 {#introduction}
+
+**Go 1.26 is not yet released. These are work-in-progress release notes.
+Go 1.26 is expected to be released in February 2026.**
diff --git a/doc/next/2-language.md b/doc/next/2-language.md
new file mode 100644
index 0000000000..ded7becf01
--- /dev/null
+++ b/doc/next/2-language.md
@@ -0,0 +1,28 @@
+## Changes to the language {#language}
+
+
+
+The built-in `new` function, which creates a new variable, now allows
+its operand to be an expression, specifying the initial value of the
+variable.
+
+This feature is particularly useful when working with serialization
+packages such as `encoding/json` or protocol buffers that use a
+pointer to represent an optional value, as it enables an optional
+field to be populated in a simple expression, for example:
+
+```go
+import "encoding/json"
+
+type Person struct {
+ Name string `json:"name"`
+ Age *int `json:"age"` // age if known; nil otherwise
+}
+
+func personJSON(name string, age int) ([]byte, error) {
+ return json.Marshal(Person{
+ Name: name,
+ Age: new(age),
+ })
+}
+```
diff --git a/doc/next/3-tools.md b/doc/next/3-tools.md
new file mode 100644
index 0000000000..c0a4601c0b
--- /dev/null
+++ b/doc/next/3-tools.md
@@ -0,0 +1,21 @@
+## Tools {#tools}
+
+### Go command {#go-command}
+
+
+`cmd/doc`, and `go tool doc` have been deleted. `go doc` can be used as
+a replacement for `go tool doc`: it takes the same flags and arguments and
+has the same behavior.
+
+
+The `go fix` command, following the pattern of `go vet` in Go 1.10,
+now uses the Go analysis framework (`golang.org/x/tools/go/analysis`).
+This means the same analyzers that provide diagnostics in `go vet`
+can be used to suggest and apply fixes in `go fix`.
+The `go fix` command's historical fixers, all of which were obsolete,
+have been removed and replaced by a suite of new analyzers that
+offer fixes to use newer features of the language and library.
+
+
+### Cgo {#cgo}
+
diff --git a/doc/next/4-runtime.md b/doc/next/4-runtime.md
new file mode 100644
index 0000000000..1f8e445e0b
--- /dev/null
+++ b/doc/next/4-runtime.md
@@ -0,0 +1 @@
+## Runtime {#runtime}
diff --git a/doc/next/5-toolchain.md b/doc/next/5-toolchain.md
new file mode 100644
index 0000000000..cc32f30a52
--- /dev/null
+++ b/doc/next/5-toolchain.md
@@ -0,0 +1,12 @@
+## Compiler {#compiler}
+
+## Assembler {#assembler}
+
+## Linker {#linker}
+
+## Bootstrap {#bootstrap}
+
+
+As mentioned in the [Go 1.24 release notes](/doc/go1.24#bootstrap), Go 1.26 now requires
+Go 1.24.6 or later for bootstrap.
+We expect that Go 1.28 will require a minor release of Go 1.26 or later for bootstrap.
diff --git a/doc/next/6-stdlib/0-heading.md b/doc/next/6-stdlib/0-heading.md
new file mode 100644
index 0000000000..a992170d43
--- /dev/null
+++ b/doc/next/6-stdlib/0-heading.md
@@ -0,0 +1,2 @@
+## Standard library {#library}
+
diff --git a/doc/next/6-stdlib/99-minor/0-heading.md b/doc/next/6-stdlib/99-minor/0-heading.md
new file mode 100644
index 0000000000..e80ebc64c3
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/0-heading.md
@@ -0,0 +1,10 @@
+### Minor changes to the library {#minor_library_changes}
+
+#### go/types
+
+The `Var.Kind` method returns an enumeration of type `VarKind` that
+classifies the variable (package-level, local, receiver, parameter,
+result, or struct field). See issue #70250.
+
+Callers of `NewVar` or `NewParam` are encouraged to call `Var.SetKind`
+to ensure that this attribute is set correctly in all cases.
diff --git a/doc/next/6-stdlib/99-minor/README b/doc/next/6-stdlib/99-minor/README
new file mode 100644
index 0000000000..fac778de05
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/README
@@ -0,0 +1 @@
+API changes and other small changes to the standard library go here.
diff --git a/doc/next/6-stdlib/99-minor/crypto/ecdsa/63963.md b/doc/next/6-stdlib/99-minor/crypto/ecdsa/63963.md
new file mode 100644
index 0000000000..81efc00bb5
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/crypto/ecdsa/63963.md
@@ -0,0 +1 @@
+The `big.Int` fields of [PublicKey] and [PrivateKey] are now deprecated.
diff --git a/doc/next/6-stdlib/99-minor/crypto/rsa/74115.md b/doc/next/6-stdlib/99-minor/crypto/rsa/74115.md
new file mode 100644
index 0000000000..a3647a79f2
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/crypto/rsa/74115.md
@@ -0,0 +1,5 @@
+If [PrivateKey] fields are modified after calling [PrivateKey.Precompute],
+[PrivateKey.Validate] now fails.
+
+[PrivateKey.D] is now checked for consistency with precomputed values, even if
+it is not used.
diff --git a/doc/next/6-stdlib/99-minor/database/sql/driver/67546.md b/doc/next/6-stdlib/99-minor/database/sql/driver/67546.md
new file mode 100644
index 0000000000..8cb9089583
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/database/sql/driver/67546.md
@@ -0,0 +1 @@
+A database driver may implement [RowsColumnScanner] to entirely override `Scan` behavior.
diff --git a/doc/next/6-stdlib/99-minor/errors/51945.md b/doc/next/6-stdlib/99-minor/errors/51945.md
new file mode 100644
index 0000000000..44ac7222e6
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/errors/51945.md
@@ -0,0 +1,2 @@
+The new [AsType] function is a generic version of [As]. It is type-safe, faster,
+and, in most cases, easier to use.
diff --git a/doc/next/6-stdlib/99-minor/image/jpeg/75603.md b/doc/next/6-stdlib/99-minor/image/jpeg/75603.md
new file mode 100644
index 0000000000..43421761fd
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/image/jpeg/75603.md
@@ -0,0 +1,2 @@
+The JPEG encoder and decoder have been replaced with new, faster, more accurate implementations.
+Code that expects specific bit-for-bit outputs from the encoder or decoder may need to be updated.
diff --git a/doc/next/6-stdlib/99-minor/log/slog/65954.md b/doc/next/6-stdlib/99-minor/log/slog/65954.md
new file mode 100644
index 0000000000..631ed665df
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/log/slog/65954.md
@@ -0,0 +1,6 @@
+The [`NewMultiHandler`](/pkg/log/slog#NewMultiHandler) function creates a
+[`MultiHandler`](/pkg/log/slog#MultiHandler) that invokes all the given Handlers.
+Its `Enable` method reports whether any of the handlers' `Enabled` methods
+return true.
+Its `Handle`, `WithAttr` and `WithGroup` methods call the corresponding method
+on each of the enabled handlers.
diff --git a/doc/next/6-stdlib/99-minor/net/49097.md b/doc/next/6-stdlib/99-minor/net/49097.md
new file mode 100644
index 0000000000..bb7947b0a1
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/net/49097.md
@@ -0,0 +1 @@
+Added context aware dial functions for TCP, UDP, IP and Unix networks.
diff --git a/doc/next/6-stdlib/99-minor/net/http/67813.md b/doc/next/6-stdlib/99-minor/net/http/67813.md
new file mode 100644
index 0000000000..74b8c7644f
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/net/http/67813.md
@@ -0,0 +1,4 @@
+The new
+[HTTP2Config.StrictMaxConcurrentRequests](/pkg/net/http#HTTP2Config.StrictMaxConcurrentRequests)
+field controls whether a new connection should be opened
+if an existing HTTP/2 connection has exceeded its stream limit.
diff --git a/doc/next/6-stdlib/99-minor/net/http/httptest/31054.md b/doc/next/6-stdlib/99-minor/net/http/httptest/31054.md
new file mode 100644
index 0000000000..ef6a4898f2
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/net/http/httptest/31054.md
@@ -0,0 +1,2 @@
+The HTTP client returned by [Server.Client] will now redirect requests for
+`example.com` and any subdomains to the server being tested.
diff --git a/doc/next/6-stdlib/99-minor/net/http/httputil/73161.md b/doc/next/6-stdlib/99-minor/net/http/httputil/73161.md
new file mode 100644
index 0000000000..f6318f8553
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/net/http/httputil/73161.md
@@ -0,0 +1,11 @@
+The [ReverseProxy.Director] configuration field is deprecated
+in favor of [ReverseProxy.Rewrite].
+
+A malicious client can remove headers added by a `Director` function
+by designating those headers as hop-by-hop. Since there is no way to address
+this problem within the scope of the `Director` API, we added a new
+`Rewrite` hook in Go 1.20. `Rewrite` hooks are provided with both the
+unmodified inbound request received by the proxy and the outbound request
+which will be sent by the proxy.
+
+Since the `Director` hook is fundamentally unsafe, we are now deprecating it.
diff --git a/doc/next/6-stdlib/99-minor/net/netip/61642.md b/doc/next/6-stdlib/99-minor/net/netip/61642.md
new file mode 100644
index 0000000000..3d79f2e76a
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/net/netip/61642.md
@@ -0,0 +1 @@
+The new [Prefix.Compare] method compares two prefixes.
diff --git a/doc/next/6-stdlib/99-minor/os/70352.md b/doc/next/6-stdlib/99-minor/os/70352.md
new file mode 100644
index 0000000000..5651639dad
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/os/70352.md
@@ -0,0 +1,4 @@
+The new [Process.WithHandle] method provides access to an internal process
+handle on supported platforms (Linux 5.4 or later and Windows). On Linux,
+the process handle is a pidfd. The method returns [ErrNoHandle] on unsupported
+platforms or when no process handle is available.
diff --git a/doc/next/6-stdlib/99-minor/os/73676.md b/doc/next/6-stdlib/99-minor/os/73676.md
new file mode 100644
index 0000000000..70d01f262d
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/os/73676.md
@@ -0,0 +1,4 @@
+On Windows, the [OpenFile] `flag` parameter can now contain any combination of
+Windows-specific file flags, such as `FILE_FLAG_OVERLAPPED` and
+`FILE_FLAG_SEQUENTIAL_SCAN`, for control of file or device caching behavior,
+access modes, and other special-purpose flags.
\ No newline at end of file
diff --git a/doc/next/6-stdlib/99-minor/testing/71287.md b/doc/next/6-stdlib/99-minor/testing/71287.md
new file mode 100644
index 0000000000..82cac63810
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/testing/71287.md
@@ -0,0 +1,18 @@
+The new methods [T.ArtifactDir], [B.ArtifactDir], and [F.ArtifactDir]
+return a directory in which to write test output files (artifacts).
+
+When the `-artifacts` flag is provided to `go test`,
+this directory will be located under the output directory
+(specified with `-outputdir`, or the current directory by default).
+Otherwise, artifacts are stored in a temporary directory
+which is removed after the test completes.
+
+The first call to `ArtifactDir` when `-artifacts` is provided
+writes the location of the directory to the test log.
+
+For example, in a test named `TestArtifacts`,
+`t.ArtifactDir()` emits:
+
+```
+=== ARTIFACTS Test /path/to/artifact/dir
+```
diff --git a/doc/next/7-ports.md b/doc/next/7-ports.md
new file mode 100644
index 0000000000..0744545422
--- /dev/null
+++ b/doc/next/7-ports.md
@@ -0,0 +1,6 @@
+## Ports {#ports}
+
+### Windows
+
+
+As [announced](/doc/go1.25#windows) in the Go 1.25 release notes, the [broken](/doc/go1.24#windows) 32-bit windows/arm port (`GOOS=windows` `GOARCH=arm`) is removed.
diff --git a/lib/fips140/Makefile b/lib/fips140/Makefile
index 8dcb8fbebe..e8fc82e08a 100644
--- a/lib/fips140/Makefile
+++ b/lib/fips140/Makefile
@@ -30,7 +30,7 @@ v%.zip:
go run ../../src/cmd/go/internal/fips140/mkzip.go v$*
# normally mkzip refuses to overwrite an existing zip file.
-# make v1.2.3.rm removes the zip file and and unpacked
+# make v1.2.3.rm removes the zip file and unpacked
# copy from the module cache.
v%.rm:
rm -f v$*.zip
diff --git a/lib/fips140/fips140.sum b/lib/fips140/fips140.sum
index 66b1e23dfe..703d1dc60e 100644
--- a/lib/fips140/fips140.sum
+++ b/lib/fips140/fips140.sum
@@ -9,4 +9,4 @@
#
# go test cmd/go/internal/fips140 -update
#
-v1.0.0.zip b50508feaeff05d22516b21e1fd210bbf5d6a1e422eaf2cfa23fe379342713b8
+v1.0.0-c2097c7c.zip daf3614e0406f67ae6323c902db3f953a1effb199142362a039e7526dfb9368b
diff --git a/lib/fips140/inprocess.txt b/lib/fips140/inprocess.txt
new file mode 100644
index 0000000000..efd3caba85
--- /dev/null
+++ b/lib/fips140/inprocess.txt
@@ -0,0 +1 @@
+v1.0.0-c2097c7c
diff --git a/lib/fips140/v1.0.0.zip b/lib/fips140/v1.0.0-c2097c7c.zip
similarity index 78%
rename from lib/fips140/v1.0.0.zip
rename to lib/fips140/v1.0.0-c2097c7c.zip
index bd9d3c19d0..aabf762d0f 100644
Binary files a/lib/fips140/v1.0.0.zip and b/lib/fips140/v1.0.0-c2097c7c.zip differ
diff --git a/lib/fips140/v1.0.0.txt b/lib/fips140/v1.0.0.txt
new file mode 100644
index 0000000000..efd3caba85
--- /dev/null
+++ b/lib/fips140/v1.0.0.txt
@@ -0,0 +1 @@
+v1.0.0-c2097c7c
diff --git a/lib/time/update.bash b/lib/time/update.bash
index 67cb016e79..66494752ea 100755
--- a/lib/time/update.bash
+++ b/lib/time/update.bash
@@ -24,8 +24,8 @@
# in the CL match the update.bash in the CL.
# Versions to use.
-CODE=2025a
-DATA=2025a
+CODE=2025b
+DATA=2025b
set -e
@@ -40,7 +40,12 @@ curl -sS -L -O https://www.iana.org/time-zones/repository/releases/tzdata$DATA.t
tar xzf tzcode$CODE.tar.gz
tar xzf tzdata$DATA.tar.gz
-if ! make CFLAGS=-DSTD_INSPIRED AWK=awk TZDIR=zoneinfo posix_only >make.out 2>&1; then
+# The PACKRATLIST and PACKRATDATA options are copied from Ubuntu:
+# https://git.launchpad.net/ubuntu/+source/tzdata/tree/debian/rules?h=debian/sid
+#
+# You can see the description of these make variables in the tzdata Makefile:
+# https://github.com/eggert/tz/blob/main/Makefile
+if ! make CFLAGS=-DSTD_INSPIRED AWK=awk TZDIR=zoneinfo PACKRATDATA=backzone PACKRATLIST=zone.tab posix_only >make.out 2>&1; then
cat make.out
exit 2
fi
diff --git a/lib/time/zoneinfo.zip b/lib/time/zoneinfo.zip
index 6ba9ff6fd6..b107695d1e 100644
Binary files a/lib/time/zoneinfo.zip and b/lib/time/zoneinfo.zip differ
diff --git a/lib/wasm/go_wasip1_wasm_exec b/lib/wasm/go_wasip1_wasm_exec
index 3b2d12ec45..2de1758793 100755
--- a/lib/wasm/go_wasip1_wasm_exec
+++ b/lib/wasm/go_wasip1_wasm_exec
@@ -14,7 +14,7 @@ case "$GOWASIRUNTIME" in
exec wazero run -mount /:/ -env-inherit -cachedir "${TMPDIR:-/tmp}"/wazero ${GOWASIRUNTIMEARGS:-} "$1" "${@:2}"
;;
"wasmtime" | "")
- exec wasmtime run --dir=/ --env PWD="$PWD" --env PATH="$PATH" -W max-wasm-stack=1048576 ${GOWASIRUNTIMEARGS:-} "$1" "${@:2}"
+ exec wasmtime run --dir=/ --env PWD="$PWD" --env PATH="$PATH" -W max-wasm-stack=8388608 ${GOWASIRUNTIMEARGS:-} "$1" "${@:2}"
;;
*)
echo "Unknown Go WASI runtime specified: $GOWASIRUNTIME"
diff --git a/misc/ios/go_ios_exec.go b/misc/ios/go_ios_exec.go
index b21ce1aaf3..e58457d051 100644
--- a/misc/ios/go_ios_exec.go
+++ b/misc/ios/go_ios_exec.go
@@ -212,7 +212,7 @@ func copyLocalData(dstbase string) (pkgpath string, err error) {
// Copy all immediate files and testdata directories between
// the package being tested and the source root.
pkgpath = ""
- for _, element := range strings.Split(finalPkgpath, string(filepath.Separator)) {
+ for element := range strings.SplitSeq(finalPkgpath, string(filepath.Separator)) {
if debug {
log.Printf("copying %s", pkgpath)
}
diff --git a/misc/linkcheck/linkcheck.go b/misc/linkcheck/linkcheck.go
deleted file mode 100644
index efe400965b..0000000000
--- a/misc/linkcheck/linkcheck.go
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright 2013 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.
-
-// The linkcheck command finds missing links in the godoc website.
-// It crawls a URL recursively and notes URLs and URL fragments
-// that it's seen and prints a report of missing links at the end.
-package main
-
-import (
- "errors"
- "flag"
- "fmt"
- "io"
- "log"
- "net/http"
- "os"
- "regexp"
- "strings"
- "sync"
-)
-
-var (
- root = flag.String("root", "http://localhost:6060", "Root to crawl")
- verbose = flag.Bool("verbose", false, "verbose")
-)
-
-var wg sync.WaitGroup // outstanding fetches
-var urlq = make(chan string) // URLs to crawl
-
-// urlFrag is a URL and its optional #fragment (without the #)
-type urlFrag struct {
- url, frag string
-}
-
-var (
- mu sync.Mutex
- crawled = make(map[string]bool) // URL without fragment -> true
- neededFrags = make(map[urlFrag][]string) // URL#frag -> who needs it
-)
-
-var aRx = regexp.MustCompile(`]+)`)
-
-// Owned by crawlLoop goroutine:
-var (
- linkSources = make(map[string][]string) // url no fragment -> sources
- fragExists = make(map[urlFrag]bool)
- problems []string
-)
-
-func localLinks(body string) (links []string) {
- seen := map[string]bool{}
- mv := aRx.FindAllStringSubmatch(body, -1)
- for _, m := range mv {
- ref := m[1]
- if strings.HasPrefix(ref, "/src/") {
- continue
- }
- if !seen[ref] {
- seen[ref] = true
- links = append(links, m[1])
- }
- }
- return
-}
-
-var idRx = regexp.MustCompile(`\bid=['"]?([^\s'">]+)`)
-
-func pageIDs(body string) (ids []string) {
- mv := idRx.FindAllStringSubmatch(body, -1)
- for _, m := range mv {
- ids = append(ids, m[1])
- }
- return
-}
-
-// url may contain a #fragment, and the fragment is then noted as needing to exist.
-func crawl(url string, sourceURL string) {
- if strings.Contains(url, "/devel/release") {
- return
- }
- mu.Lock()
- defer mu.Unlock()
- if u, frag, ok := strings.Cut(url, "#"); ok {
- url = u
- if frag != "" {
- uf := urlFrag{url, frag}
- neededFrags[uf] = append(neededFrags[uf], sourceURL)
- }
- }
- if crawled[url] {
- return
- }
- crawled[url] = true
-
- wg.Add(1)
- go func() {
- urlq <- url
- }()
-}
-
-func addProblem(url, errmsg string) {
- msg := fmt.Sprintf("Error on %s: %s (from %s)", url, errmsg, linkSources[url])
- if *verbose {
- log.Print(msg)
- }
- problems = append(problems, msg)
-}
-
-func crawlLoop() {
- for url := range urlq {
- if err := doCrawl(url); err != nil {
- addProblem(url, err.Error())
- }
- }
-}
-
-func doCrawl(url string) error {
- defer wg.Done()
-
- req, err := http.NewRequest("GET", url, nil)
- if err != nil {
- return err
- }
- res, err := http.DefaultTransport.RoundTrip(req)
- if err != nil {
- return err
- }
- // Handle redirects.
- if res.StatusCode/100 == 3 {
- newURL, err := res.Location()
- if err != nil {
- return fmt.Errorf("resolving redirect: %v", err)
- }
- if !strings.HasPrefix(newURL.String(), *root) {
- // Skip off-site redirects.
- return nil
- }
- crawl(newURL.String(), url)
- return nil
- }
- if res.StatusCode != 200 {
- return errors.New(res.Status)
- }
- slurp, err := io.ReadAll(res.Body)
- res.Body.Close()
- if err != nil {
- log.Fatalf("Error reading %s body: %v", url, err)
- }
- if *verbose {
- log.Printf("Len of %s: %d", url, len(slurp))
- }
- body := string(slurp)
- for _, ref := range localLinks(body) {
- if *verbose {
- log.Printf(" links to %s", ref)
- }
- dest := *root + ref
- linkSources[dest] = append(linkSources[dest], url)
- crawl(dest, url)
- }
- for _, id := range pageIDs(body) {
- if *verbose {
- log.Printf(" url %s has #%s", url, id)
- }
- fragExists[urlFrag{url, id}] = true
- }
- return nil
-}
-
-func main() {
- flag.Parse()
-
- go crawlLoop()
- crawl(*root, "")
-
- wg.Wait()
- close(urlq)
- for uf, needers := range neededFrags {
- if !fragExists[uf] {
- problems = append(problems, fmt.Sprintf("Missing fragment for %+v from %v", uf, needers))
- }
- }
-
- for _, s := range problems {
- fmt.Println(s)
- }
- if len(problems) > 0 {
- os.Exit(1)
- }
-}
diff --git a/src/all.bash b/src/all.bash
index 1b8ca093e4..adbc60e361 100755
--- a/src/all.bash
+++ b/src/all.bash
@@ -10,4 +10,4 @@ if [ ! -f make.bash ]; then
fi
. ./make.bash "$@" --no-banner
bash run.bash --no-rebuild
-"$GOTOOLDIR/dist" banner # print build info
+../bin/go tool dist banner # print build info
diff --git a/src/all.bat b/src/all.bat
index d5abec141f..4c681d15eb 100644
--- a/src/all.bat
+++ b/src/all.bat
@@ -6,17 +6,11 @@
setlocal
-if exist make.bat goto ok
-echo all.bat must be run from go\src
-:: cannot exit: would kill parent command interpreter
-goto end
-:ok
+if not exist make.bat (
+ echo all.bat must be run from go\src
+ exit /b 1
+)
-call .\make.bat --no-banner --no-local
-if %GOBUILDFAIL%==1 goto end
-call .\run.bat --no-rebuild --no-local
-if %GOBUILDFAIL%==1 goto end
-"%GOTOOLDIR%/dist" banner
-
-:end
-if x%GOBUILDEXIT%==x1 exit %GOBUILDFAIL%
+call .\make.bat --no-banner || exit /b 1
+call .\run.bat --no-rebuild || exit /b 1
+..\bin\go tool dist banner
diff --git a/src/all.rc b/src/all.rc
index 45b1261a20..ad8c3e143e 100755
--- a/src/all.rc
+++ b/src/all.rc
@@ -13,4 +13,4 @@ if(! test -f make.rc){
. ./make.rc --no-banner $*
bind -b $GOROOT/bin /bin
./run.rc --no-rebuild
-$GOTOOLDIR/dist banner # print build info
+../bin/go tool dist banner # print build info
diff --git a/src/archive/tar/common.go b/src/archive/tar/common.go
index 7b3945ff15..ad31bbb64a 100644
--- a/src/archive/tar/common.go
+++ b/src/archive/tar/common.go
@@ -39,6 +39,7 @@ var (
errMissData = errors.New("archive/tar: sparse file references non-existent data")
errUnrefData = errors.New("archive/tar: sparse file contains unreferenced data")
errWriteHole = errors.New("archive/tar: write non-NUL byte in sparse hole")
+ errSparseTooLong = errors.New("archive/tar: sparse map too long")
)
type headerError []string
diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go
index 8483fb52a2..16ac2f5b17 100644
--- a/src/archive/tar/reader.go
+++ b/src/archive/tar/reader.go
@@ -531,12 +531,17 @@ func readGNUSparseMap1x0(r io.Reader) (sparseDatas, error) {
cntNewline int64
buf bytes.Buffer
blk block
+ totalSize int
)
// feedTokens copies data in blocks from r into buf until there are
// at least cnt newlines in buf. It will not read more blocks than needed.
feedTokens := func(n int64) error {
for cntNewline < n {
+ totalSize += len(blk)
+ if totalSize > maxSpecialFileSize {
+ return errSparseTooLong
+ }
if _, err := mustReadFull(r, blk[:]); err != nil {
return err
}
@@ -569,8 +574,8 @@ func readGNUSparseMap1x0(r io.Reader) (sparseDatas, error) {
}
// Parse for all member entries.
- // numEntries is trusted after this since a potential attacker must have
- // committed resources proportional to what this library used.
+ // numEntries is trusted after this since feedTokens limits the number of
+ // tokens based on maxSpecialFileSize.
if err := feedTokens(2 * numEntries); err != nil {
return nil, err
}
diff --git a/src/archive/tar/reader_test.go b/src/archive/tar/reader_test.go
index 99340a3047..fca53dae74 100644
--- a/src/archive/tar/reader_test.go
+++ b/src/archive/tar/reader_test.go
@@ -621,6 +621,11 @@ func TestReader(t *testing.T) {
},
Format: FormatPAX,
}},
+ }, {
+ // Small compressed file that uncompresses to
+ // a file with a very large GNU 1.0 sparse map.
+ file: "testdata/gnu-sparse-many-zeros.tar.bz2",
+ err: errSparseTooLong,
}}
for _, v := range vectors {
diff --git a/src/archive/tar/strconv.go b/src/archive/tar/strconv.go
index ac8105efad..217efe9e2e 100644
--- a/src/archive/tar/strconv.go
+++ b/src/archive/tar/strconv.go
@@ -213,15 +213,17 @@ func parsePAXTime(s string) (time.Time, error) {
}
// Parse the nanoseconds.
- if strings.Trim(sn, "0123456789") != "" {
- return time.Time{}, ErrHeader
+ // Initialize an array with '0's to handle right padding automatically.
+ nanoDigits := [maxNanoSecondDigits]byte{'0', '0', '0', '0', '0', '0', '0', '0', '0'}
+ for i := range len(sn) {
+ switch c := sn[i]; {
+ case c < '0' || c > '9':
+ return time.Time{}, ErrHeader
+ case i < len(nanoDigits):
+ nanoDigits[i] = c
+ }
}
- if len(sn) < maxNanoSecondDigits {
- sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad
- } else {
- sn = sn[:maxNanoSecondDigits] // Right truncate
- }
- nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed
+ nsecs, _ := strconv.ParseInt(string(nanoDigits[:]), 10, 64) // Must succeed after validation
if len(ss) > 0 && ss[0] == '-' {
return time.Unix(secs, -1*nsecs), nil // Negative correction
}
diff --git a/src/archive/tar/strconv_test.go b/src/archive/tar/strconv_test.go
index add65e272a..a5305e0385 100644
--- a/src/archive/tar/strconv_test.go
+++ b/src/archive/tar/strconv_test.go
@@ -439,3 +439,66 @@ func TestFormatPAXRecord(t *testing.T) {
}
}
}
+
+func BenchmarkParsePAXTime(b *testing.B) {
+ tests := []struct {
+ name string
+ in string
+ want time.Time
+ ok bool
+ }{
+ {
+ name: "NoNanos",
+ in: "123456",
+ want: time.Unix(123456, 0),
+ ok: true,
+ },
+ {
+ name: "ExactNanos",
+ in: "1.123456789",
+ want: time.Unix(1, 123456789),
+ ok: true,
+ },
+ {
+ name: "WithNanoPadding",
+ in: "1.123",
+ want: time.Unix(1, 123000000),
+ ok: true,
+ },
+ {
+ name: "WithNanoTruncate",
+ in: "1.123456789123",
+ want: time.Unix(1, 123456789),
+ ok: true,
+ },
+ {
+ name: "TrailingError",
+ in: "1.123abc",
+ want: time.Time{},
+ ok: false,
+ },
+ {
+ name: "LeadingError",
+ in: "1.abc123",
+ want: time.Time{},
+ ok: false,
+ },
+ }
+ for _, tt := range tests {
+ b.Run(tt.name, func(b *testing.B) {
+ b.ReportAllocs()
+ for b.Loop() {
+ ts, err := parsePAXTime(tt.in)
+ if (err == nil) != tt.ok {
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.Fatal("expected error")
+ }
+ if !ts.Equal(tt.want) {
+ b.Fatalf("time mismatch: got %v, want %v", ts, tt.want)
+ }
+ }
+ })
+ }
+}
diff --git a/src/archive/tar/testdata/gnu-sparse-many-zeros.tar.bz2 b/src/archive/tar/testdata/gnu-sparse-many-zeros.tar.bz2
new file mode 100644
index 0000000000..751d7fd4b6
Binary files /dev/null and b/src/archive/tar/testdata/gnu-sparse-many-zeros.tar.bz2 differ
diff --git a/src/archive/tar/writer.go b/src/archive/tar/writer.go
index f966c5b4c6..336c9fd758 100644
--- a/src/archive/tar/writer.go
+++ b/src/archive/tar/writer.go
@@ -415,11 +415,17 @@ func (tw *Writer) AddFS(fsys fs.FS) error {
if err != nil {
return err
}
- // TODO(#49580): Handle symlinks when fs.ReadLinkFS is available.
- if !d.IsDir() && !info.Mode().IsRegular() {
+ linkTarget := ""
+ if typ := d.Type(); typ == fs.ModeSymlink {
+ var err error
+ linkTarget, err = fs.ReadLink(fsys, name)
+ if err != nil {
+ return err
+ }
+ } else if !typ.IsRegular() && typ != fs.ModeDir {
return errors.New("tar: cannot add non-regular file")
}
- h, err := FileInfoHeader(info, "")
+ h, err := FileInfoHeader(info, linkTarget)
if err != nil {
return err
}
@@ -430,7 +436,7 @@ func (tw *Writer) AddFS(fsys fs.FS) error {
if err := tw.WriteHeader(h); err != nil {
return err
}
- if d.IsDir() {
+ if !d.Type().IsRegular() {
return nil
}
f, err := fsys.Open(name)
diff --git a/src/archive/tar/writer_test.go b/src/archive/tar/writer_test.go
index 7b10bf6a70..9e484432ea 100644
--- a/src/archive/tar/writer_test.go
+++ b/src/archive/tar/writer_test.go
@@ -1342,6 +1342,7 @@ func TestWriterAddFS(t *testing.T) {
"emptyfolder": {Mode: 0o755 | os.ModeDir},
"file.go": {Data: []byte("hello")},
"subfolder/another.go": {Data: []byte("world")},
+ "symlink.go": {Mode: 0o777 | os.ModeSymlink, Data: []byte("file.go")},
// Notably missing here is the "subfolder" directory. This makes sure even
// if we don't have a subfolder directory listed.
}
@@ -1370,7 +1371,7 @@ func TestWriterAddFS(t *testing.T) {
for _, name := range names {
entriesLeft--
- entryInfo, err := fsys.Stat(name)
+ entryInfo, err := fsys.Lstat(name)
if err != nil {
t.Fatalf("getting entry info error: %v", err)
}
@@ -1396,18 +1397,23 @@ func TestWriterAddFS(t *testing.T) {
name, entryInfo.Mode(), hdr.FileInfo().Mode())
}
- if entryInfo.IsDir() {
- continue
- }
-
- data, err := io.ReadAll(tr)
- if err != nil {
- t.Fatal(err)
- }
- origdata := fsys[name].Data
- if string(data) != string(origdata) {
- t.Fatalf("test fs has file content %v; archive header has %v",
- data, origdata)
+ switch entryInfo.Mode().Type() {
+ case fs.ModeDir:
+ // No additional checks necessary.
+ case fs.ModeSymlink:
+ origtarget := string(fsys[name].Data)
+ if hdr.Linkname != origtarget {
+ t.Fatalf("test fs has link content %s; archive header %v", origtarget, hdr.Linkname)
+ }
+ default:
+ data, err := io.ReadAll(tr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ origdata := fsys[name].Data
+ if string(data) != string(origdata) {
+ t.Fatalf("test fs has file content %v; archive header has %v", origdata, data)
+ }
}
}
if entriesLeft > 0 {
diff --git a/src/archive/zip/reader.go b/src/archive/zip/reader.go
index 2246d56558..6b57f767fc 100644
--- a/src/archive/zip/reader.go
+++ b/src/archive/zip/reader.go
@@ -8,6 +8,7 @@ import (
"bufio"
"encoding/binary"
"errors"
+ "fmt"
"hash"
"hash/crc32"
"internal/godebug"
@@ -804,6 +805,9 @@ func toValidName(name string) string {
func (r *Reader) initFileList() {
r.fileListOnce.Do(func() {
+ // Preallocate the minimum size of the index.
+ // We may also synthesize additional directory entries.
+ r.fileList = make([]fileListEntry, 0, len(r.File))
// files and knownDirs map from a file/directory name
// to an index into the r.fileList entry that we are
// building. They are used to mark duplicate entries.
@@ -985,6 +989,12 @@ func (d *openDir) ReadDir(count int) ([]fs.DirEntry, error) {
s, err := d.files[d.offset+i].stat()
if err != nil {
return nil, err
+ } else if s.Name() == "." || !fs.ValidPath(s.Name()) {
+ return nil, &fs.PathError{
+ Op: "readdir",
+ Path: d.e.name,
+ Err: fmt.Errorf("invalid file name: %v", d.files[d.offset+i].name),
+ }
}
list[i] = s
}
diff --git a/src/archive/zip/reader_test.go b/src/archive/zip/reader_test.go
index bfa35c992a..cb8a0c2871 100644
--- a/src/archive/zip/reader_test.go
+++ b/src/archive/zip/reader_test.go
@@ -8,6 +8,7 @@ import (
"bytes"
"encoding/binary"
"encoding/hex"
+ "errors"
"internal/obscuretestdata"
"io"
"io/fs"
@@ -1212,7 +1213,6 @@ func TestFS(t *testing.T) {
[]string{"a/b/c"},
},
} {
- test := test
t.Run(test.file, func(t *testing.T) {
t.Parallel()
z, err := OpenReader(test.file)
@@ -1246,7 +1246,6 @@ func TestFSWalk(t *testing.T) {
wantErr: true,
},
} {
- test := test
t.Run(test.file, func(t *testing.T) {
t.Parallel()
z, err := OpenReader(test.file)
@@ -1281,6 +1280,49 @@ func TestFSWalk(t *testing.T) {
}
}
+func TestFSWalkBadFile(t *testing.T) {
+ t.Parallel()
+
+ var buf bytes.Buffer
+ zw := NewWriter(&buf)
+ hdr := &FileHeader{Name: "."}
+ hdr.SetMode(fs.ModeDir | 0o755)
+ w, err := zw.CreateHeader(hdr)
+ if err != nil {
+ t.Fatalf("create zip header: %v", err)
+ }
+ _, err = w.Write([]byte("some data"))
+ if err != nil {
+ t.Fatalf("write zip contents: %v", err)
+
+ }
+ err = zw.Close()
+ if err != nil {
+ t.Fatalf("close zip writer: %v", err)
+
+ }
+
+ zr, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
+ if err != nil {
+ t.Fatalf("create zip reader: %v", err)
+
+ }
+ var count int
+ var errRepeat = errors.New("repeated call to path")
+ err = fs.WalkDir(zr, ".", func(p string, d fs.DirEntry, err error) error {
+ count++
+ if count > 2 { // once for directory read, once for the error
+ return errRepeat
+ }
+ return err
+ })
+ if err == nil {
+ t.Fatalf("expected error from invalid file name")
+ } else if errors.Is(err, errRepeat) {
+ t.Fatal(err)
+ }
+}
+
func TestFSModTime(t *testing.T) {
t.Parallel()
z, err := OpenReader("testdata/subdir.zip")
diff --git a/src/bufio/bufio.go b/src/bufio/bufio.go
index d589701e19..141a9a1a2a 100644
--- a/src/bufio/bufio.go
+++ b/src/bufio/bufio.go
@@ -311,10 +311,7 @@ func (b *Reader) ReadRune() (r rune, size int, err error) {
if b.r == b.w {
return 0, 0, b.readErr()
}
- r, size = rune(b.buf[b.r]), 1
- if r >= utf8.RuneSelf {
- r, size = utf8.DecodeRune(b.buf[b.r:b.w])
- }
+ r, size = utf8.DecodeRune(b.buf[b.r:b.w])
b.r += size
b.lastByte = int(b.buf[b.r-1])
b.lastRuneSize = size
@@ -519,9 +516,11 @@ func (b *Reader) WriteTo(w io.Writer) (n int64, err error) {
b.lastByte = -1
b.lastRuneSize = -1
- n, err = b.writeBuf(w)
- if err != nil {
- return
+ if b.r < b.w {
+ n, err = b.writeBuf(w)
+ if err != nil {
+ return
+ }
}
if r, ok := b.rd.(io.WriterTo); ok {
diff --git a/src/bufio/bufio_test.go b/src/bufio/bufio_test.go
index 63dd2ea432..742e195425 100644
--- a/src/bufio/bufio_test.go
+++ b/src/bufio/bufio_test.go
@@ -1149,7 +1149,7 @@ func (w errorWriterToTest) Write(p []byte) (int, error) {
var errorWriterToTests = []errorWriterToTest{
{1, 0, nil, io.ErrClosedPipe, io.ErrClosedPipe},
{0, 1, io.ErrClosedPipe, nil, io.ErrClosedPipe},
- {0, 0, io.ErrUnexpectedEOF, io.ErrClosedPipe, io.ErrClosedPipe},
+ {0, 0, io.ErrUnexpectedEOF, io.ErrClosedPipe, io.ErrUnexpectedEOF},
{0, 1, io.EOF, nil, nil},
}
diff --git a/src/bufio/net_test.go b/src/bufio/net_test.go
new file mode 100644
index 0000000000..d3b47e4cb9
--- /dev/null
+++ b/src/bufio/net_test.go
@@ -0,0 +1,96 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build unix
+
+package bufio_test
+
+import (
+ "bufio"
+ "io"
+ "net"
+ "path/filepath"
+ "strings"
+ "sync"
+ "testing"
+)
+
+// TestCopyUnixpacket tests that we can use bufio when copying
+// across a unixpacket socket. This used to fail due to an unnecessary
+// empty Write call that was interpreted as an EOF.
+func TestCopyUnixpacket(t *testing.T) {
+ tmpDir := t.TempDir()
+ socket := filepath.Join(tmpDir, "unixsock")
+
+ // Start a unixpacket server.
+ addr := &net.UnixAddr{
+ Name: socket,
+ Net: "unixpacket",
+ }
+ server, err := net.ListenUnix("unixpacket", addr)
+ if err != nil {
+ t.Skipf("skipping test because opening a unixpacket socket failed: %v", err)
+ }
+
+ // Start a goroutine for the server to accept one connection
+ // and read all the data sent on the connection,
+ // reporting the number of bytes read on ch.
+ ch := make(chan int, 1)
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+
+ tot := 0
+ defer func() {
+ ch <- tot
+ }()
+
+ serverConn, err := server.Accept()
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ buf := make([]byte, 1024)
+ for {
+ n, err := serverConn.Read(buf)
+ tot += n
+ if err == io.EOF {
+ return
+ }
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ }
+ }()
+
+ clientConn, err := net.DialUnix("unixpacket", nil, addr)
+ if err != nil {
+ // Leaves the server goroutine hanging. Oh well.
+ t.Fatal(err)
+ }
+
+ defer wg.Wait()
+ defer clientConn.Close()
+
+ const data = "data"
+ r := bufio.NewReader(strings.NewReader(data))
+ n, err := io.Copy(clientConn, r)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if n != int64(len(data)) {
+ t.Errorf("io.Copy returned %d, want %d", n, len(data))
+ }
+
+ clientConn.Close()
+ tot := <-ch
+
+ if tot != len(data) {
+ t.Errorf("server read %d, want %d", tot, len(data))
+ }
+}
diff --git a/src/bufio/scan.go b/src/bufio/scan.go
index a26b2ff17d..1a0a3907c9 100644
--- a/src/bufio/scan.go
+++ b/src/bufio/scan.go
@@ -260,8 +260,11 @@ func (s *Scanner) setErr(err error) {
}
}
-// Buffer sets the initial buffer to use when scanning
+// Buffer controls memory allocation by the Scanner.
+// It sets the initial buffer to use when scanning
// and the maximum size of buffer that may be allocated during scanning.
+// The contents of the buffer are ignored.
+//
// The maximum token size must be less than the larger of max and cap(buf).
// If max <= cap(buf), [Scanner.Scan] will use this buffer only and do no allocation.
//
diff --git a/src/bytes/buffer.go b/src/bytes/buffer.go
index f90d9eca0f..9684513942 100644
--- a/src/bytes/buffer.go
+++ b/src/bytes/buffer.go
@@ -21,6 +21,12 @@ type Buffer struct {
buf []byte // contents are the bytes buf[off : len(buf)]
off int // read at &buf[off], write at &buf[len(buf)]
lastRead readOp // last read operation, so that Unread* can work correctly.
+
+ // Copying and modifying a non-zero Buffer is prone to error,
+ // but we cannot employ the noCopy trick used by WaitGroup and Mutex,
+ // which causes vet's copylocks checker to report misuse, as vet
+ // cannot reliably distinguish the zero and non-zero cases.
+ // See #26462, #25907, #47276, #48398 for history.
}
// The readOp constants describe the last action performed on
diff --git a/src/bytes/buffer_test.go b/src/bytes/buffer_test.go
index 97fca5a9d1..b46ba1204e 100644
--- a/src/bytes/buffer_test.go
+++ b/src/bytes/buffer_test.go
@@ -354,7 +354,7 @@ func TestWriteAppend(t *testing.T) {
got.Write(b)
}
if !Equal(got.Bytes(), want) {
- t.Fatalf("Bytes() = %q, want %q", got, want)
+ t.Fatalf("Bytes() = %q, want %q", &got, want)
}
// With a sufficiently sized buffer, there should be no allocations.
diff --git a/src/bytes/bytes.go b/src/bytes/bytes.go
index 4a2c9eac57..87183c0195 100644
--- a/src/bytes/bytes.go
+++ b/src/bytes/bytes.go
@@ -451,7 +451,9 @@ var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1}
// Fields interprets s as a sequence of UTF-8-encoded code points.
// It splits the slice s around each instance of one or more consecutive white space
// characters, as defined by [unicode.IsSpace], returning a slice of subslices of s or an
-// empty slice if s contains only white space.
+// empty slice if s contains only white space. Every element of the returned slice is
+// non-empty. Unlike [Split], leading and trailing runs of white space characters
+// are discarded.
func Fields(s []byte) [][]byte {
// First count the fields.
// This is an exact count if s is ASCII, otherwise it is an approximation.
@@ -505,7 +507,9 @@ func Fields(s []byte) [][]byte {
// FieldsFunc interprets s as a sequence of UTF-8-encoded code points.
// It splits the slice s at each run of code points c satisfying f(c) and
// returns a slice of subslices of s. If all code points in s satisfy f(c), or
-// len(s) == 0, an empty slice is returned.
+// len(s) == 0, an empty slice is returned. Every element of the returned slice is
+// non-empty. Unlike [Split], leading and trailing runs of code points
+// satisfying f(c) are discarded.
//
// FieldsFunc makes no guarantees about the order in which it calls f(c)
// and assumes that f always returns the same value for a given c.
@@ -524,11 +528,7 @@ func FieldsFunc(s []byte, f func(rune) bool) [][]byte {
// more efficient, possibly due to cache effects.
start := -1 // valid span start if >= 0
for i := 0; i < len(s); {
- size := 1
- r := rune(s[i])
- if r >= utf8.RuneSelf {
- r, size = utf8.DecodeRune(s[i:])
- }
+ r, size := utf8.DecodeRune(s[i:])
if f(r) {
if start >= 0 {
spans = append(spans, span{start, i})
@@ -610,11 +610,7 @@ func Map(mapping func(r rune) rune, s []byte) []byte {
// fine. It could also shrink but that falls out naturally.
b := make([]byte, 0, len(s))
for i := 0; i < len(s); {
- wid := 1
- r := rune(s[i])
- if r >= utf8.RuneSelf {
- r, wid = utf8.DecodeRune(s[i:])
- }
+ r, wid := utf8.DecodeRune(s[i:])
r = mapping(r)
if r >= 0 {
b = utf8.AppendRune(b, r)
@@ -913,11 +909,7 @@ func LastIndexFunc(s []byte, f func(r rune) bool) int {
func indexFunc(s []byte, f func(r rune) bool, truth bool) int {
start := 0
for start < len(s) {
- wid := 1
- r := rune(s[start])
- if r >= utf8.RuneSelf {
- r, wid = utf8.DecodeRune(s[start:])
- }
+ r, wid := utf8.DecodeRune(s[start:])
if f(r) == truth {
return start
}
@@ -1048,10 +1040,7 @@ func trimLeftASCII(s []byte, as *asciiSet) []byte {
func trimLeftUnicode(s []byte, cutset string) []byte {
for len(s) > 0 {
- r, n := rune(s[0]), 1
- if r >= utf8.RuneSelf {
- r, n = utf8.DecodeRune(s)
- }
+ r, n := utf8.DecodeRune(s)
if !containsRune(cutset, r) {
break
}
@@ -1113,41 +1102,34 @@ func trimRightUnicode(s []byte, cutset string) []byte {
// TrimSpace returns a subslice of s by slicing off all leading and
// trailing white space, as defined by Unicode.
func TrimSpace(s []byte) []byte {
- // Fast path for ASCII: look for the first ASCII non-space byte
- start := 0
- for ; start < len(s); start++ {
- c := s[start]
+ // Fast path for ASCII: look for the first ASCII non-space byte.
+ for lo, c := range s {
if c >= utf8.RuneSelf {
// If we run into a non-ASCII byte, fall back to the
- // slower unicode-aware method on the remaining bytes
- return TrimFunc(s[start:], unicode.IsSpace)
+ // slower unicode-aware method on the remaining bytes.
+ return TrimFunc(s[lo:], unicode.IsSpace)
}
- if asciiSpace[c] == 0 {
- break
+ if asciiSpace[c] != 0 {
+ continue
+ }
+ s = s[lo:]
+ // Now look for the first ASCII non-space byte from the end.
+ for hi := len(s) - 1; hi >= 0; hi-- {
+ c := s[hi]
+ if c >= utf8.RuneSelf {
+ return TrimFunc(s[:hi+1], unicode.IsSpace)
+ }
+ if asciiSpace[c] == 0 {
+ // At this point, s[:hi+1] starts and ends with ASCII
+ // non-space bytes, so we're done. Non-ASCII cases have
+ // already been handled above.
+ return s[:hi+1]
+ }
}
}
-
- // Now look for the first ASCII non-space byte from the end
- stop := len(s)
- for ; stop > start; stop-- {
- c := s[stop-1]
- if c >= utf8.RuneSelf {
- return TrimFunc(s[start:stop], unicode.IsSpace)
- }
- if asciiSpace[c] == 0 {
- break
- }
- }
-
- // At this point s[start:stop] starts and ends with an ASCII
- // non-space bytes, so we're done. Non-ASCII cases have already
- // been handled above.
- if start == stop {
- // Special case to preserve previous TrimLeftFunc behavior,
- // returning nil instead of empty slice if all spaces.
- return nil
- }
- return s[start:stop]
+ // Special case to preserve previous TrimLeftFunc behavior,
+ // returning nil instead of empty slice if all spaces.
+ return nil
}
// Runes interprets s as a sequence of UTF-8-encoded code points.
@@ -1188,19 +1170,22 @@ func Replace(s, old, new []byte, n int) []byte {
t := make([]byte, len(s)+n*(len(new)-len(old)))
w := 0
start := 0
- for i := 0; i < n; i++ {
- j := start
- if len(old) == 0 {
- if i > 0 {
- _, wid := utf8.DecodeRune(s[start:])
- j += wid
- }
- } else {
- j += Index(s[start:], old)
+ if len(old) > 0 {
+ for range n {
+ j := start + Index(s[start:], old)
+ w += copy(t[w:], s[start:j])
+ w += copy(t[w:], new)
+ start = j + len(old)
}
- w += copy(t[w:], s[start:j])
+ } else { // len(old) == 0
w += copy(t[w:], new)
- start = j + len(old)
+ for range n - 1 {
+ _, wid := utf8.DecodeRune(s[start:])
+ j := start + wid
+ w += copy(t[w:], s[start:j])
+ w += copy(t[w:], new)
+ start = j
+ }
}
w += copy(t[w:], s[start:])
return t[0:w]
@@ -1221,7 +1206,7 @@ func ReplaceAll(s, old, new []byte) []byte {
func EqualFold(s, t []byte) bool {
// ASCII fast path
i := 0
- for ; i < len(s) && i < len(t); i++ {
+ for n := min(len(s), len(t)); i < n; i++ {
sr := s[i]
tr := t[i]
if sr|tr >= utf8.RuneSelf {
@@ -1251,19 +1236,10 @@ hasUnicode:
t = t[i:]
for len(s) != 0 && len(t) != 0 {
// Extract first rune from each.
- var sr, tr rune
- if s[0] < utf8.RuneSelf {
- sr, s = rune(s[0]), s[1:]
- } else {
- r, size := utf8.DecodeRune(s)
- sr, s = r, s[size:]
- }
- if t[0] < utf8.RuneSelf {
- tr, t = rune(t[0]), t[1:]
- } else {
- r, size := utf8.DecodeRune(t)
- tr, t = r, t[size:]
- }
+ sr, size := utf8.DecodeRune(s)
+ s = s[size:]
+ tr, size := utf8.DecodeRune(t)
+ t = t[size:]
// If they match, keep going; if not, return false.
diff --git a/src/bytes/bytes_test.go b/src/bytes/bytes_test.go
index ead581718a..f18915c879 100644
--- a/src/bytes/bytes_test.go
+++ b/src/bytes/bytes_test.go
@@ -7,6 +7,7 @@ package bytes_test
import (
. "bytes"
"fmt"
+ "internal/asan"
"internal/testenv"
"iter"
"math"
@@ -692,14 +693,14 @@ func bmIndexRuneUnicode(rt *unicode.RangeTable, needle rune) func(b *testing.B,
for _, r16 := range rt.R16 {
for r := rune(r16.Lo); r <= rune(r16.Hi); r += rune(r16.Stride) {
if r != needle {
- rs = append(rs, rune(r))
+ rs = append(rs, r)
}
}
}
for _, r32 := range rt.R32 {
for r := rune(r32.Lo); r <= rune(r32.Hi); r += rune(r32.Stride) {
if r != needle {
- rs = append(rs, rune(r))
+ rs = append(rs, r)
}
}
}
@@ -890,9 +891,7 @@ func BenchmarkCountSingle(b *testing.B) {
b.Fatal("bad count", j, expect)
}
}
- for i := 0; i < len(buf); i++ {
- buf[i] = 0
- }
+ clear(buf)
})
}
@@ -1786,9 +1785,20 @@ var ReplaceTests = []ReplaceTest{
func TestReplace(t *testing.T) {
for _, tt := range ReplaceTests {
- in := append([]byte(tt.in), ""...)
+ var (
+ in = []byte(tt.in)
+ old = []byte(tt.old)
+ new = []byte(tt.new)
+ )
+ if !asan.Enabled {
+ allocs := testing.AllocsPerRun(10, func() { Replace(in, old, new, tt.n) })
+ if allocs > 1 {
+ t.Errorf("Replace(%q, %q, %q, %d) allocates %.2f objects", tt.in, tt.old, tt.new, tt.n, allocs)
+ }
+ }
+ in = append(in, ""...)
in = in[:len(tt.in)]
- out := Replace(in, []byte(tt.old), []byte(tt.new), tt.n)
+ out := Replace(in, old, new, tt.n)
if s := string(out); s != tt.out {
t.Errorf("Replace(%q, %q, %q, %d) = %q, want %q", tt.in, tt.old, tt.new, tt.n, s, tt.out)
}
@@ -1796,7 +1806,7 @@ func TestReplace(t *testing.T) {
t.Errorf("Replace(%q, %q, %q, %d) didn't copy", tt.in, tt.old, tt.new, tt.n)
}
if tt.n == -1 {
- out := ReplaceAll(in, []byte(tt.old), []byte(tt.new))
+ out := ReplaceAll(in, old, new)
if s := string(out); s != tt.out {
t.Errorf("ReplaceAll(%q, %q, %q) = %q, want %q", tt.in, tt.old, tt.new, s, tt.out)
}
@@ -1804,6 +1814,69 @@ func TestReplace(t *testing.T) {
}
}
+func FuzzReplace(f *testing.F) {
+ for _, tt := range ReplaceTests {
+ f.Add([]byte(tt.in), []byte(tt.old), []byte(tt.new), tt.n)
+ }
+ f.Fuzz(func(t *testing.T, in, old, new []byte, n int) {
+ differentImpl := func(in, old, new []byte, n int) []byte {
+ var out Buffer
+ if n < 0 {
+ n = math.MaxInt
+ }
+ for i := 0; i < len(in); {
+ if n == 0 {
+ out.Write(in[i:])
+ break
+ }
+ if HasPrefix(in[i:], old) {
+ out.Write(new)
+ i += len(old)
+ n--
+ if len(old) != 0 {
+ continue
+ }
+ if i == len(in) {
+ break
+ }
+ }
+ if len(old) == 0 {
+ _, length := utf8.DecodeRune(in[i:])
+ out.Write(in[i : i+length])
+ i += length
+ } else {
+ out.WriteByte(in[i])
+ i++
+ }
+ }
+ if len(old) == 0 && n != 0 {
+ out.Write(new)
+ }
+ return out.Bytes()
+ }
+ if simple, replace := differentImpl(in, old, new, n), Replace(in, old, new, n); !slices.Equal(simple, replace) {
+ t.Errorf("The two implementations do not match %q != %q for Replace(%q, %q, %q, %d)", simple, replace, in, old, new, n)
+ }
+ })
+}
+
+func BenchmarkReplace(b *testing.B) {
+ for _, tt := range ReplaceTests {
+ desc := fmt.Sprintf("%q %q %q %d", tt.in, tt.old, tt.new, tt.n)
+ var (
+ in = []byte(tt.in)
+ old = []byte(tt.old)
+ new = []byte(tt.new)
+ )
+ b.Run(desc, func(b *testing.B) {
+ b.ReportAllocs()
+ for b.Loop() {
+ Replace(in, old, new, tt.n)
+ }
+ })
+ }
+}
+
type TitleTest struct {
in, out string
}
@@ -2053,8 +2126,9 @@ func TestContainsFunc(t *testing.T) {
var makeFieldsInput = func() []byte {
x := make([]byte, 1<<20)
// Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space.
+ r := rand.New(rand.NewSource(99))
for i := range x {
- switch rand.Intn(10) {
+ switch r.Intn(10) {
case 0:
x[i] = ' '
case 1:
@@ -2073,8 +2147,9 @@ var makeFieldsInput = func() []byte {
var makeFieldsInputASCII = func() []byte {
x := make([]byte, 1<<20)
// Input is ~10% space, rest ASCII non-space.
+ r := rand.New(rand.NewSource(99))
for i := range x {
- if rand.Intn(10) == 0 {
+ if r.Intn(10) == 0 {
x[i] = ' '
} else {
x[i] = 'x'
@@ -2171,8 +2246,9 @@ func makeBenchInputHard() []byte {
"hello", "world",
}
x := make([]byte, 0, 1<<20)
+ r := rand.New(rand.NewSource(99))
for {
- i := rand.Intn(len(tokens))
+ i := r.Intn(len(tokens))
if len(x)+len(tokens[i]) >= 1<<20 {
break
}
diff --git a/src/bytes/example_test.go b/src/bytes/example_test.go
index c9086d3918..c489b950e5 100644
--- a/src/bytes/example_test.go
+++ b/src/bytes/example_test.go
@@ -245,9 +245,9 @@ func ExampleCut() {
}
func ExampleCutPrefix() {
- show := func(s, sep string) {
- after, found := bytes.CutPrefix([]byte(s), []byte(sep))
- fmt.Printf("CutPrefix(%q, %q) = %q, %v\n", s, sep, after, found)
+ show := func(s, prefix string) {
+ after, found := bytes.CutPrefix([]byte(s), []byte(prefix))
+ fmt.Printf("CutPrefix(%q, %q) = %q, %v\n", s, prefix, after, found)
}
show("Gopher", "Go")
show("Gopher", "ph")
@@ -257,9 +257,9 @@ func ExampleCutPrefix() {
}
func ExampleCutSuffix() {
- show := func(s, sep string) {
- before, found := bytes.CutSuffix([]byte(s), []byte(sep))
- fmt.Printf("CutSuffix(%q, %q) = %q, %v\n", s, sep, before, found)
+ show := func(s, suffix string) {
+ before, found := bytes.CutSuffix([]byte(s), []byte(suffix))
+ fmt.Printf("CutSuffix(%q, %q) = %q, %v\n", s, suffix, before, found)
}
show("Gopher", "Go")
show("Gopher", "er")
@@ -628,3 +628,93 @@ func ExampleToUpperSpecial() {
// Original : ahoj vývojári golang
// ToUpper : AHOJ VÝVOJÁRİ GOLANG
}
+
+func ExampleLines() {
+ text := []byte("Hello\nWorld\nGo Programming\n")
+ for line := range bytes.Lines(text) {
+ fmt.Printf("%q\n", line)
+ }
+
+ // Output:
+ // "Hello\n"
+ // "World\n"
+ // "Go Programming\n"
+}
+
+func ExampleSplitSeq() {
+ s := []byte("a,b,c,d")
+ for part := range bytes.SplitSeq(s, []byte(",")) {
+ fmt.Printf("%q\n", part)
+ }
+
+ // Output:
+ // "a"
+ // "b"
+ // "c"
+ // "d"
+}
+
+func ExampleSplitAfterSeq() {
+ s := []byte("a,b,c,d")
+ for part := range bytes.SplitAfterSeq(s, []byte(",")) {
+ fmt.Printf("%q\n", part)
+ }
+
+ // Output:
+ // "a,"
+ // "b,"
+ // "c,"
+ // "d"
+}
+
+func ExampleFieldsSeq() {
+ text := []byte("The quick brown fox")
+ fmt.Println("Split byte slice into fields:")
+ for word := range bytes.FieldsSeq(text) {
+ fmt.Printf("%q\n", word)
+ }
+
+ textWithSpaces := []byte(" lots of spaces ")
+ fmt.Println("\nSplit byte slice with multiple spaces:")
+ for word := range bytes.FieldsSeq(textWithSpaces) {
+ fmt.Printf("%q\n", word)
+ }
+
+ // Output:
+ // Split byte slice into fields:
+ // "The"
+ // "quick"
+ // "brown"
+ // "fox"
+ //
+ // Split byte slice with multiple spaces:
+ // "lots"
+ // "of"
+ // "spaces"
+}
+
+func ExampleFieldsFuncSeq() {
+ text := []byte("The quick brown fox")
+ fmt.Println("Split on whitespace(similar to FieldsSeq):")
+ for word := range bytes.FieldsFuncSeq(text, unicode.IsSpace) {
+ fmt.Printf("%q\n", word)
+ }
+
+ mixedText := []byte("abc123def456ghi")
+ fmt.Println("\nSplit on digits:")
+ for word := range bytes.FieldsFuncSeq(mixedText, unicode.IsDigit) {
+ fmt.Printf("%q\n", word)
+ }
+
+ // Output:
+ // Split on whitespace(similar to FieldsSeq):
+ // "The"
+ // "quick"
+ // "brown"
+ // "fox"
+ //
+ // Split on digits:
+ // "abc"
+ // "def"
+ // "ghi"
+}
diff --git a/src/bytes/iter.go b/src/bytes/iter.go
index 9890a478a8..a4ece881d2 100644
--- a/src/bytes/iter.go
+++ b/src/bytes/iter.go
@@ -28,30 +28,23 @@ func Lines(s []byte) iter.Seq[[]byte] {
return
}
}
- return
- }
-}
-
-// explodeSeq returns an iterator over the runes in s.
-func explodeSeq(s []byte) iter.Seq[[]byte] {
- return func(yield func([]byte) bool) {
- for len(s) > 0 {
- _, size := utf8.DecodeRune(s)
- if !yield(s[:size:size]) {
- return
- }
- s = s[size:]
- }
}
}
// splitSeq is SplitSeq or SplitAfterSeq, configured by how many
// bytes of sep to include in the results (none or all).
func splitSeq(s, sep []byte, sepSave int) iter.Seq[[]byte] {
- if len(sep) == 0 {
- return explodeSeq(s)
- }
return func(yield func([]byte) bool) {
+ if len(sep) == 0 {
+ for len(s) > 0 {
+ _, size := utf8.DecodeRune(s)
+ if !yield(s[:size:size]) {
+ return
+ }
+ s = s[size:]
+ }
+ return
+ }
for {
i := Index(s, sep)
if i < 0 {
@@ -67,26 +60,26 @@ func splitSeq(s, sep []byte, sepSave int) iter.Seq[[]byte] {
}
}
-// SplitSeq returns an iterator over all substrings of s separated by sep.
-// The iterator yields the same strings that would be returned by [Split](s, sep),
-// but without constructing the slice.
+// SplitSeq returns an iterator over all subslices of s separated by sep.
+// The iterator yields the same subslices that would be returned by [Split](s, sep),
+// but without constructing a new slice containing the subslices.
// It returns a single-use iterator.
func SplitSeq(s, sep []byte) iter.Seq[[]byte] {
return splitSeq(s, sep, 0)
}
-// SplitAfterSeq returns an iterator over substrings of s split after each instance of sep.
-// The iterator yields the same strings that would be returned by [SplitAfter](s, sep),
-// but without constructing the slice.
+// SplitAfterSeq returns an iterator over subslices of s split after each instance of sep.
+// The iterator yields the same subslices that would be returned by [SplitAfter](s, sep),
+// but without constructing a new slice containing the subslices.
// It returns a single-use iterator.
func SplitAfterSeq(s, sep []byte) iter.Seq[[]byte] {
return splitSeq(s, sep, len(sep))
}
-// FieldsSeq returns an iterator over substrings of s split around runs of
+// FieldsSeq returns an iterator over subslices of s split around runs of
// whitespace characters, as defined by [unicode.IsSpace].
-// The iterator yields the same strings that would be returned by [Fields](s),
-// but without constructing the slice.
+// The iterator yields the same subslices that would be returned by [Fields](s),
+// but without constructing a new slice containing the subslices.
func FieldsSeq(s []byte) iter.Seq[[]byte] {
return func(yield func([]byte) bool) {
start := -1
@@ -116,19 +109,15 @@ func FieldsSeq(s []byte) iter.Seq[[]byte] {
}
}
-// FieldsFuncSeq returns an iterator over substrings of s split around runs of
+// FieldsFuncSeq returns an iterator over subslices of s split around runs of
// Unicode code points satisfying f(c).
-// The iterator yields the same strings that would be returned by [FieldsFunc](s),
-// but without constructing the slice.
+// The iterator yields the same subslices that would be returned by [FieldsFunc](s),
+// but without constructing a new slice containing the subslices.
func FieldsFuncSeq(s []byte, f func(rune) bool) iter.Seq[[]byte] {
return func(yield func([]byte) bool) {
start := -1
for i := 0; i < len(s); {
- size := 1
- r := rune(s[i])
- if r >= utf8.RuneSelf {
- r, size = utf8.DecodeRune(s[i:])
- }
+ r, size := utf8.DecodeRune(s[i:])
if f(r) {
if start >= 0 {
if !yield(s[start:i:i]) {
diff --git a/src/bytes/iter_test.go b/src/bytes/iter_test.go
new file mode 100644
index 0000000000..e37fdfb96d
--- /dev/null
+++ b/src/bytes/iter_test.go
@@ -0,0 +1,56 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bytes_test
+
+import (
+ . "bytes"
+ "testing"
+)
+
+func BenchmarkSplitSeqEmptySeparator(b *testing.B) {
+ for range b.N {
+ for range SplitSeq(benchInputHard, nil) {
+ }
+ }
+}
+
+func BenchmarkSplitSeqSingleByteSeparator(b *testing.B) {
+ sep := []byte("/")
+ for range b.N {
+ for range SplitSeq(benchInputHard, sep) {
+ }
+ }
+}
+
+func BenchmarkSplitSeqMultiByteSeparator(b *testing.B) {
+ sep := []byte("hello")
+ for range b.N {
+ for range SplitSeq(benchInputHard, sep) {
+ }
+ }
+}
+
+func BenchmarkSplitAfterSeqEmptySeparator(b *testing.B) {
+ for range b.N {
+ for range SplitAfterSeq(benchInputHard, nil) {
+ }
+ }
+}
+
+func BenchmarkSplitAfterSeqSingleByteSeparator(b *testing.B) {
+ sep := []byte("/")
+ for range b.N {
+ for range SplitAfterSeq(benchInputHard, sep) {
+ }
+ }
+}
+
+func BenchmarkSplitAfterSeqMultiByteSeparator(b *testing.B) {
+ sep := []byte("hello")
+ for range b.N {
+ for range SplitAfterSeq(benchInputHard, sep) {
+ }
+ }
+}
diff --git a/src/clean.bat b/src/clean.bat
index 6688b41e5e..2e03806305 100644
--- a/src/clean.bat
+++ b/src/clean.bat
@@ -6,27 +6,16 @@
setlocal
-set GOBUILDFAIL=0
-
-go tool dist env -w -p >env.bat
-if errorlevel 1 goto fail
+go tool dist env -w -p >env.bat || exit /b 1
call .\env.bat
del env.bat
echo.
-if exist %GOTOOLDIR%\dist.exe goto distok
-echo cannot find %GOTOOLDIR%\dist; nothing to clean
-goto fail
-:distok
+if not exist %GOTOOLDIR%\dist.exe (
+ echo cannot find %GOTOOLDIR%\dist.exe; nothing to clean
+ exit /b 1
+)
"%GOBIN%\go" clean -i std
"%GOBIN%\go" tool dist clean
"%GOBIN%\go" clean -i cmd
-
-goto end
-
-:fail
-set GOBUILDFAIL=1
-
-:end
-if x%GOBUILDEXIT%==x1 exit %GOBUILDFAIL%
diff --git a/src/cmd/api/api_test.go b/src/cmd/api/api_test.go
index 32da68982b..98ebf168ef 100644
--- a/src/cmd/api/api_test.go
+++ b/src/cmd/api/api_test.go
@@ -99,6 +99,11 @@ func TestGolden(t *testing.T) {
}
func TestCompareAPI(t *testing.T) {
+ if *flagCheck {
+ // not worth repeating in -check
+ t.Skip("skipping with -check set")
+ }
+
tests := []struct {
name string
features, required, exception []string
@@ -180,6 +185,11 @@ func TestCompareAPI(t *testing.T) {
}
func TestSkipInternal(t *testing.T) {
+ if *flagCheck {
+ // not worth repeating in -check
+ t.Skip("skipping with -check set")
+ }
+
tests := []struct {
pkg string
want bool
@@ -294,14 +304,20 @@ func TestIssue41358(t *testing.T) {
}
func TestIssue64958(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping with -short")
+ }
+ if *flagCheck {
+ // slow, not worth repeating in -check
+ t.Skip("skipping with -check set")
+ }
+ testenv.MustHaveGoBuild(t)
+
defer func() {
if x := recover(); x != nil {
t.Errorf("expected no panic; recovered %v", x)
}
}()
-
- testenv.MustHaveGoBuild(t)
-
for _, context := range contexts {
w := NewWalker(context, "testdata/src/issue64958")
pkg, err := w.importFrom("p", "", 0)
diff --git a/src/cmd/api/main_test.go b/src/cmd/api/main_test.go
index a0820c2274..ed366be4e7 100644
--- a/src/cmd/api/main_test.go
+++ b/src/cmd/api/main_test.go
@@ -1058,7 +1058,7 @@ func (w *Walker) emitIfaceType(name string, typ *types.Interface) {
if w.isDeprecated(m) {
w.emitf("%s //deprecated", m.Name())
}
- w.emitf("%s%s", m.Name(), w.signatureString(m.Type().(*types.Signature)))
+ w.emitf("%s%s", m.Name(), w.signatureString(m.Signature()))
}
if !complete {
@@ -1088,7 +1088,7 @@ func (w *Walker) emitIfaceType(name string, typ *types.Interface) {
}
func (w *Walker) emitFunc(f *types.Func) {
- sig := f.Type().(*types.Signature)
+ sig := f.Signature()
if sig.Recv() != nil {
panic("method considered a regular function: " + f.String())
}
diff --git a/src/cmd/api/testdata/src/pkg/p1/golden.txt b/src/cmd/api/testdata/src/pkg/p1/golden.txt
index 65c4f35d2c..cf089f6858 100644
--- a/src/cmd/api/testdata/src/pkg/p1/golden.txt
+++ b/src/cmd/api/testdata/src/pkg/p1/golden.txt
@@ -1,6 +1,6 @@
+pkg p1, const A //deprecated
pkg p1, const A = 1
pkg p1, const A ideal-int
-pkg p1, const A //deprecated
pkg p1, const A64 = 1
pkg p1, const A64 int64
pkg p1, const AIsLowerA = 11
@@ -25,8 +25,8 @@ pkg p1, func TakesFunc(func(int) int)
pkg p1, method (*B) JustOnB()
pkg p1, method (*B) OnBothTandBPtr()
pkg p1, method (*Embedded) OnEmbedded()
-pkg p1, method (*S2) SMethod(int8, int16, int64)
pkg p1, method (*S2) SMethod //deprecated
+pkg p1, method (*S2) SMethod(int8, int16, int64)
pkg p1, method (*T) JustOnT()
pkg p1, method (*T) OnBothTandBPtr()
pkg p1, method (B) OnBothTandBVal()
@@ -53,8 +53,8 @@ pkg p1, type Error interface { Error, Temporary }
pkg p1, type Error interface, Error() string
pkg p1, type Error interface, Temporary() bool
pkg p1, type FuncType func(int, int, string) (*B, error)
-pkg p1, type I interface, Get(string) int64
pkg p1, type I interface, Get //deprecated
+pkg p1, type I interface, Get(string) int64
pkg p1, type I interface, GetNamed(string) int64
pkg p1, type I interface, Name() string
pkg p1, type I interface, PackageTwoMeth()
@@ -63,9 +63,9 @@ pkg p1, type I interface, unexported methods
pkg p1, type MyInt int
pkg p1, type Namer interface { Name }
pkg p1, type Namer interface, Name() string
+pkg p1, type Private //deprecated
pkg p1, type Private interface, X()
pkg p1, type Private interface, unexported methods
-pkg p1, type Private //deprecated
pkg p1, type Public interface { X, Y }
pkg p1, type Public interface, X()
pkg p1, type Public interface, Y()
@@ -84,8 +84,8 @@ pkg p1, type TPtrExported struct
pkg p1, type TPtrExported struct, embedded *Embedded
pkg p1, type TPtrUnexported struct
pkg p1, type Time struct
-pkg p1, type URL struct
pkg p1, type URL //deprecated
+pkg p1, type URL struct
pkg p1, var Byte uint8
pkg p1, var ByteConv []uint8
pkg p1, var ByteFunc func(uint8) int32
@@ -97,8 +97,8 @@ pkg p1, var StrConv string
pkg p1, var V string
pkg p1, var V1 uint64
pkg p1, var V2 p2.Twoer
-pkg p1, var VError Error
pkg p1, var VError //deprecated
+pkg p1, var VError Error
pkg p1, var X I
pkg p1, var X0 int64
pkg p1, var Y int
diff --git a/src/cmd/api/testdata/src/pkg/p2/golden.txt b/src/cmd/api/testdata/src/pkg/p2/golden.txt
index 735d668166..0f7de5047e 100644
--- a/src/cmd/api/testdata/src/pkg/p2/golden.txt
+++ b/src/cmd/api/testdata/src/pkg/p2/golden.txt
@@ -1,8 +1,7 @@
-pkg p2, func F() string
pkg p2, func F //deprecated
+pkg p2, func F() string
pkg p2, func G() Twoer
pkg p2, func NewError(string) error
pkg p2, type Twoer interface { PackageTwoMeth }
-pkg p2, type Twoer interface, PackageTwoMeth()
pkg p2, type Twoer interface, PackageTwoMeth //deprecated
-
+pkg p2, type Twoer interface, PackageTwoMeth()
diff --git a/src/cmd/api/testdata/src/pkg/p4/golden.txt b/src/cmd/api/testdata/src/pkg/p4/golden.txt
index 1ceae17386..3195f4228d 100644
--- a/src/cmd/api/testdata/src/pkg/p4/golden.txt
+++ b/src/cmd/api/testdata/src/pkg/p4/golden.txt
@@ -1,6 +1,6 @@
-pkg p4, func NewPair[$0 interface{ M }, $1 interface{ ~int }]($0, $1) Pair[$0, $1]
-pkg p4, method (Pair[$0, $1]) Second() $1
-pkg p4, method (Pair[$0, $1]) First() $0
-pkg p4, type Pair[$0 interface{ M }, $1 interface{ ~int }] struct
-pkg p4, func Clone[$0 interface{ ~[]$1 }, $1 interface{}]($0) $0
pkg p4, func Clone //deprecated
+pkg p4, func Clone[$0 interface{ ~[]$1 }, $1 interface{}]($0) $0
+pkg p4, func NewPair[$0 interface{ M }, $1 interface{ ~int }]($0, $1) Pair[$0, $1]
+pkg p4, method (Pair[$0, $1]) First() $0
+pkg p4, method (Pair[$0, $1]) Second() $1
+pkg p4, type Pair[$0 interface{ M }, $1 interface{ ~int }] struct
diff --git a/src/cmd/asm/internal/arch/arm64.go b/src/cmd/asm/internal/arch/arm64.go
index e63601de64..d562e5907d 100644
--- a/src/cmd/asm/internal/arch/arm64.go
+++ b/src/cmd/asm/internal/arch/arm64.go
@@ -59,10 +59,10 @@ func jumpArm64(word string) bool {
var arm64SpecialOperand map[string]arm64.SpecialOperand
-// GetARM64SpecialOperand returns the internal representation of a special operand.
-func GetARM64SpecialOperand(name string) arm64.SpecialOperand {
+// ARM64SpecialOperand returns the internal representation of a special operand.
+func ARM64SpecialOperand(name string) arm64.SpecialOperand {
if arm64SpecialOperand == nil {
- // Generate the mapping automatically when the first time the function is called.
+ // Generate mapping when function is first called.
arm64SpecialOperand = map[string]arm64.SpecialOperand{}
for opd := arm64.SPOP_BEGIN; opd < arm64.SPOP_END; opd++ {
arm64SpecialOperand[opd.String()] = opd
@@ -195,149 +195,6 @@ func ARM64RegisterShift(reg, op, count int16) (int64, error) {
return int64(reg&31)<<16 | int64(op)<<22 | int64(uint16(count)), nil
}
-// ARM64RegisterExtension constructs an ARM64 register with extension or arrangement.
-func ARM64RegisterExtension(a *obj.Addr, ext string, reg, num int16, isAmount, isIndex bool) error {
- Rnum := (reg & 31) + int16(num<<5)
- if isAmount {
- if num < 0 || num > 7 {
- return errors.New("index shift amount is out of range")
- }
- }
- if reg <= arm64.REG_R31 && reg >= arm64.REG_R0 {
- if !isAmount {
- return errors.New("invalid register extension")
- }
- switch ext {
- case "UXTB":
- if a.Type == obj.TYPE_MEM {
- return errors.New("invalid shift for the register offset addressing mode")
- }
- a.Reg = arm64.REG_UXTB + Rnum
- case "UXTH":
- if a.Type == obj.TYPE_MEM {
- return errors.New("invalid shift for the register offset addressing mode")
- }
- a.Reg = arm64.REG_UXTH + Rnum
- case "UXTW":
- // effective address of memory is a base register value and an offset register value.
- if a.Type == obj.TYPE_MEM {
- a.Index = arm64.REG_UXTW + Rnum
- } else {
- a.Reg = arm64.REG_UXTW + Rnum
- }
- case "UXTX":
- if a.Type == obj.TYPE_MEM {
- return errors.New("invalid shift for the register offset addressing mode")
- }
- a.Reg = arm64.REG_UXTX + Rnum
- case "SXTB":
- if a.Type == obj.TYPE_MEM {
- return errors.New("invalid shift for the register offset addressing mode")
- }
- a.Reg = arm64.REG_SXTB + Rnum
- case "SXTH":
- if a.Type == obj.TYPE_MEM {
- return errors.New("invalid shift for the register offset addressing mode")
- }
- a.Reg = arm64.REG_SXTH + Rnum
- case "SXTW":
- if a.Type == obj.TYPE_MEM {
- a.Index = arm64.REG_SXTW + Rnum
- } else {
- a.Reg = arm64.REG_SXTW + Rnum
- }
- case "SXTX":
- if a.Type == obj.TYPE_MEM {
- a.Index = arm64.REG_SXTX + Rnum
- } else {
- a.Reg = arm64.REG_SXTX + Rnum
- }
- case "LSL":
- a.Index = arm64.REG_LSL + Rnum
- default:
- return errors.New("unsupported general register extension type: " + ext)
-
- }
- } else if reg <= arm64.REG_V31 && reg >= arm64.REG_V0 {
- switch ext {
- case "B8":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_8B & 15) << 5)
- case "B16":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_16B & 15) << 5)
- case "H4":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_4H & 15) << 5)
- case "H8":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_8H & 15) << 5)
- case "S2":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_2S & 15) << 5)
- case "S4":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_4S & 15) << 5)
- case "D1":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_1D & 15) << 5)
- case "D2":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_2D & 15) << 5)
- case "Q1":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_1Q & 15) << 5)
- case "B":
- if !isIndex {
- return nil
- }
- a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_B & 15) << 5)
- a.Index = num
- case "H":
- if !isIndex {
- return nil
- }
- a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_H & 15) << 5)
- a.Index = num
- case "S":
- if !isIndex {
- return nil
- }
- a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_S & 15) << 5)
- a.Index = num
- case "D":
- if !isIndex {
- return nil
- }
- a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_D & 15) << 5)
- a.Index = num
- default:
- return errors.New("unsupported simd register extension type: " + ext)
- }
- } else {
- return errors.New("invalid register and extension combination")
- }
- return nil
-}
-
// ARM64RegisterArrangement constructs an ARM64 vector register arrangement.
func ARM64RegisterArrangement(reg int16, name, arng string) (int64, error) {
var curQ, curSize uint16
diff --git a/src/cmd/asm/internal/arch/loong64.go b/src/cmd/asm/internal/arch/loong64.go
index d9e428d953..21263d3433 100644
--- a/src/cmd/asm/internal/arch/loong64.go
+++ b/src/cmd/asm/internal/arch/loong64.go
@@ -23,18 +23,6 @@ func jumpLoong64(word string) bool {
return false
}
-// IsLoong64MUL reports whether the op (as defined by an loong64.A* constant) is
-// one of the MUL/DIV/REM instructions that require special handling.
-func IsLoong64MUL(op obj.As) bool {
- switch op {
- case loong64.AMUL, loong64.AMULU, loong64.AMULV, loong64.AMULVU,
- loong64.ADIV, loong64.ADIVU, loong64.ADIVV, loong64.ADIVVU,
- loong64.AREM, loong64.AREMU, loong64.AREMV, loong64.AREMVU:
- return true
- }
- return false
-}
-
// IsLoong64RDTIME reports whether the op (as defined by an loong64.A*
// constant) is one of the RDTIMELW/RDTIMEHW/RDTIMED instructions that
// require special handling.
@@ -46,6 +34,14 @@ func IsLoong64RDTIME(op obj.As) bool {
return false
}
+func IsLoong64PRELD(op obj.As) bool {
+ switch op {
+ case loong64.APRELD, loong64.APRELDX:
+ return true
+ }
+ return false
+}
+
func IsLoong64AMO(op obj.As) bool {
return loong64.IsAtomicInst(op)
}
diff --git a/src/cmd/asm/internal/arch/riscv64.go b/src/cmd/asm/internal/arch/riscv64.go
index 27a66c5e63..891d2be0e5 100644
--- a/src/cmd/asm/internal/arch/riscv64.go
+++ b/src/cmd/asm/internal/arch/riscv64.go
@@ -11,11 +11,11 @@ package arch
import (
"cmd/internal/obj"
"cmd/internal/obj/riscv"
+ "fmt"
)
-// IsRISCV64AMO reports whether the op (as defined by a riscv.A*
-// constant) is one of the AMO instructions that requires special
-// handling.
+// IsRISCV64AMO reports whether op is an AMO instruction that requires
+// special handling.
func IsRISCV64AMO(op obj.As) bool {
switch op {
case riscv.ASCW, riscv.ASCD, riscv.AAMOSWAPW, riscv.AAMOSWAPD, riscv.AAMOADDW, riscv.AAMOADDD,
@@ -26,3 +26,59 @@ func IsRISCV64AMO(op obj.As) bool {
}
return false
}
+
+// IsRISCV64VTypeI reports whether op is a vtype immediate instruction that
+// requires special handling.
+func IsRISCV64VTypeI(op obj.As) bool {
+ return op == riscv.AVSETVLI || op == riscv.AVSETIVLI
+}
+
+// IsRISCV64CSRO reports whether the op is an instruction that uses
+// CSR symbolic names and whether that instruction expects a register
+// or an immediate source operand.
+func IsRISCV64CSRO(op obj.As) (imm bool, ok bool) {
+ switch op {
+ case riscv.ACSRRCI, riscv.ACSRRSI, riscv.ACSRRWI:
+ imm = true
+ fallthrough
+ case riscv.ACSRRC, riscv.ACSRRS, riscv.ACSRRW:
+ ok = true
+ }
+ return
+}
+
+var riscv64SpecialOperand map[string]riscv.SpecialOperand
+
+// RISCV64SpecialOperand returns the internal representation of a special operand.
+func RISCV64SpecialOperand(name string) riscv.SpecialOperand {
+ if riscv64SpecialOperand == nil {
+ // Generate mapping when function is first called.
+ riscv64SpecialOperand = map[string]riscv.SpecialOperand{}
+ for opd := riscv.SPOP_RVV_BEGIN; opd < riscv.SPOP_RVV_END; opd++ {
+ riscv64SpecialOperand[opd.String()] = opd
+ }
+ // Add the CSRs
+ for csrCode, csrName := range riscv.CSRs {
+ // The set of RVV special operand names and the set of CSR special operands
+ // names are disjoint and so can safely share a single namespace. However,
+ // it's possible that a future update to the CSRs in inst.go could introduce
+ // a conflict. This check ensures that such a conflict does not go
+ // unnoticed.
+ if _, ok := riscv64SpecialOperand[csrName]; ok {
+ panic(fmt.Sprintf("riscv64 special operand %q redefined", csrName))
+ }
+ riscv64SpecialOperand[csrName] = riscv.SpecialOperand(int(csrCode) + int(riscv.SPOP_CSR_BEGIN))
+ }
+ }
+ if opd, ok := riscv64SpecialOperand[name]; ok {
+ return opd
+ }
+ return riscv.SPOP_END
+}
+
+// RISCV64ValidateVectorType reports whether the given configuration is a
+// valid vector type.
+func RISCV64ValidateVectorType(vsew, vlmul, vtail, vmask int64) error {
+ _, err := riscv.EncodeVectorType(vsew, vlmul, vtail, vmask)
+ return err
+}
diff --git a/src/cmd/asm/internal/asm/asm.go b/src/cmd/asm/internal/asm/asm.go
index 9fc7fa5598..389307af29 100644
--- a/src/cmd/asm/internal/asm/asm.go
+++ b/src/cmd/asm/internal/asm/asm.go
@@ -654,6 +654,12 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
prog.RegTo2 = a[1].Reg
break
}
+
+ if arch.IsLoong64PRELD(op) {
+ prog.From = a[0]
+ prog.AddRestSource(a[1])
+ break
+ }
}
prog.From = a[0]
prog.To = a[1]
@@ -670,6 +676,11 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
prog.From = a[0]
prog.To = a[1]
prog.RegTo2 = a[2].Reg
+
+ case arch.IsLoong64PRELD(op):
+ prog.From = a[0]
+ prog.AddRestSourceArgs([]obj.Addr{a[1], a[2]})
+
default:
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
@@ -771,6 +782,21 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
prog.RegTo2 = a[2].Reg
break
}
+ // RISCV64 instructions that reference CSRs with symbolic names.
+ if isImm, ok := arch.IsRISCV64CSRO(op); ok {
+ if a[0].Type != obj.TYPE_CONST && isImm {
+ p.errorf("invalid value for first operand to %s instruction, must be a 5 bit unsigned immediate", op)
+ return
+ }
+ if a[1].Type != obj.TYPE_SPECIAL {
+ p.errorf("invalid value for second operand to %s instruction, must be a CSR name", op)
+ return
+ }
+ prog.AddRestSourceArgs([]obj.Addr{a[1]})
+ prog.From = a[0]
+ prog.To = a[2]
+ break
+ }
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
prog.To = a[2]
@@ -915,6 +941,19 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
prog.To = a[5]
break
}
+ if p.arch.Family == sys.RISCV64 && arch.IsRISCV64VTypeI(op) {
+ prog.From = a[0]
+ vsew := p.getSpecial(prog, op, &a[1])
+ vlmul := p.getSpecial(prog, op, &a[2])
+ vtail := p.getSpecial(prog, op, &a[3])
+ vmask := p.getSpecial(prog, op, &a[4])
+ if err := arch.RISCV64ValidateVectorType(vsew, vlmul, vtail, vmask); err != nil {
+ p.errorf("invalid vtype: %v", err)
+ }
+ prog.AddRestSourceArgs([]obj.Addr{a[1], a[2], a[3], a[4]})
+ prog.To = a[5]
+ break
+ }
fallthrough
default:
p.errorf("can't handle %s instruction with %d operands", op, len(a))
@@ -950,14 +989,6 @@ func (p *Parser) getConstant(prog *obj.Prog, op obj.As, addr *obj.Addr) int64 {
return addr.Offset
}
-// getImmediate checks that addr represents an immediate constant and returns its value.
-func (p *Parser) getImmediate(prog *obj.Prog, op obj.As, addr *obj.Addr) int64 {
- if addr.Type != obj.TYPE_CONST || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 {
- p.errorf("%s: expected immediate constant; found %s", op, obj.Dconv(prog, addr))
- }
- return addr.Offset
-}
-
// getRegister checks that addr represents a register and returns its value.
func (p *Parser) getRegister(prog *obj.Prog, op obj.As, addr *obj.Addr) int16 {
if addr.Type != obj.TYPE_REG || addr.Offset != 0 || addr.Name != 0 || addr.Index != 0 {
@@ -965,3 +996,11 @@ func (p *Parser) getRegister(prog *obj.Prog, op obj.As, addr *obj.Addr) int16 {
}
return addr.Reg
}
+
+// getSpecial checks that addr represents a special operand and returns its value.
+func (p *Parser) getSpecial(prog *obj.Prog, op obj.As, addr *obj.Addr) int64 {
+ if addr.Type != obj.TYPE_SPECIAL || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 {
+ p.errorf("%s: expected special operand; found %s", op, obj.Dconv(prog, addr))
+ }
+ return addr.Offset
+}
diff --git a/src/cmd/asm/internal/asm/endtoend_test.go b/src/cmd/asm/internal/asm/endtoend_test.go
index 6e1aa1cd95..afaf02815f 100644
--- a/src/cmd/asm/internal/asm/endtoend_test.go
+++ b/src/cmd/asm/internal/asm/endtoend_test.go
@@ -465,9 +465,15 @@ func TestLOONG64Encoder(t *testing.T) {
testEndToEnd(t, "loong64", "loong64enc1")
testEndToEnd(t, "loong64", "loong64enc2")
testEndToEnd(t, "loong64", "loong64enc3")
+ testEndToEnd(t, "loong64", "loong64enc4")
+ testEndToEnd(t, "loong64", "loong64enc5")
testEndToEnd(t, "loong64", "loong64")
}
+func TestLOONG64Errors(t *testing.T) {
+ testErrors(t, "loong64", "loong64error")
+}
+
func TestPPC64EndToEnd(t *testing.T) {
defer func(old int) { buildcfg.GOPPC64 = old }(buildcfg.GOPPC64)
for _, goppc64 := range []int{8, 9, 10} {
@@ -487,6 +493,10 @@ func TestRISCVErrors(t *testing.T) {
testErrors(t, "riscv64", "riscv64error")
}
+func TestRISCVValidation(t *testing.T) {
+ testErrors(t, "riscv64", "riscv64validation")
+}
+
func TestS390XEndToEnd(t *testing.T) {
testEndToEnd(t, "s390x", "s390x")
}
diff --git a/src/cmd/asm/internal/asm/parse.go b/src/cmd/asm/internal/asm/parse.go
index 638f4e2fc4..545f6c7553 100644
--- a/src/cmd/asm/internal/asm/parse.go
+++ b/src/cmd/asm/internal/asm/parse.go
@@ -21,6 +21,7 @@ import (
"cmd/asm/internal/lex"
"cmd/internal/obj"
"cmd/internal/obj/arm64"
+ "cmd/internal/obj/riscv"
"cmd/internal/obj/x86"
"cmd/internal/objabi"
"cmd/internal/src"
@@ -398,16 +399,21 @@ func (p *Parser) operand(a *obj.Addr) {
tok := p.next()
name := tok.String()
if tok.ScanToken == scanner.Ident && !p.atStartOfRegister(name) {
+ // See if this is an architecture specific special operand.
switch p.arch.Family {
case sys.ARM64:
- // arm64 special operands.
- if opd := arch.GetARM64SpecialOperand(name); opd != arm64.SPOP_END {
+ if opd := arch.ARM64SpecialOperand(name); opd != arm64.SPOP_END {
a.Type = obj.TYPE_SPECIAL
a.Offset = int64(opd)
- break
}
- fallthrough
- default:
+ case sys.RISCV64:
+ if opd := arch.RISCV64SpecialOperand(name); opd != riscv.SPOP_END {
+ a.Type = obj.TYPE_SPECIAL
+ a.Offset = int64(opd)
+ }
+ }
+
+ if a.Type != obj.TYPE_SPECIAL {
// We have a symbol. Parse $sym±offset(symkind)
p.symbolReference(a, p.qualifySymbol(name), prefix)
}
@@ -769,7 +775,7 @@ func (p *Parser) registerExtension(a *obj.Addr, name string, prefix rune) {
switch p.arch.Family {
case sys.ARM64:
- err := arch.ARM64RegisterExtension(a, ext, reg, num, isAmount, isIndex)
+ err := arm64.ARM64RegisterExtension(a, ext, reg, num, isAmount, isIndex)
if err != nil {
p.errorf("%v", err)
}
diff --git a/src/cmd/asm/internal/asm/testdata/amd64enc_extra.s b/src/cmd/asm/internal/asm/testdata/amd64enc_extra.s
index 08cb20c707..197b2ce628 100644
--- a/src/cmd/asm/internal/asm/testdata/amd64enc_extra.s
+++ b/src/cmd/asm/internal/asm/testdata/amd64enc_extra.s
@@ -1059,5 +1059,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
RDPID DX // f30fc7fa
RDPID R11 // f3410fc7fb
+ ENDBR64 // f30f1efa
+
// End of tests.
RET
diff --git a/src/cmd/asm/internal/asm/testdata/arm64.s b/src/cmd/asm/internal/asm/testdata/arm64.s
index 14a1c9141d..109a3d8316 100644
--- a/src/cmd/asm/internal/asm/testdata/arm64.s
+++ b/src/cmd/asm/internal/asm/testdata/arm64.s
@@ -1888,4 +1888,18 @@ next:
DC CIGDVAC, R25 // b97e0bd5
DC CVAP, R26 // 3a7c0bd5
DC CVADP, R27 // 3b7d0bd5
+
+// Branch Target Identification
+ BTI C // 5f2403d5
+ BTI J // 9f2403d5
+ BTI JC // df2403d5
+
+// Pointer Authentication Codes (PAC)
+ PACIASP // 3f2303d5
+ AUTIASP // bf2303d5
+ PACIBSP // 7f2303d5
+ AUTIBSP // ff2303d5
+ AUTIA1716 // 9f2103d5
+ AUTIB1716 // df2103d5
+
END
diff --git a/src/cmd/asm/internal/asm/testdata/arm64error.s b/src/cmd/asm/internal/asm/testdata/arm64error.s
index 3ac8788424..ce88e3ca54 100644
--- a/src/cmd/asm/internal/asm/testdata/arm64error.s
+++ b/src/cmd/asm/internal/asm/testdata/arm64error.s
@@ -420,4 +420,12 @@ TEXT errors(SB),$0
AESE V1.B16, V2.B8 // ERROR "invalid arrangement"
SHA256SU1 V1.S4, V2.B16, V3.S4 // ERROR "invalid arrangement"
SHA1H V1.B16, V2.B16 // ERROR "invalid operands"
+ BTI // ERROR "missing operand"
+ BTI PLDL1KEEP // ERROR "illegal argument"
+ PACIASP C // ERROR "illegal combination"
+ AUTIASP R2 // ERROR "illegal combination"
+ PACIBSP R0 // ERROR "illegal combination"
+ AUTIBSP C // ERROR "illegal combination"
+ AUTIA1716 $45 // ERROR "illegal combination"
+ AUTIB1716 R0 // ERROR "illegal combination"
RET
diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc1.s b/src/cmd/asm/internal/asm/testdata/loong64enc1.s
index 4a88aca031..fd86db7a4f 100644
--- a/src/cmd/asm/internal/asm/testdata/loong64enc1.s
+++ b/src/cmd/asm/internal/asm/testdata/loong64enc1.s
@@ -6,12 +6,16 @@
TEXT asmtest(SB),DUPOK|NOSPLIT,$0
lable1:
- BFPT 1(PC) // 00050048
- BFPT lable1 // BFPT 2 //1ffdff4b
+ BFPT 1(PC) // 00050048
+ BFPT lable1 // BFPT 2 // 1ffdff4b
+ BFPT FCC0, lable1 // BFPT FCC0, 2 // 1ff9ff4b
+ BFPT FCC7, lable1 // BFPT FCC7, 2 // fff5ff4b
lable2:
- BFPF 1(PC) // 00040048
- BFPF lable2 // BFPF 4 // 1ffcff4b
+ BFPF 1(PC) // 00040048
+ BFPF lable2 // BFPF 6 // 1ffcff4b
+ BFPF FCC0, lable2 // BFPF FCC0, 6 // 1ff8ff4b
+ BFPF FCC7, lable2 // BFPF FCC7, 6 // fff4ff4b
// relocation in play so the assembled offset should be 0
JMP foo(SB) // 00000050
@@ -107,8 +111,8 @@ lable2:
MOVV $4(R4), R5 // 8510c002
MOVW $-1, R4 // 04fcff02
MOVV $-1, R4 // 04fcff02
- MOVW $1, R4 // 0404c002
- MOVV $1, R4 // 0404c002
+ MOVW $1, R4 // 04048003
+ MOVV $1, R4 // 04048003
ADD $-1, R4, R5 // 85fcbf02
ADD $-1, R4 // 84fcbf02
ADDV $-1, R4, R5 // 85fcff02
@@ -256,6 +260,35 @@ lable2:
MOVV FCC0, R4 // 04dc1401
MOVV R4, FCC0 // 80d81401
+ // LDPTR.{W/D} and STPTR.{W/D} instructions
+ MOVWP R5, -32768(R4) // 85008025
+ MOVWP R5, 32764(R4) // 85fc7f25
+ MOVWP R5, 32(R4) // 85200025
+ MOVWP R5, 4(R4) // 85040025
+ MOVWP R5, (R4) // 85000025
+ MOVVP R5, -32768(R4) // 85008027
+ MOVVP R5, 32764(R4) // 85fc7f27
+ MOVVP R5, 32(R4) // 85200027
+ MOVVP R5, 4(R4) // 85040027
+ MOVVP R5, (R4) // 85000027
+ MOVWP -32768(R5), R4 // a4008024
+ MOVWP 32764(R5), R4 // a4fc7f24
+ MOVWP 32(R5), R4 // a4200024
+ MOVWP 4(R5), R4 // a4040024
+ MOVWP (R5), R4 // a4000024
+ MOVVP -32768(R5), R4 // a4008026
+ MOVVP 32764(R5), R4 // a4fc7f26
+ MOVVP 32(R5), R4 // a4200026
+ MOVVP 4(R5), R4 // a4040026
+ MOVVP (R5), R4 // a4000026
+
+ // ADDU16I.D instruction
+ ADDV16 $(-32768<<16), R4, R5 // ADDV16 $-2147483648, R4, R5 // 85000012
+ ADDV16 $(0<<16), R4, R5 // ADDV16 $0, R4, R5 // 85000010
+ ADDV16 $(8<<16), R4, R5 // ADDV16 $524288, R4, R5 // 85200010
+ ADDV16 $(32767<<16), R4, R5 // ADDV16 $2147418112, R4, R5 // 85fcff11
+ ADDV16 $(16<<16), R4 // ADDV16 $1048576, R4 // 84400010
+
// Loong64 atomic memory access instructions
AMSWAPB R14, (R13), R12 // ac395c38
AMSWAPH R14, (R13), R12 // acb95c38
@@ -346,6 +379,15 @@ lable2:
FTINTVF F0, F1 // 01241b01
FTINTVD F0, F1 // 01281b01
+ FMAXAF F4, F5, F6 // a6900c01
+ FMAXAF F4, F5 // a5900c01
+ FMAXAD F4, F5, F6 // a6100d01
+ FMAXAD F4, F5 // a5100d01
+ FMINAF F4, F5, F6 // a6900e01
+ FMINAF F4, F5 // a5900e01
+ FMINAD F4, F5, F6 // a6100f01
+ FMINAD F4, F5 // a5100f01
+
FTINTRMWF F0, F2 // 02041a01
FTINTRMWD F0, F2 // 02081a01
FTINTRMVF F0, F2 // 02241a01
@@ -363,6 +405,10 @@ lable2:
FTINTRNEVF F0, F2 // 02e41a01
FTINTRNEVD F0, F2 // 02e81a01
+ // FSEL instruction
+ FSEL FCC0, F1, F2, F3 // 4304000d
+ FSEL FCC1, F1, F2 // 4284000d
+
// LDX.{B,BU,H,HU,W,WU,D} instructions
MOVB (R14)(R13), R12 // cc350038
MOVBU (R14)(R13), R12 // cc352038
@@ -497,6 +543,32 @@ lable2:
VMOVQ V3.W[1], V7.W4 // 67e4f772
VMOVQ V4.V[0], V6.V2 // 86f0f772
+ // Load data from memory and broadcast to each element of a vector register: VMOVQ offset(Rj), .
+ VMOVQ (R4), V0.B16 // 80008030
+ VMOVQ 1(R4), V0.B16 // 80048030
+ VMOVQ -3(R4), V0.B16 // 80f4bf30
+ VMOVQ (R4), V1.H8 // 81004030
+ VMOVQ 2(R4), V1.H8 // 81044030
+ VMOVQ -6(R4), V1.H8 // 81f45f30
+ VMOVQ (R4), V2.W4 // 82002030
+ VMOVQ 8(R4), V2.W4 // 82082030
+ VMOVQ -12(R4), V2.W4 // 82f42f30
+ VMOVQ (R4), V3.V2 // 83001030
+ VMOVQ 24(R4), V3.V2 // 830c1030
+ VMOVQ -16(R4), V3.V2 // 83f81730
+ XVMOVQ (R4), X0.B32 // 80008032
+ XVMOVQ 1(R4), X0.B32 // 80048032
+ XVMOVQ -5(R4), X0.B32 // 80ecbf32
+ XVMOVQ (R4), X1.H16 // 81004032
+ XVMOVQ 2(R4), X1.H16 // 81044032
+ XVMOVQ -10(R4), X1.H16 // 81ec5f32
+ XVMOVQ (R4), X2.W8 // 82002032
+ XVMOVQ 8(R4), X2.W8 // 82082032
+ XVMOVQ -20(R4), X2.W8 // 82ec2f32
+ XVMOVQ (R4), X3.V4 // 83001032
+ XVMOVQ 24(R4), X3.V4 // 830c1032
+ XVMOVQ -24(R4), X3.V4 // 83f41732
+
// VSEQ{B,H,W,V}, XVSEQ{B,H,W,V} instruction
VSEQB V1, V2, V3 // 43040070
VSEQH V1, V2, V3 // 43840070
@@ -506,6 +578,16 @@ lable2:
XVSEQH X3, X2, X4 // 448c0074
XVSEQW X3, X2, X4 // 440c0174
XVSEQV X3, X2, X4 // 448c0174
+ VSEQB $0, V2, V3 // 43008072
+ VSEQH $1, V2, V3 // 43848072
+ VSEQW $8, V2, V3 // 43208172
+ VSEQV $15, V2, V3 // 43bc8172
+ VSEQV $-15, V2, V3 // 43c48172
+ XVSEQB $0, X2, X4 // 44008076
+ XVSEQH $3, X2, X4 // 448c8076
+ XVSEQW $12, X2, X4 // 44308176
+ XVSEQV $15, X2, X4 // 44bc8176
+ XVSEQV $-15, X2, X4 // 44c48176
// VPCNT{B,H,W,V}, XVPCNT{B,H,W,V} instruction
VPCNTB V1, V2 // 22209c72
@@ -516,3 +598,554 @@ lable2:
XVPCNTH X3, X2 // 62249c76
XVPCNTW X3, X2 // 62289c76
XVPCNTV X3, X2 // 622c9c76
+
+ // VANDV,VORV,VXORV,VNORV,VANDNV,VORNV
+ VANDV V1, V2, V3 // 43042671
+ VORV V1, V2, V3 // 43842671
+ VXORV V1, V2, V3 // 43042771
+ VNORV V1, V2, V3 // 43842771
+ VANDNV V1, V2, V3 // 43042871
+ VORNV V1, V2, V3 // 43842871
+ VANDV V1, V2 // 42042671
+ VORV V1, V2 // 42842671
+ VXORV V1, V2 // 42042771
+ VNORV V1, V2 // 42842771
+ VANDNV V1, V2 // 42042871
+ VORNV V1, V2 // 42842871
+
+ // VANDB,VORB,VXORB,VNORB
+ VANDB $0, V2, V3 // 4300d073
+ VORB $64, V2, V3 // 4300d573
+ VXORB $128, V2, V3 // 4300da73
+ VNORB $255, V2, V3 // 43fcdf73
+ VANDB $0, V2 // 4200d073
+ VORB $64, V2 // 4200d573
+ VXORB $128, V2 // 4200da73
+ VNORB $255, V2 // 42fcdf73
+
+ // XVANDV,XVORV,XVXORV,XVNORV,XVANDNV,XVORNV
+ XVANDV X1, X2, X3 // 43042675
+ XVORV X1, X2, X3 // 43842675
+ XVXORV X1, X2, X3 // 43042775
+ XVNORV X1, X2, X3 // 43842775
+ XVANDNV X1, X2, X3 // 43042875
+ XVORNV X1, X2, X3 // 43842875
+ XVANDV X1, X2 // 42042675
+ XVORV X1, X2 // 42842675
+ XVXORV X1, X2 // 42042775
+ XVNORV X1, X2 // 42842775
+ XVANDNV X1, X2 // 42042875
+ XVORNV X1, X2 // 42842875
+
+ // XVANDB,XVORB,XVXORB,XVNORB
+ XVANDB $0, X2, X3 // 4300d077
+ XVORB $1, X2, X3 // 4304d477
+ XVXORB $127, X2, X3 // 43fcd977
+ XVNORB $255, X2, X3 // 43fcdf77
+ XVANDB $0, X2 // 4200d077
+ XVORB $1, X2 // 4204d477
+ XVXORB $127, X2 // 42fcd977
+ XVNORB $255, X2 // 42fcdf77
+
+ // MOVV C_DCON12_0, r
+ MOVV $0x7a90000000000000, R4 // MOVV $8831558869273542656, R4 // 04a41e03
+ MOVV $0xea90000000000000, R4 // MOVV $-1544734672188080128, R4 // 04a43a03
+
+ // MOVV C_UCON, r
+ MOVV $0x54321000, R4 // MOVV $1412567040, R4 // 2464a814
+ MOVV $0xffffffff8432f000, R4 // MOVV $-2077036544, R4 // e4650815
+
+ // MOVV C_ADDCON, r
+ MOVV $0xfffffffffffff821, R4 // MOVV $-2015, R4 // 0484e002
+
+ // MOVV C_ANDCON, r
+ MOVV $0x821, R4 // MOVV $2081, R4 // 0484a003
+
+ // ADDV C_SCON, [r1], r2
+ ADDV $0x321, R4 // ADDV $801, R4 // 8484cc02
+ ADDV $0x321, R5, R4 // ADDV $801, R5, R4 // a484cc02
+ ADDV $0xfffffffffffffc21, R4 // ADDV $-991, R4 // 8484f002
+ ADDV $0xfffffffffffffc21, R5, R4 // ADDV $-991, R5, R4 // a484f002
+
+ // AND C_SCON, [r1], r2
+ AND $0x321, R4 // AND $801, R4 // 84844c03
+ AND $0x321, R5, R4 // AND $801, R5, R4 // a4844c03
+
+ // [X]{VSLL/VSRL/VSRA/VROTR}{B,H,W,V} instructions
+ VSLLB V1, V2, V3 // 4304e870
+ VSLLH V1, V2, V3 // 4384e870
+ VSLLW V1, V2, V3 // 4304e970
+ VSLLV V1, V2, V3 // 4384e970
+ VSRLB V1, V2, V3 // 4304ea70
+ VSRLH V1, V2, V3 // 4384ea70
+ VSRLW V1, V2, V3 // 4304eb70
+ VSRLV V1, V2, V3 // 4384eb70
+ VSRAB V1, V2, V3 // 4304ec70
+ VSRAH V1, V2, V3 // 4384ec70
+ VSRAW V1, V2, V3 // 4304ed70
+ VSRAV V1, V2, V3 // 4384ed70
+ VROTRB V1, V2, V3 // 4304ee70
+ VROTRH V1, V2, V3 // 4384ee70
+ VROTRW V1, V2, V3 // 4304ef70
+ VROTRV V1, V2, V3 // 4384ef70
+ XVSLLB X3, X2, X1 // 410ce874
+ XVSLLH X3, X2, X1 // 418ce874
+ XVSLLW X3, X2, X1 // 410ce974
+ XVSLLV X3, X2, X1 // 418ce974
+ XVSRLB X3, X2, X1 // 410cea74
+ XVSRLH X3, X2, X1 // 418cea74
+ XVSRLW X3, X2, X1 // 410ceb74
+ XVSRLV X3, X2, X1 // 418ceb74
+ XVSRAB X3, X2, X1 // 410cec74
+ XVSRAH X3, X2, X1 // 418cec74
+ XVSRAW X3, X2, X1 // 410ced74
+ XVSRAV X3, X2, X1 // 418ced74
+ XVROTRB X3, X2, X1 // 410cee74
+ XVROTRH X3, X2, X1 // 418cee74
+ XVROTRW X3, X2, X1 // 410cef74
+ XVROTRV X3, X2, X1 // 418cef74
+ VSLLB $0, V1, V2 // 22202c73
+ VSLLB $7, V1, V2 // 223c2c73
+ VSLLB $5, V1 // 21342c73
+ VSLLH $0, V1, V2 // 22402c73
+ VSLLH $15, V1, V2 // 227c2c73
+ VSLLH $10, V1 // 21682c73
+ VSLLW $0, V1, V2 // 22802c73
+ VSLLW $31, V1, V2 // 22fc2c73
+ VSLLW $11, V1 // 21ac2c73
+ VSLLV $0, V1, V2 // 22002d73
+ VSLLV $63, V1, V2 // 22fc2d73
+ VSLLV $30, V1 // 21782d73
+ VSRLB $0, V1, V2 // 22203073
+ VSRLB $7, V1, V2 // 223c3073
+ VSRLB $4, V1 // 21303073
+ VSRLH $0, V1, V2 // 22403073
+ VSRLH $15, V1, V2 // 227c3073
+ VSRLH $9, V1 // 21643073
+ VSRLW $0, V1, V2 // 22803073
+ VSRLW $31, V1, V2 // 22fc3073
+ VSRLW $16, V1 // 21c03073
+ VSRLV $0, V1, V2 // 22003173
+ VSRLV $63, V1, V2 // 22fc3173
+ VSRLV $40, V1 // 21a03173
+ VSRAB $0, V1, V2 // 22203473
+ VSRAB $7, V1, V2 // 223c3473
+ VSRAB $6, V1 // 21383473
+ VSRAH $0, V1, V2 // 22403473
+ VSRAH $15, V1, V2 // 227c3473
+ VSRAH $8, V1 // 21603473
+ VSRAW $0, V1, V2 // 22803473
+ VSRAW $31, V1, V2 // 22fc3473
+ VSRAW $12, V1 // 21b03473
+ VSRAV $0, V1, V2 // 22003573
+ VSRAV $63, V1, V2 // 22fc3573
+ VSRAV $50, V1 // 21c83573
+ VROTRB $0, V1, V2 // 2220a072
+ VROTRB $7, V1, V2 // 223ca072
+ VROTRB $3, V1 // 212ca072
+ VROTRH $0, V1, V2 // 2240a072
+ VROTRH $15, V1, V2 // 227ca072
+ VROTRH $5, V1 // 2154a072
+ VROTRW $0, V1, V2 // 2280a072
+ VROTRW $31, V1, V2 // 22fca072
+ VROTRW $18, V1 // 21c8a072
+ VROTRV $0, V1, V2 // 2200a172
+ VROTRV $63, V1, V2 // 22fca172
+ VROTRV $52, V1 // 21d0a172
+ XVSLLB $0, X2, X1 // 41202c77
+ XVSLLB $7, X2, X1 // 413c2c77
+ XVSLLB $4, X2 // 42302c77
+ XVSLLH $0, X2, X1 // 41402c77
+ XVSLLH $15, X2, X1 // 417c2c77
+ XVSLLH $8, X2 // 42602c77
+ XVSLLW $0, X2, X1 // 41802c77
+ XVSLLW $31, X2, X1 // 41fc2c77
+ XVSLLW $13, X2 // 42b42c77
+ XVSLLV $0, X2, X1 // 41002d77
+ XVSLLV $63, X2, X1 // 41fc2d77
+ XVSLLV $36, X2 // 42902d77
+ XVSRLB $0, X2, X1 // 41203077
+ XVSRLB $7, X2, X1 // 413c3077
+ XVSRLB $5, X2 // 42343077
+ XVSRLH $0, X2, X1 // 41403077
+ XVSRLH $15, X2, X1 // 417c3077
+ XVSRLH $9, X2 // 42643077
+ XVSRLW $0, X2, X1 // 41803077
+ XVSRLW $31, X2, X1 // 41fc3077
+ XVSRLW $14, X2 // 42b83077
+ XVSRLV $0, X2, X1 // 41003177
+ XVSRLV $63, X2, X1 // 41fc3177
+ XVSRLV $45, X2 // 42b43177
+ XVSRAB $0, X2, X1 // 41203477
+ XVSRAB $7, X2, X1 // 413c3477
+ XVSRAB $6, X2 // 42383477
+ XVSRAH $0, X2, X1 // 41403477
+ XVSRAH $15, X2, X1 // 417c3477
+ XVSRAH $10, X2 // 42683477
+ XVSRAW $0, X2, X1 // 41803477
+ XVSRAW $31, X2, X1 // 41fc3477
+ XVSRAW $16, X2 // 42c03477
+ XVSRAV $0, X2, X1 // 41003577
+ XVSRAV $63, X2, X1 // 41fc3577
+ XVSRAV $48, X2 // 42c03577
+ XVROTRB $0, X2, X1 // 4120a076
+ XVROTRB $7, X2, X1 // 413ca076
+ XVROTRB $3, X2 // 422ca076
+ XVROTRH $0, X2, X1 // 4140a076
+ XVROTRH $15, X2, X1 // 417ca076
+ XVROTRH $13, X2 // 4274a076
+ XVROTRW $0, X2, X1 // 4180a076
+ XVROTRW $31, X2, X1 // 41fca076
+ XVROTRW $24, X2 // 42e0a076
+ XVROTRV $0, X2, X1 // 4100a176
+ XVROTRV $63, X2, X1 // 41fca176
+ XVROTRV $52, X2 // 42d0a176
+
+ // [X]VADD{B,H,W,V,Q}, [X]VSUB{B,H,W,V,Q} instructions
+ VADDB V1, V2, V3 // 43040a70
+ VADDH V1, V2, V3 // 43840a70
+ VADDW V1, V2, V3 // 43040b70
+ VADDV V1, V2, V3 // 43840b70
+ VADDQ V1, V2, V3 // 43042d71
+ VSUBB V1, V2, V3 // 43040c70
+ VSUBH V1, V2, V3 // 43840c70
+ VSUBW V1, V2, V3 // 43040d70
+ VSUBV V1, V2, V3 // 43840d70
+ VSUBQ V1, V2, V3 // 43842d71
+ XVADDB X3, X2, X1 // 410c0a74
+ XVADDH X3, X2, X1 // 418c0a74
+ XVADDW X3, X2, X1 // 410c0b74
+ XVADDV X3, X2, X1 // 418c0b74
+ XVADDQ X3, X2, X1 // 410c2d75
+ XVSUBB X3, X2, X1 // 410c0c74
+ XVSUBH X3, X2, X1 // 418c0c74
+ XVSUBW X3, X2, X1 // 410c0d74
+ XVSUBV X3, X2, X1 // 418c0d74
+ XVSUBQ X3, X2, X1 // 418c2d75
+
+ // [X]VADD{B,H,W,V}U, [X]VSUB{B,H,W,V}U instructions
+ VADDBU $1, V2, V1 // 41048a72
+ VADDHU $2, V2, V1 // 41888a72
+ VADDWU $3, V2, V1 // 410c8b72
+ VADDVU $4, V2, V1 // 41908b72
+ VSUBBU $5, V2, V1 // 41148c72
+ VSUBHU $6, V2, V1 // 41988c72
+ VSUBWU $7, V2, V1 // 411c8d72
+ VSUBVU $8, V2, V1 // 41a08d72
+ XVADDBU $9, X1, X2 // 22248a76
+ XVADDHU $10, X1, X2 // 22a88a76
+ XVADDWU $11, X1, X2 // 222c8b76
+ XVADDVU $12, X1, X2 // 22b08b76
+ XVSUBBU $13, X1, X2 // 22348c76
+ XVSUBHU $14, X1, X2 // 22b88c76
+ XVSUBWU $15, X1, X2 // 223c8d76
+ XVSUBVU $16, X1, X2 // 22c08d76
+
+ // [X]VILV{L/H}{B,H,W,V} instructions
+ VILVLB V1, V2, V3 // 43041a71
+ VILVLH V1, V2, V3 // 43841a71
+ VILVLW V1, V2, V3 // 43041b71
+ VILVLV V1, V2, V3 // 43841b71
+ VILVHB V1, V2, V3 // 43041c71
+ VILVHH V1, V2, V3 // 43841c71
+ VILVHW V1, V2, V3 // 43041d71
+ VILVHV V1, V2, V3 // 43841d71
+ XVILVLB X3, X2, X1 // 410c1a75
+ XVILVLH X3, X2, X1 // 418c1a75
+ XVILVLW X3, X2, X1 // 410c1b75
+ XVILVLV X3, X2, X1 // 418c1b75
+ XVILVHB X3, X2, X1 // 410c1c75
+ XVILVHH X3, X2, X1 // 418c1c75
+ XVILVHW X3, X2, X1 // 410c1d75
+ XVILVHV X3, X2, X1 // 418c1d75
+
+ // [X]VMUL{B/H/W/V} and [X]VMUH{B/H/W/V}[U] instructions
+ VMULB V1, V2, V3 // 43048470
+ VMULH V1, V2, V3 // 43848470
+ VMULW V1, V2, V3 // 43048570
+ VMULV V1, V2, V3 // 43848570
+ VMUHB V1, V2, V3 // 43048670
+ VMUHH V1, V2, V3 // 43848670
+ VMUHW V1, V2, V3 // 43048770
+ VMUHV V1, V2, V3 // 43848770
+ VMUHBU V1, V2, V3 // 43048870
+ VMUHHU V1, V2, V3 // 43848870
+ VMUHWU V1, V2, V3 // 43048970
+ VMUHVU V1, V2, V3 // 43848970
+ XVMULB X3, X2, X1 // 410c8474
+ XVMULH X3, X2, X1 // 418c8474
+ XVMULW X3, X2, X1 // 410c8574
+ XVMULV X3, X2, X1 // 418c8574
+ XVMUHB X3, X2, X1 // 410c8674
+ XVMUHH X3, X2, X1 // 418c8674
+ XVMUHW X3, X2, X1 // 410c8774
+ XVMUHV X3, X2, X1 // 418c8774
+ XVMUHBU X3, X2, X1 // 410c8874
+ XVMUHHU X3, X2, X1 // 418c8874
+ XVMUHWU X3, X2, X1 // 410c8974
+ XVMUHVU X3, X2, X1 // 418c8974
+
+ // [X]VDIV{B/H/W/V}[U] and [X]VMOD{B/H/W/V}[U] instructions
+ VDIVB V1, V2, V3 // 4304e070
+ VDIVH V1, V2, V3 // 4384e070
+ VDIVW V1, V2, V3 // 4304e170
+ VDIVV V1, V2, V3 // 4384e170
+ VDIVBU V1, V2, V3 // 4304e470
+ VDIVHU V1, V2, V3 // 4384e470
+ VDIVWU V1, V2, V3 // 4304e570
+ VDIVVU V1, V2, V3 // 4384e570
+ VMODB V1, V2, V3 // 4304e270
+ VMODH V1, V2, V3 // 4384e270
+ VMODW V1, V2, V3 // 4304e370
+ VMODV V1, V2, V3 // 4384e370
+ VMODBU V1, V2, V3 // 4304e670
+ VMODHU V1, V2, V3 // 4384e670
+ VMODWU V1, V2, V3 // 4304e770
+ VMODVU V1, V2, V3 // 4384e770
+ XVDIVB X3, X2, X1 // 410ce074
+ XVDIVH X3, X2, X1 // 418ce074
+ XVDIVW X3, X2, X1 // 410ce174
+ XVDIVV X3, X2, X1 // 418ce174
+ XVDIVBU X3, X2, X1 // 410ce474
+ XVDIVHU X3, X2, X1 // 418ce474
+ XVDIVWU X3, X2, X1 // 410ce574
+ XVDIVVU X3, X2, X1 // 418ce574
+ XVMODB X3, X2, X1 // 410ce274
+ XVMODH X3, X2, X1 // 418ce274
+ XVMODW X3, X2, X1 // 410ce374
+ XVMODV X3, X2, X1 // 418ce374
+ XVMODBU X3, X2, X1 // 410ce674
+ XVMODHU X3, X2, X1 // 418ce674
+ XVMODWU X3, X2, X1 // 410ce774
+ XVMODVU X3, X2, X1 // 418ce774
+
+ // [X]VF{SQRT/RECIP/RSQRT}{F/D} instructions
+ VFSQRTF V1, V2 // 22e49c72
+ VFSQRTD V1, V2 // 22e89c72
+ VFRECIPF V1, V2 // 22f49c72
+ VFRECIPD V1, V2 // 22f89c72
+ VFRSQRTF V1, V2 // 22049d72
+ VFRSQRTD V1, V2 // 22089d72
+ XVFSQRTF X2, X1 // 41e49c76
+ XVFSQRTD X2, X1 // 41e89c76
+ XVFRECIPF X2, X1 // 41f49c76
+ XVFRECIPD X2, X1 // 41f89c76
+ XVFRSQRTF X2, X1 // 41049d76
+ XVFRSQRTD X2, X1 // 41089d76
+
+ // [X]VNEG{B/H/W/V} instructions
+ VNEGB V1, V2 // 22309c72
+ VNEGH V1, V2 // 22349c72
+ VNEGW V1, V2 // 22389c72
+ VNEGV V1, V2 // 223c9c72
+ XVNEGB X2, X1 // 41309c76
+ XVNEGH X2, X1 // 41349c76
+ XVNEGW X2, X1 // 41389c76
+ XVNEGV X2, X1 // 413c9c76
+
+ // [X]{VMULW}{EV/OD}.{H.B/W.H/D.W/Q.D}[U] instructions
+ VMULWEVHB V1, V2, V3 // 43049070
+ VMULWEVWH V1, V2, V3 // 43849070
+ VMULWEVVW V1, V2, V3 // 43049170
+ VMULWEVQV V1, V2, V3 // 43849170
+ VMULWODHB V1, V2, V3 // 43049270
+ VMULWODWH V1, V2, V3 // 43849270
+ VMULWODVW V1, V2, V3 // 43049370
+ VMULWODQV V1, V2, V3 // 43849370
+ VMULWEVHBU V1, V2, V3 // 43049870
+ VMULWEVWHU V1, V2, V3 // 43849870
+ VMULWEVVWU V1, V2, V3 // 43049970
+ VMULWEVQVU V1, V2, V3 // 43849970
+ VMULWODHBU V1, V2, V3 // 43049a70
+ VMULWODWHU V1, V2, V3 // 43849a70
+ VMULWODVWU V1, V2, V3 // 43049b70
+ VMULWODQVU V1, V2, V3 // 43849b70
+ XVMULWEVHB X1, X2, X3 // 43049074
+ XVMULWEVWH X1, X2, X3 // 43849074
+ XVMULWEVVW X1, X2, X3 // 43049174
+ XVMULWEVQV X1, X2, X3 // 43849174
+ XVMULWODHB X1, X2, X3 // 43049274
+ XVMULWODWH X1, X2, X3 // 43849274
+ XVMULWODVW X1, X2, X3 // 43049374
+ XVMULWODQV X1, X2, X3 // 43849374
+ XVMULWEVHBU X1, X2, X3 // 43049874
+ XVMULWEVWHU X1, X2, X3 // 43849874
+ XVMULWEVVWU X1, X2, X3 // 43049974
+ XVMULWEVQVU X1, X2, X3 // 43849974
+ XVMULWODHBU X1, X2, X3 // 43049a74
+ XVMULWODWHU X1, X2, X3 // 43849a74
+ XVMULWODVWU X1, X2, X3 // 43049b74
+ XVMULWODQVU X1, X2, X3 // 43849b74
+
+ // [X]{VMULW}{EV/OD}.{H.BU.B/W.HU.H/D.WU.W/Q.DU.D} instructions
+ VMULWEVHBUB V1, V2, V3 // 4304a070
+ VMULWEVWHUH V1, V2, V3 // 4384a070
+ VMULWEVVWUW V1, V2, V3 // 4304a170
+ VMULWEVQVUV V1, V2, V3 // 4384a170
+ VMULWODHBUB V1, V2, V3 // 4304a270
+ VMULWODWHUH V1, V2, V3 // 4384a270
+ VMULWODVWUW V1, V2, V3 // 4304a370
+ VMULWODQVUV V1, V2, V3 // 4384a370
+ XVMULWEVHBUB X1, X2, X3 // 4304a074
+ XVMULWEVWHUH X1, X2, X3 // 4384a074
+ XVMULWEVVWUW X1, X2, X3 // 4304a174
+ XVMULWEVQVUV X1, X2, X3 // 4384a174
+ XVMULWODHBUB X1, X2, X3 // 4304a274
+ XVMULWODWHUH X1, X2, X3 // 4384a274
+ XVMULWODVWUW X1, X2, X3 // 4304a374
+ XVMULWODQVUV X1, X2, X3 // 4384a374
+
+ // [X]VSHUF4I.{B/H/W/D} instructions
+ VSHUF4IB $0, V2, V1 // 41009073
+ VSHUF4IB $16, V2, V1 // 41409073
+ VSHUF4IB $255, V2, V1 // 41fc9373
+ VSHUF4IH $0, V2, V1 // 41009473
+ VSHUF4IH $128, V2, V1 // 41009673
+ VSHUF4IH $255, V2, V1 // 41fc9773
+ VSHUF4IW $0, V2, V1 // 41009873
+ VSHUF4IW $96, V2, V1 // 41809973
+ VSHUF4IW $255, V2, V1 // 41fc9b73
+ VSHUF4IV $0, V2, V1 // 41009c73
+ VSHUF4IV $8, V2, V1 // 41209c73
+ VSHUF4IV $15, V2, V1 // 413c9c73
+ XVSHUF4IB $0, X1, X2 // 22009077
+ XVSHUF4IB $16, X1, X2 // 22409077
+ XVSHUF4IB $255, X1, X2 // 22fc9377
+ XVSHUF4IH $0, X1, X2 // 22009477
+ XVSHUF4IH $128, X1, X2 // 22009677
+ XVSHUF4IH $255, X1, X2 // 22fc9777
+ XVSHUF4IW $0, X1, X2 // 22009877
+ XVSHUF4IW $96, X1, X2 // 22809977
+ XVSHUF4IW $255, X1, X2 // 22fc9b77
+ XVSHUF4IV $0, X1, X2 // 22009c77
+ XVSHUF4IV $8, X1, X2 // 22209c77
+ XVSHUF4IV $15, X1, X2 // 223c9c77
+
+ // [X]VSETEQZ.V, [X]VSETNEZ.V
+ VSETEQV V1, FCC0 // 20989c72
+ VSETNEV V1, FCC0 // 209c9c72
+ XVSETEQV X1, FCC0 // 20989c76
+ XVSETNEV X1, FCC0 // 209c9c76
+ // [X]VSETANYEQZ.{B/H/W/D} instructions
+ VSETANYEQB V1, FCC0 // 20a09c72
+ VSETANYEQH V1, FCC0 // 20a49c72
+ VSETANYEQW V1, FCC0 // 20a89c72
+ VSETANYEQV V1, FCC0 // 20ac9c72
+ VSETALLNEB V1, FCC0 // 20b09c72
+ VSETALLNEH V1, FCC0 // 20b49c72
+ VSETALLNEW V1, FCC0 // 20b89c72
+ VSETALLNEV V1, FCC0 // 20bc9c72
+ XVSETANYEQB X1, FCC0 // 20a09c76
+ XVSETANYEQH X1, FCC0 // 20a49c76
+ XVSETANYEQW X1, FCC0 // 20a89c76
+ XVSETANYEQV X1, FCC0 // 20ac9c76
+ XVSETALLNEB X1, FCC0 // 20b09c76
+ XVSETALLNEH X1, FCC0 // 20b49c76
+ XVSETALLNEW X1, FCC0 // 20b89c76
+ XVSETALLNEV X1, FCC0 // 20bc9c76
+
+ // [X]VFRINT[{RNE/RZ/RP/RM}].{S/D} instructions
+ VFRINTRNEF V1, V2 // 22749d72
+ VFRINTRNED V1, V2 // 22789d72
+ VFRINTRZF V1, V2 // 22649d72
+ VFRINTRZD V1, V2 // 22689d72
+ VFRINTRPF V1, V2 // 22549d72
+ VFRINTRPD V1, V2 // 22589d72
+ VFRINTRMF V1, V2 // 22449d72
+ VFRINTRMD V1, V2 // 22489d72
+ VFRINTF V1, V2 // 22349d72
+ VFRINTD V1, V2 // 22389d72
+ XVFRINTRNEF X1, X2 // 22749d76
+ XVFRINTRNED X1, X2 // 22789d76
+ XVFRINTRZF X1, X2 // 22649d76
+ XVFRINTRZD X1, X2 // 22689d76
+ XVFRINTRPF X1, X2 // 22549d76
+ XVFRINTRPD X1, X2 // 22589d76
+ XVFRINTRMF X1, X2 // 22449d76
+ XVFRINTRMD X1, X2 // 22489d76
+ XVFRINTF X1, X2 // 22349d76
+ XVFRINTD X1, X2 // 22389d76
+
+ // [X]VF{ADD/SUB/MUL/DIV}.{S/D} instructions
+ VADDF V1, V2, V3 // 43843071
+ VADDD V1, V2, V3 // 43043171
+ VSUBF V1, V2, V3 // 43843271
+ VSUBD V1, V2, V3 // 43043371
+ VMULF V1, V2, V3 // 43843871
+ VMULD V1, V2, V3 // 43043971
+ VDIVF V1, V2, V3 // 43843a71
+ VDIVD V1, V2, V3 // 43043b71
+ XVADDF X1, X2, X3 // 43843075
+ XVADDD X1, X2, X3 // 43043175
+ XVSUBF X1, X2, X3 // 43843275
+ XVSUBD X1, X2, X3 // 43043375
+ XVMULF X1, X2, X3 // 43843875
+ XVMULD X1, X2, X3 // 43043975
+ XVDIVF X1, X2, X3 // 43843a75
+ XVDIVD X1, X2, X3 // 43043b75
+
+ // [X]VFCLASS.{S/D} instructions
+ VFCLASSF V1, V2 // 22d49c72
+ VFCLASSD V1, V2 // 22d89c72
+ XVFCLASSF X1, X2 // 22d49c76
+ XVFCLASSD X1, X2 // 22d89c76
+
+ // PRELD{,X} instructions
+ PRELD (R4), $0 // 8000c02a
+ PRELD -1(R4), $8 // 88fcff2a
+ PRELD 8(R4), $31 // 9f20c02a
+
+ // [X]{VBITCLR/VBITSET/VBITREV}{B,H,W,V} instructions
+ VBITCLRB V1, V2, V3 // 43040c71
+ VBITCLRH V1, V2, V3 // 43840c71
+ VBITCLRW V1, V2, V3 // 43040d71
+ VBITCLRV V1, V2, V3 // 43840d71
+ VBITSETB V1, V2, V3 // 43040e71
+ VBITSETH V1, V2, V3 // 43840e71
+ VBITSETW V1, V2, V3 // 43040f71
+ VBITSETV V1, V2, V3 // 43840f71
+ VBITREVB V1, V2, V3 // 43041071
+ VBITREVH V1, V2, V3 // 43841071
+ VBITREVW V1, V2, V3 // 43041171
+ VBITREVV V1, V2, V3 // 43841171
+ XVBITCLRB X3, X2, X1 // 410c0c75
+ XVBITCLRH X3, X2, X1 // 418c0c75
+ XVBITCLRW X3, X2, X1 // 410c0d75
+ XVBITCLRV X3, X2, X1 // 418c0d75
+ XVBITSETB X3, X2, X1 // 410c0e75
+ XVBITSETH X3, X2, X1 // 418c0e75
+ XVBITSETW X3, X2, X1 // 410c0f75
+ XVBITSETV X3, X2, X1 // 418c0f75
+ XVBITREVB X3, X2, X1 // 410c1075
+ XVBITREVH X3, X2, X1 // 418c1075
+ XVBITREVW X3, X2, X1 // 410c1175
+ XVBITREVV X3, X2, X1 // 418c1175
+ VBITCLRB $7, V2, V3 // 433c1073
+ VBITCLRH $15, V2, V3 // 437c1073
+ VBITCLRW $31, V2, V3 // 43fc1073
+ VBITCLRV $63, V2, V3 // 43fc1173
+ VBITSETB $7, V2, V3 // 433c1473
+ VBITSETH $15, V2, V3 // 437c1473
+ VBITSETW $31, V2, V3 // 43fc1473
+ VBITSETV $63, V2, V3 // 43fc1573
+ VBITREVB $7, V2, V3 // 433c1873
+ VBITREVH $15, V2, V3 // 437c1873
+ VBITREVW $31, V2, V3 // 43fc1873
+ VBITREVV $63, V2, V3 // 43fc1973
+ XVBITCLRB $7, X2, X1 // 413c1077
+ XVBITCLRH $15, X2, X1 // 417c1077
+ XVBITCLRW $31, X2, X1 // 41fc1077
+ XVBITCLRV $63, X2, X1 // 41fc1177
+ XVBITSETB $7, X2, X1 // 413c1477
+ XVBITSETH $15, X2, X1 // 417c1477
+ XVBITSETW $31, X2, X1 // 41fc1477
+ XVBITSETV $63, X2, X1 // 41fc1577
+ XVBITREVB $7, X2, X1 // 413c1877
+ XVBITREVH $15, X2, X1 // 417c1877
+ XVBITREVW $31, X2, X1 // 41fc1877
+ XVBITREVV $63, X2, X1 // 41fc1977
+
+ // ALSL{W/WU/D}
+ ALSLW $4, R4, R5, R6 // 86940500
+ ALSLWU $4, R4, R5, R6 // 86940700
+ ALSLV $4, R4, R5, R6 // 86942d00
diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc2.s b/src/cmd/asm/internal/asm/testdata/loong64enc2.s
index e497b83627..91aed4e2c7 100644
--- a/src/cmd/asm/internal/asm/testdata/loong64enc2.s
+++ b/src/cmd/asm/internal/asm/testdata/loong64enc2.s
@@ -12,7 +12,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
AND $-1, R4, R5 // 1efcbf0285f81400
AND $-1, R4 // 1efcbf0284f81400
MOVW $-1, F4 // 1efcbf02c4a71401
- MOVW $1, F4 // 1e048002c4a71401
+ MOVW $1, F4 // 1e048003c4a71401
TEQ $4, R4, R5 // 8508005c04002a00
TEQ $4, R4 // 0408005c04002a00
TNE $4, R4, R5 // 8508005804002a00
@@ -77,3 +77,49 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
MOVH name(SB), R4 // 1e00001ac4034028
MOVHU R4, name(SB) // 1e00001ac4034029
MOVHU name(SB), R4 // 1e00001ac403402a
+
+ // MOVV C_DCON12_20S, r
+ MOVV $0x273fffff80000000, R4 // MOVV $2828260563841187840, R4 // 0400001584cc0903
+ MOVV $0xf73fffff80000000, R4 // MOVV $-630503949979353088, R4 // 0400001584cc3d03
+
+ // MOVV C_DCON20S_20, r
+ MOVV $0xfff800000f000000, R4 // MOVV $-2251799562027008, R4 // 04001e1404000017
+
+ // MOVV C_DCON12_12S, r
+ MOVV $0x273ffffffffff800, R4 // MOVV $2828260565988669440, R4 // 0400e00284cc0903
+ MOVV $0xf73ffffffffff800, R4 // MOVV $-630503947831871488, R4 // 0400e00284cc3d03
+
+ // MOVV C_DCON20S_12S, r
+ MOVV $0xfff80000fffff800, R4 // MOVV $-2251795518720000, R4 // 0400a00204000017
+ MOVV $0xfff8000000000000, R4 // MOVV $-2251799813685248, R4 // 0400800204000017
+
+ // MOVV C_DCON12_12U, r
+ MOVV $0x2730000000000800, R4 // MOVV $2823756966361303040, R4 // 0400a00384cc0903
+ MOVV $0xf730000000000800, R4 // MOVV $-635007547459237888, R4 // 0400a00384cc3d03
+
+ // MOVV C_DCON20S_12U, r
+ MOVV $0xfff8000000000800, R4 // MOVV $-2251799813683200, R4 // 0400a00304000017
+
+ // ADDV/AND C_DCON12_0, [r1], r2
+ ADDV $0x3210000000000000, R4 // ADDV $3607383301523767296, R4 // 1e840c0384f81000
+ ADDV $0x3210000000000000, R5, R4 // ADDV $3607383301523767296, R5, R4 // 1e840c03a4f81000
+ ADDV $0xc210000000000000, R4 // ADDV $-4463067230724161536, R4 // 1e84300384f81000
+ ADDV $0xc210000000000000, R5, R4 // ADDV $-4463067230724161536, R5, R4 // 1e843003a4f81000
+ AND $0x3210000000000000, R4 // AND $3607383301523767296, R4 // 1e840c0384f81400
+ AND $0x3210000000000000, R5, R4 // AND $3607383301523767296, R5, R4 // 1e840c03a4f81400
+ AND $0xc210000000000000, R4 // AND $-4463067230724161536, R4 // 1e84300384f81400
+ AND $0xc210000000000000, R5, R4 // AND $-4463067230724161536, R5, R4 // 1e843003a4f81400
+
+ // ADDV/AND C_UCON, [r1], r2
+ ADDV $0x43210000, R4 // ADDV $1126236160, R4 // 1e42861484f81000
+ ADDV $0x43210000, R5, R4 // ADDV $1126236160, R5, R4 // 1e428614a4f81000
+ ADDV $0xffffffffc3210000, R4 // ADDV $-1021247488, R4 // 1e42861584f81000
+ ADDV $0xffffffffc3210000, R5, R4 // ADDV $-1021247488, R5, R4 // 1e428615a4f81000
+ AND $0x43210000, R4 // AND $1126236160, R4 // 1e42861484f81400
+ AND $0x43210000, R5, R4 // AND $1126236160, R5, R4 // 1e428614a4f81400
+ AND $0xffffffffc3210000, R4 // AND $-1021247488, R4 // 1e42861584f81400
+ AND $0xffffffffc3210000, R5, R4 // AND $-1021247488, R5, R4 // 1e428615a4f81400
+
+ // AND C_ADDCON, [r1], r2
+ AND $0xfffffffffffffc21, R4 // AND $-991, R4 // 1e84b00284f81400
+ AND $0xfffffffffffffc21, R5, R4 // AND $-991, R5, R4 // 1e84b002a4f81400
diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc3.s b/src/cmd/asm/internal/asm/testdata/loong64enc3.s
index 2600884309..2d83bd719a 100644
--- a/src/cmd/asm/internal/asm/testdata/loong64enc3.s
+++ b/src/cmd/asm/internal/asm/testdata/loong64enc3.s
@@ -121,3 +121,68 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
XOR $74565, R4, R5 // 5e020014de178d0385f81500
XOR $4097, R4 // 3e000014de07800384f81500
XOR $4097, R4, R5 // 3e000014de07800385f81500
+
+ // MOVV C_DCON32_12S, r
+ MOVV $0x27312345fffff800, R4 // MOVV $2824077224892692480, R4 // 0400a002a468241684cc0903
+ MOVV $0xf7312345fffff800, R4 // MOVV $-634687288927848448, R4 // 0400a002a468241684cc3d03
+
+ // MOVV C_DCON32_0, r
+ MOVV $0x2731234500000000, R4 // MOVV $2824077220597727232, R4 // 04008002a468241684cc0903
+ MOVV $0xf731234500000000, R4 // MOVV $-634687293222813696, R4 // 04008002a468241684cc3d03
+
+ // MOVV C_DCON32_20, r
+ MOVV $0x2731234512345000, R4 // MOVV $2824077220903145472, R4 // a4682414a468241684cc0903
+ MOVV $0xf731234512345000, R4 // MOVV $-634687292917395456, R4 // a4682414a468241684cc3d03
+
+ // MOVV C_DCON12_32S, r
+ MOVV $0x273fffff80000800, R4 // MOVV $2828260563841189888, R4 // 040000158400a00384cc0903
+ MOVV $0xf73fffff80000800, R4 // MOVV $-630503949979351040, R4 // 040000158400a00384cc3d03
+
+ // MOVV C_DCON20S_32, r
+ MOVV $0xfff8000080000800, R4 // MOVV $-2251797666199552, R4 // 040000158400a00304000017
+
+ // MOVV C_DCON32_12U, r
+ MOVV $0x2731234500000800, R4 // MOVV $2824077220597729280, R4 // 0400a003a468241684cc0903
+ MOVV $0xf731234500000800, R4 // MOVV $-634687293222811648, R4 // 0400a003a468241684cc3d03
+
+ // ADDV/AND C_DCON12_20S, [r1], r2
+ ADDV $0x273fffff80000000, R4 // ADDV $2828260563841187840, R4 // 1e000015decf090384f81000
+ ADDV $0x273fffff80000000, R4, R5 // ADDV $2828260563841187840, R4, R5 // 1e000015decf090385f81000
+ AND $0x273fffff80000000, R4 // AND $2828260563841187840, R4 // 1e000015decf090384f81400
+ AND $0x273fffff80000000, R4, R5 // AND $2828260563841187840, R4, R5 // 1e000015decf090385f81400
+
+ // ADDV/AND C_DCON20S_20, [r1], r2
+ ADDV $0xfff800000f000000, R4 // ADDV $-2251799562027008, R4 // 1e001e141e00001784f81000
+ ADDV $0xfff800000f000000, R4, R5 // ADDV $-2251799562027008, R4, R5 // 1e001e141e00001785f81000
+ AND $0xfff800000f000000, R4 // AND $-2251799562027008, R4 // 1e001e141e00001784f81400
+ AND $0xfff800000f000000, R4, R5 // AND $-2251799562027008, R4, R5 // 1e001e141e00001785f81400
+
+ // ADDV/AND C_DCON12_12S, [r1], r2
+ ADDV $0x273ffffffffff800, R4 // ADDV $2828260565988669440, R4 // 1e00e002decf090384f81000
+ ADDV $0x273ffffffffff800, R4, R5 // ADDV $2828260565988669440, R4, R5 // 1e00e002decf090385f81000
+ AND $0x273ffffffffff800, R4 // AND $2828260565988669440, R4 // 1e00e002decf090384f81400
+ AND $0x273ffffffffff800, R4, R5 // AND $2828260565988669440, R4, R5 // 1e00e002decf090385f81400
+
+ // ADDV/AND C_DCON20S_12S, [r1], r2
+ ADDV $0xfff80000fffff800, R4 // ADDV $-2251795518720000, R4 // 1e00a0021e00001784f81000
+ ADDV $0xfff80000fffff800, R4, R5 // ADDV $-2251795518720000, R4, R5 // 1e00a0021e00001785f81000
+ AND $0xfff80000fffff800, R4 // AND $-2251795518720000, R4 // 1e00a0021e00001784f81400
+ AND $0xfff80000fffff800, R4, R5 // AND $-2251795518720000, R4, R5 // 1e00a0021e00001785f81400
+
+ // ADDV/AND C_DCON20S_0, [r1], r2
+ ADDV $0xfff8000000000000, R4 // ADDV $-2251799813685248, R4 // 1e0080021e00001784f81000
+ ADDV $0xfff8000000000000, R4, R5 // ADDV $-2251799813685248, R4, R5 // 1e0080021e00001785f81000
+ AND $0xfff8000000000000, R4 // AND $-2251799813685248, R4 // 1e0080021e00001784f81400
+ AND $0xfff8000000000000, R4, R5 // AND $-2251799813685248, R4, R5 // 1e0080021e00001785f81400
+
+ // ADDV/AND C_DCON12_12U, [r1], r2
+ ADDV $0x2730000000000800, R4 // ADDV $2823756966361303040, R4 // 1e00a003decf090384f81000
+ ADDV $0x2730000000000800, R4, R5 // ADDV $2823756966361303040, R4, R5 // 1e00a003decf090385f81000
+ AND $0x2730000000000800, R4 // AND $2823756966361303040, R4 // 1e00a003decf090384f81400
+ AND $0x2730000000000800, R4, R5 // AND $2823756966361303040, R4, R5 // 1e00a003decf090385f81400
+
+ // ADDV/AND C_DCON20S_12U, [r1], r2
+ ADDV $0xfff8000000000800, R4 // ADDV $-2251799813683200, R4 // 1e00a0031e00001784f81000
+ ADDV $0xfff8000000000800, R4, R5 // ADDV $-2251799813683200, R4, R5 // 1e00a0031e00001785f81000
+ AND $0xfff8000000000800, R4 // AND $-2251799813683200, R4 // 1e00a0031e00001784f81400
+ AND $0xfff8000000000800, R4, R5 // AND $-2251799813683200, R4, R5 // 1e00a0031e00001785f81400
diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc4.s b/src/cmd/asm/internal/asm/testdata/loong64enc4.s
new file mode 100644
index 0000000000..16c06a3501
--- /dev/null
+++ b/src/cmd/asm/internal/asm/testdata/loong64enc4.s
@@ -0,0 +1,42 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "../../../../../runtime/textflag.h"
+
+TEXT asmtest(SB),DUPOK|NOSPLIT,$0
+ // ADDV/AND C_DCON32_12S, [r1], r2
+ ADDV $0x27312345fffff800, R4 // ADDV $2824077224892692480, R4 // 1e00a002be682416decf090384f81000
+ ADDV $0x27312345fffff800, R4, R5 // ADDV $2824077224892692480, R4, R5 // 1e00a002be682416decf090385f81000
+ AND $0x27312345fffff800, R4 // AND $2824077224892692480, R4 // 1e00a002be682416decf090384f81400
+ AND $0x27312345fffff800, R4, R5 // AND $2824077224892692480, R4, R5 // 1e00a002be682416decf090385f81400
+
+ // ADDV/AND C_DCON32_0, [r1], r2
+ ADDV $0x2731234500000000, R4 // ADDV $2824077220597727232, R4 // 1e008002be682416decf090384f81000
+ ADDV $0x2731234500000000, R4, R5 // ADDV $2824077220597727232, R4, R5 // 1e008002be682416decf090385f81000
+ AND $0x2731234500000000, R4 // AND $2824077220597727232, R4 // 1e008002be682416decf090384f81400
+ AND $0x2731234500000000, R4, R5 // AND $2824077220597727232, R4, R5 // 1e008002be682416decf090385f81400
+
+ // ADDV/AND C_DCON32_20, [r1], r2
+ ADDV $0x2731234512345000, R4 // ADDV $2824077220903145472, R4 // be682414be682416decf090384f81000
+ ADDV $0x2731234512345000, R4, R5 // ADDV $2824077220903145472, R4, R5 // be682414be682416decf090385f81000
+ AND $0x2731234512345000, R4 // AND $2824077220903145472, R4 // be682414be682416decf090384f81400
+ AND $0x2731234512345000, R4, R5 // AND $2824077220903145472, R4, R5 // be682414be682416decf090385f81400
+
+ // ADDV/AND C_DCON12_32S, [r1], r2
+ ADDV $0x273fffff80000800, R4 // ADDV $2828260563841189888, R4 // 1e000015de03a003decf090384f81000
+ ADDV $0x273fffff80000800, R4, R5 // ADDV $2828260563841189888, R4, R5 // 1e000015de03a003decf090385f81000
+ AND $0x273fffff80000800, R4 // AND $2828260563841189888, R4 // 1e000015de03a003decf090384f81400
+ AND $0x273fffff80000800, R4, R5 // AND $2828260563841189888, R4, R5 // 1e000015de03a003decf090385f81400
+
+ // ADDV/AND C_DCON20S_32, [r1], r2
+ ADDV $0xfff8000080000800, R4 // ADDV $-2251797666199552, R4 // 1e000015de03a0031e00001784f81000
+ ADDV $0xfff8000080000800, R4, R5 // ADDV $-2251797666199552, R4, R5 // 1e000015de03a0031e00001785f81000
+ AND $0xfff8000080000800, R4 // AND $-2251797666199552, R4 // 1e000015de03a0031e00001784f81400
+ AND $0xfff8000080000800, R4, R5 // AND $-2251797666199552, R4, R5 // 1e000015de03a0031e00001785f81400
+
+ // ADDV/AND C_DCON32_12U, [r1], r2
+ ADDV $0x2731234500000800, R4 // ADDV $2824077220597729280, R4 // 1e00a003be682416decf090384f81000
+ ADDV $0x2731234500000800, R4, R5 // ADDV $2824077220597729280, R4, R5 // 1e00a003be682416decf090385f81000
+ AND $0x2731234500000800, R4 // AND $2824077220597729280, R4 // 1e00a003be682416decf090384f81400
+ AND $0x2731234500000800, R4, R5 // AND $2824077220597729280, R4, R5 // 1e00a003be682416decf090385f81400
diff --git a/src/cmd/asm/internal/asm/testdata/loong64enc5.s b/src/cmd/asm/internal/asm/testdata/loong64enc5.s
new file mode 100644
index 0000000000..b7ecd6b63b
--- /dev/null
+++ b/src/cmd/asm/internal/asm/testdata/loong64enc5.s
@@ -0,0 +1,22 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "../../../../../runtime/textflag.h"
+
+TEXT asmtest(SB),DUPOK|NOSPLIT,$0
+ // ADDV/AND C_DCON, [r1], r2
+ ADDV $0xfedcba9876543210, R4 // ADDV $-81985529216486896, R4 // 7ea8ec14de4388031e539717deb73f0384f81000
+ ADDV $0xfedcba9876543210, R5, R4 // ADDV $-81985529216486896, R5, R4 // 7ea8ec14de4388031e539717deb73f03a4f81000
+ ADDV $0x4edcba9876543210, R4 // ADDV $5682621993817747984, R4 // 7ea8ec14de4388031e539717deb7130384f81000
+ ADDV $0x4edcba9876543210, R5, R4 // ADDV $5682621993817747984, R5, R4 // 7ea8ec14de4388031e539717deb71303a4f81000
+ AND $0x4edcba9876543210, R4 // AND $5682621993817747984, R4 // 7ea8ec14de4388031e539717deb7130384f81400
+ AND $0x4edcba9876543210, R5, R4 // AND $5682621993817747984, R5, R4 // 7ea8ec14de4388031e539717deb71303a4f81400
+ AND $0xfedcba9876543210, R4 // AND $-81985529216486896, R4 // 7ea8ec14de4388031e539717deb73f0384f81400
+ AND $0xfedcba9876543210, R5, R4 // AND $-81985529216486896, R5, R4 // 7ea8ec14de4388031e539717deb73f03a4f81400
+
+ PRELDX 0(R7), $0x80001021, $0 // PRELDX (R7), $2147487777, $0 // 1e020014de0380031e000016de130003e0782c38
+ PRELDX -1(R7), $0x1021, $2 // PRELDX -1(R7), $4129, $2 // fe030014deffbf031e000016de030003e2782c38
+ PRELDX 8(R7), $0x80100800, $31 // PRELDX 8(R7), $2148534272, $31 // 1ee00714de238003fe1f0016de130003ff782c38
+ PRELDX 16(R7), $0x202040, $1 // PRELDX 16(R7), $2105408, $1 // 1e200014de4380033e000216de030003e1782c38
+
diff --git a/src/cmd/asm/internal/asm/testdata/loong64error.s b/src/cmd/asm/internal/asm/testdata/loong64error.s
new file mode 100644
index 0000000000..2dcd34bf61
--- /dev/null
+++ b/src/cmd/asm/internal/asm/testdata/loong64error.s
@@ -0,0 +1,9 @@
+// Copyright 2025 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.
+
+TEXT errors(SB),$0
+ VSHUF4IV $16, V1, V2 // ERROR "operand out of range 0 to 15"
+ XVSHUF4IV $16, X1, X2 // ERROR "operand out of range 0 to 15"
+ ADDV16 $1, R4, R5 // ERROR "the constant must be a multiple of 65536."
+ ADDV16 $65535, R4, R5 // ERROR "the constant must be a multiple of 65536."
diff --git a/src/cmd/asm/internal/asm/testdata/riscv64.s b/src/cmd/asm/internal/asm/testdata/riscv64.s
index 37c0c1d858..702b82223b 100644
--- a/src/cmd/asm/internal/asm/testdata/riscv64.s
+++ b/src/cmd/asm/internal/asm/testdata/riscv64.s
@@ -172,11 +172,35 @@ start:
SD X5, (X6) // 23305300
SD X5, 4(X6) // 23325300
+ // 7.1: CSR Instructions
+ CSRRC X0, CYCLE, X5 // f33200c0
+ CSRRC X0, CYCLE, X0 // 733000c0
+ CSRRC X10, CYCLE, X5 // f33205c0
+ CSRRC $2, TIME, X5 // f37211c0
+ CSRRCI $2, TIME, X5 // f37211c0
+ CSRRS X0, CYCLE, X5 // f32200c0
+ CSRRS X0, CYCLE, X0 // 732000c0
+ CSRRS X10, CYCLE, X5 // f32205c0
+ CSRRS $2, TIME, X5 // f36211c0
+ CSRRS X0, VLENB, X5 // f32220c2
+ CSRRSI $2, TIME, X5 // f36211c0
+ CSRRW X0, CYCLE, X5 // f31200c0
+ CSRRW X0, CYCLE, X0 // 731000c0
+ CSRRW X10, CYCLE, X5 // f31205c0
+ CSRRW $2, TIME, X5 // f35211c0
+ CSRRWI $2, TIME, X5 // f35211c0
+
// 8.1: Base Counters and Timers (Zicntr)
RDCYCLE X5 // f32200c0
RDTIME X5 // f32210c0
RDINSTRET X5 // f32220c0
+ // 12.3: Integer Conditional Operations (Zicond)
+ CZEROEQZ X5, X6, X7 // b353530e
+ CZEROEQZ X5, X7 // b3d3530e
+ CZERONEZ X5, X6, X7 // b373530e
+ CZERONEZ X5, X7 // b3f3530e
+
// 13.1: Multiplication Operations
MUL X5, X6, X7 // b3035302
MULH X5, X6, X7 // b3135302
@@ -340,6 +364,11 @@ start:
FNMSUBD F1, F2, F3, F4 // 4b82201a
FNMADDD F1, F2, F3, F4 // 4f82201a
+ // 21.6: Double-Precision Floating-Point Compare Instructions
+ FEQD F0, F1, X7 // d3a300a2
+ FLTD F0, F1, X7 // d39300a2
+ FLED F0, F1, X7 // d38300a2
+
// 21.7: Double-Precision Floating-Point Classify Instruction
FCLASSD F0, X5 // d31200e2
@@ -363,6 +392,10 @@ start:
SLLIUW $63, X17, X18 // 1b99f80b
SLLIUW $1, X18, X19 // 9b191908
+ //
+ // "B" Extension for Bit Manipulation, Version 1.0.0
+ //
+
// 28.4.2: Basic Bit Manipulation (Zbb)
ANDN X19, X20, X21 // b37a3a41 or 93caf9ffb37a5a01
ANDN X19, X20 // 337a3a41 or 93cff9ff337afa01
@@ -372,14 +405,14 @@ start:
CPOPW X23, X24 // 1b9c2b60
CTZ X24, X25 // 931c1c60
CTZW X25, X26 // 1b9d1c60
- MAX X26, X28, X29 // b36eae0b
- MAX X26, X28 // 336eae0b
- MAXU X28, X29, X30 // 33ffce0b
- MAXU X28, X29 // b3fece0b
- MIN X29, X30, X5 // b342df0b
- MIN X29, X30 // 334fdf0b
- MINU X30, X5, X6 // 33d3e20b
- MINU X30, X5 // b3d2e20b
+ MAX X26, X28, X29 // b36eae0b or b32fae01b30ff041b34eae01b3fedf01b34ede01
+ MAX X26, X28 // 336eae0b or b32fcd01b30ff041334ecd0133fecf01334ecd01
+ MAXU X28, X29, X30 // 33ffce0b or b3bfce01b30ff04133cfce0133ffef0133cfee01
+ MAXU X28, X29 // b3fece0b or b33fde01b30ff041b34ede01b3fedf01b34ede01
+ MIN X29, X30, X5 // b342df0b or b3afee01b30ff041b342df01b3f25f00b3425f00
+ MIN X29, X30 // 334fdf0b or b32fdf01b30ff04133cfee0133ffef0133cfee01
+ MINU X30, X5, X6 // 33d3e20b or b33f5f00b30ff04133c3e20133f36f0033c36200
+ MINU X30, X5 // b3d2e20b or b3bfe201b30ff041b3425f00b3f25f00b3425f00
ORN X6, X7, X8 // 33e46340 or 1344f3ff33e48300
ORN X6, X7 // b3e36340 or 934ff3ffb3e3f301
SEXTB X16, X17 // 93184860
@@ -420,6 +453,1448 @@ start:
BSET $63, X9 // 9394f42b
BSETI $1, X10, X11 // 93151528
+ //
+ // "V" Standard Extension for Vector Operations, Version 1.0
+ //
+
+ // 31.6: Configuration Setting Instructions
+ VSETVLI X10, E8, M1, TU, MU, X12 // 57760500
+ VSETVLI X10, E16, M1, TU, MU, X12 // 57768500
+ VSETVLI X10, E32, M1, TU, MU, X12 // 57760501
+ VSETVLI X10, E64, M1, TU, MU, X12 // 57768501
+ VSETVLI X10, E32, M1, TU, MA, X12 // 57760509
+ VSETVLI X10, E32, M1, TA, MA, X12 // 5776050d
+ VSETVLI X10, E32, M2, TA, MA, X12 // 5776150d
+ VSETVLI X10, E32, M4, TA, MA, X12 // 5776250d
+ VSETVLI X10, E32, M8, TA, MA, X12 // 5776350d
+ VSETVLI X10, E32, MF8, TA, MA, X12 // 5776550d
+ VSETVLI X10, E32, MF4, TA, MA, X12 // 5776650d
+ VSETVLI X10, E32, MF2, TA, MA, X12 // 5776750d
+ VSETVLI X10, E32, M1, TA, MA, X12 // 5776050d
+ VSETVLI $15, E32, M1, TA, MA, X12 // 57f607cd
+ VSETIVLI $0, E32, M1, TA, MA, X12 // 577600cd
+ VSETIVLI $15, E32, M1, TA, MA, X12 // 57f607cd
+ VSETIVLI $31, E32, M1, TA, MA, X12 // 57f60fcd
+ VSETVL X10, X11, X12 // 57f6a580
+
+ // 31.7.4: Vector Unit-Stride Instructions
+ VLE8V (X10), V3 // 87010502
+ VLE8V (X10), V0, V3 // 87010500
+ VLE16V (X10), V3 // 87510502
+ VLE16V (X10), V0, V3 // 87510500
+ VLE32V (X10), V3 // 87610502
+ VLE32V (X10), V0, V3 // 87610500
+ VLE64V (X10), V3 // 87710502
+ VLE64V (X10), V0, V3 // 87710500
+ VSE8V V3, (X10) // a7010502
+ VSE8V V3, V0, (X10) // a7010500
+ VSE16V V3, (X10) // a7510502
+ VSE16V V3, V0, (X10) // a7510500
+ VSE32V V3, (X10) // a7610502
+ VSE32V V3, V0, (X10) // a7610500
+ VSE64V V3, (X10) // a7710502
+ VSE64V V3, V0, (X10) // a7710500
+ VLMV (X10), V3 // 8701b502
+ VSMV V3, (X10) // a701b502
+
+ // 31.7.5: Vector Strided Instructions
+ VLSE8V (X10), X11, V3 // 8701b50a
+ VLSE8V (X10), X11, V0, V3 // 8701b508
+ VLSE16V (X10), X11, V3 // 8751b50a
+ VLSE16V (X10), X11, V0, V3 // 8751b508
+ VLSE32V (X10), X11, V3 // 8761b50a
+ VLSE32V (X10), X11, V0, V3 // 8761b508
+ VLSE64V (X10), X11, V3 // 8771b50a
+ VLSE64V (X10), X11, V0, V3 // 8771b508
+ VSSE8V V3, X11, (X10) // a701b50a
+ VSSE8V V3, X11, V0, (X10) // a701b508
+ VSSE16V V3, X11, (X10) // a751b50a
+ VSSE16V V3, X11, V0, (X10) // a751b508
+ VSSE32V V3, X11, (X10) // a761b50a
+ VSSE32V V3, X11, V0, (X10) // a761b508
+ VSSE64V V3, X11, (X10) // a771b50a
+ VSSE64V V3, X11, V0, (X10) // a771b508
+
+ // 31.7.6: Vector Indexed Instructions
+ VLUXEI8V (X10), V2, V3 // 87012506
+ VLUXEI8V (X10), V2, V0, V3 // 87012504
+ VLUXEI16V (X10), V2, V3 // 87512506
+ VLUXEI16V (X10), V2, V0, V3 // 87512504
+ VLUXEI32V (X10), V2, V3 // 87612506
+ VLUXEI32V (X10), V2, V0, V3 // 87612504
+ VLUXEI64V (X10), V2, V3 // 87712506
+ VLUXEI64V (X10), V2, V0, V3 // 87712504
+ VLOXEI8V (X10), V2, V3 // 8701250e
+ VLOXEI8V (X10), V2, V0, V3 // 8701250c
+ VLOXEI16V (X10), V2, V3 // 8751250e
+ VLOXEI16V (X10), V2, V0, V3 // 8751250c
+ VLOXEI32V (X10), V2, V3 // 8761250e
+ VLOXEI32V (X10), V2, V0, V3 // 8761250c
+ VLOXEI64V (X10), V2, V3 // 8771250e
+ VLOXEI64V (X10), V2, V0, V3 // 8771250c
+ VSUXEI8V V3, V2, (X10) // a7012506
+ VSUXEI8V V3, V2, V0, (X10) // a7012504
+ VSUXEI16V V3, V2, (X10) // a7512506
+ VSUXEI16V V3, V2, V0, (X10) // a7512504
+ VSUXEI32V V3, V2, (X10) // a7612506
+ VSUXEI32V V3, V2, V0, (X10) // a7612504
+ VSUXEI64V V3, V2, (X10) // a7712506
+ VSUXEI64V V3, V2, V0, (X10) // a7712504
+ VSOXEI8V V3, V2, (X10) // a701250e
+ VSOXEI8V V3, V2, V0, (X10) // a701250c
+ VSOXEI16V V3, V2, (X10) // a751250e
+ VSOXEI16V V3, V2, V0, (X10) // a751250c
+ VSOXEI32V V3, V2, (X10) // a761250e
+ VSOXEI32V V3, V2, V0, (X10) // a761250c
+ VSOXEI64V V3, V2, (X10) // a771250e
+ VSOXEI64V V3, V2, V0, (X10) // a771250c
+
+ // 31.7.7: Unit-stride Fault-Only-First Loads
+ VLE8FFV (X10), V8 // 07040503
+ VLE16FFV (X10), V8 // 07540503
+ VLE32FFV (X10), V8 // 07640503
+ VLE64FFV (X10), V8 // 07740503
+ VLE8FFV (X10), V0, V8 // 07040501
+ VLE16FFV (X10), V0, V8 // 07540501
+ VLE32FFV (X10), V0, V8 // 07640501
+ VLE64FFV (X10), V0, V8 // 07740501
+
+ // 31.7.8: Vector Load/Store Segment Instructions
+
+ // 31.7.8.1: Vector Unit-Stride Segment Loads and Stores
+ VLSEG2E8V (X10), V8 // 07040522
+ VLSEG2E16V (X10), V8 // 07540522
+ VLSEG2E32V (X10), V8 // 07640522
+ VLSEG2E64V (X10), V8 // 07740522
+ VLSEG2E8V (X10), V0, V8 // 07040520
+ VLSEG2E16V (X10), V0, V8 // 07540520
+ VLSEG2E32V (X10), V0, V8 // 07640520
+ VLSEG2E64V (X10), V0, V8 // 07740520
+
+ VLSEG3E8V (X10), V8 // 07040542
+ VLSEG3E16V (X10), V8 // 07540542
+ VLSEG3E32V (X10), V8 // 07640542
+ VLSEG3E64V (X10), V8 // 07740542
+ VLSEG3E8V (X10), V0, V8 // 07040540
+ VLSEG3E16V (X10), V0, V8 // 07540540
+ VLSEG3E32V (X10), V0, V8 // 07640540
+ VLSEG3E64V (X10), V0, V8 // 07740540
+
+ VLSEG4E8V (X10), V8 // 07040562
+ VLSEG4E16V (X10), V8 // 07540562
+ VLSEG4E32V (X10), V8 // 07640562
+ VLSEG4E64V (X10), V8 // 07740562
+ VLSEG4E8V (X10), V0, V8 // 07040560
+ VLSEG4E16V (X10), V0, V8 // 07540560
+ VLSEG4E32V (X10), V0, V8 // 07640560
+ VLSEG4E64V (X10), V0, V8 // 07740560
+
+ VLSEG5E8V (X10), V8 // 07040582
+ VLSEG5E16V (X10), V8 // 07540582
+ VLSEG5E32V (X10), V8 // 07640582
+ VLSEG5E64V (X10), V8 // 07740582
+ VLSEG5E8V (X10), V0, V8 // 07040580
+ VLSEG5E16V (X10), V0, V8 // 07540580
+ VLSEG5E32V (X10), V0, V8 // 07640580
+ VLSEG5E64V (X10), V0, V8 // 07740580
+
+ VLSEG6E8V (X10), V8 // 070405a2
+ VLSEG6E16V (X10), V8 // 075405a2
+ VLSEG6E32V (X10), V8 // 076405a2
+ VLSEG6E64V (X10), V8 // 077405a2
+ VLSEG6E8V (X10), V0, V8 // 070405a0
+ VLSEG6E16V (X10), V0, V8 // 075405a0
+ VLSEG6E32V (X10), V0, V8 // 076405a0
+ VLSEG6E64V (X10), V0, V8 // 077405a0
+
+ VLSEG7E8V (X10), V8 // 070405c2
+ VLSEG7E16V (X10), V8 // 075405c2
+ VLSEG7E32V (X10), V8 // 076405c2
+ VLSEG7E64V (X10), V8 // 077405c2
+ VLSEG7E8V (X10), V0, V8 // 070405c0
+ VLSEG7E16V (X10), V0, V8 // 075405c0
+ VLSEG7E32V (X10), V0, V8 // 076405c0
+ VLSEG7E64V (X10), V0, V8 // 077405c0
+
+ VLSEG8E8V (X10), V8 // 070405e2
+ VLSEG8E16V (X10), V8 // 075405e2
+ VLSEG8E32V (X10), V8 // 076405e2
+ VLSEG8E64V (X10), V8 // 077405e2
+ VLSEG8E8V (X10), V0, V8 // 070405e0
+ VLSEG8E16V (X10), V0, V8 // 075405e0
+ VLSEG8E32V (X10), V0, V8 // 076405e0
+ VLSEG8E64V (X10), V0, V8 // 077405e0
+
+ VSSEG2E8V V24, (X10) // 270c0522
+ VSSEG2E16V V24, (X10) // 275c0522
+ VSSEG2E32V V24, (X10) // 276c0522
+ VSSEG2E64V V24, (X10) // 277c0522
+ VSSEG2E8V V24, V0, (X10) // 270c0520
+ VSSEG2E16V V24, V0, (X10) // 275c0520
+ VSSEG2E32V V24, V0, (X10) // 276c0520
+ VSSEG2E64V V24, V0, (X10) // 277c0520
+
+ VSSEG3E8V V24, (X10) // 270c0542
+ VSSEG3E16V V24, (X10) // 275c0542
+ VSSEG3E32V V24, (X10) // 276c0542
+ VSSEG3E64V V24, (X10) // 277c0542
+ VSSEG3E8V V24, V0, (X10) // 270c0540
+ VSSEG3E16V V24, V0, (X10) // 275c0540
+ VSSEG3E32V V24, V0, (X10) // 276c0540
+ VSSEG3E64V V24, V0, (X10) // 277c0540
+
+ VSSEG4E8V V24, (X10) // 270c0562
+ VSSEG4E16V V24, (X10) // 275c0562
+ VSSEG4E32V V24, (X10) // 276c0562
+ VSSEG4E64V V24, (X10) // 277c0562
+ VSSEG4E8V V24, V0, (X10) // 270c0560
+ VSSEG4E16V V24, V0, (X10) // 275c0560
+ VSSEG4E32V V24, V0, (X10) // 276c0560
+ VSSEG4E64V V24, V0, (X10) // 277c0560
+
+ VSSEG5E8V V24, (X10) // 270c0582
+ VSSEG5E16V V24, (X10) // 275c0582
+ VSSEG5E32V V24, (X10) // 276c0582
+ VSSEG5E64V V24, (X10) // 277c0582
+ VSSEG5E8V V24, V0, (X10) // 270c0580
+ VSSEG5E16V V24, V0, (X10) // 275c0580
+ VSSEG5E32V V24, V0, (X10) // 276c0580
+ VSSEG5E64V V24, V0, (X10) // 277c0580
+
+ VSSEG6E8V V24, (X10) // 270c05a2
+ VSSEG6E16V V24, (X10) // 275c05a2
+ VSSEG6E32V V24, (X10) // 276c05a2
+ VSSEG6E64V V24, (X10) // 277c05a2
+ VSSEG6E8V V24, V0, (X10) // 270c05a0
+ VSSEG6E16V V24, V0, (X10) // 275c05a0
+ VSSEG6E32V V24, V0, (X10) // 276c05a0
+ VSSEG6E64V V24, V0, (X10) // 277c05a0
+
+ VSSEG7E8V V24, (X10) // 270c05c2
+ VSSEG7E16V V24, (X10) // 275c05c2
+ VSSEG7E32V V24, (X10) // 276c05c2
+ VSSEG7E64V V24, (X10) // 277c05c2
+ VSSEG7E8V V24, V0, (X10) // 270c05c0
+ VSSEG7E16V V24, V0, (X10) // 275c05c0
+ VSSEG7E32V V24, V0, (X10) // 276c05c0
+ VSSEG7E64V V24, V0, (X10) // 277c05c0
+
+ VSSEG8E8V V24, (X10) // 270c05e2
+ VSSEG8E16V V24, (X10) // 275c05e2
+ VSSEG8E32V V24, (X10) // 276c05e2
+ VSSEG8E64V V24, (X10) // 277c05e2
+ VSSEG8E8V V24, V0, (X10) // 270c05e0
+ VSSEG8E16V V24, V0, (X10) // 275c05e0
+ VSSEG8E32V V24, V0, (X10) // 276c05e0
+ VSSEG8E64V V24, V0, (X10) // 277c05e0
+
+ VLSEG2E8FFV (X10), V8 // 07040523
+ VLSEG2E16FFV (X10), V8 // 07540523
+ VLSEG2E32FFV (X10), V8 // 07640523
+ VLSEG2E64FFV (X10), V8 // 07740523
+ VLSEG2E8FFV (X10), V0, V8 // 07040521
+ VLSEG2E16FFV (X10), V0, V8 // 07540521
+ VLSEG2E32FFV (X10), V0, V8 // 07640521
+ VLSEG2E64FFV (X10), V0, V8 // 07740521
+
+ VLSEG3E8FFV (X10), V8 // 07040543
+ VLSEG3E16FFV (X10), V8 // 07540543
+ VLSEG3E32FFV (X10), V8 // 07640543
+ VLSEG3E64FFV (X10), V8 // 07740543
+ VLSEG3E8FFV (X10), V0, V8 // 07040541
+ VLSEG3E16FFV (X10), V0, V8 // 07540541
+ VLSEG3E32FFV (X10), V0, V8 // 07640541
+ VLSEG3E64FFV (X10), V0, V8 // 07740541
+
+ VLSEG4E8FFV (X10), V8 // 07040563
+ VLSEG4E16FFV (X10), V8 // 07540563
+ VLSEG4E32FFV (X10), V8 // 07640563
+ VLSEG4E64FFV (X10), V8 // 07740563
+ VLSEG4E8FFV (X10), V0, V8 // 07040561
+ VLSEG4E16FFV (X10), V0, V8 // 07540561
+ VLSEG4E32FFV (X10), V0, V8 // 07640561
+ VLSEG4E64FFV (X10), V0, V8 // 07740561
+
+ VLSEG5E8FFV (X10), V8 // 07040583
+ VLSEG5E16FFV (X10), V8 // 07540583
+ VLSEG5E32FFV (X10), V8 // 07640583
+ VLSEG5E64FFV (X10), V8 // 07740583
+ VLSEG5E8FFV (X10), V0, V8 // 07040581
+ VLSEG5E16FFV (X10), V0, V8 // 07540581
+ VLSEG5E32FFV (X10), V0, V8 // 07640581
+ VLSEG5E64FFV (X10), V0, V8 // 07740581
+
+ VLSEG6E8FFV (X10), V8 // 070405a3
+ VLSEG6E16FFV (X10), V8 // 075405a3
+ VLSEG6E32FFV (X10), V8 // 076405a3
+ VLSEG6E64FFV (X10), V8 // 077405a3
+ VLSEG6E8FFV (X10), V0, V8 // 070405a1
+ VLSEG6E16FFV (X10), V0, V8 // 075405a1
+ VLSEG6E32FFV (X10), V0, V8 // 076405a1
+ VLSEG6E64FFV (X10), V0, V8 // 077405a1
+
+ VLSEG7E8FFV (X10), V8 // 070405c3
+ VLSEG7E16FFV (X10), V8 // 075405c3
+ VLSEG7E32FFV (X10), V8 // 076405c3
+ VLSEG7E64FFV (X10), V8 // 077405c3
+ VLSEG7E8FFV (X10), V0, V8 // 070405c1
+ VLSEG7E16FFV (X10), V0, V8 // 075405c1
+ VLSEG7E32FFV (X10), V0, V8 // 076405c1
+ VLSEG7E64FFV (X10), V0, V8 // 077405c1
+
+ VLSEG8E8FFV (X10), V8 // 070405e3
+ VLSEG8E16FFV (X10), V8 // 075405e3
+ VLSEG8E32FFV (X10), V8 // 076405e3
+ VLSEG8E64FFV (X10), V8 // 077405e3
+ VLSEG8E8FFV (X10), V0, V8 // 070405e1
+ VLSEG8E16FFV (X10), V0, V8 // 075405e1
+ VLSEG8E32FFV (X10), V0, V8 // 076405e1
+ VLSEG8E64FFV (X10), V0, V8 // 077405e1
+
+ // 31.7.8.2: Vector Strided Segment Loads and Stores
+ VLSSEG2E8V (X10), X11, V8 // 0704b52a
+ VLSSEG2E16V (X10), X11, V8 // 0754b52a
+ VLSSEG2E32V (X10), X11, V8 // 0764b52a
+ VLSSEG2E64V (X10), X11, V8 // 0774b52a
+ VLSSEG2E8V (X10), X11, V0, V8 // 0704b528
+ VLSSEG2E16V (X10), X11, V0, V8 // 0754b528
+ VLSSEG2E32V (X10), X11, V0, V8 // 0764b528
+ VLSSEG2E64V (X10), X11, V0, V8 // 0774b528
+
+ VLSSEG3E8V (X10), X11, V8 // 0704b54a
+ VLSSEG3E16V (X10), X11, V8 // 0754b54a
+ VLSSEG3E32V (X10), X11, V8 // 0764b54a
+ VLSSEG3E64V (X10), X11, V8 // 0774b54a
+ VLSSEG3E8V (X10), X11, V0, V8 // 0704b548
+ VLSSEG3E16V (X10), X11, V0, V8 // 0754b548
+ VLSSEG3E32V (X10), X11, V0, V8 // 0764b548
+ VLSSEG3E64V (X10), X11, V0, V8 // 0774b548
+
+ VLSSEG4E8V (X10), X11, V8 // 0704b56a
+ VLSSEG4E16V (X10), X11, V8 // 0754b56a
+ VLSSEG4E32V (X10), X11, V8 // 0764b56a
+ VLSSEG4E64V (X10), X11, V8 // 0774b56a
+ VLSSEG4E8V (X10), X11, V0, V8 // 0704b568
+ VLSSEG4E16V (X10), X11, V0, V8 // 0754b568
+ VLSSEG4E32V (X10), X11, V0, V8 // 0764b568
+ VLSSEG4E64V (X10), X11, V0, V8 // 0774b568
+
+ VLSSEG5E8V (X10), X11, V8 // 0704b58a
+ VLSSEG5E16V (X10), X11, V8 // 0754b58a
+ VLSSEG5E32V (X10), X11, V8 // 0764b58a
+ VLSSEG5E64V (X10), X11, V8 // 0774b58a
+ VLSSEG5E8V (X10), X11, V0, V8 // 0704b588
+ VLSSEG5E16V (X10), X11, V0, V8 // 0754b588
+ VLSSEG5E32V (X10), X11, V0, V8 // 0764b588
+ VLSSEG5E64V (X10), X11, V0, V8 // 0774b588
+
+ VLSSEG6E8V (X10), X11, V8 // 0704b5aa
+ VLSSEG6E16V (X10), X11, V8 // 0754b5aa
+ VLSSEG6E32V (X10), X11, V8 // 0764b5aa
+ VLSSEG6E64V (X10), X11, V8 // 0774b5aa
+ VLSSEG6E8V (X10), X11, V0, V8 // 0704b5a8
+ VLSSEG6E16V (X10), X11, V0, V8 // 0754b5a8
+ VLSSEG6E32V (X10), X11, V0, V8 // 0764b5a8
+ VLSSEG6E64V (X10), X11, V0, V8 // 0774b5a8
+
+ VLSSEG7E8V (X10), X11, V8 // 0704b5ca
+ VLSSEG7E16V (X10), X11, V8 // 0754b5ca
+ VLSSEG7E32V (X10), X11, V8 // 0764b5ca
+ VLSSEG7E64V (X10), X11, V8 // 0774b5ca
+ VLSSEG7E8V (X10), X11, V0, V8 // 0704b5c8
+ VLSSEG7E16V (X10), X11, V0, V8 // 0754b5c8
+ VLSSEG7E32V (X10), X11, V0, V8 // 0764b5c8
+ VLSSEG7E64V (X10), X11, V0, V8 // 0774b5c8
+
+ VLSSEG8E8V (X10), X11, V8 // 0704b5ea
+ VLSSEG8E16V (X10), X11, V8 // 0754b5ea
+ VLSSEG8E32V (X10), X11, V8 // 0764b5ea
+ VLSSEG8E64V (X10), X11, V8 // 0774b5ea
+ VLSSEG8E8V (X10), X11, V0, V8 // 0704b5e8
+ VLSSEG8E16V (X10), X11, V0, V8 // 0754b5e8
+ VLSSEG8E32V (X10), X11, V0, V8 // 0764b5e8
+ VLSSEG8E64V (X10), X11, V0, V8 // 0774b5e8
+
+ VSSSEG2E8V V24, X11, (X10) // 270cb52a
+ VSSSEG2E16V V24, X11, (X10) // 275cb52a
+ VSSSEG2E32V V24, X11, (X10) // 276cb52a
+ VSSSEG2E64V V24, X11, (X10) // 277cb52a
+ VSSSEG2E8V V24, X11, V0, (X10) // 270cb528
+ VSSSEG2E16V V24, X11, V0, (X10) // 275cb528
+ VSSSEG2E32V V24, X11, V0, (X10) // 276cb528
+ VSSSEG2E64V V24, X11, V0, (X10) // 277cb528
+
+ VSSSEG3E8V V24, X11, (X10) // 270cb54a
+ VSSSEG3E16V V24, X11, (X10) // 275cb54a
+ VSSSEG3E32V V24, X11, (X10) // 276cb54a
+ VSSSEG3E64V V24, X11, (X10) // 277cb54a
+ VSSSEG3E8V V24, X11, V0, (X10) // 270cb548
+ VSSSEG3E16V V24, X11, V0, (X10) // 275cb548
+ VSSSEG3E32V V24, X11, V0, (X10) // 276cb548
+ VSSSEG3E64V V24, X11, V0, (X10) // 277cb548
+
+ VSSSEG4E8V V24, X11, (X10) // 270cb56a
+ VSSSEG4E16V V24, X11, (X10) // 275cb56a
+ VSSSEG4E32V V24, X11, (X10) // 276cb56a
+ VSSSEG4E64V V24, X11, (X10) // 277cb56a
+ VSSSEG4E8V V24, X11, V0, (X10) // 270cb568
+ VSSSEG4E16V V24, X11, V0, (X10) // 275cb568
+ VSSSEG4E32V V24, X11, V0, (X10) // 276cb568
+ VSSSEG4E64V V24, X11, V0, (X10) // 277cb568
+
+ VSSSEG5E8V V24, X11, (X10) // 270cb58a
+ VSSSEG5E16V V24, X11, (X10) // 275cb58a
+ VSSSEG5E32V V24, X11, (X10) // 276cb58a
+ VSSSEG5E64V V24, X11, (X10) // 277cb58a
+ VSSSEG5E8V V24, X11, V0, (X10) // 270cb588
+ VSSSEG5E16V V24, X11, V0, (X10) // 275cb588
+ VSSSEG5E32V V24, X11, V0, (X10) // 276cb588
+ VSSSEG5E64V V24, X11, V0, (X10) // 277cb588
+
+ VSSSEG6E8V V24, X11, (X10) // 270cb5aa
+ VSSSEG6E16V V24, X11, (X10) // 275cb5aa
+ VSSSEG6E32V V24, X11, (X10) // 276cb5aa
+ VSSSEG6E64V V24, X11, (X10) // 277cb5aa
+ VSSSEG6E8V V24, X11, V0, (X10) // 270cb5a8
+ VSSSEG6E16V V24, X11, V0, (X10) // 275cb5a8
+ VSSSEG6E32V V24, X11, V0, (X10) // 276cb5a8
+ VSSSEG6E64V V24, X11, V0, (X10) // 277cb5a8
+
+ VSSSEG7E8V V24, X11, (X10) // 270cb5ca
+ VSSSEG7E16V V24, X11, (X10) // 275cb5ca
+ VSSSEG7E32V V24, X11, (X10) // 276cb5ca
+ VSSSEG7E64V V24, X11, (X10) // 277cb5ca
+ VSSSEG7E8V V24, X11, V0, (X10) // 270cb5c8
+ VSSSEG7E16V V24, X11, V0, (X10) // 275cb5c8
+ VSSSEG7E32V V24, X11, V0, (X10) // 276cb5c8
+ VSSSEG7E64V V24, X11, V0, (X10) // 277cb5c8
+
+ VSSSEG8E8V V24, X11, (X10) // 270cb5ea
+ VSSSEG8E16V V24, X11, (X10) // 275cb5ea
+ VSSSEG8E32V V24, X11, (X10) // 276cb5ea
+ VSSSEG8E64V V24, X11, (X10) // 277cb5ea
+ VSSSEG8E8V V24, X11, V0, (X10) // 270cb5e8
+ VSSSEG8E16V V24, X11, V0, (X10) // 275cb5e8
+ VSSSEG8E32V V24, X11, V0, (X10) // 276cb5e8
+ VSSSEG8E64V V24, X11, V0, (X10) // 277cb5e8
+
+ // 31.7.8.3: Vector Indexed Segment Loads and Stores
+
+ VLUXSEG2EI8V (X10), V4, V8 // 07044526
+ VLUXSEG2EI16V (X10), V4, V8 // 07544526
+ VLUXSEG2EI32V (X10), V4, V8 // 07644526
+ VLUXSEG2EI64V (X10), V4, V8 // 07744526
+ VLUXSEG2EI8V (X10), V4, V0, V8 // 07044524
+ VLUXSEG2EI16V (X10), V4, V0, V8 // 07544524
+ VLUXSEG2EI32V (X10), V4, V0, V8 // 07644524
+ VLUXSEG2EI64V (X10), V4, V0, V8 // 07744524
+
+ VLUXSEG3EI8V (X10), V4, V8 // 07044546
+ VLUXSEG3EI16V (X10), V4, V8 // 07544546
+ VLUXSEG3EI32V (X10), V4, V8 // 07644546
+ VLUXSEG3EI64V (X10), V4, V8 // 07744546
+ VLUXSEG3EI8V (X10), V4, V0, V8 // 07044544
+ VLUXSEG3EI16V (X10), V4, V0, V8 // 07544544
+ VLUXSEG3EI32V (X10), V4, V0, V8 // 07644544
+ VLUXSEG3EI64V (X10), V4, V0, V8 // 07744544
+
+ VLUXSEG4EI8V (X10), V4, V8 // 07044566
+ VLUXSEG4EI16V (X10), V4, V8 // 07544566
+ VLUXSEG4EI32V (X10), V4, V8 // 07644566
+ VLUXSEG4EI64V (X10), V4, V8 // 07744566
+ VLUXSEG4EI8V (X10), V4, V0, V8 // 07044564
+ VLUXSEG4EI16V (X10), V4, V0, V8 // 07544564
+ VLUXSEG4EI32V (X10), V4, V0, V8 // 07644564
+ VLUXSEG4EI64V (X10), V4, V0, V8 // 07744564
+
+ VLUXSEG5EI8V (X10), V4, V8 // 07044586
+ VLUXSEG5EI16V (X10), V4, V8 // 07544586
+ VLUXSEG5EI32V (X10), V4, V8 // 07644586
+ VLUXSEG5EI64V (X10), V4, V8 // 07744586
+ VLUXSEG5EI8V (X10), V4, V0, V8 // 07044584
+ VLUXSEG5EI16V (X10), V4, V0, V8 // 07544584
+ VLUXSEG5EI32V (X10), V4, V0, V8 // 07644584
+ VLUXSEG5EI64V (X10), V4, V0, V8 // 07744584
+
+ VLUXSEG6EI8V (X10), V4, V8 // 070445a6
+ VLUXSEG6EI16V (X10), V4, V8 // 075445a6
+ VLUXSEG6EI32V (X10), V4, V8 // 076445a6
+ VLUXSEG6EI64V (X10), V4, V8 // 077445a6
+ VLUXSEG6EI8V (X10), V4, V0, V8 // 070445a4
+ VLUXSEG6EI16V (X10), V4, V0, V8 // 075445a4
+ VLUXSEG6EI32V (X10), V4, V0, V8 // 076445a4
+ VLUXSEG6EI64V (X10), V4, V0, V8 // 077445a4
+
+ VLOXSEG6EI8V (X10), V4, V8 // 070445ae
+ VLOXSEG6EI16V (X10), V4, V8 // 075445ae
+ VLOXSEG6EI32V (X10), V4, V8 // 076445ae
+ VLOXSEG6EI64V (X10), V4, V8 // 077445ae
+ VLOXSEG6EI8V (X10), V4, V0, V8 // 070445ac
+ VLOXSEG6EI16V (X10), V4, V0, V8 // 075445ac
+ VLOXSEG6EI32V (X10), V4, V0, V8 // 076445ac
+ VLOXSEG6EI64V (X10), V4, V0, V8 // 077445ac
+
+ VLUXSEG7EI8V (X10), V4, V8 // 070445c6
+ VLUXSEG7EI16V (X10), V4, V8 // 075445c6
+ VLUXSEG7EI32V (X10), V4, V8 // 076445c6
+ VLUXSEG7EI64V (X10), V4, V8 // 077445c6
+ VLUXSEG7EI8V (X10), V4, V0, V8 // 070445c4
+ VLUXSEG7EI16V (X10), V4, V0, V8 // 075445c4
+ VLUXSEG7EI32V (X10), V4, V0, V8 // 076445c4
+ VLUXSEG7EI64V (X10), V4, V0, V8 // 077445c4
+
+ VLUXSEG8EI8V (X10), V4, V8 // 070445e6
+ VLUXSEG8EI16V (X10), V4, V8 // 075445e6
+ VLUXSEG8EI32V (X10), V4, V8 // 076445e6
+ VLUXSEG8EI64V (X10), V4, V8 // 077445e6
+ VLUXSEG8EI8V (X10), V4, V0, V8 // 070445e4
+ VLUXSEG8EI16V (X10), V4, V0, V8 // 075445e4
+ VLUXSEG8EI32V (X10), V4, V0, V8 // 076445e4
+ VLUXSEG8EI64V (X10), V4, V0, V8 // 077445e4
+
+ VSUXSEG2EI8V V24, V4, (X10) // 270c4526
+ VSUXSEG2EI16V V24, V4, (X10) // 275c4526
+ VSUXSEG2EI32V V24, V4, (X10) // 276c4526
+ VSUXSEG2EI64V V24, V4, (X10) // 277c4526
+ VSUXSEG2EI8V V24, V4, V0, (X10) // 270c4524
+ VSUXSEG2EI16V V24, V4, V0, (X10) // 275c4524
+ VSUXSEG2EI32V V24, V4, V0, (X10) // 276c4524
+ VSUXSEG2EI64V V24, V4, V0, (X10) // 277c4524
+
+ VSUXSEG3EI8V V24, V4, (X10) // 270c4546
+ VSUXSEG3EI16V V24, V4, (X10) // 275c4546
+ VSUXSEG3EI32V V24, V4, (X10) // 276c4546
+ VSUXSEG3EI64V V24, V4, (X10) // 277c4546
+ VSUXSEG3EI8V V24, V4, V0, (X10) // 270c4544
+ VSUXSEG3EI16V V24, V4, V0, (X10) // 275c4544
+ VSUXSEG3EI32V V24, V4, V0, (X10) // 276c4544
+ VSUXSEG3EI64V V24, V4, V0, (X10) // 277c4544
+
+ VSUXSEG4EI8V V24, V4, (X10) // 270c4566
+ VSUXSEG4EI16V V24, V4, (X10) // 275c4566
+ VSUXSEG4EI32V V24, V4, (X10) // 276c4566
+ VSUXSEG4EI64V V24, V4, (X10) // 277c4566
+ VSUXSEG4EI8V V24, V4, V0, (X10) // 270c4564
+ VSUXSEG4EI16V V24, V4, V0, (X10) // 275c4564
+ VSUXSEG4EI32V V24, V4, V0, (X10) // 276c4564
+ VSUXSEG4EI64V V24, V4, V0, (X10) // 277c4564
+
+ VSUXSEG5EI8V V24, V4, (X10) // 270c4586
+ VSUXSEG5EI16V V24, V4, (X10) // 275c4586
+ VSUXSEG5EI32V V24, V4, (X10) // 276c4586
+ VSUXSEG5EI64V V24, V4, (X10) // 277c4586
+ VSUXSEG5EI8V V24, V4, V0, (X10) // 270c4584
+ VSUXSEG5EI16V V24, V4, V0, (X10) // 275c4584
+ VSUXSEG5EI32V V24, V4, V0, (X10) // 276c4584
+ VSUXSEG5EI64V V24, V4, V0, (X10) // 277c4584
+
+ VSUXSEG6EI8V V24, V4, (X10) // 270c45a6
+ VSUXSEG6EI16V V24, V4, (X10) // 275c45a6
+ VSUXSEG6EI32V V24, V4, (X10) // 276c45a6
+ VSUXSEG6EI64V V24, V4, (X10) // 277c45a6
+ VSUXSEG6EI8V V24, V4, V0, (X10) // 270c45a4
+ VSUXSEG6EI16V V24, V4, V0, (X10) // 275c45a4
+ VSUXSEG6EI32V V24, V4, V0, (X10) // 276c45a4
+ VSUXSEG6EI64V V24, V4, V0, (X10) // 277c45a4
+
+ VSUXSEG7EI8V V24, V4, (X10) // 270c45c6
+ VSUXSEG7EI16V V24, V4, (X10) // 275c45c6
+ VSUXSEG7EI32V V24, V4, (X10) // 276c45c6
+ VSUXSEG7EI64V V24, V4, (X10) // 277c45c6
+ VSUXSEG7EI8V V24, V4, V0, (X10) // 270c45c4
+ VSUXSEG7EI16V V24, V4, V0, (X10) // 275c45c4
+ VSUXSEG7EI32V V24, V4, V0, (X10) // 276c45c4
+ VSUXSEG7EI64V V24, V4, V0, (X10) // 277c45c4
+
+ VSUXSEG8EI8V V24, V4, (X10) // 270c45e6
+ VSUXSEG8EI16V V24, V4, (X10) // 275c45e6
+ VSUXSEG8EI32V V24, V4, (X10) // 276c45e6
+ VSUXSEG8EI64V V24, V4, (X10) // 277c45e6
+ VSUXSEG8EI8V V24, V4, V0, (X10) // 270c45e4
+ VSUXSEG8EI16V V24, V4, V0, (X10) // 275c45e4
+ VSUXSEG8EI32V V24, V4, V0, (X10) // 276c45e4
+ VSUXSEG8EI64V V24, V4, V0, (X10) // 277c45e4
+
+ VLOXSEG2EI8V (X10), V4, V8 // 0704452e
+ VLOXSEG2EI16V (X10), V4, V8 // 0754452e
+ VLOXSEG2EI32V (X10), V4, V8 // 0764452e
+ VLOXSEG2EI64V (X10), V4, V8 // 0774452e
+ VLOXSEG2EI8V (X10), V4, V0, V8 // 0704452c
+ VLOXSEG2EI16V (X10), V4, V0, V8 // 0754452c
+ VLOXSEG2EI32V (X10), V4, V0, V8 // 0764452c
+ VLOXSEG2EI64V (X10), V4, V0, V8 // 0774452c
+
+ VLOXSEG3EI8V (X10), V4, V8 // 0704454e
+ VLOXSEG3EI16V (X10), V4, V8 // 0754454e
+ VLOXSEG3EI32V (X10), V4, V8 // 0764454e
+ VLOXSEG3EI64V (X10), V4, V8 // 0774454e
+ VLOXSEG3EI8V (X10), V4, V0, V8 // 0704454c
+ VLOXSEG3EI16V (X10), V4, V0, V8 // 0754454c
+ VLOXSEG3EI32V (X10), V4, V0, V8 // 0764454c
+ VLOXSEG3EI64V (X10), V4, V0, V8 // 0774454c
+ VLOXSEG4EI8V (X10), V4, V8 // 0704456e
+ VLOXSEG4EI16V (X10), V4, V8 // 0754456e
+ VLOXSEG4EI32V (X10), V4, V8 // 0764456e
+ VLOXSEG4EI64V (X10), V4, V8 // 0774456e
+ VLOXSEG4EI8V (X10), V4, V0, V8 // 0704456c
+ VLOXSEG4EI16V (X10), V4, V0, V8 // 0754456c
+ VLOXSEG4EI32V (X10), V4, V0, V8 // 0764456c
+ VLOXSEG4EI64V (X10), V4, V0, V8 // 0774456c
+
+ VLOXSEG5EI8V (X10), V4, V8 // 0704458e
+ VLOXSEG5EI16V (X10), V4, V8 // 0754458e
+ VLOXSEG5EI32V (X10), V4, V8 // 0764458e
+ VLOXSEG5EI64V (X10), V4, V8 // 0774458e
+ VLOXSEG5EI8V (X10), V4, V0, V8 // 0704458c
+ VLOXSEG5EI16V (X10), V4, V0, V8 // 0754458c
+ VLOXSEG5EI32V (X10), V4, V0, V8 // 0764458c
+ VLOXSEG5EI64V (X10), V4, V0, V8 // 0774458c
+
+ VLOXSEG7EI8V (X10), V4, V8 // 070445ce
+ VLOXSEG7EI16V (X10), V4, V8 // 075445ce
+ VLOXSEG7EI32V (X10), V4, V8 // 076445ce
+ VLOXSEG7EI64V (X10), V4, V8 // 077445ce
+ VLOXSEG7EI8V (X10), V4, V0, V8 // 070445cc
+ VLOXSEG7EI16V (X10), V4, V0, V8 // 075445cc
+ VLOXSEG7EI32V (X10), V4, V0, V8 // 076445cc
+ VLOXSEG7EI64V (X10), V4, V0, V8 // 077445cc
+
+ VLOXSEG8EI8V (X10), V4, V8 // 070445ee
+ VLOXSEG8EI16V (X10), V4, V8 // 075445ee
+ VLOXSEG8EI32V (X10), V4, V8 // 076445ee
+ VLOXSEG8EI64V (X10), V4, V8 // 077445ee
+ VLOXSEG8EI8V (X10), V4, V0, V8 // 070445ec
+ VLOXSEG8EI16V (X10), V4, V0, V8 // 075445ec
+ VLOXSEG8EI32V (X10), V4, V0, V8 // 076445ec
+ VLOXSEG8EI64V (X10), V4, V0, V8 // 077445ec
+
+ VSOXSEG2EI8V V24, V4, (X10) // 270c452e
+ VSOXSEG2EI16V V24, V4, (X10) // 275c452e
+ VSOXSEG2EI32V V24, V4, (X10) // 276c452e
+ VSOXSEG2EI64V V24, V4, (X10) // 277c452e
+ VSOXSEG2EI8V V24, V4, V0, (X10) // 270c452c
+ VSOXSEG2EI16V V24, V4, V0, (X10) // 275c452c
+ VSOXSEG2EI32V V24, V4, V0, (X10) // 276c452c
+ VSOXSEG2EI64V V24, V4, V0, (X10) // 277c452c
+
+ VSOXSEG3EI8V V24, V4, (X10) // 270c454e
+ VSOXSEG3EI16V V24, V4, (X10) // 275c454e
+ VSOXSEG3EI32V V24, V4, (X10) // 276c454e
+ VSOXSEG3EI64V V24, V4, (X10) // 277c454e
+ VSOXSEG3EI8V V24, V4, V0, (X10) // 270c454c
+ VSOXSEG3EI16V V24, V4, V0, (X10) // 275c454c
+ VSOXSEG3EI32V V24, V4, V0, (X10) // 276c454c
+ VSOXSEG3EI64V V24, V4, V0, (X10) // 277c454c
+
+ VSOXSEG4EI8V V24, V4, (X10) // 270c456e
+ VSOXSEG4EI16V V24, V4, (X10) // 275c456e
+ VSOXSEG4EI32V V24, V4, (X10) // 276c456e
+ VSOXSEG4EI64V V24, V4, (X10) // 277c456e
+ VSOXSEG4EI8V V24, V4, V0, (X10) // 270c456c
+ VSOXSEG4EI16V V24, V4, V0, (X10) // 275c456c
+ VSOXSEG4EI32V V24, V4, V0, (X10) // 276c456c
+ VSOXSEG4EI64V V24, V4, V0, (X10) // 277c456c
+
+ VSOXSEG5EI8V V24, V4, (X10) // 270c458e
+ VSOXSEG5EI16V V24, V4, (X10) // 275c458e
+ VSOXSEG5EI32V V24, V4, (X10) // 276c458e
+ VSOXSEG5EI64V V24, V4, (X10) // 277c458e
+ VSOXSEG5EI8V V24, V4, V0, (X10) // 270c458c
+ VSOXSEG5EI16V V24, V4, V0, (X10) // 275c458c
+ VSOXSEG5EI32V V24, V4, V0, (X10) // 276c458c
+ VSOXSEG5EI64V V24, V4, V0, (X10) // 277c458c
+
+ VSOXSEG6EI8V V24, V4, (X10) // 270c45ae
+ VSOXSEG6EI16V V24, V4, (X10) // 275c45ae
+ VSOXSEG6EI32V V24, V4, (X10) // 276c45ae
+ VSOXSEG6EI64V V24, V4, (X10) // 277c45ae
+ VSOXSEG6EI8V V24, V4, V0, (X10) // 270c45ac
+ VSOXSEG6EI16V V24, V4, V0, (X10) // 275c45ac
+ VSOXSEG6EI32V V24, V4, V0, (X10) // 276c45ac
+ VSOXSEG6EI64V V24, V4, V0, (X10) // 277c45ac
+
+ VSOXSEG7EI8V V24, V4, (X10) // 270c45ce
+ VSOXSEG7EI16V V24, V4, (X10) // 275c45ce
+ VSOXSEG7EI32V V24, V4, (X10) // 276c45ce
+ VSOXSEG7EI64V V24, V4, (X10) // 277c45ce
+ VSOXSEG7EI8V V24, V4, V0, (X10) // 270c45cc
+ VSOXSEG7EI16V V24, V4, V0, (X10) // 275c45cc
+ VSOXSEG7EI32V V24, V4, V0, (X10) // 276c45cc
+ VSOXSEG7EI64V V24, V4, V0, (X10) // 277c45cc
+
+ VSOXSEG8EI8V V24, V4, (X10) // 270c45ee
+ VSOXSEG8EI16V V24, V4, (X10) // 275c45ee
+ VSOXSEG8EI32V V24, V4, (X10) // 276c45ee
+ VSOXSEG8EI64V V24, V4, (X10) // 277c45ee
+ VSOXSEG8EI8V V24, V4, V0, (X10) // 270c45ec
+ VSOXSEG8EI16V V24, V4, V0, (X10) // 275c45ec
+ VSOXSEG8EI32V V24, V4, V0, (X10) // 276c45ec
+ VSOXSEG8EI64V V24, V4, V0, (X10) // 277c45ec
+
+ // 31.7.9: Vector Load/Store Whole Register Instructions
+ VL1RV (X10), V3 // 87018502
+ VL1RE8V (X10), V3 // 87018502
+ VL1RE16V (X10), V3 // 87518502
+ VL1RE32V (X10), V3 // 87618502
+ VL1RE64V (X10), V3 // 87718502
+ VL2RV (X10), V2 // 07018522
+ VL2RE8V (X10), V2 // 07018522
+ VL2RE16V (X10), V2 // 07518522
+ VL2RE32V (X10), V2 // 07618522
+ VL2RE64V (X10), V2 // 07718522
+ VL4RV (X10), V4 // 07028562
+ VL4RE8V (X10), V4 // 07028562
+ VL4RE16V (X10), V4 // 07528562
+ VL4RE32V (X10), V4 // 07628562
+ VL4RE64V (X10), V4 // 07728562
+ VL8RV (X10), V8 // 070485e2
+ VL8RE8V (X10), V8 // 070485e2
+ VL8RE16V (X10), V8 // 075485e2
+ VL8RE32V (X10), V8 // 076485e2
+ VL8RE64V (X10), V8 // 077485e2
+ VS1RV V3, (X11) // a7818502
+ VS2RV V2, (X11) // 27818522
+ VS4RV V4, (X11) // 27828562
+ VS8RV V8, (X11) // 278485e2
+
+ // 31.11.1: Vector Single-Width Integer Add and Subtract
+ VADDVV V1, V2, V3 // d7812002
+ VADDVV V1, V2, V0, V3 // d7812000
+ VADDVX X10, V2, V3 // d7412502
+ VADDVX X10, V2, V0, V3 // d7412500
+ VADDVI $15, V2, V3 // d7b12702
+ VADDVI $15, V2, V0, V3 // d7b12700
+ VADDVI $-16, V2, V3 // d7312802
+ VADDVI $-16, V2, V0, V3 // d7312800
+ VSUBVV V1, V2, V3 // d781200a
+ VSUBVV V1, V2, V0, V3 // d7812008
+ VSUBVX X10, V2, V3 // d741250a
+ VSUBVX X10, V2, V0, V3 // d7412508
+ VRSUBVX X10, V2, V3 // d741250e
+ VRSUBVX X10, V2, V0, V3 // d741250c
+ VRSUBVI $15, V2, V0, V3 // d7b1270c
+ VRSUBVI $-16, V2, V0, V3 // d731280c
+ VNEGV V2, V3 // d741200e
+ VNEGV V2, V0, V3 // d741200c
+
+ // 31.11.2: Vector Widening Integer Add/Subtract
+ VWADDUVV V1, V2, V3 // d7a120c2
+ VWADDUVV V1, V2, V0, V3 // d7a120c0
+ VWADDUVX X10, V2, V3 // d76125c2
+ VWADDUVX X10, V2, V0, V3 // d76125c0
+ VWSUBUVV V1, V2, V3 // d7a120ca
+ VWSUBUVV V1, V2, V0, V3 // d7a120c8
+ VWSUBUVX X10, V2, V3 // d76125ca
+ VWSUBUVX X10, V2, V0, V3 // d76125c8
+ VWADDVV V1, V2, V3 // d7a120c6
+ VWADDVV V1, V2, V0, V3 // d7a120c4
+ VWADDVX X10, V2, V3 // d76125c6
+ VWADDVX X10, V2, V0, V3 // d76125c4
+ VWSUBVV V1, V2, V3 // d7a120ce
+ VWSUBVV V1, V2, V0, V3 // d7a120cc
+ VWSUBVX X10, V2, V3 // d76125ce
+ VWSUBVX X10, V2, V0, V3 // d76125cc
+ VWADDUWV V1, V2, V3 // d7a120d2
+ VWADDUWV V1, V2, V0, V3 // d7a120d0
+ VWADDUWX X10, V2, V3 // d76125d2
+ VWADDUWX X10, V2, V0, V3 // d76125d0
+ VWSUBUWV V1, V2, V3 // d7a120da
+ VWSUBUWV V1, V2, V0, V3 // d7a120d8
+ VWSUBUWX X10, V2, V3 // d76125da
+ VWSUBUWX X10, V2, V0, V3 // d76125d8
+ VWADDWV V1, V2, V3 // d7a120d6
+ VWADDWV V1, V2, V0, V3 // d7a120d4
+ VWADDWX X10, V2, V3 // d76125d6
+ VWADDWX X10, V2, V0, V3 // d76125d4
+ VWSUBWV V1, V2, V3 // d7a120de
+ VWSUBWV V1, V2, V0, V3 // d7a120dc
+ VWSUBWX X10, V2, V3 // d76125de
+ VWSUBWX X10, V2, V0, V3 // d76125dc
+ VWCVTXXV V2, V3 // d76120c6
+ VWCVTXXV V2, V0, V3 // d76120c4
+ VWCVTUXXV V2, V3 // d76120c2
+ VWCVTUXXV V2, V0, V3 // d76120c0
+
+ // 31.11.3: Vector Integer Extension
+ VZEXTVF2 V2, V3 // d721234a
+ VZEXTVF2 V2, V0, V3 // d7212348
+ VSEXTVF2 V2, V3 // d7a1234a
+ VSEXTVF2 V2, V0, V3 // d7a12348
+ VZEXTVF4 V2, V3 // d721224a
+ VZEXTVF4 V2, V0, V3 // d7212248
+ VSEXTVF4 V2, V3 // d7a1224a
+ VSEXTVF4 V2, V0, V3 // d7a12248
+ VZEXTVF8 V2, V3 // d721214a
+ VZEXTVF8 V2, V0, V3 // d7212148
+ VSEXTVF8 V2, V3 // d7a1214a
+ VSEXTVF8 V2, V0, V3 // d7a12148
+
+ // 31.11.4: Vector Integer Add-with-Carry / Subtract-with-Borrow Instructions
+ VADCVVM V1, V2, V0, V3 // d7812040
+ VADCVXM X11, V2, V0, V3 // d7c12540
+ VADCVIM $15, V2, V0, V3 // d7b12740
+ VMADCVVM V1, V2, V0, V3 // d7812044
+ VMADCVVM V1, V2, V0, V0 // 57802044
+ VMADCVXM X11, V2, V0, V3 // d7c12544
+ VMADCVXM X11, V2, V0, V0 // 57c02544
+ VMADCVIM $15, V2, V0, V3 // d7b12744
+ VMADCVIM $15, V2, V0, V0 // 57b02744
+ VMADCVV V1, V2, V3 // d7812046
+ VMADCVV V1, V2, V0 // 57802046
+ VMADCVX X11, V2, V3 // d7c12546
+ VMADCVX X11, V2, V0 // 57c02546
+ VMADCVI $15, V2, V3 // d7b12746
+ VMADCVI $15, V2, V0 // 57b02746
+ VSBCVVM V1, V2, V0, V3 // d7812048
+ VSBCVXM X11, V2, V0, V3 // d7c12548
+ VMSBCVVM V1, V2, V0, V3 // d781204c
+ VMSBCVVM V1, V2, V0, V0 // 5780204c
+ VMSBCVXM X11, V2, V0, V3 // d7c1254c
+ VMSBCVXM X11, V2, V0, V0 // 57c0254c
+ VMSBCVV V1, V2, V3 // d781204e
+ VMSBCVV V1, V2, V0 // 5780204e
+ VMSBCVX X11, V2, V3 // d7c1254e
+ VMSBCVX X11, V2, V0 // 57c0254e
+
+ // 31.11.5: Vector Bitwise Logical Instructions
+ VANDVV V1, V2, V3 // d7812026
+ VANDVV V1, V2, V0, V3 // d7812024
+ VANDVX X11, V2, V3 // d7c12526
+ VANDVX X11, V2, V0, V3 // d7c12524
+ VANDVI $15, V2, V3 // d7b12726
+ VANDVI $15, V2, V0, V3 // d7b12724
+ VORVV V1, V2, V3 // d781202a
+ VORVV V1, V2, V0, V3 // d7812028
+ VORVX X11, V2, V3 // d7c1252a
+ VORVX X11, V2, V0, V3 // d7c12528
+ VORVI $15, V2, V3 // d7b1272a
+ VORVI $15, V2, V0, V3 // d7b12728
+ VXORVV V1, V2, V3 // d781202e
+ VXORVV V1, V2, V0, V3 // d781202c
+ VXORVX X11, V2, V3 // d7c1252e
+ VXORVX X11, V2, V0, V3 // d7c1252c
+ VXORVI $15, V2, V3 // d7b1272e
+ VXORVI $15, V2, V0, V3 // d7b1272c
+ VNOTV V2, V3 // d7b12f2e
+ VNOTV V2, V0, V3 // d7b12f2c
+
+ // 31.11.6: Vector Single-Width Shift Instructions
+ VSLLVV V1, V2, V3 // d7812096
+ VSLLVV V1, V2, V0, V3 // d7812094
+ VSLLVX X11, V2, V3 // d7c12596
+ VSLLVX X11, V2, V0, V3 // d7c12594
+ VSLLVI $15, V2, V3 // d7b12796
+ VSLLVI $15, V2, V0, V3 // d7b12794
+ VSRLVV V1, V2, V3 // d78120a2
+ VSRLVV V1, V2, V0, V3 // d78120a0
+ VSRLVX X11, V2, V3 // d7c125a2
+ VSRLVX X11, V2, V0, V3 // d7c125a0
+ VSRLVI $15, V2, V3 // d7b127a2
+ VSRLVI $15, V2, V0, V3 // d7b127a0
+ VSRAVV V1, V2, V3 // d78120a6
+ VSRAVV V1, V2, V0, V3 // d78120a4
+ VSRAVX X11, V2, V3 // d7c125a6
+ VSRAVX X11, V2, V0, V3 // d7c125a4
+ VSRAVI $15, V2, V3 // d7b127a6
+ VSRAVI $15, V2, V0, V3 // d7b127a4
+
+ // 31.11.7: Vector Narrowing Integer Right Shift Instructions
+ VNSRLWV V1, V2, V3 // d78120b2
+ VNSRLWV V1, V2, V0, V3 // d78120b0
+ VNSRLWX X10, V2, V3 // d74125b2
+ VNSRLWX X10, V2, V0, V3 // d74125b0
+ VNSRLWI $31, V2, V3 // d7b12fb2
+ VNSRLWI $31, V2, V0, V3 // d7b12fb0
+ VNSRAWV V1, V2, V3 // d78120b6
+ VNSRAWV V1, V2, V0, V3 // d78120b4
+ VNSRAWX X10, V2, V3 // d74125b6
+ VNSRAWX X10, V2, V0, V3 // d74125b4
+ VNSRAWI $31, V2, V3 // d7b12fb6
+ VNSRAWI $31, V2, V0, V3 // d7b12fb4
+ VNCVTXXW V2, V3 // d74120b2
+ VNCVTXXW V2, V0, V3 // d74120b0
+
+ // 31.11.8: Vector Integer Compare Instructions
+ VMSEQVV V1, V2, V3 // d7812062
+ VMSEQVV V1, V2, V0, V3 // d7812060
+ VMSEQVX X10, V2, V3 // d7412562
+ VMSEQVX X10, V2, V0, V3 // d7412560
+ VMSEQVI $15, V2, V3 // d7b12762
+ VMSEQVI $15, V2, V0, V3 // d7b12760
+ VMSNEVV V1, V2, V3 // d7812066
+ VMSNEVV V1, V2, V0, V3 // d7812064
+ VMSNEVX X10, V2, V3 // d7412566
+ VMSNEVX X10, V2, V0, V3 // d7412564
+ VMSNEVI $15, V2, V3 // d7b12766
+ VMSNEVI $15, V2, V0, V3 // d7b12764
+ VMSLTUVV V1, V2, V3 // d781206a
+ VMSLTUVV V1, V2, V0, V3 // d7812068
+ VMSLTUVX X10, V2, V3 // d741256a
+ VMSLTUVX X10, V2, V0, V3 // d7412568
+ VMSLTVV V1, V2, V3 // d781206e
+ VMSLTVV V1, V2, V0, V3 // d781206c
+ VMSLTVX X10, V2, V3 // d741256e
+ VMSLTVX X10, V2, V0, V3 // d741256c
+ VMSLEUVV V1, V2, V3 // d7812072
+ VMSLEUVV V1, V2, V0, V3 // d7812070
+ VMSLEUVX X10, V2, V3 // d7412572
+ VMSLEUVX X10, V2, V0, V3 // d7412570
+ VMSLEUVI $15, V2, V3 // d7b12772
+ VMSLEUVI $15, V2, V0, V3 // d7b12770
+ VMSLEVV V1, V2, V3 // d7812076
+ VMSLEVV V1, V2, V0, V3 // d7812074
+ VMSLEVX X10, V2, V3 // d7412576
+ VMSLEVX X10, V2, V0, V3 // d7412574
+ VMSLEVI $15, V2, V3 // d7b12776
+ VMSLEVI $15, V2, V0, V3 // d7b12774
+ VMSGTUVX X10, V2, V3 // d741257a
+ VMSGTUVX X10, V2, V0, V3 // d7412578
+ VMSGTUVI $15, V2, V3 // d7b1277a
+ VMSGTUVI $15, V2, V0, V3 // d7b12778
+ VMSGTVX X10, V2, V3 // d741257e
+ VMSGTVX X10, V2, V0, V3 // d741257c
+ VMSGTVI $15, V2, V3 // d7b1277e
+ VMSGTVI $15, V2, V0, V3 // d7b1277c
+ VMSGTVV V1, V2, V3 // d701116e
+ VMSGTVV V1, V2, V0, V3 // d701116c
+ VMSGTUVV V1, V2, V3 // d701116a
+ VMSGTUVV V1, V2, V0, V3 // d7011168
+ VMSGEVV V1, V2, V3 // d7011176
+ VMSGEVV V1, V2, V0, V3 // d7011174
+ VMSGEUVV V1, V2, V3 // d7011172
+ VMSGEUVV V1, V2, V0, V3 // d7011170
+ VMSLTVI $15, V2, V3 // d7312776
+ VMSLTVI $15, V2, V0, V3 // d7312774
+ VMSLTUVI $15, V2, V3 // d7312772
+ VMSLTUVI $15, V2, V0, V3 // d7312770
+ VMSGEVI $15, V2, V3 // d731277e
+ VMSGEVI $15, V2, V0, V3 // d731277c
+ VMSGEUVI $15, V2, V3 // d731277a
+ VMSGEUVI $15, V2, V0, V3 // d7312778
+
+ // 31.11.9: Vector Integer Min/Max Instructions
+ VMINUVV V1, V2, V3 // d7812012
+ VMINUVV V1, V2, V0, V3 // d7812010
+ VMINUVX X10, V2, V3 // d7412512
+ VMINUVX X10, V2, V0, V3 // d7412510
+ VMINVV V1, V2, V3 // d7812016
+ VMINVV V1, V2, V0, V3 // d7812014
+ VMINVX X10, V2, V3 // d7412516
+ VMINVX X10, V2, V0, V3 // d7412514
+ VMAXUVV V1, V2, V3 // d781201a
+ VMAXUVV V1, V2, V0, V3 // d7812018
+ VMAXUVX X10, V2, V3 // d741251a
+ VMAXUVX X10, V2, V0, V3 // d7412518
+ VMAXVV V1, V2, V3 // d781201e
+ VMAXVV V1, V2, V0, V3 // d781201c
+ VMAXVX X10, V2, V3 // d741251e
+ VMAXVX X10, V2, V0, V3 // d741251c
+
+ // 31.11.10: Vector Single-Width Integer Multiply Instructions
+ VMULVV V1, V2, V3 // d7a12096
+ VMULVV V1, V2, V0, V3 // d7a12094
+ VMULVX X10, V2, V3 // d7612596
+ VMULVX X10, V2, V0, V3 // d7612594
+ VMULHVV V1, V2, V3 // d7a1209e
+ VMULHVV V1, V2, V0, V3 // d7a1209c
+ VMULHVX X10, V2, V3 // d761259e
+ VMULHVX X10, V2, V0, V3 // d761259c
+ VMULHUVV V1, V2, V3 // d7a12092
+ VMULHUVV V1, V2, V0, V3 // d7a12090
+ VMULHUVX X10, V2, V3 // d7612592
+ VMULHUVX X10, V2, V0, V3 // d7612590
+ VMULHSUVV V1, V2, V3 // d7a1209a
+ VMULHSUVV V1, V2, V0, V3 // d7a12098
+ VMULHSUVX X10, V2, V3 // d761259a
+ VMULHSUVX X10, V2, V0, V3 // d7612598
+
+ // 31.11.11: Vector Integer Divide Instructions
+ VDIVUVV V1, V2, V3 // d7a12082
+ VDIVUVV V1, V2, V0, V3 // d7a12080
+ VDIVUVX X10, V2, V3 // d7612582
+ VDIVUVX X10, V2, V0, V3 // d7612580
+ VDIVVV V1, V2, V3 // d7a12086
+ VDIVVV V1, V2, V0, V3 // d7a12084
+ VDIVVX X10, V2, V3 // d7612586
+ VDIVVX X10, V2, V0, V3 // d7612584
+ VREMUVV V1, V2, V3 // d7a1208a
+ VREMUVV V1, V2, V0, V3 // d7a12088
+ VREMUVX X10, V2, V3 // d761258a
+ VREMUVX X10, V2, V0, V3 // d7612588
+ VREMVV V1, V2, V3 // d7a1208e
+ VREMVV V1, V2, V0, V3 // d7a1208c
+ VREMVX X10, V2, V3 // d761258e
+ VREMVX X10, V2, V0, V3 // d761258c
+
+ // 31.11.12: Vector Widening Integer Multiply Instructions
+ VWMULVV V1, V2, V3 // d7a120ee
+ VWMULVV V1, V2, V0, V3 // d7a120ec
+ VWMULVX X10, V2, V3 // d76125ee
+ VWMULVX X10, V2, V0, V3 // d76125ec
+ VWMULUVV V1, V2, V3 // d7a120e2
+ VWMULUVV V1, V2, V0, V3 // d7a120e0
+ VWMULUVX X10, V2, V3 // d76125e2
+ VWMULUVX X10, V2, V0, V3 // d76125e0
+ VWMULSUVV V1, V2, V3 // d7a120ea
+ VWMULSUVV V1, V2, V0, V3 // d7a120e8
+ VWMULSUVX X10, V2, V3 // d76125ea
+ VWMULSUVX X10, V2, V0, V3 // d76125e8
+
+ // 31.11.13: Vector Single-Width Integer Multiply-Add Instructions
+ VMACCVV V2, V1, V3 // d7a120b6
+ VMACCVV V2, V1, V0, V3 // d7a120b4
+ VMACCVX V2, X10, V3 // d76125b6
+ VMACCVX V2, X10, V0, V3 // d76125b4
+ VNMSACVV V2, V1, V3 // d7a120be
+ VNMSACVV V2, V1, V0, V3 // d7a120bc
+ VNMSACVX V2, X10, V3 // d76125be
+ VNMSACVX V2, X10, V0, V3 // d76125bc
+ VMADDVV V2, V1, V3 // d7a120a6
+ VMADDVV V2, V1, V0, V3 // d7a120a4
+ VMADDVX V2, X10, V3 // d76125a6
+ VMADDVX V2, X10, V0, V3 // d76125a4
+ VNMSUBVV V2, V1, V3 // d7a120ae
+ VNMSUBVV V2, V1, V0, V3 // d7a120ac
+ VNMSUBVX V2, X10, V3 // d76125ae
+ VNMSUBVX V2, X10, V0, V3 // d76125ac
+
+ // 31.11.14: Vector Widening Integer Multiply-Add Instructions
+ VWMACCUVV V2, V1, V3 // d7a120f2
+ VWMACCUVV V2, V1, V0, V3 // d7a120f0
+ VWMACCUVX V2, X10, V3 // d76125f2
+ VWMACCUVX V2, X10, V0, V3 // d76125f0
+ VWMACCVV V2, V1, V3 // d7a120f6
+ VWMACCVV V2, V1, V0, V3 // d7a120f4
+ VWMACCVX V2, X10, V3 // d76125f6
+ VWMACCVX V2, X10, V0, V3 // d76125f4
+ VWMACCSUVV V2, V1, V3 // d7a120fe
+ VWMACCSUVV V2, V1, V0, V3 // d7a120fc
+ VWMACCSUVX V2, X10, V3 // d76125fe
+ VWMACCSUVX V2, X10, V0, V3 // d76125fc
+ VWMACCUSVX V2, X10, V3 // d76125fa
+ VWMACCUSVX V2, X10, V0, V3 // d76125f8
+
+ // 31.11.15: Vector Integer Merge Instructions
+ VMERGEVVM V1, V2, V0, V3 // d781205c
+ VMERGEVXM X10, V2, V0, V3 // d741255c
+ VMERGEVIM $15, V2, V0, V3 // d7b1275c
+
+ // 31.11.16: Vector Integer Move Instructions
+ VMVVV V2, V3 // d701015e
+ VMVVX X10, V3 // d741055e
+ VMVVI $15, V3 // d7b1075e
+
+ // 31.12.1: Vector Single-Width Saturating Add and Subtract
+ VSADDUVV V1, V2, V3 // d7812082
+ VSADDUVV V1, V2, V0, V3 // d7812080
+ VSADDUVX X10, V2, V3 // d7412582
+ VSADDUVX X10, V2, V0, V3 // d7412580
+ VSADDUVI $15, V2, V3 // d7b12782
+ VSADDUVI $15, V2, V0, V3 // d7b12780
+ VSADDVV V1, V2, V3 // d7812086
+ VSADDVV V1, V2, V0, V3 // d7812084
+ VSADDVX X10, V2, V3 // d7412586
+ VSADDVX X10, V2, V0, V3 // d7412584
+ VSADDVI $15, V2, V3 // d7b12786
+ VSADDVI $15, V2, V0, V3 // d7b12784
+ VSSUBUVV V1, V2, V3 // d781208a
+ VSSUBUVV V1, V2, V0, V3 // d7812088
+ VSSUBUVX X10, V2, V3 // d741258a
+ VSSUBUVX X10, V2, V0, V3 // d7412588
+ VSSUBVV V1, V2, V3 // d781208e
+ VSSUBVV V1, V2, V0, V3 // d781208c
+ VSSUBVX X10, V2, V3 // d741258e
+ VSSUBVX X10, V2, V0, V3 // d741258c
+
+ // 31.12.2: Vector Single-Width Averaging Add and Subtract
+ VAADDUVV V1, V2, V3 // d7a12022
+ VAADDUVV V1, V2, V0, V3 // d7a12020
+ VAADDUVX X10, V2, V3 // d7612522
+ VAADDUVX X10, V2, V0, V3 // d7612520
+ VAADDVV V1, V2, V3 // d7a12026
+ VAADDVV V1, V2, V0, V3 // d7a12024
+ VAADDVX X10, V2, V3 // d7612526
+ VAADDVX X10, V2, V0, V3 // d7612524
+ VASUBUVV V1, V2, V3 // d7a1202a
+ VASUBUVV V1, V2, V0, V3 // d7a12028
+ VASUBUVX X10, V2, V3 // d761252a
+ VASUBUVX X10, V2, V0, V3 // d7612528
+ VASUBVV V1, V2, V3 // d7a1202e
+ VASUBVV V1, V2, V0, V3 // d7a1202c
+ VASUBVX X10, V2, V3 // d761252e
+ VASUBVX X10, V2, V0, V3 // d761252c
+
+ // 31.12.3: Vector Single-Width Fractional Multiply with Rounding and Saturation
+ VSMULVV V1, V2, V3 // d781209e
+ VSMULVV V1, V2, V0, V3 // d781209c
+ VSMULVX X10, V2, V3 // d741259e
+ VSMULVX X10, V2, V0, V3 // d741259c
+
+ // 31.12.4: Vector Single-Width Scaling Shift Instructions
+ VSSRLVV V1, V2, V3 // d78120aa
+ VSSRLVV V1, V2, V0, V3 // d78120a8
+ VSSRLVX X10, V2, V3 // d74125aa
+ VSSRLVX X10, V2, V0, V3 // d74125a8
+ VSSRLVI $15, V2, V3 // d7b127aa
+ VSSRLVI $15, V2, V0, V3 // d7b127a8
+ VSSRAVV V1, V2, V3 // d78120ae
+ VSSRAVV V1, V2, V0, V3 // d78120ac
+ VSSRAVX X10, V2, V3 // d74125ae
+ VSSRAVX X10, V2, V0, V3 // d74125ac
+ VSSRAVI $16, V2, V3 // d73128ae
+ VSSRAVI $16, V2, V0, V3 // d73128ac
+
+ // 31.12.5: Vector Narrowing Fixed-Point Clip Instructions
+ VNCLIPUWV V1, V2, V3 // d78120ba
+ VNCLIPUWV V1, V2, V0, V3 // d78120b8
+ VNCLIPUWX X10, V2, V3 // d74125ba
+ VNCLIPUWX X10, V2, V0, V3 // d74125b8
+ VNCLIPUWI $16, V2, V3 // d73128ba
+ VNCLIPUWI $16, V2, V0, V3 // d73128b8
+ VNCLIPWV V1, V2, V3 // d78120be
+ VNCLIPWV V1, V2, V0, V3 // d78120bc
+ VNCLIPWX X10, V2, V3 // d74125be
+ VNCLIPWX X10, V2, V0, V3 // d74125bc
+ VNCLIPWI $16, V2, V3 // d73128be
+ VNCLIPWI $16, V2, V0, V3 // d73128bc
+
+ // 31.13.2: Vector Single-Width Floating-Point Add/Subtract Instructions
+ VFADDVV V1, V2, V3 // d7912002
+ VFADDVV V1, V2, V0, V3 // d7912000
+ VFADDVF F10, V2, V3 // d7512502
+ VFADDVF F10, V2, V0, V3 // d7512500
+ VFSUBVV V1, V2, V3 // d791200a
+ VFSUBVV V1, V2, V0, V3 // d7912008
+ VFSUBVF F10, V2, V3 // d751250a
+ VFSUBVF F10, V2, V0, V3 // d7512508
+ VFRSUBVF F10, V2, V3 // d751259e
+ VFRSUBVF F10, V2, V0, V3 // d751259c
+
+ // 31.13.3: Vector Widening Floating-Point Add/Subtract Instructions
+ VFWADDVV V1, V2, V3 // d79120c2
+ VFWADDVV V1, V2, V0, V3 // d79120c0
+ VFWADDVF F10, V2, V3 // d75125c2
+ VFWADDVF F10, V2, V0, V3 // d75125c0
+ VFWSUBVV V1, V2, V3 // d79120ca
+ VFWSUBVV V1, V2, V0, V3 // d79120c8
+ VFWSUBVF F10, V2, V3 // d75125ca
+ VFWSUBVF F10, V2, V0, V3 // d75125c8
+ VFWADDWV V1, V2, V3 // d79120d2
+ VFWADDWV V1, V2, V0, V3 // d79120d0
+ VFWADDWF F10, V2, V3 // d75125d2
+ VFWADDWF F10, V2, V0, V3 // d75125d0
+ VFWSUBWV V1, V2, V3 // d79120da
+ VFWSUBWV V1, V2, V0, V3 // d79120d8
+ VFWSUBWF F10, V2, V3 // d75125da
+ VFWSUBWF F10, V2, V0, V3 // d75125d8
+
+ // 31.13.4: Vector Single-Width Floating-Point Multiply/Divide Instructions
+ VFMULVV V1, V2, V3 // d7912092
+ VFMULVV V1, V2, V0, V3 // d7912090
+ VFMULVF F10, V2, V3 // d7512592
+ VFMULVF F10, V2, V0, V3 // d7512590
+ VFDIVVV V1, V2, V3 // d7912082
+ VFDIVVV V1, V2, V0, V3 // d7912080
+ VFDIVVF F10, V2, V3 // d7512582
+ VFDIVVF F10, V2, V0, V3 // d7512580
+ VFRDIVVF F10, V2, V3 // d7512586
+ VFRDIVVF F10, V2, V0, V3 // d7512584
+
+ // 31.13.5: Vector Widening Floating-Point Multiply
+ VFWMULVV V1, V2, V3 // d79120e2
+ VFWMULVV V1, V2, V0, V3 // d79120e0
+ VFWMULVF F10, V2, V3 // d75125e2
+ VFWMULVF F10, V2, V0, V3 // d75125e0
+
+ // 31.13.6: Vector Single-Width Floating-Point Fused Multiply-Add Instructions
+ VFMACCVV V2, V1, V3 // d79120b2
+ VFMACCVV V2, V1, V0, V3 // d79120b0
+ VFMACCVF V2, F10, V3 // d75125b2
+ VFMACCVF V2, F10, V0, V3 // d75125b0
+ VFNMACCVV V2, V1, V3 // d79120b6
+ VFNMACCVV V2, V1, V0, V3 // d79120b4
+ VFNMACCVF V2, F10, V3 // d75125b6
+ VFNMACCVF V2, F10, V0, V3 // d75125b4
+ VFMSACVV V2, V1, V3 // d79120ba
+ VFMSACVV V2, V1, V0, V3 // d79120b8
+ VFMSACVF V2, F10, V3 // d75125ba
+ VFMSACVF V2, F10, V0, V3 // d75125b8
+ VFNMSACVV V2, V1, V3 // d79120be
+ VFNMSACVV V2, V1, V0, V3 // d79120bc
+ VFNMSACVF V2, F10, V3 // d75125be
+ VFNMSACVF V2, F10, V0, V3 // d75125bc
+ VFMADDVV V2, V1, V3 // d79120a2
+ VFMADDVV V2, V1, V0, V3 // d79120a0
+ VFMADDVF V2, F10, V3 // d75125a2
+ VFMADDVF V2, F10, V0, V3 // d75125a0
+ VFNMADDVV V2, V1, V3 // d79120a6
+ VFNMADDVV V2, V1, V0, V3 // d79120a4
+ VFNMADDVF V2, F10, V3 // d75125a6
+ VFNMADDVF V2, F10, V0, V3 // d75125a4
+ VFMSUBVV V2, V1, V3 // d79120aa
+ VFMSUBVV V2, V1, V0, V3 // d79120a8
+ VFMSUBVF V2, F10, V3 // d75125aa
+ VFMSUBVF V2, F10, V0, V3 // d75125a8
+ VFNMSUBVV V2, V1, V3 // d79120ae
+ VFNMSUBVV V2, V1, V0, V3 // d79120ac
+ VFNMSUBVF V2, F10, V3 // d75125ae
+ VFNMSUBVF V2, F10, V0, V3 // d75125ac
+
+ // 31.13.7: Vector Widening Floating-Point Fused Multiply-Add Instructions
+ VFWMACCVV V2, V1, V3 // d79120f2
+ VFWMACCVV V2, V1, V0, V3 // d79120f0
+ VFWMACCVF V2, F10, V3 // d75125f2
+ VFWMACCVF V2, F10, V0, V3 // d75125f0
+ VFWNMACCVV V2, V1, V3 // d79120f6
+ VFWNMACCVV V2, V1, V0, V3 // d79120f4
+ VFWNMACCVF V2, F10, V3 // d75125f6
+ VFWNMACCVF V2, F10, V0, V3 // d75125f4
+ VFWMSACVV V2, V1, V3 // d79120fa
+ VFWMSACVV V2, V1, V0, V3 // d79120f8
+ VFWMSACVF V2, F10, V3 // d75125fa
+ VFWMSACVF V2, F10, V0, V3 // d75125f8
+ VFWNMSACVV V2, V1, V3 // d79120fe
+ VFWNMSACVV V2, V1, V0, V3 // d79120fc
+ VFWNMSACVF V2, F10, V3 // d75125fe
+ VFWNMSACVF V2, F10, V0, V3 // d75125fc
+
+ // 31.13.8: Vector Floating-Point Square-Root Instruction
+ VFSQRTV V2, V3 // d711204e
+ VFSQRTV V2, V0, V3 // d711204c
+
+ // 31.13.9: Vector Floating-Point Reciprocal Square-Root Estimate Instruction
+ VFRSQRT7V V2, V3 // d711224e
+ VFRSQRT7V V2, V0, V3 // d711224c
+
+ // 31.13.10: Vector Floating-Point Reciprocal Estimate Instruction
+ VFREC7V V2, V3 // d791224e
+ VFREC7V V2, V0, V3 // d791224c
+
+ // 31.13.11: Vector Floating-Point MIN/MAX Instructions
+ VFMINVV V1, V2, V3 // d7912012
+ VFMINVV V1, V2, V0, V3 // d7912010
+ VFMINVF F10, V2, V3 // d7512512
+ VFMINVF F10, V2, V0, V3 // d7512510
+ VFMAXVV V1, V2, V3 // d791201a
+ VFMAXVV V1, V2, V0, V3 // d7912018
+ VFMAXVF F10, V2, V3 // d751251a
+ VFMAXVF F10, V2, V0, V3 // d7512518
+
+ // 31.13.12: Vector Floating-Point Sign-Injection Instructions
+ VFSGNJVV V1, V2, V3 // d7912022
+ VFSGNJVV V1, V2, V0, V3 // d7912020
+ VFSGNJVF F10, V2, V3 // d7512522
+ VFSGNJVF F10, V2, V0, V3 // d7512520
+ VFSGNJNVV V1, V2, V3 // d7912026
+ VFSGNJNVV V1, V2, V0, V3 // d7912024
+ VFSGNJNVF F10, V2, V3 // d7512526
+ VFSGNJNVF F10, V2, V0, V3 // d7512524
+ VFSGNJXVV V1, V2, V3 // d791202a
+ VFSGNJXVV V1, V2, V0, V3 // d7912028
+ VFSGNJXVF F10, V2, V3 // d751252a
+ VFSGNJXVF F10, V2, V0, V3 // d7512528
+ VFNEGV V2, V3 // d7112126
+ VFNEGV V2, V0, V3 // d7112124
+ VFABSV V2, V3 // d711212a
+ VFABSV V2, V0, V3 // d7112128
+
+ // 31.13.13: Vector Floating-Point Compare Instructions
+ VMFEQVV V1, V2, V3 // d7912062
+ VMFEQVV V1, V2, V0, V3 // d7912060
+ VMFEQVF F10, V2, V3 // d7512562
+ VMFEQVF F10, V2, V0, V3 // d7512560
+ VMFNEVV V1, V2, V3 // d7912072
+ VMFNEVV V1, V2, V0, V3 // d7912070
+ VMFNEVF F10, V2, V3 // d7512572
+ VMFNEVF F10, V2, V0, V3 // d7512570
+ VMFLTVV V1, V2, V3 // d791206e
+ VMFLTVV V1, V2, V0, V3 // d791206c
+ VMFLTVF F10, V2, V3 // d751256e
+ VMFLTVF F10, V2, V0, V3 // d751256c
+ VMFLEVV V1, V2, V3 // d7912066
+ VMFLEVV V1, V2, V0, V3 // d7912064
+ VMFLEVF F10, V2, V3 // d7512566
+ VMFLEVF F10, V2, V0, V3 // d7512564
+ VMFGTVF F10, V2, V3 // d7512576
+ VMFGTVF F10, V2, V0, V3 // d7512574
+ VMFGEVF F10, V2, V3 // d751257e
+ VMFGEVF F10, V2, V0, V3 // d751257c
+ VMFGTVV V1, V2, V3 // d711116e
+ VMFGTVV V1, V2, V0, V3 // d711116c
+ VMFGEVV V1, V2, V3 // d7111166
+ VMFGEVV V1, V2, V0, V3 // d7111164
+
+ // 31.13.14: Vector Floating-Point Classify Instruction
+ VFCLASSV V2, V3 // d711284e
+ VFCLASSV V2, V0, V3 // d711284c
+
+ // 31.13.15: Vector Floating-Point Merge Instruction
+ VFMERGEVFM F10, V2, V0, V3 // d751255c
+
+ // 31.13.16: Vector Floating-Point Move Instruction
+ VFMVVF F10, V3 // d751055e
+
+ // 31.13.17: Single-Width Floating-Point/Integer Type-Convert Instructions
+ VFCVTXUFV V2, V3 // d711204a
+ VFCVTXUFV V2, V0, V3 // d7112048
+ VFCVTXFV V2, V3 // d791204a
+ VFCVTXFV V2, V0, V3 // d7912048
+ VFCVTRTZXUFV V2, V3 // d711234a
+ VFCVTRTZXUFV V2, V0, V3 // d7112348
+ VFCVTRTZXFV V2, V3 // d791234a
+ VFCVTRTZXFV V2, V0, V3 // d7912348
+ VFCVTFXUV V2, V3 // d711214a
+ VFCVTFXUV V2, V0, V3 // d7112148
+ VFCVTFXV V2, V3 // d791214a
+ VFCVTFXV V2, V0, V3 // d7912148
+
+ // 31.13.18: Widening Floating-Point/Integer Type-Convert Instructions
+ VFWCVTXUFV V2, V3 // d711244a
+ VFWCVTXUFV V2, V0, V3 // d7112448
+ VFWCVTXFV V2, V3 // d791244a
+ VFWCVTXFV V2, V0, V3 // d7912448
+ VFWCVTRTZXUFV V2, V3 // d711274a
+ VFWCVTRTZXUFV V2, V0, V3 // d7112748
+ VFWCVTRTZXFV V2, V3 // d791274a
+ VFWCVTRTZXFV V2, V0, V3 // d7912748
+ VFWCVTFXUV V2, V3 // d711254a
+ VFWCVTFXUV V2, V0, V3 // d7112548
+ VFWCVTFXV V2, V3 // d791254a
+ VFWCVTFXV V2, V0, V3 // d7912548
+ VFWCVTFFV V2, V3 // d711264a
+ VFWCVTFFV V2, V0, V3 // d7112648
+
+ // 31.13.19: Narrowing Floating-Point/Integer Type-Convert Instructions
+ VFNCVTXUFW V2, V3 // d711284a
+ VFNCVTXUFW V2, V0, V3 // d7112848
+ VFNCVTXFW V2, V3 // d791284a
+ VFNCVTXFW V2, V0, V3 // d7912848
+ VFNCVTRTZXUFW V2, V3 // d7112b4a
+ VFNCVTRTZXUFW V2, V0, V3 // d7112b48
+ VFNCVTRTZXFW V2, V3 // d7912b4a
+ VFNCVTRTZXFW V2, V0, V3 // d7912b48
+ VFNCVTFXUW V2, V3 // d711294a
+ VFNCVTFXUW V2, V0, V3 // d7112948
+ VFNCVTFXW V2, V3 // d791294a
+ VFNCVTFXW V2, V0, V3 // d7912948
+ VFNCVTFFW V2, V3 // d7112a4a
+ VFNCVTFFW V2, V0, V3 // d7112a48
+ VFNCVTRODFFW V2, V3 // d7912a4a
+ VFNCVTRODFFW V2, V0, V3 // d7912a48
+
+ // 31.14.1: Vector Single-Width Integer Reduction Instructions
+ VREDSUMVS V1, V2, V3 // d7a12002
+ VREDSUMVS V1, V2, V0, V3 // d7a12000
+ VREDMAXUVS V1, V2, V3 // d7a1201a
+ VREDMAXUVS V1, V2, V0, V3 // d7a12018
+ VREDMAXVS V1, V2, V3 // d7a1201e
+ VREDMAXVS V1, V2, V0, V3 // d7a1201c
+ VREDMINUVS V1, V2, V3 // d7a12012
+ VREDMINUVS V1, V2, V0, V3 // d7a12010
+ VREDMINVS V1, V2, V3 // d7a12016
+ VREDMINVS V1, V2, V0, V3 // d7a12014
+ VREDANDVS V1, V2, V3 // d7a12006
+ VREDANDVS V1, V2, V0, V3 // d7a12004
+ VREDORVS V1, V2, V3 // d7a1200a
+ VREDORVS V1, V2, V0, V3 // d7a12008
+ VREDXORVS V1, V2, V3 // d7a1200e
+ VREDXORVS V1, V2, V0, V3 // d7a1200c
+
+ // 31.14.2: Vector Widening Integer Reduction Instructions
+ VWREDSUMUVS V1, V2, V3 // d78120c2
+ VWREDSUMUVS V1, V2, V0, V3 // d78120c0
+ VWREDSUMVS V1, V2, V3 // d78120c6
+ VWREDSUMVS V1, V2, V0, V3 // d78120c4
+
+ // 31.14.3: Vector Single-Width Floating-Point Reduction Instructions
+ VFREDOSUMVS V1, V2, V3 // d791200e
+ VFREDOSUMVS V1, V2, V0, V3 // d791200c
+ VFREDUSUMVS V1, V2, V3 // d7912006
+ VFREDUSUMVS V1, V2, V0, V3 // d7912004
+ VFREDMAXVS V1, V2, V3 // d791201e
+ VFREDMAXVS V1, V2, V0, V3 // d791201c
+ VFREDMINVS V1, V2, V3 // d7912016
+ VFREDMINVS V1, V2, V0, V3 // d7912014
+
+ // 31.14.4: Vector Widening Floating-Point Reduction Instructions
+ VFWREDOSUMVS V1, V2, V3 // d79120ce
+ VFWREDOSUMVS V1, V2, V0, V3 // d79120cc
+ VFWREDUSUMVS V1, V2, V3 // d79120c6
+ VFWREDUSUMVS V1, V2, V0, V3 // d79120c4
+
+ // 31.15: Vector Mask Instructions
+ VMANDMM V1, V2, V3 // d7a12066
+ VMNANDMM V1, V2, V3 // d7a12076
+ VMANDNMM V1, V2, V3 // d7a12062
+ VMXORMM V1, V2, V3 // d7a1206e
+ VMORMM V1, V2, V3 // d7a1206a
+ VMNORMM V1, V2, V3 // d7a1207a
+ VMORNMM V1, V2, V3 // d7a12072
+ VMXNORMM V1, V2, V3 // d7a1207e
+ VMMVM V2, V3 // d7212166
+ VMCLRM V3 // d7a1316e
+ VMSETM V3 // d7a1317e
+ VMNOTM V2, V3 // d7212176
+ VCPOPM V2, X10 // 57252842
+ VCPOPM V2, V0, X10 // 57252840
+ VFIRSTM V2, X10 // 57a52842
+ VFIRSTM V2, V0, X10 // 57a52840
+ VMSBFM V2, V3 // d7a12052
+ VMSBFM V2, V0, V3 // d7a12050
+ VMSIFM V2, V3 // d7a12152
+ VMSIFM V2, V0, V3 // d7a12150
+ VMSOFM V2, V3 // d7212152
+ VMSOFM V2, V0, V3 // d7212150
+ VIOTAM V2, V3 // d7212852
+ VIOTAM V2, V0, V3 // d7212850
+ VIDV V3 // d7a10852
+ VIDV V0, V3 // d7a10850
+
+ // 31.16.1: Integer Scalar Move Instructions
+ VMVXS V2, X10 // 57252042
+ VMVSX X10, V2 // 57610542
+
+ // 31.16.2: Floating-Point Scalar Move Instructions
+ VFMVFS V2, F10 // 57152042
+ VFMVSF F10, V2 // 57510542
+
+ // 31.16.3: Vector Slide Instructions
+ VSLIDEUPVX X10, V2, V3 // d741253a
+ VSLIDEUPVX X10, V2, V0, V3 // d7412538
+ VSLIDEUPVI $16, V2, V3 // d731283a
+ VSLIDEUPVI $16, V2, V0, V3 // d7312838
+ VSLIDEDOWNVX X10, V2, V3 // d741253e
+ VSLIDEDOWNVX X10, V2, V0, V3 // d741253c
+ VSLIDEDOWNVI $16, V2, V3 // d731283e
+ VSLIDEDOWNVI $16, V2, V0, V3 // d731283c
+ VSLIDE1UPVX X10, V2, V3 // d761253a
+ VSLIDE1UPVX X10, V2, V0, V3 // d7612538
+ VFSLIDE1UPVF F10, V2, V3 // d751253a
+ VFSLIDE1UPVF F10, V2, V0, V3 // d7512538
+ VSLIDE1DOWNVX X10, V2, V3 // d761253e
+ VSLIDE1DOWNVX X10, V2, V0, V3 // d761253c
+ VFSLIDE1DOWNVF F10, V2, V3 // d751253e
+ VFSLIDE1DOWNVF F10, V2, V0, V3 // d751253c
+
+ // 31.16.4: Vector Register Gather Instructions
+ VRGATHERVV V1, V2, V3 // d7812032
+ VRGATHERVV V1, V2, V0, V3 // d7812030
+ VRGATHEREI16VV V1, V2, V3 // d781203a
+ VRGATHEREI16VV V1, V2, V0, V3 // d7812038
+ VRGATHERVX X10, V2, V3 // d7412532
+ VRGATHERVX X10, V2, V0, V3 // d7412530
+ VRGATHERVI $16, V2, V3 // d7312832
+ VRGATHERVI $16, V2, V0, V3 // d7312830
+
+ // 31.16.5: Vector Compress Instruction
+ VCOMPRESSVM V1, V2, V3 // d7a1205e
+
+ // 31.16.6: Whole Vector Register Move
+ VMV1RV V2, V1 // d730209e
+ VMV2RV V12, V10 // 57b5c09e
+ VMV4RV V8, V4 // 57b2819e
+ VMV8RV V8, V0 // 57b0839e
+
//
// Privileged ISA
//
@@ -435,20 +1910,36 @@ start:
WORD $0x9abcdef0 // WORD $2596069104 // f0debc9a
// MOV pseudo-instructions
- MOV X5, X6 // 13830200
- MOV $2047, X5 // 9302f07f
- MOV $-2048, X5 // 93020080
- MOV $2048, X5 // b71200009b820280
- MOV $-2049, X5 // b7f2ffff9b82f27f
- MOV $4096, X5 // b7120000
- MOV $2147479552, X5 // b7f2ff7f
- MOV $2147483647, X5 // b70200809b82f2ff
- MOV $-2147483647, X5 // b70200809b821200
+ MOV X5, X6 // 13830200
+ MOV $2047, X5 // 9302f07f
+ MOV $-2048, X5 // 93020080
+ MOV $2048, X5 // b71200009b820280
+ MOV $-2049, X5 // b7f2ffff9b82f27f
+ MOV $4096, X5 // b7120000
+ MOV $0x7ffff000, X5 // MOV $2147479552, X5 // b7f2ff7f
+ MOV $-0x7ffff000, X5 // MOV $-2147479552, X5 // b7120080
+ MOV $0x7fffffff, X5 // MOV $2147483647, X5 // b70200809b82f2ff
+ MOV $-0x7fffffff, X5 // MOV $-2147483647, X5 // b70200809b821200
+
+ // Converted to load and shift(s)
+ MOV $0xffffffff, X5 // MOV $4294967295, X5 // 9302f0ff93d20202
+ MOV $0x100000000, X5 // MOV $4294967296, X5 // 9302100093920202
+ MOV $0xfffffffffffda, X5 // MOV $4503599627370458, X5 // 9302d0fe9392d20093d2c200
+ MOV $0xffffffffffffe, X5 // MOV $4503599627370494, X5 // 9302f0ff9392d20093d2c200
+ MOV $0x7fffffff00000000, X5 // MOV $9223372032559808512, X5 // b70200809b82f2ff93920202
+ MOV $0x8000000100000000, X5 // MOV $-9223372032559808512, X5 // b70200809b82120093920202
+ MOV $0xffffffff00000000, X5 // MOV $-4294967296, X5 // 9302f0ff93920202
+ MOV $0x1ffffffff0000000, X5 // MOV $2305843008945258496, X5 // 9302f0ff9392f20193d23200
+ MOV $0x7fffffffffffffff, X5 // MOV $9223372036854775807, X5 // 9302f0ff93d21200
// Converted to load of symbol (AUIPC + LD)
- MOV $4294967295, X5 // 9702000083b20200
- // Converted to MOV $1, X5 + SLLI $32, X5
- MOV $4294967296, X5 // 9302100093920202
+ MOV $0x80000001, X5 // MOV $2147483649, X5 // 9702000083b20200
+ MOV $0x100000001, X5 // MOV $4294967297, X5 // 9702000083b20200
+ MOV $0x0800000010000000, X5 // MOV $576460752571858944, X5 // 9702000083b20200
+ MOV $0x8000000010000000, X5 // MOV $-9223372036586340352, X5 // 9702000083b20200
+ MOV $0x0abcdabcd0000000, X5 // MOV $773733740479250432, X5 // 9702000083b20200
+ MOV $0x8abcdabcd0000000, X5 // MOV $-8449638296375525376, X5 // 9702000083b20200
+ MOV $0xfff0000000ffffff, X5 // MOV $-4503599610593281, X5 // 9702000083b20200
MOV (X5), X6 // 03b30200
MOV 4(X5), X6 // 03b34200
@@ -477,10 +1968,24 @@ start:
MOVF 4(X5), F0 // 07a04200
MOVF F0, 4(X5) // 27a20200
MOVF F0, F1 // d3000020
+ MOVF X1, F3 // d38100f0
+ MOVF F3, X1 // d38001e0
+ MOVF X0, F3 // d30100f0
+ MOVF $(0.0), F3 // d30100f0
+
+ // Converted to load of symbol (AUIPC + FLW)
+ MOVF $(709.78271289338397), F3 // 970f000087a10f00
MOVD 4(X5), F0 // 07b04200
MOVD F0, 4(X5) // 27b20200
MOVD F0, F1 // d3000022
+ MOVD F3, X1 // d38001e2
+ MOVD X1, F3 // d38100f2
+ MOVD X0, F3 // d30100f2
+ MOVD $(0.0), F3 // d30100f2
+
+ // Converted to load of symbol (AUIPC + FLD)
+ MOVD $(709.78271289338397), F3 // 970f000087b10f00
// TLS load with local-exec (LUI + ADDIW + ADD of TP + load)
MOV tls(SB), X5 // b70f00009b8f0f00b38f4f0083b20f00
diff --git a/src/cmd/asm/internal/asm/testdata/riscv64error.s b/src/cmd/asm/internal/asm/testdata/riscv64error.s
index 0b0184aaa7..3c09770d2a 100644
--- a/src/cmd/asm/internal/asm/testdata/riscv64error.s
+++ b/src/cmd/asm/internal/asm/testdata/riscv64error.s
@@ -3,6 +3,27 @@
// license that can be found in the LICENSE file.
TEXT errors(SB),$0
+ CSRRC (X10), CYCLE, X5 // ERROR "integer register or immediate expected for 1st operand"
+ CSRRC X0, TU, X5 // ERROR "unknown CSR"
+ CSRRC X0, CYCLE // ERROR "missing CSR name"
+ CSRRC X0, CYCLE, (X10) // ERROR "needs an integer register output"
+ CSRRC $-1, TIME, X15 // ERROR "immediate out of range 0 to 31"
+ CSRRCI $32, TIME, X15 // ERROR "immediate out of range 0 to 31"
+ CSRRCI $1, TIME, (X15) // ERROR "needs an integer register output"
+ CSRRS (X10), CYCLE, X5 // ERROR "integer register or immediate expected for 1st operand"
+ CSRRS X0, CYCLE, (X10) // ERROR "needs an integer register output"
+ CSRRS X0, TU, X5 // ERROR "unknown CSR"
+ CSRRS X0, CYCLE // ERROR "missing CSR name"
+ CSRRS $-1, TIME, X15 // ERROR "immediate out of range 0 to 31"
+ CSRRSI $32, TIME, X15 // ERROR "immediate out of range 0 to 31"
+ CSRRSI $1, TIME, (X15) // ERROR "needs an integer register output"
+ CSRRW (X10), CYCLE, X5 // ERROR "integer register or immediate expected for 1st operand"
+ CSRRW X0, TU, X5 // ERROR "unknown CSR"
+ CSRRW X0, CYCLE // ERROR "missing CSR name"
+ CSRRW X0, CYCLE, (X5) // ERROR "needs an integer register output"
+ CSRRW $-1, TIME, X15 // ERROR "immediate out of range 0 to 31"
+ CSRRWI $32, TIME, X15 // ERROR "immediate out of range 0 to 31"
+ CSRRWI $1, TIME, (X15) // ERROR "needs an integer register output"
MOV $errors(SB), (X5) // ERROR "address load must target register"
MOV $8(SP), (X5) // ERROR "address load must target register"
MOVB $8(SP), X5 // ERROR "unsupported address load"
@@ -30,6 +51,8 @@ TEXT errors(SB),$0
SLLI $64, X5, X6 // ERROR "immediate out of range 0 to 63"
SRLI $64, X5, X6 // ERROR "immediate out of range 0 to 63"
SRAI $64, X5, X6 // ERROR "immediate out of range 0 to 63"
+ BEQ X5, X6, $1 // ERROR "instruction with branch-like opcode lacks destination"
+ BEQ X5, X6, 31(X10) // ERROR "instruction with branch-like opcode lacks destination"
RORI $-1, X5, X6 // ERROR "immediate out of range 0 to 63"
SLLI $-1, X5, X6 // ERROR "immediate out of range 0 to 63"
SRLI $-1, X5, X6 // ERROR "immediate out of range 0 to 63"
@@ -43,7 +66,355 @@ TEXT errors(SB),$0
SRLIW $-1, X5, X6 // ERROR "immediate out of range 0 to 31"
SRAIW $-1, X5, X6 // ERROR "immediate out of range 0 to 31"
SD X5, 4294967296(X6) // ERROR "constant 4294967296 too large"
- SRLI $1, X5, F1 // ERROR "expected integer register in rd position but got non-integer register F1"
- SRLI $1, F1, X5 // ERROR "expected integer register in rs1 position but got non-integer register F1"
FNES F1, (X5) // ERROR "needs an integer register output"
+
+ //
+ // "V" Standard Extension for Vector Operations, Version 1.0
+ //
+ VSETIVLI X10, E32, M2, TA, MA, X12 // ERROR "expected immediate value"
+ VLE8V (X10), V1, V3 // ERROR "invalid vector mask register"
+ VLE8FFV (X10), V1, V3 // ERROR "invalid vector mask register"
+ VSE8V V3, V1, (X10) // ERROR "invalid vector mask register"
+ VLSE8V (X10), X10, V1, V3 // ERROR "invalid vector mask register"
+ VSSE8V V3, X11, V1, (X10) // ERROR "invalid vector mask register"
+ VLUXEI8V (X10), V2, V1, V3 // ERROR "invalid vector mask register"
+ VSUXEI8V V3, V2, V1, (X10) // ERROR "invalid vector mask register"
+ VLOXEI8V (X10), V2, V1, V3 // ERROR "invalid vector mask register"
+ VSOXEI8V V3, V2, V1, (X10) // ERROR "invalid vector mask register"
+ VLSEG2E8V (X10), V1, V3 // ERROR "invalid vector mask register"
+ VLSEG2E8FFV (X10), V1, V3 // ERROR "invalid vector mask register"
+ VSSEG2E8V V3, V1, (X10) // ERROR "invalid vector mask register"
+ VLSSEG2E8V (X10), X10, V1, V3 // ERROR "invalid vector mask register"
+ VSSSEG2E8V V3, X11, V1, (X10) // ERROR "invalid vector mask register"
+ VLUXSEG2EI8V (X10), V2, V1, V3 // ERROR "invalid vector mask register"
+ VSUXSEG2EI8V V3, V2, V1, (X10) // ERROR "invalid vector mask register"
+ VLOXSEG2EI8V (X10), V2, V1, V3 // ERROR "invalid vector mask register"
+ VSOXSEG2EI8V V3, V2, V1, (X10) // ERROR "invalid vector mask register"
+ VL1RV (X10), V0, V3 // ERROR "too many operands for instruction"
+ VS1RV V3, V0, (X11) // ERROR "too many operands for instruction"
+ VADDVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VADDVX X10, V2, V1, V3 // ERROR "invalid vector mask register"
+ VADDVI $15, V4, V1, V2 // ERROR "invalid vector mask register"
+ VSUBVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSUBVX X10, V2, V1, V3 // ERROR "invalid vector mask register"
+ VRSUBVX X10, V2, V1, V3 // ERROR "invalid vector mask register"
+ VRSUBVI $15, V4, V1, V2 // ERROR "invalid vector mask register"
+ VNEGV V2, V3, V4 // ERROR "invalid vector mask register"
+ VWADDUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWADDUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWSUBUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWSUBUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWADDVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWADDVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWSUBVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWSUBVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWADDUWV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWADDUWX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWSUBUWV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWSUBUWX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWADDWV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWADDWX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWSUBWV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWSUBWX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWCVTXXV V2, V1, V3 // ERROR "invalid vector mask register"
+ VWCVTUXXV V2, V1, V3 // ERROR "invalid vector mask register"
+ VZEXTVF2 V2, V3, V4 // ERROR "invalid vector mask register"
+ VSEXTVF2 V2, V3, V4 // ERROR "invalid vector mask register"
+ VZEXTVF4 V2, V3, V4 // ERROR "invalid vector mask register"
+ VSEXTVF4 V2, V3, V4 // ERROR "invalid vector mask register"
+ VZEXTVF8 V2, V3, V4 // ERROR "invalid vector mask register"
+ VSEXTVF8 V2, V3, V4 // ERROR "invalid vector mask register"
+ VADCVVM V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VADCVVM V1, V2, V3 // ERROR "invalid vector mask register"
+ VADCVVM V1, V2, V0, V0 // ERROR "invalid destination register V0"
+ VADCVXM X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VADCVXM X10, V2, V3 // ERROR "invalid vector mask register"
+ VADCVXM X10, V2, V0, V0 // ERROR "invalid destination register V0"
+ VADCVIM $15, V2, V1, V3 // ERROR "invalid vector mask register"
+ VADCVIM $15, V2, V3 // ERROR "invalid vector mask register"
+ VADCVIM $15, V2, V0, V0 // ERROR "invalid destination register V0"
+ VMADCVVM V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMADCVVM V1, V2, V3 // ERROR "invalid vector mask register"
+ VMADCVXM X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMADCVXM X10, V2, V3 // ERROR "invalid vector mask register"
+ VMADCVIM $15, V2, V1, V3 // ERROR "invalid vector mask register"
+ VMADCVIM $15, V2, V3 // ERROR "invalid vector mask register"
+ VSBCVVM V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSBCVVM V1, V2, V3 // ERROR "invalid vector mask register"
+ VSBCVVM V1, V2, V0, V0 // ERROR "invalid destination register V0"
+ VSBCVXM X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSBCVXM X10, V2, V3 // ERROR "invalid vector mask register"
+ VSBCVXM X10, V2, V0, V0 // ERROR "invalid destination register V0"
+ VMSBCVVM V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSBCVVM V1, V2, V3 // ERROR "invalid vector mask register"
+ VMSBCVXM X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSBCVXM X10, V2, V3 // ERROR "invalid vector mask register"
+ VANDVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VANDVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VANDVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VORVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VORVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VORVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VXORVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VXORVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VXORVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNOTV V1, V2, V3 // ERROR "invalid vector mask register"
+ VSLLVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSLLVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSLLVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSRLVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSRLVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSRLVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSRAVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSRAVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSRAVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNSRLWV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNSRLWX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNSRLWI $31, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNSRAWV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNSRAWX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNSRAWI $31, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNCVTXXW V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSEQVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSEQVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSEQVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSNEVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSNEVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSNEVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSLTUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSLTUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSLTVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSLTVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSLEUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSLEUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSLEUVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSLEVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSLEVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSLEVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSGTUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSGTUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSGTUVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSGTVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSGTVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSGTVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSGEVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSGEUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSLTVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSLTUVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSGEVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSGEUVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMINUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMINUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMINVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMINVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMAXUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMAXUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMAXVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMAXVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMULVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMULVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMULHVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMULHVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMULHUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMULHUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMULHSUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMULHSUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VDIVUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VDIVUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VDIVVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VDIVVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VREMUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VREMUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VREMVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VREMVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWMULVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWMULVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWMULUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWMULUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWMULSUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWMULSUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMACCVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMACCVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNMSACVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNMSACVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMADDVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMADDVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNMSUBVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNMSUBVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWMACCUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWMACCUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWMACCVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWMACCVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWMACCSUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWMACCSUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWMACCUSVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMERGEVVM V1, V2, V3 // ERROR "invalid vector mask register"
+ VMERGEVVM V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMERGEVXM X10, V2, V3 // ERROR "invalid vector mask register"
+ VMERGEVXM X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMERGEVIM $15, V2, V3 // ERROR "invalid vector mask register"
+ VMERGEVIM $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMVVV V1, V2, V3 // ERROR "too many operands for instruction"
+ VMVVX X10, V2, V3 // ERROR "too many operands for instruction"
+ VMVVI $15, V2, V3 // ERROR "too many operands for instruction"
+ VSADDUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSADDUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSADDUVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSADDVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSADDVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSADDVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSSUBUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSSUBUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSSUBVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSSUBVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VAADDUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VAADDUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VAADDVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VAADDVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VASUBUVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VASUBUVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VASUBVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VASUBVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSMULVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSMULVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSSRLVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSSRLVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSSRLVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSSRAVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSSRAVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSSRAVI $15, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNCLIPUWV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNCLIPUWX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNCLIPUWI $16, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNCLIPWV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNCLIPWX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VNCLIPWI $16, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFADDVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFADDVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFSUBVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFSUBVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFRSUBVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWADDVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWADDVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWSUBVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWSUBVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWADDWV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWADDWF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWSUBWV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWSUBWF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFMULVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFMULVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFDIVVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFDIVVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFRDIVVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWMULVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWMULVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFMACCVV V2, V1, V4, V3 // ERROR "invalid vector mask register"
+ VFMACCVF V2, F10, V4, V3 // ERROR "invalid vector mask register"
+ VFNMACCVV V2, V1, V4, V3 // ERROR "invalid vector mask register"
+ VFNMACCVF V2, F10, V4, V3 // ERROR "invalid vector mask register"
+ VFMSACVV V2, V1, V4, V3 // ERROR "invalid vector mask register"
+ VFMSACVF V2, F10, V4, V3 // ERROR "invalid vector mask register"
+ VFNMSACVV V2, V1, V4, V3 // ERROR "invalid vector mask register"
+ VFNMSACVF V2, F10, V4, V3 // ERROR "invalid vector mask register"
+ VFMADDVV V2, V1, V4, V3 // ERROR "invalid vector mask register"
+ VFMADDVF V2, F10, V4, V3 // ERROR "invalid vector mask register"
+ VFNMADDVV V2, V1, V4, V3 // ERROR "invalid vector mask register"
+ VFNMADDVF V2, F10, V4, V3 // ERROR "invalid vector mask register"
+ VFMSUBVV V2, V1, V4, V3 // ERROR "invalid vector mask register"
+ VFMSUBVF V2, F10, V4, V3 // ERROR "invalid vector mask register"
+ VFNMSUBVV V2, V1, V4, V3 // ERROR "invalid vector mask register"
+ VFNMSUBVF V2, F10, V4, V3 // ERROR "invalid vector mask register"
+ VFWMACCVV V2, V1, V4, V3 // ERROR "invalid vector mask register"
+ VFWMACCVF V2, F10, V4, V3 // ERROR "invalid vector mask register"
+ VFWNMACCVV V2, V1, V4, V3 // ERROR "invalid vector mask register"
+ VFWNMACCVF V2, F10, V4, V3 // ERROR "invalid vector mask register"
+ VFWMSACVV V2, V1, V4, V3 // ERROR "invalid vector mask register"
+ VFWMSACVF V2, F10, V4, V3 // ERROR "invalid vector mask register"
+ VFWNMSACVV V2, V1, V4, V3 // ERROR "invalid vector mask register"
+ VFWNMSACVF V2, F10, V4, V3 // ERROR "invalid vector mask register"
+ VFSQRTV V2, V4, V3 // ERROR "invalid vector mask register"
+ VFRSQRT7V V2, V4, V3 // ERROR "invalid vector mask register"
+ VFREC7V V2, V4, V3 // ERROR "invalid vector mask register"
+ VFMINVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFMINVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFMAXVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFMAXVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFSGNJVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFSGNJVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFSGNJNVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFSGNJNVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFSGNJXVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFSGNJXVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFNEGV V2, V4, V3 // ERROR "invalid vector mask register"
+ VFABSV V2, V4, V3 // ERROR "invalid vector mask register"
+ VMFEQVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMFEQVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMFNEVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMFNEVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMFLTVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMFLTVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMFLEVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMFLEVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMFGTVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMFGEVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMFGTVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VMFGEVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFMERGEVFM X10, V2, V3 // ERROR "invalid vector mask register"
+ VFMERGEVFM F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFCVTXUFV V2, V4, V3 // ERROR "invalid vector mask register"
+ VFCVTXFV V2, V4, V3 // ERROR "invalid vector mask register"
+ VFCVTRTZXUFV V2, V4, V3 // ERROR "invalid vector mask register"
+ VFCVTRTZXFV V2, V4, V3 // ERROR "invalid vector mask register"
+ VFCVTFXUV V2, V4, V3 // ERROR "invalid vector mask register"
+ VFCVTFXV V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWCVTXUFV V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWCVTXFV V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWCVTRTZXUFV V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWCVTRTZXFV V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWCVTFXUV V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWCVTFXV V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWCVTFFV V2, V4, V3 // ERROR "invalid vector mask register"
+ VFNCVTXUFW V2, V4, V3 // ERROR "invalid vector mask register"
+ VFNCVTXFW V2, V4, V3 // ERROR "invalid vector mask register"
+ VFNCVTRTZXUFW V2, V4, V3 // ERROR "invalid vector mask register"
+ VFNCVTRTZXFW V2, V4, V3 // ERROR "invalid vector mask register"
+ VFNCVTFXUW V2, V4, V3 // ERROR "invalid vector mask register"
+ VFNCVTFXW V2, V4, V3 // ERROR "invalid vector mask register"
+ VFNCVTFFW V2, V4, V3 // ERROR "invalid vector mask register"
+ VFNCVTRODFFW V2, V4, V3 // ERROR "invalid vector mask register"
+ VREDSUMVS V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VREDMAXUVS V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VREDMAXVS V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VREDMINUVS V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VREDMINVS V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VREDANDVS V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VREDORVS V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VREDXORVS V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWREDSUMUVS V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VWREDSUMVS V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFREDOSUMVS V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFREDUSUMVS V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFREDMAXVS V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFREDMINVS V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFWREDOSUMVS V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VCPOPM V2, V4, X10 // ERROR "invalid vector mask register"
+ VFIRSTM V2, V4, X10 // ERROR "invalid vector mask register"
+ VMSBFM V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSIFM V2, V4, V3 // ERROR "invalid vector mask register"
+ VMSOFM V2, V4, V3 // ERROR "invalid vector mask register"
+ VIOTAM V2, V4, V3 // ERROR "invalid vector mask register"
+ VSLIDEUPVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSLIDEUPVI $16, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSLIDEDOWNVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSLIDEDOWNVI $16, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSLIDE1UPVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFSLIDE1UPVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VSLIDE1DOWNVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VFSLIDE1DOWNVF F10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VRGATHERVV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VRGATHEREI16VV V1, V2, V4, V3 // ERROR "invalid vector mask register"
+ VRGATHERVX X10, V2, V4, V3 // ERROR "invalid vector mask register"
+ VRGATHERVI $16, V2, V4, V3 // ERROR "invalid vector mask register"
+
RET
diff --git a/src/cmd/asm/internal/asm/testdata/riscv64validation.s b/src/cmd/asm/internal/asm/testdata/riscv64validation.s
new file mode 100644
index 0000000000..6549765916
--- /dev/null
+++ b/src/cmd/asm/internal/asm/testdata/riscv64validation.s
@@ -0,0 +1,460 @@
+// Copyright 2024 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.
+
+// This file is for validation errors only, i.e., errors reported by the validate function.
+// Negative test cases for errors generated earlier in the assembler's preprocess stage
+// should be added to riscv64error.s. If they are added to this file, they will prevent
+// the validate function from being run and TestRISCVValidation will report missing
+// errors.
+
+TEXT validation(SB),$0
+ SRLI $1, X5, F1 // ERROR "expected integer register in rd position but got non-integer register F1"
+ SRLI $1, F1, X5 // ERROR "expected integer register in rs1 position but got non-integer register F1"
+
+ //
+ // "V" Standard Extension for Vector Operations, Version 1.0
+ //
+ VSETVLI $32, E16, M1, TU, MU, X12 // ERROR "must be in range [0, 31] (5 bits)"
+ VSETVLI $-1, E32, M2, TA, MA, X12 // ERROR "must be in range [0, 31] (5 bits)"
+ VSETVL X10, X11 // ERROR "expected integer register in rs1 position"
+ VLE8V (X10), X10 // ERROR "expected vector register in vd position"
+ VLE8V (V1), V3 // ERROR "expected integer register in rs1 position"
+ VLE8FFV (X10), X10 // ERROR "expected vector register in vd position"
+ VLE8FFV (V1), V3 // ERROR "expected integer register in rs1 position"
+ VSE8V X10, (X10) // ERROR "expected vector register in vs1 position"
+ VSE8V V3, (V1) // ERROR "expected integer register in rd position"
+ VLSE8V (X10), V3 // ERROR "expected integer register in rs2 position"
+ VLSE8V (X10), X10, X11 // ERROR "expected vector register in vd position"
+ VLSE8V (V1), X10, V3 // ERROR "expected integer register in rs1 position"
+ VLSE8V (X10), V1, V0, V3 // ERROR "expected integer register in rs2 position"
+ VSSE8V V3, (X10) // ERROR "expected integer register in rs2 position"
+ VSSE8V X10, X11, (X10) // ERROR "expected vector register in vd position"
+ VSSE8V V3, X11, (V1) // ERROR "expected integer register in rs1 position"
+ VSSE8V V3, V1, V0, (X10) // ERROR "expected integer register in rs2 position"
+ VLUXEI8V (X10), V2, X11 // ERROR "expected vector register in vd position"
+ VLUXEI8V (X10), V2, X11 // ERROR "expected vector register in vd position"
+ VLUXEI8V (V1), V2, V3 // ERROR "expected integer register in rs1 position"
+ VLUXEI8V (X10), X11, V0, V3 // ERROR "expected vector register in vs2 position"
+ VSUXEI8V X10, V2, (X10) // ERROR "expected vector register in vd position"
+ VSUXEI8V V3, V2, (V1) // ERROR "expected integer register in rs1 position"
+ VSUXEI8V V3, X11, V0, (X10) // ERROR "expected vector register in vs2 position"
+ VLOXEI8V (X10), V2, X11 // ERROR "expected vector register in vd position"
+ VLOXEI8V (V1), V2, V3 // ERROR "expected integer register in rs1 position"
+ VLOXEI8V (X10), X11, V0, V3 // ERROR "expected vector register in vs2 position"
+ VSOXEI8V X10, V2, (X10) // ERROR "expected vector register in vd position"
+ VSOXEI8V V3, V2, (V1) // ERROR "expected integer register in rs1 position"
+ VSOXEI8V V3, X11, V0, (X10) // ERROR "expected vector register in vs2 position"
+ VLSEG2E8V (X10), X10 // ERROR "expected vector register in vd position"
+ VLSEG2E8V (V1), V3 // ERROR "expected integer register in rs1 position"
+ VLSEG2E8FFV (X10), X10 // ERROR "expected vector register in vd position"
+ VLSEG2E8FFV (V1), V3 // ERROR "expected integer register in rs1 position"
+ VSSEG2E8V X10, (X10) // ERROR "expected vector register in vs1 position"
+ VSSEG2E8V V3, (V1) // ERROR "expected integer register in rd position"
+ VLSSEG2E8V (X10), V3 // ERROR "expected integer register in rs2 position"
+ VLSSEG2E8V (X10), X10, X11 // ERROR "expected vector register in vd position"
+ VLSSEG2E8V (V1), X10, V3 // ERROR "expected integer register in rs1 position"
+ VLSSEG2E8V (X10), V1, V0, V3 // ERROR "expected integer register in rs2 position"
+ VSSSEG2E8V V3, (X10) // ERROR "expected integer register in rs2 position"
+ VSSSEG2E8V X10, X11, (X10) // ERROR "expected vector register in vd position"
+ VSSSEG2E8V V3, X11, (V1) // ERROR "expected integer register in rs1 position"
+ VSSSEG2E8V V3, V1, V0, (X10) // ERROR "expected integer register in rs2 position"
+ VLUXSEG2EI8V (X10), V2, X11 // ERROR "expected vector register in vd position"
+ VLUXSEG2EI8V (X10), V2, X11 // ERROR "expected vector register in vd position"
+ VLUXSEG2EI8V (V1), V2, V3 // ERROR "expected integer register in rs1 position"
+ VLUXSEG2EI8V (X10), X11, V0, V3 // ERROR "expected vector register in vs2 position"
+ VSUXSEG2EI8V X10, V2, (X10) // ERROR "expected vector register in vd position"
+ VSUXSEG2EI8V V3, V2, (V1) // ERROR "expected integer register in rs1 position"
+ VSUXSEG2EI8V V3, X11, V0, (X10) // ERROR "expected vector register in vs2 position"
+ VLOXSEG2EI8V (X10), V2, X11 // ERROR "expected vector register in vd position"
+ VLOXSEG2EI8V (V1), V2, V3 // ERROR "expected integer register in rs1 position"
+ VLOXSEG2EI8V (X10), X11, V0, V3 // ERROR "expected vector register in vs2 position"
+ VSOXSEG2EI8V X10, V2, (X10) // ERROR "expected vector register in vd position"
+ VSOXSEG2EI8V V3, V2, (V1) // ERROR "expected integer register in rs1 position"
+ VSOXSEG2EI8V V3, X11, V0, (X10) // ERROR "expected vector register in vs2 position"
+ VL1RV (X10), X10 // ERROR "expected vector register in vd position"
+ VL1RV (V1), V3 // ERROR "expected integer register in rs1 position"
+ VS1RV X11, (X11) // ERROR "expected vector register in vs1 position"
+ VS1RV V3, (V1) // ERROR "expected integer register in rd position"
+ VADDVV V1, X10, V3 // ERROR "expected vector register in vs2 position"
+ VADDVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VADDVI $16, V4, V2 // ERROR "signed immediate 16 must be in range [-16, 15] (5 bits)"
+ VADDVI $-17, V4, V2 // ERROR "signed immediate -17 must be in range [-16, 15] (5 bits)"
+ VSUBVV V1, X10, V3 // ERROR "expected vector register in vs2 position"
+ VSUBVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VRSUBVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VRSUBVI $16, V4, V2 // ERROR "signed immediate 16 must be in range [-16, 15] (5 bits)"
+ VRSUBVI $-17, V4, V2 // ERROR "signed immediate -17 must be in range [-16, 15] (5 bits)"
+ VNEGV X10, V3 // ERROR "expected vector register in vs2 position"
+ VNEGV V2 // ERROR "expected vector register in vd position"
+ VWADDUVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VWADDUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VWSUBUVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VWSUBUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VWADDVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VWADDVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VWSUBVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VWSUBVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VWADDUWV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VWADDUWX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VWSUBUWV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VWSUBUWX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VWADDWV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VWADDWX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VWSUBWV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VWSUBWX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VWCVTXXV X10, V3 // ERROR "expected vector register in vs2 position"
+ VWCVTUXXV X10, V3 // ERROR "expected vector register in vs2 position"
+ VZEXTVF2 V2, V0, V3, V4 // ERROR "expected no register in rs3"
+ VZEXTVF2 X10, V3 // ERROR "expected vector register in vs2 position"
+ VSEXTVF2 V2, V0, V3, V4 // ERROR "expected no register in rs3"
+ VSEXTVF2 X10, V3 // ERROR "expected vector register in vs2 position"
+ VZEXTVF4 V2, V0, V3, V4 // ERROR "expected no register in rs3"
+ VZEXTVF4 X10, V3 // ERROR "expected vector register in vs2 position"
+ VSEXTVF4 V2, V0, V3, V4 // ERROR "expected no register in rs3"
+ VSEXTVF4 X10, V3 // ERROR "expected vector register in vs2 position"
+ VZEXTVF8 V2, V0, V3, V4 // ERROR "expected no register in rs3"
+ VZEXTVF8 X10, V3 // ERROR "expected vector register in vs2 position"
+ VSEXTVF8 V2, V0, V3, V4 // ERROR "expected no register in rs3"
+ VSEXTVF8 X10, V3 // ERROR "expected vector register in vs2 position"
+ VADCVVM X10, V2, V0, V3 // ERROR "expected vector register in vs1 position"
+ VADCVXM V1, V2, V0, V3 // ERROR "expected integer register in rs1 position"
+ VADCVIM $16, V2, V0, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VADCVIM $-17, V2, V0, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VMADCVVM X10, V2, V0, V3 // ERROR "expected vector register in vs1 position"
+ VMADCVXM V1, V2, V0, V3 // ERROR "expected integer register in rs1 position"
+ VMADCVIM $16, V2, V0, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VMADCVIM $-17, V2, V0, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VMADCVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMADCVV V1, V2, V0, V3 // ERROR "expected no register in rs3"
+ VMADCVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMADCVX X10, V2, V0, V3 // ERROR "expected no register in rs3"
+ VMADCVI $16, V2, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VMADCVI $-17, V2, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VMADCVI $15, V2, V0, V3 // ERROR "expected no register in rs3"
+ VSBCVVM X10, V2, V0, V3 // ERROR "expected vector register in vs1 position"
+ VSBCVXM V1, V2, V0, V3 // ERROR "expected integer register in rs1 position"
+ VMSBCVVM X10, V2, V0, V3 // ERROR "expected vector register in vs1 position"
+ VMSBCVXM V1, V2, V0, V3 // ERROR "expected integer register in rs1 position"
+ VMSBCVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMSBCVV V1, V2, V0, V3 // ERROR "expected no register in rs3"
+ VMSBCVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMSBCVX X10, V2, V0, V3 // ERROR "expected no register in rs3"
+ VANDVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VANDVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VANDVI $16, V2, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VANDVI $-17, V2, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VORVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VORVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VORVI $16, V2, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VORVI $-17, V2, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VXORVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VXORVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VXORVI $16, V2, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VXORVI $-17, V2, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VNOTV V3 // ERROR "expected vector register in vd position"
+ VNOTV X10, V3 // ERROR "expected vector register in vs2 position"
+ VSLLVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VSLLVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VSLLVI $32, V2, V3 // ERROR "unsigned immediate 32 must be in range [0, 31]"
+ VSLLVI $-1, V2, V3 // ERROR "unsigned immediate -1 must be in range [0, 31]"
+ VSRLVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VSRLVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VSRLVI $32, V2, V3 // ERROR "unsigned immediate 32 must be in range [0, 31]"
+ VSRLVI $-1, V2, V3 // ERROR "unsigned immediate -1 must be in range [0, 31]"
+ VSRAVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VSRAVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VSRAVI $32, V2, V3 // ERROR "unsigned immediate 32 must be in range [0, 31]"
+ VSRAVI $-1, V2, V3 // ERROR "unsigned immediate -1 must be in range [0, 31]"
+ VNSRLWV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VNSRLWX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VNSRLWI $32, V2, V3 // ERROR "unsigned immediate 32 must be in range [0, 31]"
+ VNSRLWI $-1, V2, V3 // ERROR "unsigned immediate -1 must be in range [0, 31]"
+ VNSRAWV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VNSRAWX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VNSRAWI $32, V2, V3 // ERROR "unsigned immediate 32 must be in range [0, 31]"
+ VNSRAWI $-1, V2, V3 // ERROR "unsigned immediate -1 must be in range [0, 31]"
+ VNCVTXXW X10, V3 // ERROR "expected vector register in vs2 position"
+ VMSEQVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMSEQVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMSEQVI $16, V2, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VMSEQVI $-17, V2, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VMSNEVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMSNEVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMSNEVI $16, V2, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VMSNEVI $-17, V2, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VMSLTUVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMSLTUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMSLTVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMSLTVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMSLEUVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMSLEUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMSLEUVI $16, V2, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VMSLEUVI $-17, V2, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VMSLEVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMSLEVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMSLEVI $16, V2, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VMSLEVI $-17, V2, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VMSGTUVV X10, V2, V3 // ERROR "expected vector register in vs2 position"
+ VMSGTUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMSGTUVI $16, V2, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VMSGTUVI $-17, V2, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VMSGTVV X10, V2, V3 // ERROR "expected vector register in vs2 position"
+ VMSGTVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMSGTVI $16, V2, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VMSGTVI $-17, V2, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VMSGEVV X10, V2, V3 // ERROR "expected vector register in vs2 position"
+ VMSGEUVV X10, V2, V3 // ERROR "expected vector register in vs2 position"
+ VMSLTVI $17, V2, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VMSLTVI $-16, V2, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VMSLTUVI $17, V2, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VMSLTUVI $-16, V2, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VMSGEVI $17, V2, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VMSGEVI $-16, V2, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VMSGEUVI $17, V2, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VMSGEUVI $-16, V2, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VMINUVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMINUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMINVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMINVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMAXUVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMAXUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMAXVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMAXVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMULVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMULVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMULHVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMULHVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMULHUVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMULHUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMULHSUVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMULHSUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VDIVUVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VDIVUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VDIVVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VDIVVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VREMUVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VREMUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VREMVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VREMVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VWMULVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VWMULVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VWMULUVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VWMULUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VWMULSUVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VWMULSUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMACCVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VMACCVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VNMSACVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VNMSACVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMADDVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VMADDVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VNMSUBVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VNMSUBVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VWMACCUVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VWMACCUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VWMACCVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VWMACCVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VWMACCSUVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VWMACCSUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VWMACCUSVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VMERGEVVM X10, V2, V0, V3 // ERROR "expected vector register in vs1 position"
+ VMERGEVXM V1, V2, V0, V3 // ERROR "expected integer register in rs1 position"
+ VMERGEVIM $16, V2, V0, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VMERGEVIM $-17, V2, V0, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VMVVV X10, V3 // ERROR "expected vector register in vs1 position"
+ VMVVX V1, V2 // ERROR "expected integer register in rs1 position"
+ VMVVI $16, V2 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VMVVI $-17, V2 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VSADDUVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VSADDUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VSADDUVI $16, V2, V3 // ERROR "signed immediate 16 must be in range [-16, 15]"
+ VSADDUVI $-17, V2, V3 // ERROR "signed immediate -17 must be in range [-16, 15]"
+ VSSUBUVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VSSUBUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VAADDUVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VAADDUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VAADDVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VAADDVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VASUBUVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VASUBUVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VASUBVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VASUBVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VSMULVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VSMULVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VSSRLVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VSSRLVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VSSRLVI $32, V2, V3 // ERROR "signed immediate 32 must be in range [0, 31]"
+ VSSRLVI $-1, V2, V3 // ERROR "signed immediate -1 must be in range [0, 31]"
+ VSSRAVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VSSRAVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VSSRAVI $32, V2, V3 // ERROR "signed immediate 32 must be in range [0, 31]"
+ VSSRAVI $-1, V2, V3 // ERROR "signed immediate -1 must be in range [0, 31]"
+ VNCLIPUWV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VNCLIPUWX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VNCLIPUWI $32, V2, V3 // ERROR "signed immediate 32 must be in range [0, 31]"
+ VNCLIPUWI $-1, V2, V3 // ERROR "signed immediate -1 must be in range [0, 31]"
+ VNCLIPWV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VNCLIPWX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VNCLIPWI $32, V2, V3 // ERROR "signed immediate 32 must be in range [0, 31]"
+ VNCLIPWI $-1, V2, V3 // ERROR "signed immediate -1 must be in range [0, 31]"
+ VFADDVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFADDVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VFSUBVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFSUBVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VFRSUBVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VFWADDVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFWADDVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VFWSUBVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFWSUBVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VFWADDWV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFWADDWF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VFWSUBWV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFWSUBWF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VFMULVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFMULVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VFDIVVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFDIVVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VFRDIVVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VFWMULVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFWMULVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VFMACCVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VFMACCVF V2, X10, V3 // ERROR "expected float register in rs1 position"
+ VFNMACCVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VFNMACCVF V2, X10, V3 // ERROR "expected float register in rs1 position"
+ VFMSACVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VFMSACVF V2, X10, V3 // ERROR "expected float register in rs1 position"
+ VFNMSACVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VFNMSACVF V2, X10, V3 // ERROR "expected float register in rs1 position"
+ VFMADDVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VFMADDVF V2, X10, V3 // ERROR "expected float register in rs1 position"
+ VFNMADDVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VFNMADDVF V2, X10, V3 // ERROR "expected float register in rs1 position"
+ VFMSUBVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VFMSUBVF V2, X10, V3 // ERROR "expected float register in rs1 position"
+ VFNMSUBVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VFNMSUBVF V2, X10, V3 // ERROR "expected float register in rs1 position"
+ VFWMACCVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VFWMACCVF V2, X10, V3 // ERROR "expected float register in rs1 position"
+ VFWNMACCVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VFWNMACCVF V2, X10, V3 // ERROR "expected float register in rs1 position"
+ VFWMSACVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VFWMSACVF V2, X10, V3 // ERROR "expected float register in rs1 position"
+ VFWNMSACVV V2, X10, V3 // ERROR "expected vector register in vs1 position"
+ VFWNMSACVF V2, X10, V3 // ERROR "expected float register in rs1 position"
+ VFSQRTV X10, V3 // ERROR "expected vector register in vs2 position"
+ VFRSQRT7V X10, V3 // ERROR "expected vector register in vs2 position"
+ VFREC7V X10, V3 // ERROR "expected vector register in vs2 position"
+ VFMINVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFMINVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VFMAXVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFMAXVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VFSGNJVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFSGNJVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VFSGNJNVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFSGNJNVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VFSGNJXVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFSGNJXVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VFNEGV V2, X10 // ERROR "expected vector register in vd position"
+ VFABSV V2, X10 // ERROR "expected vector register in vd position"
+ VMFEQVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMFEQVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VMFNEVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMFNEVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VMFLTVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMFLTVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VMFLEVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMFLEVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VMFGTVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VMFGEVF X10, V2, V3 // ERROR "expected float register in rs1 position"
+ VMFGTVV X10, V2, V3 // ERROR "expected vector register in vs2 position"
+ VMFGEVV X10, V2, V3 // ERROR "expected vector register in vs2 position"
+ VFCLASSV X10, V3 // ERROR "expected vector register in vs2 position"
+ VFMERGEVFM X10, V2, V0, V3 // ERROR "expected float register in rs1 position"
+ VFMVVF X10, V3 // ERROR "expected float register in rs1 position"
+ VFCVTXUFV X10, V3 // ERROR "expected vector register in vs2 position"
+ VFCVTXFV X10, V3 // ERROR "expected vector register in vs2 position"
+ VFCVTRTZXUFV X10, V3 // ERROR "expected vector register in vs2 position"
+ VFCVTRTZXFV X10, V3 // ERROR "expected vector register in vs2 position"
+ VFCVTFXUV X10, V3 // ERROR "expected vector register in vs2 position"
+ VFCVTFXV X10, V3 // ERROR "expected vector register in vs2 position"
+ VFWCVTXUFV X10, V3 // ERROR "expected vector register in vs2 position"
+ VFWCVTXFV X10, V3 // ERROR "expected vector register in vs2 position"
+ VFWCVTRTZXUFV X10, V3 // ERROR "expected vector register in vs2 position"
+ VFWCVTRTZXFV X10, V3 // ERROR "expected vector register in vs2 position"
+ VFWCVTFXUV X10, V3 // ERROR "expected vector register in vs2 position"
+ VFWCVTFXV X10, V3 // ERROR "expected vector register in vs2 position"
+ VFWCVTFFV X10, V3 // ERROR "expected vector register in vs2 position"
+ VFNCVTXUFW X10, V3 // ERROR "expected vector register in vs2 position"
+ VFNCVTXFW X10, V3 // ERROR "expected vector register in vs2 position"
+ VFNCVTRTZXUFW X10, V3 // ERROR "expected vector register in vs2 position"
+ VFNCVTRTZXFW X10, V3 // ERROR "expected vector register in vs2 position"
+ VFNCVTFXUW X10, V3 // ERROR "expected vector register in vs2 position"
+ VFNCVTFXW X10, V3 // ERROR "expected vector register in vs2 position"
+ VFNCVTFFW X10, V3 // ERROR "expected vector register in vs2 position"
+ VFNCVTRODFFW X10, V3 // ERROR "expected vector register in vs2 position"
+ VREDSUMVS X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VREDMAXUVS X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VREDMAXVS X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VREDMINUVS X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VREDMINVS X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VREDANDVS X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VREDORVS X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VREDXORVS X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VWREDSUMUVS X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VWREDSUMVS X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFREDOSUMVS X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFREDUSUMVS X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFREDMAXVS X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFREDMINVS X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFWREDOSUMVS X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VFWREDUSUMVS X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMANDMM X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMNANDMM X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMANDNMM X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMXORMM X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMORMM X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMNORMM X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMORNMM X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMXNORMM X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMMVM V3, X10 // ERROR "expected vector register in vd position"
+ VMNOTM V3, X10 // ERROR "expected vector register in vd position"
+ VCPOPM V2, V1 // ERROR "expected integer register in rd position"
+ VCPOPM X11, X10 // ERROR "expected vector register in vs2 position"
+ VFIRSTM V2, V1 // ERROR "expected integer register in rd position"
+ VFIRSTM X11, X10 // ERROR "expected vector register in vs2 position"
+ VMSBFM X10, V3 // ERROR "expected vector register in vs2 position"
+ VMSIFM X10, V3 // ERROR "expected vector register in vs2 position"
+ VMSOFM X10, V3 // ERROR "expected vector register in vs2 position"
+ VIOTAM X10, V3 // ERROR "expected vector register in vs2 position"
+ VIDV X10 // ERROR "expected vector register in vd position"
+ VMVXS X11, X10 // ERROR "expected vector register in vs2 position"
+ VMVXS V2, V1 // ERROR "expected integer register in rd position"
+ VMVSX X11, X10 // ERROR "expected vector register in vd position"
+ VMVSX V2, V1 // ERROR "expected integer register in rs2 position"
+ VFMVFS X10, F10 // ERROR "expected vector register in vs2 position"
+ VFMVFS V2, V1 // ERROR "expected float register in rd position"
+ VFMVSF X10, V2 // ERROR "expected float register in rs2 position"
+ VFMVSF V2, V1 // ERROR "expected float register in rs2 position"
+ VSLIDEUPVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VSLIDEUPVI $-1, V2, V3 // ERROR "unsigned immediate -1 must be in range [0, 31]"
+ VSLIDEUPVI $32, V2, V3 // ERROR "unsigned immediate 32 must be in range [0, 31]"
+ VSLIDEDOWNVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VSLIDEDOWNVI $-1, V2, V3 // ERROR "unsigned immediate -1 must be in range [0, 31]"
+ VSLIDEDOWNVI $32, V2, V3 // ERROR "unsigned immediate 32 must be in range [0, 31]"
+ VSLIDE1UPVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VFSLIDE1UPVF V1, V2, V3 // ERROR "expected float register in rs1 position"
+ VSLIDE1DOWNVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VFSLIDE1DOWNVF V1, V2, V3 // ERROR "expected float register in rs1 position"
+ VRGATHERVV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VRGATHEREI16VV X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VRGATHERVX V1, V2, V3 // ERROR "expected integer register in rs1 position"
+ VRGATHERVI $-1, V2, V3 // ERROR "unsigned immediate -1 must be in range [0, 31]"
+ VRGATHERVI $32, V2, V3 // ERROR "unsigned immediate 32 must be in range [0, 31]"
+ VCOMPRESSVM X10, V2, V3 // ERROR "expected vector register in vs1 position"
+ VMV1RV X10, V1 // ERROR "expected vector register in vs2 position"
+ VMV2RV X10, V10 // ERROR "expected vector register in vs2 position"
+ VMV4RV X10, V4 // ERROR "expected vector register in vs2 position"
+ VMV8RV X10, V0 // ERROR "expected vector register in vs2 position"
+
+ RET
diff --git a/src/cmd/asm/internal/asm/testdata/s390x.s b/src/cmd/asm/internal/asm/testdata/s390x.s
index db2ee199cf..93c3ec9ea7 100644
--- a/src/cmd/asm/internal/asm/testdata/s390x.s
+++ b/src/cmd/asm/internal/asm/testdata/s390x.s
@@ -263,10 +263,15 @@ TEXT main·foo(SB),DUPOK|NOSPLIT,$16-0 // TEXT main.foo(SB), DUPOK|NOSPLIT, $16-
NC $8, (R15), n-8(SP) // d407f010f000
OC $8, (R15), n-8(SP) // d607f010f000
MVC $8, (R15), n-8(SP) // d207f010f000
+ MVC $256, 8192(R1), 8192(R2) // b90400a2c2a800002000b90400b1c2b800002000d2ffa000b000
MVCIN $8, (R15), n-8(SP) // e807f010f000
CLC $8, (R15), n-8(SP) // d507f000f010
XC $256, -8(R15), -8(R15) // b90400afc2a8fffffff8d7ffa000a000
- MVC $256, 8192(R1), 8192(R2) // b90400a2c2a800002000b90400b1c2b800002000d2ffa000b000
+ MVCLE 0, R4, R6 // a8640000
+ MVCLE 4095, R4, R6 // a8640fff
+ MVCLE $4095, R4, R6 // a8640fff
+ MVCLE (R3), R4, R6 // a8643000
+ MVCLE 10(R3), R4, R6 // a864300a
CMP R1, R2 // b9200012
CMP R3, $32767 // a73f7fff
@@ -401,6 +406,7 @@ TEXT main·foo(SB),DUPOK|NOSPLIT,$16-0 // TEXT main.foo(SB), DUPOK|NOSPLIT, $16-
FMADDS F1, F2, F3 // b30e3012
FMSUB F4, F5, F5 // b31f5045
FMSUBS F6, F6, F7 // b30f7066
+ LCDBR F0, F2 // b3130020
LPDFR F1, F2 // b3700021
LNDFR F3, F4 // b3710043
CPSDR F5, F6, F7 // b3725076
@@ -420,8 +426,8 @@ TEXT main·foo(SB),DUPOK|NOSPLIT,$16-0 // TEXT main.foo(SB), DUPOK|NOSPLIT, $16-
KLMD R2, R8 // b93f0028
KIMD R0, R4 // b93e0004
KDSA R0, R8 // b93a0008
- KMA R2, R6, R4 // b9296024
- KMCTR R2, R6, R4 // b92d6024
+ KMA R2, R6, R4 // b9296024
+ KMCTR R2, R6, R4 // b92d6024
// vector add and sub instructions
VAB V3, V4, V4 // e743400000f3
@@ -534,6 +540,18 @@ TEXT main·foo(SB),DUPOK|NOSPLIT,$16-0 // TEXT main.foo(SB), DUPOK|NOSPLIT, $16-
VSTRCZBS V18, V20, V22, V24 // e78240306f8a
VSTRCZHS V18, V20, V22, V24 // e78241306f8a
VSTRCZFS V18, V20, V22, V24 // e78242306f8a
+ VFMAXSB $1, V2, V3, V4 // e742301020ef
+ WFMAXSB $2, V5, V6, V7 // e775602820ef
+ WFMAXSB $2, F5, F6, F7 // e775602820ef
+ VFMAXDB $3, V8, V9, V10 // e7a8903030ef
+ WFMAXDB $4, V11, V12, V13 // e7dbc04830ef
+ WFMAXDB $4, F11, F12, F13 // e7dbc04830ef
+ VFMINSB $7, V14, V15, V16 // e70ef07028ee
+ WFMINSB $8, V17, V18, V19 // e73120882eee
+ WFMINSB $8, F1, F2, F3 // e731208820ee
+ VFMINDB $9, V20, V21, V22 // e76450903eee
+ WFMINDB $10, V23, V24, V25 // e79780a83eee
+ WFMINDB $10, F7, F8, F9 // e79780a830ee
RET
RET foo(SB)
diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go
index 9fdb7c49a2..2a9ebe9b3e 100644
--- a/src/cmd/asm/main.go
+++ b/src/cmd/asm/main.go
@@ -49,6 +49,7 @@ func main() {
ctxt.Debugpcln = flags.DebugFlags.PCTab
ctxt.IsAsm = true
ctxt.Pkgpath = *flags.Importpath
+ ctxt.DwTextCount = objabi.DummyDwarfFunctionCountForAssembler()
switch *flags.Spectre {
default:
log.Printf("unknown setting -spectre=%s", *flags.Spectre)
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index d2d7b894b5..6c1695bdb0 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -27,6 +27,7 @@ import (
"slices"
"strconv"
"strings"
+ "sync/atomic"
"unicode"
"unicode/utf8"
@@ -182,25 +183,22 @@ func splitQuoted(s string) (r []string, err error) {
return args, err
}
-// Translate rewrites f.AST, the original Go input, to remove
-// references to the imported package C, replacing them with
-// references to the equivalent Go types, functions, and variables.
-func (p *Package) Translate(f *File) {
+// loadDebug runs gcc to load debug information for the File. The debug
+// information will be saved to the debugs field of the file, and be
+// processed when Translate is called on the file later.
+// loadDebug is called concurrently with different files.
+func (f *File) loadDebug(p *Package) {
for _, cref := range f.Ref {
// Convert C.ulong to C.unsigned long, etc.
cref.Name.C = cname(cref.Name.Go)
}
- var conv typeConv
- conv.Init(p.PtrSize, p.IntSize)
-
- p.typedefs = map[string]bool{}
- p.typedefList = nil
+ ft := fileTypedefs{typedefs: make(map[string]bool)}
numTypedefs := -1
- for len(p.typedefs) > numTypedefs {
- numTypedefs = len(p.typedefs)
+ for len(ft.typedefs) > numTypedefs {
+ numTypedefs = len(ft.typedefs)
// Also ask about any typedefs we've seen so far.
- for _, info := range p.typedefList {
+ for _, info := range ft.typedefList {
if f.Name[info.typedef] != nil {
continue
}
@@ -213,7 +211,7 @@ func (p *Package) Translate(f *File) {
}
needType := p.guessKinds(f)
if len(needType) > 0 {
- p.loadDWARF(f, &conv, needType)
+ f.debugs = append(f.debugs, p.loadDWARF(f, &ft, needType))
}
// In godefs mode we're OK with the typedefs, which
@@ -223,6 +221,18 @@ func (p *Package) Translate(f *File) {
break
}
}
+}
+
+// Translate rewrites f.AST, the original Go input, to remove
+// references to the imported package C, replacing them with
+// references to the equivalent Go types, functions, and variables.
+// Preconditions: File.loadDebug must be called prior to translate.
+func (p *Package) Translate(f *File) {
+ var conv typeConv
+ conv.Init(p.PtrSize, p.IntSize)
+ for _, d := range f.debugs {
+ p.recordTypes(f, d, &conv)
+ }
p.prepareNames(f)
if p.rewriteCalls(f) {
// Add `import _cgo_unsafe "unsafe"` after the package statement.
@@ -241,7 +251,7 @@ func (f *File) loadDefines(gccOptions []string) bool {
stdout := gccDefines(b.Bytes(), gccOptions)
var gccIsClang bool
- for _, line := range strings.Split(stdout, "\n") {
+ for line := range strings.SplitSeq(stdout, "\n") {
if len(line) < 9 || line[0:7] != "#define" {
continue
}
@@ -279,6 +289,7 @@ func (f *File) loadDefines(gccOptions []string) bool {
// guessKinds tricks gcc into revealing the kind of each
// name xxx for the references C.xxx in the Go input.
// The kind is either a constant, type, or variable.
+// guessKinds is called concurrently with different files.
func (p *Package) guessKinds(f *File) []*Name {
// Determine kinds for names we already know about,
// like #defines or 'struct foo', before bothering with gcc.
@@ -417,7 +428,7 @@ func (p *Package) guessKinds(f *File) []*Name {
notDeclared
)
sawUnmatchedErrors := false
- for _, line := range strings.Split(stderr, "\n") {
+ for line := range strings.SplitSeq(stderr, "\n") {
// Ignore warnings and random comments, with one
// exception: newer GCC versions will sometimes emit
// an error on a macro #define with a note referring
@@ -522,7 +533,8 @@ func (p *Package) guessKinds(f *File) []*Name {
// loadDWARF parses the DWARF debug information generated
// by gcc to learn the details of the constants, variables, and types
// being referred to as C.xxx.
-func (p *Package) loadDWARF(f *File, conv *typeConv, names []*Name) {
+// loadDwarf is called concurrently with different files.
+func (p *Package) loadDWARF(f *File, ft *fileTypedefs, names []*Name) *debug {
// Extract the types from the DWARF section of an object
// from a well-formed C program. Gcc only generates DWARF info
// for symbols in the object file, so it is not enough to print the
@@ -636,13 +648,28 @@ func (p *Package) loadDWARF(f *File, conv *typeConv, names []*Name) {
fatalf("malformed __cgo__ name: %s", name)
}
types[i] = t.Type
- p.recordTypedefs(t.Type, f.NamePos[names[i]])
+ ft.recordTypedefs(t.Type, f.NamePos[names[i]])
}
if e.Tag != dwarf.TagCompileUnit {
r.SkipChildren()
}
}
+ return &debug{names, types, ints, floats, strs}
+}
+
+// debug is the data extracted by running an iteration of loadDWARF on a file.
+type debug struct {
+ names []*Name
+ types []dwarf.Type
+ ints []int64
+ floats []float64
+ strs []string
+}
+
+func (p *Package) recordTypes(f *File, data *debug, conv *typeConv) {
+ names, types, ints, floats, strs := data.names, data.types, data.ints, data.floats, data.strs
+
// Record types and typedef information.
for i, n := range names {
if strings.HasSuffix(n.Go, "GetTypeID") && types[i].String() == "func() CFTypeID" {
@@ -701,12 +728,17 @@ func (p *Package) loadDWARF(f *File, conv *typeConv, names []*Name) {
}
}
-// recordTypedefs remembers in p.typedefs all the typedefs used in dtypes and its children.
-func (p *Package) recordTypedefs(dtype dwarf.Type, pos token.Pos) {
- p.recordTypedefs1(dtype, pos, map[dwarf.Type]bool{})
+type fileTypedefs struct {
+ typedefs map[string]bool // type names that appear in the types of the objects we're interested in
+ typedefList []typedefInfo
}
-func (p *Package) recordTypedefs1(dtype dwarf.Type, pos token.Pos, visited map[dwarf.Type]bool) {
+// recordTypedefs remembers in ft.typedefs all the typedefs used in dtypes and its children.
+func (ft *fileTypedefs) recordTypedefs(dtype dwarf.Type, pos token.Pos) {
+ ft.recordTypedefs1(dtype, pos, map[dwarf.Type]bool{})
+}
+
+func (ft *fileTypedefs) recordTypedefs1(dtype dwarf.Type, pos token.Pos, visited map[dwarf.Type]bool) {
if dtype == nil {
return
}
@@ -720,25 +752,25 @@ func (p *Package) recordTypedefs1(dtype dwarf.Type, pos token.Pos, visited map[d
// Don't look inside builtin types. There be dragons.
return
}
- if !p.typedefs[dt.Name] {
- p.typedefs[dt.Name] = true
- p.typedefList = append(p.typedefList, typedefInfo{dt.Name, pos})
- p.recordTypedefs1(dt.Type, pos, visited)
+ if !ft.typedefs[dt.Name] {
+ ft.typedefs[dt.Name] = true
+ ft.typedefList = append(ft.typedefList, typedefInfo{dt.Name, pos})
+ ft.recordTypedefs1(dt.Type, pos, visited)
}
case *dwarf.PtrType:
- p.recordTypedefs1(dt.Type, pos, visited)
+ ft.recordTypedefs1(dt.Type, pos, visited)
case *dwarf.ArrayType:
- p.recordTypedefs1(dt.Type, pos, visited)
+ ft.recordTypedefs1(dt.Type, pos, visited)
case *dwarf.QualType:
- p.recordTypedefs1(dt.Type, pos, visited)
+ ft.recordTypedefs1(dt.Type, pos, visited)
case *dwarf.FuncType:
- p.recordTypedefs1(dt.ReturnType, pos, visited)
+ ft.recordTypedefs1(dt.ReturnType, pos, visited)
for _, a := range dt.ParamType {
- p.recordTypedefs1(a, pos, visited)
+ ft.recordTypedefs1(a, pos, visited)
}
case *dwarf.StructType:
- for _, f := range dt.Field {
- p.recordTypedefs1(f.Type, pos, visited)
+ for _, l := range dt.Field {
+ ft.recordTypedefs1(l.Type, pos, visited)
}
}
}
@@ -1756,20 +1788,24 @@ func gccMachine() []string {
return nil
}
+var n atomic.Int64
+
func gccTmp() string {
- return *objDir + "_cgo_.o"
+ c := strconv.Itoa(int(n.Add(1)))
+ return *objDir + "_cgo_" + c + ".o"
}
// gccCmd returns the gcc command line to use for compiling
// the input.
-func (p *Package) gccCmd() []string {
+// gccCommand is called concurrently for different files.
+func (p *Package) gccCmd(ofile string) []string {
c := append(gccBaseCmd,
- "-w", // no warnings
- "-Wno-error", // warnings are not errors
- "-o"+gccTmp(), // write object to tmp
- "-gdwarf-2", // generate DWARF v2 debugging symbols
- "-c", // do not link
- "-xc", // input language is C
+ "-w", // no warnings
+ "-Wno-error", // warnings are not errors
+ "-o"+ofile, // write object to tmp
+ "-gdwarf-2", // generate DWARF v2 debugging symbols
+ "-c", // do not link
+ "-xc", // input language is C
)
if p.GccIsClang {
c = append(c,
@@ -1805,8 +1841,10 @@ func (p *Package) gccCmd() []string {
// gccDebug runs gcc -gdwarf-2 over the C program stdin and
// returns the corresponding DWARF data and, if present, debug data block.
+// gccDebug is called concurrently with different C programs.
func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int64, floats []float64, strs []string) {
- runGcc(stdin, p.gccCmd())
+ ofile := gccTmp()
+ runGcc(stdin, p.gccCmd(ofile))
isDebugInts := func(s string) bool {
// Some systems use leading _ to denote non-assembly symbols.
@@ -1856,11 +1894,11 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6
}
}
- if f, err := macho.Open(gccTmp()); err == nil {
+ if f, err := macho.Open(ofile); err == nil {
defer f.Close()
d, err := f.DWARF()
if err != nil {
- fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
+ fatalf("cannot load DWARF output from %s: %v", ofile, err)
}
bo := f.ByteOrder
if f.Symtab != nil {
@@ -1934,11 +1972,11 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6
return d, ints, floats, strs
}
- if f, err := elf.Open(gccTmp()); err == nil {
+ if f, err := elf.Open(ofile); err == nil {
defer f.Close()
d, err := f.DWARF()
if err != nil {
- fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
+ fatalf("cannot load DWARF output from %s: %v", ofile, err)
}
bo := f.ByteOrder
symtab, err := f.Symbols()
@@ -2034,11 +2072,11 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6
return d, ints, floats, strs
}
- if f, err := pe.Open(gccTmp()); err == nil {
+ if f, err := pe.Open(ofile); err == nil {
defer f.Close()
d, err := f.DWARF()
if err != nil {
- fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
+ fatalf("cannot load DWARF output from %s: %v", ofile, err)
}
bo := binary.LittleEndian
for _, s := range f.Symbols {
@@ -2106,11 +2144,11 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6
return d, ints, floats, strs
}
- if f, err := xcoff.Open(gccTmp()); err == nil {
+ if f, err := xcoff.Open(ofile); err == nil {
defer f.Close()
d, err := f.DWARF()
if err != nil {
- fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
+ fatalf("cannot load DWARF output from %s: %v", ofile, err)
}
bo := binary.BigEndian
for _, s := range f.Symbols {
@@ -2176,7 +2214,7 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6
buildStrings()
return d, ints, floats, strs
}
- fatalf("cannot parse gcc output %s as ELF, Mach-O, PE, XCOFF object", gccTmp())
+ fatalf("cannot parse gcc output %s as ELF, Mach-O, PE, XCOFF object", ofile)
panic("not reached")
}
@@ -2194,9 +2232,10 @@ func gccDefines(stdin []byte, gccOptions []string) string {
// gccErrors runs gcc over the C program stdin and returns
// the errors that gcc prints. That is, this function expects
// gcc to fail.
+// gccErrors is called concurrently with different C programs.
func (p *Package) gccErrors(stdin []byte, extraArgs ...string) string {
// TODO(rsc): require failure
- args := p.gccCmd()
+ args := p.gccCmd(gccTmp())
// Optimization options can confuse the error messages; remove them.
nargs := make([]string, 0, len(args)+len(extraArgs))
@@ -2863,7 +2902,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
if ss, ok := dwarfToName[s]; ok {
s = ss
}
- s = strings.Replace(s, " ", "", -1)
+ s = strings.ReplaceAll(s, " ", "")
name := c.Ident("_Ctype_" + s)
tt := *t
typedef[name.Name] = &tt
diff --git a/src/cmd/cgo/internal/swig/swig_test.go b/src/cmd/cgo/internal/swig/swig_test.go
index 923378b2dd..603dab4917 100644
--- a/src/cmd/cgo/internal/swig/swig_test.go
+++ b/src/cmd/cgo/internal/swig/swig_test.go
@@ -40,8 +40,9 @@ func TestCallback(t *testing.T) {
}
func run(t *testing.T, dir string, lto bool, args ...string) {
+ testenv.MustHaveGoRun(t)
runArgs := append([]string{"run", "."}, args...)
- cmd := exec.Command("go", runArgs...)
+ cmd := exec.Command(testenv.GoToolPath(t), runArgs...)
cmd.Dir = dir
if lto {
// On the builders we're using the default /usr/bin/ld, but
@@ -68,7 +69,7 @@ func run(t *testing.T, dir string, lto bool, args ...string) {
func mustHaveCxx(t *testing.T) {
// Ask the go tool for the CXX it's configured to use.
- cxx, err := exec.Command("go", "env", "CXX").CombinedOutput()
+ cxx, err := exec.Command(testenv.GoToolPath(t), "env", "CXX").CombinedOutput()
if err != nil {
t.Fatalf("go env CXX failed: %s", err)
}
diff --git a/src/cmd/cgo/internal/test/issue18146.go b/src/cmd/cgo/internal/test/issue18146.go
index 112b7ee2e7..04e5b5ffb8 100644
--- a/src/cmd/cgo/internal/test/issue18146.go
+++ b/src/cmd/cgo/internal/test/issue18146.go
@@ -11,6 +11,7 @@ package cgotest
import (
"bytes"
"crypto/md5"
+ "internal/testenv"
"os"
"os/exec"
"runtime"
@@ -73,7 +74,7 @@ func test18146(t *testing.T) {
}
runtime.GOMAXPROCS(threads)
argv := append(os.Args, "-test.run=^$")
- if err := syscall.Exec(os.Args[0], argv, os.Environ()); err != nil {
+ if err := syscall.Exec(testenv.Executable(t), argv, os.Environ()); err != nil {
t.Fatal(err)
}
}
@@ -87,7 +88,7 @@ func test18146(t *testing.T) {
args := append(append([]string(nil), os.Args[1:]...), "-test.run=^Test18146$")
for n := attempts; n > 0; n-- {
- cmd := exec.Command(os.Args[0], args...)
+ cmd := exec.Command(testenv.Executable(t), args...)
cmd.Env = append(os.Environ(), "test18146=exec")
buf := bytes.NewBuffer(nil)
cmd.Stdout = buf
diff --git a/src/cmd/cgo/internal/test/issue4029.go b/src/cmd/cgo/internal/test/issue4029.go
index 506c999bdb..702af3f531 100644
--- a/src/cmd/cgo/internal/test/issue4029.go
+++ b/src/cmd/cgo/internal/test/issue4029.go
@@ -11,6 +11,7 @@ package cgotest
/*
#include
+#include
#include
#cgo linux LDFLAGS: -ldl
@@ -24,6 +25,7 @@ import "C"
import (
"testing"
+ "unsafe"
)
var callbacks int
@@ -66,7 +68,9 @@ func loadThySelf(t *testing.T, symbol string) {
}
defer C.dlclose4029(this_process)
- symbol_address := C.dlsym4029(this_process, C.CString(symbol))
+ symCStr := C.CString(symbol)
+ defer C.free(unsafe.Pointer(symCStr))
+ symbol_address := C.dlsym4029(this_process, symCStr)
if symbol_address == 0 {
t.Error("dlsym:", C.GoString(C.dlerror()))
return
diff --git a/src/cmd/cgo/internal/test/issue42018_windows.go b/src/cmd/cgo/internal/test/issue42018_windows.go
index 8f4570ab2a..ea11b8b20b 100644
--- a/src/cmd/cgo/internal/test/issue42018_windows.go
+++ b/src/cmd/cgo/internal/test/issue42018_windows.go
@@ -27,6 +27,7 @@ func test42018(t *testing.T) {
recurseHWND(400, hwnd, uintptr(unsafe.Pointer(&i)))
}
+//go:noinline
func recurseHANDLE(n int, p C.HANDLE, v uintptr) {
if n > 0 {
recurseHANDLE(n-1, p, v)
@@ -36,6 +37,7 @@ func recurseHANDLE(n int, p C.HANDLE, v uintptr) {
}
}
+//go:noinline
func recurseHWND(n int, p C.HWND, v uintptr) {
if n > 0 {
recurseHWND(n-1, p, v)
diff --git a/src/cmd/cgo/internal/test/test.go b/src/cmd/cgo/internal/test/test.go
index fcac076225..9626407d88 100644
--- a/src/cmd/cgo/internal/test/test.go
+++ b/src/cmd/cgo/internal/test/test.go
@@ -245,7 +245,7 @@ static void *thread(void *p) {
return NULL;
}
void testSendSIG() {
- const int N = 20;
+ enum { N = 20 };
int i;
pthread_t tid[N];
for (i = 0; i < N; i++) {
@@ -1096,8 +1096,15 @@ func testErrno(t *testing.T) {
}
func testMultipleAssign(t *testing.T) {
+ if runtime.GOOS == "windows" && usesUCRT(t) {
+ // UCRT's strtol throws an unrecoverable crash when
+ // using an invalid base (that is, not 0 or 2..36).
+ // See go.dev/issue/62887.
+ t.Skip("skipping test on Windows when linking with UCRT")
+ }
p := C.CString("234")
n, m := C.strtol(p, nil, 345), C.strtol(p, nil, 10)
+ defer C.free(unsafe.Pointer(p))
if runtime.GOOS == "openbsd" {
// Bug in OpenBSD strtol(3) - base > 36 succeeds.
if (n != 0 && n != 239089) || m != 234 {
@@ -1106,7 +1113,6 @@ func testMultipleAssign(t *testing.T) {
} else if n != 0 || m != 234 {
t.Fatal("Strtol x2: ", n, m)
}
- C.free(unsafe.Pointer(p))
}
var (
@@ -1632,7 +1638,9 @@ func testNaming(t *testing.T) {
func test6907(t *testing.T) {
want := "yarn"
- if got := C.GoString(C.Issue6907CopyString(want)); got != want {
+ s := C.Issue6907CopyString(want)
+ defer C.free(unsafe.Pointer(s))
+ if got := C.GoString(s); got != want {
t.Errorf("C.GoString(C.Issue6907CopyString(%q)) == %q, want %q", want, got, want)
}
}
@@ -1881,6 +1889,7 @@ func test17537(t *testing.T) {
}
p := (*C.char)(C.malloc(1))
+ defer C.free(unsafe.Pointer(p))
*p = 17
if got, want := C.F17537(&p), C.int(17); got != want {
t.Errorf("got %d, want %d", got, want)
diff --git a/src/cmd/cgo/internal/test/test_unix.go b/src/cmd/cgo/internal/test/test_unix.go
index 664c4850d3..9f35583125 100644
--- a/src/cmd/cgo/internal/test/test_unix.go
+++ b/src/cmd/cgo/internal/test/test_unix.go
@@ -6,6 +6,13 @@
package cgotest
-import "syscall"
+import (
+ "syscall"
+ "testing"
+)
var syscall_dot_SIGCHLD = syscall.SIGCHLD
+
+func usesUCRT(t *testing.T) bool {
+ return false
+}
diff --git a/src/cmd/cgo/internal/test/test_windows.go b/src/cmd/cgo/internal/test/test_windows.go
index 7bfb33a83c..c6f31430cf 100644
--- a/src/cmd/cgo/internal/test/test_windows.go
+++ b/src/cmd/cgo/internal/test/test_windows.go
@@ -4,6 +4,20 @@
package cgotest
-import "syscall"
+import (
+ "internal/syscall/windows"
+ "syscall"
+ "testing"
+)
var syscall_dot_SIGCHLD syscall.Signal
+
+// usesUCRT reports whether the test is using the Windows UCRT (Universal C Runtime).
+func usesUCRT(t *testing.T) bool {
+ name, err := syscall.UTF16PtrFromString("ucrtbase.dll")
+ if err != nil {
+ t.Fatal(err)
+ }
+ h, err := windows.GetModuleHandle(name)
+ return err == nil && h != 0
+}
diff --git a/src/cmd/cgo/internal/test/testx.go b/src/cmd/cgo/internal/test/testx.go
index 0e2a51a522..9a63b9e100 100644
--- a/src/cmd/cgo/internal/test/testx.go
+++ b/src/cmd/cgo/internal/test/testx.go
@@ -447,7 +447,7 @@ func issue7978check(t *testing.T, wantFunc string, badFunc string, depth int) {
runtime.GC()
buf := make([]byte, 65536)
trace := string(buf[:runtime.Stack(buf, true)])
- for _, goroutine := range strings.Split(trace, "\n\n") {
+ for goroutine := range strings.SplitSeq(trace, "\n\n") {
if strings.Contains(goroutine, "test.issue7978go") {
trace := strings.Split(goroutine, "\n")
// look for the expected function in the stack
diff --git a/src/cmd/cgo/internal/testcarchive/carchive_test.go b/src/cmd/cgo/internal/testcarchive/carchive_test.go
index c263b82d57..c0ad79f231 100644
--- a/src/cmd/cgo/internal/testcarchive/carchive_test.go
+++ b/src/cmd/cgo/internal/testcarchive/carchive_test.go
@@ -58,7 +58,7 @@ func TestMain(m *testing.M) {
}
func testMain(m *testing.M) int {
- if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
+ if testing.Short() && testenv.Builder() == "" {
globalSkip = func(t testing.TB) { t.Skip("short mode and $GO_BUILDER_NAME not set") }
return m.Run()
}
@@ -218,7 +218,7 @@ func genHeader(t *testing.T, header, dir string) {
t.Fatal(err)
}
- cmd := exec.Command("go", "tool", "cgo",
+ cmd := exec.Command(testenv.GoToolPath(t), "tool", "cgo",
"-objdir", objDir,
"-exportheader", header)
cmd.Args = append(cmd.Args, files...)
@@ -524,7 +524,7 @@ func TestEarlySignalHandler(t *testing.T) {
}()
}
- cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
if out, err := cmd.CombinedOutput(); err != nil {
t.Logf("%s", out)
t.Fatal(err)
@@ -674,7 +674,7 @@ func buildSignalForwardingTest(t *testing.T) {
}
t.Log("go build -buildmode=c-archive -o libgo2.a ./libgo2")
- cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
out, err := cmd.CombinedOutput()
if len(out) > 0 {
t.Logf("%s", out)
@@ -801,7 +801,7 @@ func TestOsSignal(t *testing.T) {
}()
}
- cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo3.a", "./libgo3")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", "libgo3.a", "./libgo3")
if out, err := cmd.CombinedOutput(); err != nil {
t.Logf("%s", out)
t.Fatal(err)
@@ -843,7 +843,7 @@ func TestSigaltstack(t *testing.T) {
}()
}
- cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo4.a", "./libgo4")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", "libgo4.a", "./libgo4")
if out, err := cmd.CombinedOutput(); err != nil {
t.Logf("%s", out)
t.Fatal(err)
@@ -908,7 +908,7 @@ func TestExtar(t *testing.T) {
t.Fatal(err)
}
- cmd := exec.Command("go", "build", "-buildmode=c-archive", "-ldflags=-extar="+filepath.Join(dir, "testar"), "-o", "libgo4.a", "./libgo4")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-ldflags=-extar="+filepath.Join(dir, "testar"), "-o", "libgo4.a", "./libgo4")
if out, err := cmd.CombinedOutput(); err != nil {
t.Logf("%s", out)
t.Fatal(err)
@@ -955,7 +955,7 @@ func TestPIE(t *testing.T) {
// be running this test in a GOROOT owned by root.)
genHeader(t, "p.h", "./p")
- cmd := exec.Command("go", "build", "-buildmode=c-archive", "./libgo")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "./libgo")
if out, err := cmd.CombinedOutput(); err != nil {
t.Logf("%s", out)
t.Fatal(err)
@@ -1042,7 +1042,7 @@ func TestSIGPROF(t *testing.T) {
}()
}
- cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "./libgo6")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", "libgo6.a", "./libgo6")
out, err := cmd.CombinedOutput()
t.Logf("%v\n%s", cmd.Args, out)
if err != nil {
@@ -1089,7 +1089,7 @@ func TestCompileWithoutShared(t *testing.T) {
}()
}
- cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "./libgo2")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "./libgo2")
out, err := cmd.CombinedOutput()
t.Logf("%v\n%s", cmd.Args, out)
if err != nil {
@@ -1204,7 +1204,7 @@ func TestManyCalls(t *testing.T) {
}()
}
- cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo7.a", "./libgo7")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", "libgo7.a", "./libgo7")
out, err := cmd.CombinedOutput()
t.Logf("%v\n%s", cmd.Args, out)
if err != nil {
@@ -1259,7 +1259,7 @@ func TestPreemption(t *testing.T) {
}()
}
- cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo8.a", "./libgo8")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", "libgo8.a", "./libgo8")
out, err := cmd.CombinedOutput()
t.Logf("%v\n%s", cmd.Args, out)
if err != nil {
@@ -1309,7 +1309,7 @@ func TestDeepStack(t *testing.T) {
}()
}
- cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo9.a", "./libgo9")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", "libgo9.a", "./libgo9")
out, err := cmd.CombinedOutput()
t.Logf("%v\n%s", cmd.Args, out)
if err != nil {
@@ -1372,7 +1372,7 @@ func BenchmarkCgoCallbackMainThread(b *testing.B) {
}()
}
- cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo10.a", "./libgo10")
+ cmd := exec.Command(testenv.GoToolPath(b), "build", "-buildmode=c-archive", "-o", "libgo10.a", "./libgo10")
out, err := cmd.CombinedOutput()
b.Logf("%v\n%s", cmd.Args, out)
if err != nil {
@@ -1414,7 +1414,7 @@ func TestSharedObject(t *testing.T) {
}()
}
- cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo_s.a", "./libgo")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", "libgo_s.a", "./libgo")
out, err := cmd.CombinedOutput()
t.Logf("%v\n%s", cmd.Args, out)
if err != nil {
diff --git a/src/cmd/cgo/internal/testcshared/cshared_test.go b/src/cmd/cgo/internal/testcshared/cshared_test.go
index 7e9a274d05..2ce705adba 100644
--- a/src/cmd/cgo/internal/testcshared/cshared_test.go
+++ b/src/cmd/cgo/internal/testcshared/cshared_test.go
@@ -8,6 +8,7 @@ import (
"bufio"
"bytes"
"cmd/cgo/internal/cgotest"
+ "cmp"
"debug/elf"
"debug/pe"
"encoding/binary"
@@ -44,7 +45,7 @@ func TestMain(m *testing.M) {
func testMain(m *testing.M) int {
log.SetFlags(log.Lshortfile)
flag.Parse()
- if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
+ if testing.Short() && testenv.Builder() == "" {
globalSkip = func(t *testing.T) { t.Skip("short mode and $GO_BUILDER_NAME not set") }
return m.Run()
}
@@ -272,7 +273,7 @@ func createHeaders() error {
// 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"),
+ []byte("LIBRARY libgo.dll\nEXPORTS\n\tDidInitRun\n\tDidMainRun\n\tDivu\n\tFromPkg\n"),
0644)
if err != nil {
return fmt.Errorf("unable to write def file: %v", err)
@@ -375,27 +376,22 @@ func TestExportedSymbols(t *testing.T) {
}
}
-func checkNumberOfExportedFunctionsWindows(t *testing.T, exportAllSymbols bool) {
- const prog = `
+func checkNumberOfExportedSymbolsWindows(t *testing.T, exportedSymbols int, wantAll bool) {
+ t.Parallel()
+ tmpdir := t.TempDir()
+
+ prog := `
package main
-
import "C"
-
-//export GoFunc
-func GoFunc() {
- println(42)
-}
-
-//export GoFunc2
-func GoFunc2() {
- println(24)
-}
-
-func main() {
-}
+func main() {}
`
- tmpdir := t.TempDir()
+ for i := range exportedSymbols {
+ prog += fmt.Sprintf(`
+//export GoFunc%d
+func GoFunc%d() {}
+`, i, i)
+ }
srcfile := filepath.Join(tmpdir, "test.go")
objfile := filepath.Join(tmpdir, "test.dll")
@@ -403,11 +399,11 @@ func main() {
t.Fatal(err)
}
argv := []string{"build", "-buildmode=c-shared"}
- if exportAllSymbols {
+ if wantAll {
argv = append(argv, "-ldflags", "-extldflags=-Wl,--export-all-symbols")
}
argv = append(argv, "-o", objfile, srcfile)
- out, err := exec.Command("go", argv...).CombinedOutput()
+ out, err := exec.Command(testenv.GoToolPath(t), argv...).CombinedOutput()
if err != nil {
t.Fatalf("build failure: %s\n%s\n", err, string(out))
}
@@ -417,11 +413,37 @@ func main() {
t.Fatalf("pe.Open failed: %v", err)
}
defer f.Close()
- section := f.Section(".edata")
- if section == nil {
- t.Skip(".edata section is not present")
+
+ _, pe64 := f.OptionalHeader.(*pe.OptionalHeader64)
+ // grab the export data directory entry
+ var idd pe.DataDirectory
+ if pe64 {
+ idd = f.OptionalHeader.(*pe.OptionalHeader64).DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT]
+ } else {
+ idd = f.OptionalHeader.(*pe.OptionalHeader32).DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT]
}
+ // figure out which section contains the import directory table
+ var section *pe.Section
+ for _, s := range f.Sections {
+ if s.Offset == 0 {
+ continue
+ }
+ if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress-s.VirtualAddress < s.VirtualSize {
+ section = s
+ break
+ }
+ }
+ if section == nil {
+ t.Fatal("no section contains export directory")
+ }
+ d, err := section.Data()
+ if err != nil {
+ t.Fatal(err)
+ }
+ // seek to the virtual address specified in the export data directory
+ d = d[idd.VirtualAddress-section.VirtualAddress:]
+
// TODO: deduplicate this struct from cmd/link/internal/ld/pe.go
type IMAGE_EXPORT_DIRECTORY struct {
_ [2]uint32
@@ -432,26 +454,23 @@ func main() {
_ [3]uint32
}
var e IMAGE_EXPORT_DIRECTORY
- if err := binary.Read(section.Open(), binary.LittleEndian, &e); err != nil {
+ if err := binary.Read(bytes.NewReader(d), binary.LittleEndian, &e); err != nil {
t.Fatalf("binary.Read failed: %v", err)
}
- // Only the two exported functions and _cgo_dummy_export should be exported
- expectedNumber := uint32(3)
+ exportedSymbols = cmp.Or(exportedSymbols, 1) // _cgo_stub_export is exported if there are no other symbols exported
- if exportAllSymbols {
- if e.NumberOfFunctions <= expectedNumber {
- t.Fatalf("missing exported functions: %v", e.NumberOfFunctions)
- }
- if e.NumberOfNames <= expectedNumber {
- t.Fatalf("missing exported names: %v", e.NumberOfNames)
+ // NumberOfNames is the number of functions exported with a unique name.
+ // NumberOfFunctions can be higher than that because it also counts
+ // functions exported only by ordinal, a unique number asigned by the linker,
+ // and linkers might add an unknown number of their own ordinal-only functions.
+ if wantAll {
+ if e.NumberOfNames <= uint32(exportedSymbols) {
+ t.Errorf("got %d exported names, want > %d", e.NumberOfNames, exportedSymbols)
}
} else {
- if e.NumberOfFunctions != expectedNumber {
- t.Fatalf("got %d exported functions; want %d", e.NumberOfFunctions, expectedNumber)
- }
- if e.NumberOfNames != expectedNumber {
- t.Fatalf("got %d exported names; want %d", e.NumberOfNames, expectedNumber)
+ if e.NumberOfNames != uint32(exportedSymbols) {
+ t.Errorf("got %d exported names, want %d", e.NumberOfNames, exportedSymbols)
}
}
}
@@ -467,12 +486,14 @@ func TestNumberOfExportedFunctions(t *testing.T) {
t.Parallel()
- t.Run("OnlyExported", func(t *testing.T) {
- checkNumberOfExportedFunctionsWindows(t, false)
- })
- t.Run("All", func(t *testing.T) {
- checkNumberOfExportedFunctionsWindows(t, true)
- })
+ for i := range 3 {
+ t.Run(fmt.Sprintf("OnlyExported/%d", i), func(t *testing.T) {
+ checkNumberOfExportedSymbolsWindows(t, i, false)
+ })
+ t.Run(fmt.Sprintf("All/%d", i), func(t *testing.T) {
+ checkNumberOfExportedSymbolsWindows(t, i, true)
+ })
+ }
}
// test1: shared library can be dynamically loaded and exported symbols are accessible.
@@ -880,3 +901,44 @@ func TestIssue36233(t *testing.T) {
t.Error("missing functions")
}
}
+
+func TestIssue68411(t *testing.T) {
+ globalSkip(t)
+ testenv.MustHaveCGO(t)
+
+ t.Parallel()
+
+ // Test that the export header uses a void function parameter for
+ // exported Go functions with no parameters.
+
+ tmpdir := t.TempDir()
+
+ const exportHeader = "issue68411.h"
+
+ run(t, nil, "go", "tool", "cgo", "-exportheader", exportHeader, "-objdir", tmpdir, "./issue68411/issue68411.go")
+ data, err := os.ReadFile(exportHeader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ funcs := []struct{ name, signature string }{
+ {"exportFuncWithNoParams", "void exportFuncWithNoParams(void)"},
+ {"exportFuncWithParams", "exportFuncWithParams(GoInt a, GoInt b)"},
+ }
+
+ var found int
+ for line := range bytes.Lines(data) {
+ for _, fn := range funcs {
+ if bytes.Contains(line, []byte(fn.name)) {
+ found++
+ if !bytes.Contains(line, []byte(fn.signature)) {
+ t.Errorf("function signature mismatch; got %q, want %q", line, fn.signature)
+ }
+ }
+ }
+ }
+
+ if found != len(funcs) {
+ t.Error("missing functions")
+ }
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/issue68411/issue68411.go b/src/cmd/cgo/internal/testcshared/testdata/issue68411/issue68411.go
new file mode 100644
index 0000000000..6a2be8b53c
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/issue68411/issue68411.go
@@ -0,0 +1,15 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "C"
+
+//export exportFuncWithNoParams
+func exportFuncWithNoParams() {}
+
+//export exportFuncWithParams
+func exportFuncWithParams(a, b int) {}
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testerrors/argposition_test.go b/src/cmd/cgo/internal/testerrors/argposition_test.go
index 714f9b45f8..035552127b 100644
--- a/src/cmd/cgo/internal/testerrors/argposition_test.go
+++ b/src/cmd/cgo/internal/testerrors/argposition_test.go
@@ -81,7 +81,7 @@ func TestArgumentsPositions(t *testing.T) {
t.Fatal(err)
}
- cmd := exec.Command("go", "tool", "cgo",
+ cmd := exec.Command(testenv.GoToolPath(t), "tool", "cgo",
"-srcdir", testdata,
"-objdir", dir,
"issue42580.go")
diff --git a/src/cmd/cgo/internal/testerrors/badsym_test.go b/src/cmd/cgo/internal/testerrors/badsym_test.go
index 6c87977bd1..4fd5c44505 100644
--- a/src/cmd/cgo/internal/testerrors/badsym_test.go
+++ b/src/cmd/cgo/internal/testerrors/badsym_test.go
@@ -136,7 +136,7 @@ func TestBadSymbol(t *testing.T) {
makeFile(godir, "go.mod", "module badsym")
// Try to build our little package.
- cmd := exec.Command("go", "build", "-ldflags=-v")
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-v")
cmd.Dir = godir
output, err := cmd.CombinedOutput()
diff --git a/src/cmd/cgo/internal/testerrors/errors_test.go b/src/cmd/cgo/internal/testerrors/errors_test.go
index 941c7eff20..80d2c402ce 100644
--- a/src/cmd/cgo/internal/testerrors/errors_test.go
+++ b/src/cmd/cgo/internal/testerrors/errors_test.go
@@ -76,7 +76,7 @@ func expect(t *testing.T, errors []*regexp.Regexp, files ...string) {
for _, file := range files {
args = append(args, path(file))
}
- cmd := exec.Command("go", args...)
+ cmd := exec.Command(testenv.GoToolPath(t), args...)
out, err := cmd.CombinedOutput()
if err == nil {
t.Errorf("expected cgo to fail but it succeeded")
@@ -105,10 +105,10 @@ func expect(t *testing.T, errors []*regexp.Regexp, files ...string) {
func sizeofLongDouble(t *testing.T) int {
testenv.MustHaveGoRun(t)
testenv.MustHaveCGO(t)
- cmd := exec.Command("go", "run", path("long_double_size.go"))
+ cmd := exec.Command(testenv.GoToolPath(t), "run", path("long_double_size.go"))
out, err := cmd.CombinedOutput()
if err != nil {
- t.Fatalf("%#q: %v:\n%s", strings.Join(cmd.Args, " "), err, out)
+ t.Fatalf("%#q: %v:\n%s", cmd, err, out)
}
i, err := strconv.Atoi(strings.TrimSpace(string(out)))
@@ -158,11 +158,11 @@ func TestToleratesOptimizationFlag(t *testing.T) {
testenv.MustHaveCGO(t)
t.Parallel()
- cmd := exec.Command("go", "build", path("issue14669.go"))
+ cmd := exec.Command(testenv.GoToolPath(t), "build", path("issue14669.go"))
cmd.Env = append(os.Environ(), "CGO_CFLAGS="+cflags)
out, err := cmd.CombinedOutput()
if err != nil {
- t.Errorf("%#q: %v:\n%s", strings.Join(cmd.Args, " "), err, out)
+ t.Errorf("%#q: %v:\n%s", cmd, err, out)
}
})
}
@@ -173,10 +173,10 @@ func TestMallocCrashesOnNil(t *testing.T) {
testenv.MustHaveGoRun(t)
t.Parallel()
- cmd := exec.Command("go", "run", path("malloc.go"))
+ cmd := exec.Command(testenv.GoToolPath(t), "run", path("malloc.go"))
out, err := cmd.CombinedOutput()
if err == nil {
- t.Logf("%#q:\n%s", strings.Join(cmd.Args, " "), out)
+ t.Logf("%#q:\n%s", cmd, out)
t.Fatalf("succeeded unexpectedly")
}
}
diff --git a/src/cmd/cgo/internal/testerrors/ptr_test.go b/src/cmd/cgo/internal/testerrors/ptr_test.go
index 9a8187f55f..beba0d26ac 100644
--- a/src/cmd/cgo/internal/testerrors/ptr_test.go
+++ b/src/cmd/cgo/internal/testerrors/ptr_test.go
@@ -609,7 +609,7 @@ func buildPtrTests(t *testing.T, gopath string, cgocheck2 bool) (exe string) {
if cgocheck2 {
exeName = "ptrtest2.exe"
}
- cmd := exec.Command("go", "build", "-o", exeName)
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exeName)
cmd.Dir = src
cmd.Env = append(os.Environ(), "GOPATH="+gopath)
diff --git a/src/cmd/cgo/internal/testfortran/fortran_test.go b/src/cmd/cgo/internal/testfortran/fortran_test.go
index 0eae7c5f53..9ccf916d84 100644
--- a/src/cmd/cgo/internal/testfortran/fortran_test.go
+++ b/src/cmd/cgo/internal/testfortran/fortran_test.go
@@ -75,7 +75,7 @@ func TestFortran(t *testing.T) {
// Finally, run the actual test.
t.Log("go", "run", "./testdata/testprog")
var stdout, stderr strings.Builder
- cmd := exec.Command("go", "run", "./testdata/testprog")
+ cmd := exec.Command(testenv.GoToolPath(t), "run", "./testdata/testprog")
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
diff --git a/src/cmd/cgo/internal/testgodefs/testgodefs_test.go b/src/cmd/cgo/internal/testgodefs/testgodefs_test.go
index 8138b7fa3d..d68fd4fc14 100644
--- a/src/cmd/cgo/internal/testgodefs/testgodefs_test.go
+++ b/src/cmd/cgo/internal/testgodefs/testgodefs_test.go
@@ -51,7 +51,7 @@ func TestGoDefs(t *testing.T) {
}
for _, fp := range filePrefixes {
- cmd := exec.Command("go", "tool", "cgo",
+ cmd := exec.Command(testenv.GoToolPath(t), "tool", "cgo",
"-godefs",
"-srcdir", testdata,
"-objdir", dir,
@@ -60,7 +60,7 @@ func TestGoDefs(t *testing.T) {
out, err := cmd.Output()
if err != nil {
- t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
+ t.Fatalf("%#q: %v\n%s", cmd, err, cmd.Stderr)
}
fn := fp + "_defs.go"
@@ -107,10 +107,10 @@ func TestGoDefs(t *testing.T) {
// instead of invoking 'go build' and the resulting binary separately, so that
// this test can pass on mobile builders, which do not copy artifacts back
// from remote invocations.
- cmd := exec.Command("go", "run", ".")
+ cmd := exec.Command(testenv.GoToolPath(t), "run", ".")
cmd.Env = append(os.Environ(), "GOPATH="+gopath)
cmd.Dir = dir
if out, err := cmd.CombinedOutput(); err != nil {
- t.Fatalf("%s [%s]: %v\n%s", strings.Join(cmd.Args, " "), dir, err, out)
+ t.Fatalf("%#q [%s]: %v\n%s", cmd, dir, err, out)
}
}
diff --git a/src/cmd/cgo/internal/testlife/life_test.go b/src/cmd/cgo/internal/testlife/life_test.go
index e93d29c4d9..3b8f570995 100644
--- a/src/cmd/cgo/internal/testlife/life_test.go
+++ b/src/cmd/cgo/internal/testlife/life_test.go
@@ -50,7 +50,7 @@ func TestTestRun(t *testing.T) {
testenv.MustHaveGoRun(t)
testenv.MustHaveCGO(t)
- cmd := exec.Command("go", "run", "main.go")
+ cmd := exec.Command(testenv.GoToolPath(t), "run", "main.go")
got, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("%v: %s\n%s", cmd, err, got)
diff --git a/src/cmd/cgo/internal/testout/out_test.go b/src/cmd/cgo/internal/testout/out_test.go
new file mode 100644
index 0000000000..81dfa36587
--- /dev/null
+++ b/src/cmd/cgo/internal/testout/out_test.go
@@ -0,0 +1,144 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package out_test
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "internal/testenv"
+ "internal/goarch"
+ "os"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+type methodAlign struct {
+ Method string
+ Align int
+}
+
+var wantAligns = map[string]int{
+ "ReturnEmpty": 1,
+ "ReturnOnlyUint8": 1,
+ "ReturnOnlyUint16": 2,
+ "ReturnOnlyUint32": 4,
+ "ReturnOnlyUint64": goarch.PtrSize,
+ "ReturnOnlyInt": goarch.PtrSize,
+ "ReturnOnlyPtr": goarch.PtrSize,
+ "ReturnByteSlice": goarch.PtrSize,
+ "ReturnString": goarch.PtrSize,
+ "InputAndReturnUint8": 1,
+ "MixedTypes": goarch.PtrSize,
+}
+
+// TestAligned tests that the generated _cgo_export.c file has the wanted
+// align attributes for struct types used as arguments or results of
+// //exported functions.
+func TestAligned(t *testing.T) {
+ testenv.MustHaveGoRun(t)
+ testenv.MustHaveCGO(t)
+
+ testdata, err := filepath.Abs("testdata")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ objDir := t.TempDir()
+
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "cgo",
+ "-objdir", objDir,
+ filepath.Join(testdata, "aligned.go"))
+ cmd.Stderr = new(bytes.Buffer)
+
+ err = cmd.Run()
+ if err != nil {
+ t.Fatalf("%#q: %v\n%s", cmd, err, cmd.Stderr)
+ }
+
+ haveAligns, err := parseAlign(filepath.Join(objDir, "_cgo_export.c"))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Check that we have all the wanted methods
+ if len(haveAligns) != len(wantAligns) {
+ t.Fatalf("have %d methods with aligned, want %d", len(haveAligns), len(wantAligns))
+ }
+
+ for i := range haveAligns {
+ method := haveAligns[i].Method
+ haveAlign := haveAligns[i].Align
+
+ wantAlign, ok := wantAligns[method]
+ if !ok {
+ t.Errorf("method %s: have aligned %d, want missing entry", method, haveAlign)
+ } else if haveAlign != wantAlign {
+ t.Errorf("method %s: have aligned %d, want %d", method, haveAlign, wantAlign)
+ }
+ }
+}
+
+func parseAlign(filename string) ([]methodAlign, error) {
+ file, err := os.Open(filename)
+ if err != nil {
+ return nil, fmt.Errorf("failed to open file: %w", err)
+ }
+ defer file.Close()
+
+ var results []methodAlign
+ scanner := bufio.NewScanner(file)
+
+ // Regex to match function declarations like "struct MethodName_return MethodName("
+ funcRegex := regexp.MustCompile(`^struct\s+(\w+)_return\s+(\w+)\(`)
+ // Regex to match simple function declarations like "GoSlice MethodName("
+ simpleFuncRegex := regexp.MustCompile(`^Go\w+\s+(\w+)\(`)
+ // Regex to match void-returning exported functions like "void ReturnEmpty("
+ voidFuncRegex := regexp.MustCompile(`^void\s+(\w+)\(`)
+ // Regex to match align attributes like "__attribute__((aligned(8)))"
+ alignRegex := regexp.MustCompile(`__attribute__\(\(aligned\((\d+)\)\)\)`)
+
+ var currentMethod string
+
+ for scanner.Scan() {
+ line := strings.TrimSpace(scanner.Text())
+
+ // Check if this line declares a function with struct return type
+ if matches := funcRegex.FindStringSubmatch(line); matches != nil {
+ currentMethod = matches[2] // Extract the method name
+ } else if matches := simpleFuncRegex.FindStringSubmatch(line); matches != nil {
+ // Check if this line declares a function with simple return type (like GoSlice)
+ currentMethod = matches[1] // Extract the method name
+ } else if matches := voidFuncRegex.FindStringSubmatch(line); matches != nil {
+ // Check if this line declares a void-returning function
+ currentMethod = matches[1] // Extract the method name
+ }
+
+ // Check if this line contains align information
+ if alignMatches := alignRegex.FindStringSubmatch(line); alignMatches != nil && currentMethod != "" {
+ alignStr := alignMatches[1]
+ align, err := strconv.Atoi(alignStr)
+ if err != nil {
+ // Skip this entry if we can't parse the align as integer
+ currentMethod = ""
+ continue
+ }
+ results = append(results, methodAlign{
+ Method: currentMethod,
+ Align: align,
+ })
+ currentMethod = "" // Reset for next method
+ }
+ }
+
+ if err := scanner.Err(); err != nil {
+ return nil, fmt.Errorf("error reading file: %w", err)
+ }
+
+ return results, nil
+}
diff --git a/src/cmd/cgo/internal/testout/testdata/aligned.go b/src/cmd/cgo/internal/testout/testdata/aligned.go
new file mode 100644
index 0000000000..cea6f2889a
--- /dev/null
+++ b/src/cmd/cgo/internal/testout/testdata/aligned.go
@@ -0,0 +1,63 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "C"
+
+//export ReturnEmpty
+func ReturnEmpty() {
+ return
+}
+
+//export ReturnOnlyUint8
+func ReturnOnlyUint8() (uint8, uint8, uint8) {
+ return 1, 2, 3
+}
+
+//export ReturnOnlyUint16
+func ReturnOnlyUint16() (uint16, uint16, uint16) {
+ return 1, 2, 3
+}
+
+//export ReturnOnlyUint32
+func ReturnOnlyUint32() (uint32, uint32, uint32) {
+ return 1, 2, 3
+}
+
+//export ReturnOnlyUint64
+func ReturnOnlyUint64() (uint64, uint64, uint64) {
+ return 1, 2, 3
+}
+
+//export ReturnOnlyInt
+func ReturnOnlyInt() (int, int, int) {
+ return 1, 2, 3
+}
+
+//export ReturnOnlyPtr
+func ReturnOnlyPtr() (*int, *int, *int) {
+ a, b, c := 1, 2, 3
+ return &a, &b, &c
+}
+
+//export ReturnString
+func ReturnString() string {
+ return "hello"
+}
+
+//export ReturnByteSlice
+func ReturnByteSlice() []byte {
+ return []byte{1, 2, 3}
+}
+
+//export InputAndReturnUint8
+func InputAndReturnUint8(a, b, c uint8) (uint8, uint8, uint8) {
+ return a, b, c
+}
+
+//export MixedTypes
+func MixedTypes(a uint8, b uint16, c uint32, d uint64, e int, f *int) (uint8, uint16, uint32, uint64, int, *int) {
+ return a, b, c, d, e, f
+}
diff --git a/src/cmd/cgo/internal/testplugin/plugin_test.go b/src/cmd/cgo/internal/testplugin/plugin_test.go
index 85dfd31123..2afb542ec4 100644
--- a/src/cmd/cgo/internal/testplugin/plugin_test.go
+++ b/src/cmd/cgo/internal/testplugin/plugin_test.go
@@ -46,7 +46,7 @@ func prettyPrintf(format string, args ...interface{}) {
}
func testMain(m *testing.M) int {
- if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
+ if testing.Short() && testenv.Builder() == "" {
globalSkip = func(t *testing.T) { t.Skip("short mode and $GO_BUILDER_NAME not set") }
return m.Run()
}
@@ -145,8 +145,8 @@ func goCmd(t *testing.T, op string, args ...string) string {
// escape converts a string to something suitable for a shell command line.
func escape(s string) string {
- s = strings.Replace(s, "\\", "\\\\", -1)
- s = strings.Replace(s, "'", "\\'", -1)
+ s = strings.ReplaceAll(s, "\\", "\\\\")
+ s = strings.ReplaceAll(s, "'", "\\'")
// Conservative guess at characters that will force quoting
if s == "" || strings.ContainsAny(s, "\\ ;#*&$~?!|[]()<>{}`") {
s = "'" + s + "'"
@@ -194,10 +194,10 @@ func run(t *testing.T, bin string, args ...string) string {
out, err := cmd.Output()
if err != nil {
if t == nil {
- log.Panicf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
+ log.Panicf("%#q: %v\n%s", cmd, err, cmd.Stderr)
} else {
t.Helper()
- t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
+ t.Fatalf("%#q: %v\n%s", cmd, err, cmd.Stderr)
}
}
@@ -245,7 +245,7 @@ func TestIssue18676(t *testing.T) {
cmd := exec.CommandContext(ctx, "./issue18676.exe")
out, err := cmd.CombinedOutput()
if err != nil {
- t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, out)
+ t.Fatalf("%#q: %v\n%s", cmd, err, out)
}
}
@@ -422,3 +422,11 @@ func TestIssue67976(t *testing.T) {
globalSkip(t)
goCmd(t, "build", "-buildmode=plugin", "-o", "issue67976.so", "./issue67976/plugin.go")
}
+
+func TestIssue75102(t *testing.T) {
+ globalSkip(t)
+ // add gcflags different from the executable file to trigger plugin open failed.
+ goCmd(t, "build", "-gcflags=all=-N -l", "-buildmode=plugin", "-o", "issue75102.so", "./issue75102/plugin.go")
+ goCmd(t, "build", "-o", "issue75102.exe", "./issue75102/main.go")
+ run(t, "./issue75102.exe")
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue75102/main.go b/src/cmd/cgo/internal/testplugin/testdata/issue75102/main.go
new file mode 100644
index 0000000000..217a47b04a
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue75102/main.go
@@ -0,0 +1,21 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "plugin"
+)
+
+func init() {
+ _, err := plugin.Open("issue75102.so")
+ if err == nil {
+ panic("unexpected success to open a different version plugin")
+ }
+}
+
+func main() {
+ fmt.Println("done")
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue75102/plugin.go b/src/cmd/cgo/internal/testplugin/testdata/issue75102/plugin.go
new file mode 100644
index 0000000000..d09137d288
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue75102/plugin.go
@@ -0,0 +1,11 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ panic("unexpected call to init")
+}
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testsanitizers/asan_test.go b/src/cmd/cgo/internal/testsanitizers/asan_test.go
index 19810aafb6..cb7d857280 100644
--- a/src/cmd/cgo/internal/testsanitizers/asan_test.go
+++ b/src/cmd/cgo/internal/testsanitizers/asan_test.go
@@ -8,6 +8,7 @@ package sanitizers_test
import (
"bytes"
+ "crypto/fips140"
"fmt"
"internal/platform"
"internal/testenv"
@@ -42,6 +43,8 @@ func TestASAN(t *testing.T) {
{src: "asan_global3_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global3_fail.go:13"},
{src: "asan_global4_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global4_fail.go:21"},
{src: "asan_global5.go"},
+ {src: "asan_global_asm"},
+ {src: "asan_global_asm2_fail", memoryAccessError: "global-buffer-overflow", errorLocation: "main.go:17"},
{src: "arena_fail.go", memoryAccessError: "use-after-poison", errorLocation: "arena_fail.go:26", experiments: []string{"arenas"}},
}
for _, tc := range cases {
@@ -71,11 +74,11 @@ func TestASAN(t *testing.T) {
!strings.Contains(out, noSymbolizer) &&
compilerSupportsLocation() {
- t.Errorf("%#q exited without expected location of the error\n%s; got failure\n%s", strings.Join(cmd.Args, " "), tc.errorLocation, out)
+ t.Errorf("%#q exited without expected location of the error\n%s; got failure\n%s", cmd, tc.errorLocation, out)
}
return
}
- t.Fatalf("%#q exited without expected memory access error\n%s; got failure\n%s", strings.Join(cmd.Args, " "), tc.memoryAccessError, out)
+ t.Fatalf("%#q exited without expected memory access error\n%s; got failure\n%s", cmd, tc.memoryAccessError, out)
}
mustRun(t, cmd)
})
@@ -135,6 +138,9 @@ func TestASANFuzz(t *testing.T) {
if bytes.Contains(out, []byte("AddressSanitizer")) {
t.Error(`output contains "AddressSanitizer", but should not`)
}
+ if !bytes.Contains(out, []byte("FUZZ FAILED")) {
+ t.Error(`fuzz test did not fail with a "FUZZ FAILED" sentinel error`)
+ }
}
func mustHaveASAN(t *testing.T) *config {
@@ -152,6 +158,10 @@ func mustHaveASAN(t *testing.T) *config {
t.Skipf("skipping on %s/%s; -asan option is not supported.", goos, goarch)
}
+ if fips140.Enabled() {
+ t.Skipf("skipping with FIPS 140 mode; -asan option is not supported.")
+ }
+
// The current implementation is only compatible with the ASan library from version
// v7 to v9 (See the description in src/runtime/asan/asan.go). Therefore, using the
// -asan option must use a compatible version of ASan library, which requires that
diff --git a/src/cmd/cgo/internal/testsanitizers/cc_test.go b/src/cmd/cgo/internal/testsanitizers/cc_test.go
index 96a9e71cd7..f2239fb9cf 100644
--- a/src/cmd/cgo/internal/testsanitizers/cc_test.go
+++ b/src/cmd/cgo/internal/testsanitizers/cc_test.go
@@ -266,7 +266,12 @@ func compilerSupportsLocation() bool {
}
switch compiler.name {
case "gcc":
- return compiler.major >= 10
+ // TODO(72752): the asan runtime support library
+ // (libasan.so.6) shipped with GCC 10 has problems digesting
+ // version 5 DWARF produced by the Go toolchain. Disable
+ // location checking if gcc is not sufficiently up to date in
+ // this case.
+ return compiler.major > 10
case "clang":
// TODO(65606): The clang toolchain on the LUCI builders is not built against
// zlib, the ASAN runtime can't actually symbolize its own stack trace. Once
@@ -328,6 +333,12 @@ func compilerRequiredAsanVersion(goos, goarch string) bool {
}
}
+// compilerRequiredLsanVersion reports whether the compiler is the
+// version required by Lsan.
+func compilerRequiredLsanVersion(goos, goarch string) bool {
+ return compilerRequiredAsanVersion(goos, goarch)
+}
+
type compilerCheck struct {
once sync.Once
err error
@@ -355,10 +366,18 @@ func configure(sanitizer string) *config {
return c
}
+ sanitizerOpt := sanitizer
+ // For the leak detector, we use "go build -asan",
+ // which implies the address sanitizer.
+ // We may want to adjust this someday.
+ if sanitizer == "leak" {
+ sanitizerOpt = "address"
+ }
+
c := &config{
sanitizer: sanitizer,
- cFlags: []string{"-fsanitize=" + sanitizer},
- ldFlags: []string{"-fsanitize=" + sanitizer},
+ cFlags: []string{"-fsanitize=" + sanitizerOpt},
+ ldFlags: []string{"-fsanitize=" + sanitizerOpt},
}
if testing.Verbose() {
@@ -377,7 +396,7 @@ func configure(sanitizer string) *config {
c.ldFlags = append(c.ldFlags, "-fPIC", "-static-libtsan")
}
- case "address":
+ case "address", "leak":
c.goFlags = append(c.goFlags, "-asan")
// Set the debug mode to print the C stack trace.
c.cFlags = append(c.cFlags, "-g")
@@ -475,7 +494,7 @@ func (c *config) checkCSanitizer() (skip bool, err error) {
bytes.Contains(out, []byte("unsupported"))) {
return true, errors.New(string(out))
}
- return true, fmt.Errorf("%#q failed: %v\n%s", strings.Join(cmd.Args, " "), err, out)
+ return true, fmt.Errorf("%#q failed: %v\n%s", cmd, err, out)
}
if c.sanitizer == "fuzzer" {
@@ -485,10 +504,10 @@ func (c *config) checkCSanitizer() (skip bool, err error) {
if out, err := exec.Command(dst).CombinedOutput(); err != nil {
if os.IsNotExist(err) {
- return true, fmt.Errorf("%#q failed to produce executable: %v", strings.Join(cmd.Args, " "), err)
+ return true, fmt.Errorf("%#q failed to produce executable: %v", cmd, err)
}
snippet, _, _ := bytes.Cut(out, []byte("\n"))
- return true, fmt.Errorf("%#q generated broken executable: %v\n%s", strings.Join(cmd.Args, " "), err, snippet)
+ return true, fmt.Errorf("%#q generated broken executable: %v\n%s", cmd, err, snippet)
}
return false, nil
@@ -523,20 +542,19 @@ func (c *config) checkRuntime() (skip bool, err error) {
return false, err
}
cmd.Args = append(cmd.Args, "-dM", "-E", "../../../../runtime/cgo/libcgo.h")
- cmdStr := strings.Join(cmd.Args, " ")
out, err := cmd.CombinedOutput()
if err != nil {
- return false, fmt.Errorf("%#q exited with %v\n%s", cmdStr, err, out)
+ return false, fmt.Errorf("%#q exited with %v\n%s", cmd, err, out)
}
if !bytes.Contains(out, []byte("#define CGO_TSAN")) {
- return true, fmt.Errorf("%#q did not define CGO_TSAN", cmdStr)
+ return true, fmt.Errorf("%#q did not define CGO_TSAN", cmd)
}
return false, nil
}
// srcPath returns the path to the given file relative to this test's source tree.
func srcPath(path string) string {
- return filepath.Join("testdata", path)
+ return "./testdata/" + path
}
// A tempDir manages a temporary directory within a test.
diff --git a/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go b/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go
index 85c8f7bbfb..9f548d66ea 100644
--- a/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go
+++ b/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go
@@ -95,6 +95,8 @@ func libFuzzerSupported(goos, goarch string) bool {
default:
return false
}
+ case "loong64":
+ return true
default:
return false
}
diff --git a/src/cmd/cgo/internal/testsanitizers/lsan_test.go b/src/cmd/cgo/internal/testsanitizers/lsan_test.go
new file mode 100644
index 0000000000..4dde3d20ec
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/lsan_test.go
@@ -0,0 +1,102 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux || (freebsd && amd64)
+
+package sanitizers_test
+
+import (
+ "internal/platform"
+ "internal/testenv"
+ "strings"
+ "testing"
+)
+
+func TestLSAN(t *testing.T) {
+ config := mustHaveLSAN(t)
+
+ t.Parallel()
+ mustRun(t, config.goCmd("build", "std"))
+
+ cases := []struct {
+ src string
+ leakError string
+ errorLocation string
+ }{
+ {src: "lsan1.go", leakError: "detected memory leaks", errorLocation: "lsan1.go:11"},
+ {src: "lsan2.go"},
+ {src: "lsan3.go"},
+ }
+ for _, tc := range cases {
+ name := strings.TrimSuffix(tc.src, ".go")
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+
+ dir := newTempDir(t)
+ defer dir.RemoveAll(t)
+
+ outPath := dir.Join(name)
+ mustRun(t, config.goCmd("build", "-o", outPath, srcPath(tc.src)))
+
+ cmd := hangProneCmd(outPath)
+ if tc.leakError == "" {
+ mustRun(t, cmd)
+ } else {
+ outb, err := cmd.CombinedOutput()
+ out := string(outb)
+ if err != nil || len(out) > 0 {
+ t.Logf("%s\n%v\n%s", cmd, err, out)
+ }
+ if err != nil && strings.Contains(out, tc.leakError) {
+ // This string is output if the
+ // sanitizer library needs a
+ // symbolizer program and can't find it.
+ const noSymbolizer = "external symbolizer"
+ if tc.errorLocation != "" &&
+ !strings.Contains(out, tc.errorLocation) &&
+ !strings.Contains(out, noSymbolizer) &&
+ compilerSupportsLocation() {
+
+ t.Errorf("output does not contain expected location of the error %q", tc.errorLocation)
+ }
+ } else {
+ t.Errorf("output does not contain expected leak error %q", tc.leakError)
+ }
+
+ // Make sure we can disable the leak check.
+ cmd = hangProneCmd(outPath)
+ replaceEnv(cmd, "ASAN_OPTIONS", "detect_leaks=0")
+ mustRun(t, cmd)
+ }
+ })
+ }
+}
+
+func mustHaveLSAN(t *testing.T) *config {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ goos, err := goEnv("GOOS")
+ if err != nil {
+ t.Fatal(err)
+ }
+ goarch, err := goEnv("GOARCH")
+ if err != nil {
+ t.Fatal(err)
+ }
+ // LSAN is a subset of ASAN, so just check for ASAN support.
+ if !platform.ASanSupported(goos, goarch) {
+ t.Skipf("skipping on %s/%s; -asan option is not supported.", goos, goarch)
+ }
+
+ if !compilerRequiredLsanVersion(goos, goarch) {
+ t.Skipf("skipping on %s/%s: too old version of compiler", goos, goarch)
+ }
+
+ requireOvercommit(t)
+
+ config := configure("leak")
+ config.skipIfCSanitizerBroken(t)
+
+ return config
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/msan_test.go b/src/cmd/cgo/internal/testsanitizers/msan_test.go
index 83d66f6660..554cceaff8 100644
--- a/src/cmd/cgo/internal/testsanitizers/msan_test.go
+++ b/src/cmd/cgo/internal/testsanitizers/msan_test.go
@@ -79,7 +79,7 @@ func TestMSAN(t *testing.T) {
if err != nil {
return
}
- t.Fatalf("%#q exited without error; want MSAN failure\n%s", strings.Join(cmd.Args, " "), out)
+ t.Fatalf("%#q exited without error; want MSAN failure\n%s", cmd, out)
}
mustRun(t, cmd)
})
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan_fuzz_test.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan_fuzz_test.go
index fb7ebd4078..1a51819d7d 100644
--- a/src/cmd/cgo/internal/testsanitizers/testdata/asan_fuzz_test.go
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan_fuzz_test.go
@@ -24,7 +24,7 @@ func FuzzReverse(f *testing.F) {
r1 := Reverse(s)
r2 := Reverse(r1)
if s != r2 {
- t.Errorf("got %q want %q", r2, s)
+ t.Errorf("FUZZ FAILED: got %q want %q", r2, s)
}
})
}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm/asm.s b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm/asm.s
new file mode 100644
index 0000000000..b4b9766f57
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm/asm.s
@@ -0,0 +1,8 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+DATA ·x(SB)/8, $123
+GLOBL ·x(SB), NOPTR, $8
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm/main.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm/main.go
new file mode 100644
index 0000000000..2ae54486f3
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm/main.go
@@ -0,0 +1,11 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+var x uint64
+
+func main() {
+ println(x)
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm2_fail/asm.s b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm2_fail/asm.s
new file mode 100644
index 0000000000..b4b9766f57
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm2_fail/asm.s
@@ -0,0 +1,8 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+DATA ·x(SB)/8, $123
+GLOBL ·x(SB), NOPTR, $8
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm2_fail/main.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm2_fail/main.go
new file mode 100644
index 0000000000..2d02a1b542
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global_asm2_fail/main.go
@@ -0,0 +1,20 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "unsafe"
+
+var x uint64
+
+func main() {
+ bar(&x)
+}
+
+func bar(a *uint64) {
+ p := (*uint64)(unsafe.Add(unsafe.Pointer(a), 1*unsafe.Sizeof(uint64(1))))
+ if *p == 10 { // BOOM
+ println("its value is 10")
+ }
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/lsan1.go b/src/cmd/cgo/internal/testsanitizers/testdata/lsan1.go
new file mode 100644
index 0000000000..5f99bd4886
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/lsan1.go
@@ -0,0 +1,39 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include
+
+int* test() {
+ return malloc(sizeof(int));
+}
+
+void clearStack(int n) {
+ if (n > 0) {
+ clearStack(n - 1);
+ }
+}
+
+*/
+import "C"
+
+//go:noinline
+func F() {
+ C.test()
+}
+
+func clearStack(n int) {
+ if n > 0 {
+ clearStack(n - 1)
+ }
+}
+
+func main() {
+ // Test should fail: memory allocated by C is leaked.
+ F()
+ clearStack(100)
+ C.clearStack(100)
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/lsan2.go b/src/cmd/cgo/internal/testsanitizers/testdata/lsan2.go
new file mode 100644
index 0000000000..b904d138c0
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/lsan2.go
@@ -0,0 +1,42 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include
+
+int* test() {
+ return malloc(sizeof(int));
+}
+
+void clearStack(int n) {
+ if (n > 0) {
+ clearStack(n - 1);
+ }
+}
+
+*/
+import "C"
+
+var p *C.int
+
+//go:noinline
+func F() {
+ p = C.test()
+}
+
+func clearStack(n int) {
+ if n > 0 {
+ clearStack(n - 1)
+ }
+}
+
+func main() {
+ // Test should pass: memory allocated by C does not leak
+ // because a Go global variable points to it.
+ F()
+ clearStack(100)
+ C.clearStack(100)
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/lsan3.go b/src/cmd/cgo/internal/testsanitizers/testdata/lsan3.go
new file mode 100644
index 0000000000..824e1535d1
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/lsan3.go
@@ -0,0 +1,46 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include
+
+int* test() {
+ return malloc(sizeof(int));
+}
+
+void clearStack(int n) {
+ if (n > 0) {
+ clearStack(n - 1);
+ }
+}
+
+*/
+import "C"
+
+type S struct {
+ p *C.int
+}
+
+var p *S
+
+//go:noinline
+func F() {
+ p = &S{p: C.test()}
+}
+
+func clearStack(n int) {
+ if n > 0 {
+ clearStack(n - 1)
+ }
+}
+
+func main() {
+ // Test should pass: memory allocated by C does not leak
+ // because a Go global variable points to it.
+ F()
+ clearStack(100)
+ C.clearStack(100)
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan_tracebackctxt/main.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan_tracebackctxt/main.go
new file mode 100644
index 0000000000..998a08ca53
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan_tracebackctxt/main.go
@@ -0,0 +1,78 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+// Defined in tracebackctxt_c.c.
+extern void C1(void);
+extern void C2(void);
+extern void tcContext(void*);
+extern void tcTraceback(void*);
+extern void tcSymbolizer(void*);
+*/
+import "C"
+
+import (
+ "fmt"
+ "runtime"
+ "sync"
+ "unsafe"
+)
+
+// Regression test for https://go.dev/issue/73949. TSAN should not report races
+// on writes to the argument passed to the symbolizer function.
+//
+// Triggering this race requires calls to the symbolizer function with the same
+// argument pointer on multiple threads. The runtime passes a stack variable to
+// this function, so that means we need to get a single goroutine to execute on
+// two threads, calling the symbolizer function on each.
+//
+// runtime.CallersFrames / Next will call the symbolizer function (if there are
+// C frames). So the approach here is, with GOMAXPROCS=2, have 2 goroutines
+// that use CallersFrames over and over, both frequently calling Gosched in an
+// attempt to get picked up by the other P.
+
+var tracebackOK bool
+
+func main() {
+ runtime.GOMAXPROCS(2)
+ runtime.SetCgoTraceback(0, unsafe.Pointer(C.tcTraceback), unsafe.Pointer(C.tcContext), unsafe.Pointer(C.tcSymbolizer))
+ C.C1()
+ if tracebackOK {
+ fmt.Println("OK")
+ }
+}
+
+//export G1
+func G1() {
+ C.C2()
+}
+
+//export G2
+func G2() {
+ pc := make([]uintptr, 32)
+ n := runtime.Callers(0, pc)
+
+ var wg sync.WaitGroup
+ for range 2 {
+ wg.Go(func() {
+ for range 1000 {
+ cf := runtime.CallersFrames(pc[:n])
+ var frames []runtime.Frame
+ for {
+ frame, more := cf.Next()
+ frames = append(frames, frame)
+ if !more {
+ break
+ }
+ }
+ runtime.Gosched()
+ }
+ })
+ }
+ wg.Wait()
+
+ tracebackOK = true
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan_tracebackctxt/tracebackctxt_c.c b/src/cmd/cgo/internal/testsanitizers/testdata/tsan_tracebackctxt/tracebackctxt_c.c
new file mode 100644
index 0000000000..9ddaa4aaf2
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan_tracebackctxt/tracebackctxt_c.c
@@ -0,0 +1,70 @@
+// Copyright 2025 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.
+
+// The C definitions for tracebackctxt.go. That file uses //export so
+// it can't put function definitions in the "C" import comment.
+
+#include
+#include
+
+// Functions exported from Go.
+extern void G1(void);
+extern void G2(void);
+
+void C1() {
+ G1();
+}
+
+void C2() {
+ G2();
+}
+
+struct cgoContextArg {
+ uintptr_t context;
+};
+
+struct cgoTracebackArg {
+ uintptr_t context;
+ uintptr_t sigContext;
+ uintptr_t* buf;
+ uintptr_t max;
+};
+
+struct cgoSymbolizerArg {
+ uintptr_t pc;
+ const char* file;
+ uintptr_t lineno;
+ const char* func;
+ uintptr_t entry;
+ uintptr_t more;
+ uintptr_t data;
+};
+
+void tcContext(void* parg) {
+ struct cgoContextArg* arg = (struct cgoContextArg*)(parg);
+ if (arg->context == 0) {
+ arg->context = 1;
+ }
+}
+
+void tcTraceback(void* parg) {
+ int base, i;
+ struct cgoTracebackArg* arg = (struct cgoTracebackArg*)(parg);
+ if (arg->max < 1) {
+ return;
+ }
+ arg->buf[0] = 6; // Chosen by fair dice roll.
+}
+
+void tcSymbolizer(void *parg) {
+ struct cgoSymbolizerArg* arg = (struct cgoSymbolizerArg*)(parg);
+ if (arg->pc == 0) {
+ return;
+ }
+ // Report two lines per PC returned by traceback, to test more handling.
+ arg->more = arg->file == NULL;
+ arg->file = "tracebackctxt.go";
+ arg->func = "cFunction";
+ arg->lineno = arg->pc + (arg->more << 16);
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/tsan_test.go b/src/cmd/cgo/internal/testsanitizers/tsan_test.go
index 265c5e3605..589db2e6bc 100644
--- a/src/cmd/cgo/internal/testsanitizers/tsan_test.go
+++ b/src/cmd/cgo/internal/testsanitizers/tsan_test.go
@@ -56,6 +56,7 @@ func TestTSAN(t *testing.T) {
{src: "tsan13.go", needsRuntime: true},
{src: "tsan14.go", needsRuntime: true},
{src: "tsan15.go", needsRuntime: true},
+ {src: "tsan_tracebackctxt", needsRuntime: true}, // Subdirectory
}
for _, tc := range cases {
tc := tc
@@ -67,7 +68,7 @@ func TestTSAN(t *testing.T) {
defer dir.RemoveAll(t)
outPath := dir.Join(name)
- mustRun(t, config.goCmd("build", "-o", outPath, srcPath(tc.src)))
+ mustRun(t, config.goCmd("build", "-o", outPath, "./"+srcPath(tc.src)))
cmdArgs := []string{outPath}
if goos == "linux" {
diff --git a/src/cmd/cgo/internal/testshared/shared_test.go b/src/cmd/cgo/internal/testshared/shared_test.go
index 814b9994f8..3d401b604e 100644
--- a/src/cmd/cgo/internal/testshared/shared_test.go
+++ b/src/cmd/cgo/internal/testshared/shared_test.go
@@ -79,10 +79,10 @@ func goCmd(t *testing.T, args ...string) string {
if err != nil {
if t != nil {
t.Helper()
- t.Fatalf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, stderr)
+ t.Fatalf("executing %#q failed %v:\n%s\n%s", c, err, stderr, output)
} else {
// Panic instead of using log.Fatalf so that deferred cleanup may run in testMain.
- log.Panicf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, stderr)
+ log.Panicf("executing %#q failed %v:\n%s\n%s", c, err, stderr, output)
}
}
if testing.Verbose() && t != nil {
@@ -96,7 +96,7 @@ func goCmd(t *testing.T, args ...string) string {
// TestMain calls testMain so that the latter can use defer (TestMain exits with os.Exit).
func testMain(m *testing.M) (int, error) {
- if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
+ if testing.Short() && testenv.Builder() == "" {
globalSkip = func(t testing.TB) { t.Skip("short mode and $GO_BUILDER_NAME not set") }
return m.Run(), nil
}
@@ -554,7 +554,7 @@ func checkPIE(t *testing.T, name string) {
}
func TestTrivialPIE(t *testing.T) {
- if strings.HasSuffix(os.Getenv("GO_BUILDER_NAME"), "-alpine") {
+ if strings.Contains(testenv.Builder(), "-alpine") {
t.Skip("skipping on alpine until issue #54354 resolved")
}
globalSkip(t)
diff --git a/src/cmd/cgo/internal/testso/so_test.go b/src/cmd/cgo/internal/testso/so_test.go
index e011167f38..48eb97c8de 100644
--- a/src/cmd/cgo/internal/testso/so_test.go
+++ b/src/cmd/cgo/internal/testso/so_test.go
@@ -52,11 +52,11 @@ func testSO(t *testing.T, dir string) {
cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
out, err := cmd.Output()
if err != nil {
- t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
+ t.Fatalf("%#q: %v\n%s", cmd, err, cmd.Stderr)
}
lines := strings.Split(string(out), "\n")
if len(lines) != 3 || lines[2] != "" {
- t.Fatalf("Unexpected output from %s:\n%s", strings.Join(cmd.Args, " "), lines)
+ t.Fatalf("Unexpected output from %q:\n%s", cmd, lines)
}
cc := lines[0]
@@ -90,9 +90,9 @@ func testSO(t *testing.T, dir string) {
cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
out, err = cmd.CombinedOutput()
if err != nil {
- t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out)
+ t.Fatalf("%#q: %s\n%s", cmd, err, out)
}
- t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out)
+ t.Logf("%#q:\n%s", cmd, out)
if runtime.GOOS == "aix" {
// Shared object must be wrapped by an archive
@@ -100,18 +100,18 @@ func testSO(t *testing.T, dir string) {
cmd.Dir = modRoot
out, err = cmd.CombinedOutput()
if err != nil {
- t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out)
+ t.Fatalf("%#q: %s\n%s", cmd, err, out)
}
}
- cmd = exec.Command("go", "build", "-o", "main.exe", "main.go")
+ cmd = exec.Command(testenv.GoToolPath(t), "build", "-o", "main.exe", "main.go")
cmd.Dir = modRoot
cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
out, err = cmd.CombinedOutput()
if err != nil {
- t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out)
+ t.Fatalf("%#q: %s\n%s", cmd, err, out)
}
- t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out)
+ t.Logf("%#q:\n%s", cmd, out)
cmd = exec.Command("./main.exe")
cmd.Dir = modRoot
@@ -131,7 +131,7 @@ func testSO(t *testing.T, dir string) {
}
out, err = cmd.CombinedOutput()
if err != nil {
- t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out)
+ t.Fatalf("%#q: %s\n%s", cmd, err, out)
}
- t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out)
+ t.Logf("%#q:\n%s", cmd, out)
}
diff --git a/src/cmd/cgo/internal/teststdio/stdio_test.go b/src/cmd/cgo/internal/teststdio/stdio_test.go
index 3883422d6f..9150281ae8 100644
--- a/src/cmd/cgo/internal/teststdio/stdio_test.go
+++ b/src/cmd/cgo/internal/teststdio/stdio_test.go
@@ -59,7 +59,7 @@ func TestTestRun(t *testing.T) {
file := file
wantFile := strings.Replace(file, ".go", ".out", 1)
t.Run(file, func(t *testing.T) {
- cmd := exec.Command("go", "run", file)
+ cmd := exec.Command(testenv.GoToolPath(t), "run", file)
got, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("%v: %s\n%s", cmd, err, got)
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go
index 939e282ff0..5e08427daf 100644
--- a/src/cmd/cgo/main.go
+++ b/src/cmd/cgo/main.go
@@ -30,6 +30,7 @@ import (
"cmd/internal/edit"
"cmd/internal/hash"
"cmd/internal/objabi"
+ "cmd/internal/par"
"cmd/internal/telemetry/counter"
)
@@ -49,8 +50,6 @@ type Package struct {
GoFiles []string // list of Go files
GccFiles []string // list of gcc output files
Preamble string // collected preamble for _cgo_export.h
- typedefs map[string]bool // type names that appear in the types of the objects we're interested in
- typedefList []typedefInfo
noCallbacks map[string]bool // C function names with #cgo nocallback directive
noEscapes map[string]bool // C function names with #cgo noescape directive
}
@@ -76,6 +75,8 @@ type File struct {
NoCallbacks map[string]bool // C function names that with #cgo nocallback directive
NoEscapes map[string]bool // C function names that with #cgo noescape directive
Edit *edit.Buffer
+
+ debugs []*debug // debug data from iterations of gccDebug. Initialized by File.loadDebug.
}
func (f *File) offset(p token.Pos) int {
@@ -390,10 +391,10 @@ func main() {
// We already put _cgo_ at the beginning, so the main
// concern is other cgo wrappers for the same functions.
// Use the beginning of the 16 bytes hash of the input to disambiguate.
- h := hash.New16()
+ h := hash.New32()
io.WriteString(h, *importPath)
var once sync.Once
- var wg sync.WaitGroup
+ q := par.NewQueue(runtime.GOMAXPROCS(0))
fs := make([]*File, len(goFiles))
for i, input := range goFiles {
if *srcDir != "" {
@@ -415,9 +416,7 @@ func main() {
fatalf("%s", err)
}
- wg.Add(1)
- go func() {
- defer wg.Done()
+ q.Add(func() {
// Apply trimpath to the file path. The path won't be read from after this point.
input, _ = objabi.ApplyRewrites(input, *trimpath)
if strings.ContainsAny(input, "\r\n") {
@@ -438,10 +437,12 @@ func main() {
})
fs[i] = f
- }()
+
+ f.loadDebug(p)
+ })
}
- wg.Wait()
+ <-q.Idle()
cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6])
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 5e67cc2d33..a2bcdf89c5 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -251,8 +251,8 @@ func (p *Package) writeDefs() {
}
if callsMalloc && !*gccgo {
- fmt.Fprint(fgo2, strings.Replace(cMallocDefGo, "PREFIX", cPrefix, -1))
- fmt.Fprint(fgcc, strings.Replace(strings.Replace(cMallocDefC, "PREFIX", cPrefix, -1), "PACKED", p.packedAttribute(), -1))
+ fmt.Fprint(fgo2, strings.ReplaceAll(cMallocDefGo, "PREFIX", cPrefix))
+ fmt.Fprint(fgcc, strings.ReplaceAll(strings.Replace(cMallocDefC, "PREFIX", cPrefix, -1), "PACKED", p.packedAttribute()))
}
if err := fgcc.Close(); err != nil {
@@ -942,25 +942,34 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
// just have to agree. The gcc struct will be compiled
// with __attribute__((packed)) so all padding must be
// accounted for explicitly.
- ctype := "struct {\n"
+ var ctype strings.Builder
+ const start = "struct {\n"
+ ctype.WriteString(start)
gotype := new(bytes.Buffer)
fmt.Fprintf(gotype, "struct {\n")
off := int64(0)
npad := 0
+ // the align is at least 1 (for char)
+ maxAlign := int64(1)
argField := func(typ ast.Expr, namePat string, args ...interface{}) {
name := fmt.Sprintf(namePat, args...)
t := p.cgoType(typ)
if off%t.Align != 0 {
pad := t.Align - off%t.Align
- ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad)
+ fmt.Fprintf(&ctype, "\t\tchar __pad%d[%d];\n", npad, pad)
off += pad
npad++
}
- ctype += fmt.Sprintf("\t\t%s %s;\n", t.C, name)
+ fmt.Fprintf(&ctype, "\t\t%s %s;\n", t.C, name)
fmt.Fprintf(gotype, "\t\t%s ", name)
noSourceConf.Fprint(gotype, fset, typ)
fmt.Fprintf(gotype, "\n")
off += t.Size
+ // keep track of the maximum alignment among all fields
+ // so that we can align the struct correctly
+ if t.Align > maxAlign {
+ maxAlign = t.Align
+ }
}
if fn.Recv != nil {
argField(fn.Recv.List[0].Type, "recv")
@@ -974,10 +983,10 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
func(i int, aname string, atype ast.Expr) {
argField(atype, "r%d", i)
})
- if ctype == "struct {\n" {
- ctype += "\t\tchar unused;\n" // avoid empty struct
+ if ctype.Len() == len(start) {
+ ctype.WriteString("\t\tchar unused;\n") // avoid empty struct
}
- ctype += "\t}"
+ ctype.WriteString("\t}")
fmt.Fprintf(gotype, "\t}")
// Get the return type of the wrapper function
@@ -1003,23 +1012,25 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
}
// Build the wrapper function compiled by gcc.
- gccExport := ""
- if goos == "windows" {
- gccExport = "__declspec(dllexport) "
- }
- s := fmt.Sprintf("%s%s %s(", gccExport, gccResult, exp.ExpName)
+ var s strings.Builder
+ fmt.Fprintf(&s, "%s %s(", gccResult, exp.ExpName)
if fn.Recv != nil {
- s += p.cgoType(fn.Recv.List[0].Type).C.String()
- s += " recv"
+ s.WriteString(p.cgoType(fn.Recv.List[0].Type).C.String())
+ s.WriteString(" recv")
}
- forFieldList(fntype.Params,
- func(i int, aname string, atype ast.Expr) {
- if i > 0 || fn.Recv != nil {
- s += ", "
- }
- s += fmt.Sprintf("%s %s", p.cgoType(atype).C, exportParamName(aname, i))
- })
- s += ")"
+
+ if len(fntype.Params.List) > 0 {
+ forFieldList(fntype.Params,
+ func(i int, aname string, atype ast.Expr) {
+ if i > 0 || fn.Recv != nil {
+ s.WriteString(", ")
+ }
+ fmt.Fprintf(&s, "%s %s", p.cgoType(atype).C, exportParamName(aname, i))
+ })
+ } else {
+ s.WriteString("void")
+ }
+ s.WriteByte(')')
if len(exp.Doc) > 0 {
fmt.Fprintf(fgcch, "\n%s", exp.Doc)
@@ -1027,11 +1038,11 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprint(fgcch, "\n")
}
}
- fmt.Fprintf(fgcch, "extern %s;\n", s)
+ fmt.Fprintf(fgcch, "extern %s;\n", s.String())
fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *);\n", cPrefix, exp.ExpName)
fmt.Fprintf(fgcc, "\nCGO_NO_SANITIZE_THREAD")
- fmt.Fprintf(fgcc, "\n%s\n", s)
+ fmt.Fprintf(fgcc, "\n%s\n", s.String())
fmt.Fprintf(fgcc, "{\n")
fmt.Fprintf(fgcc, "\tsize_t _cgo_ctxt = _cgo_wait_runtime_init_done();\n")
// The results part of the argument structure must be
@@ -1043,7 +1054,11 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
// string.h for memset, and is also robust to C++
// types with constructors. Both GCC and LLVM optimize
// this into just zeroing _cgo_a.
- fmt.Fprintf(fgcc, "\ttypedef %s %v _cgo_argtype;\n", ctype, p.packedAttribute())
+ //
+ // The struct should be aligned to the maximum alignment
+ // of any of its fields. This to avoid alignment
+ // issues.
+ fmt.Fprintf(fgcc, "\ttypedef %s %v __attribute__((aligned(%d))) _cgo_argtype;\n", ctype.String(), p.packedAttribute(), maxAlign)
fmt.Fprintf(fgcc, "\tstatic _cgo_argtype _cgo_zero;\n")
fmt.Fprintf(fgcc, "\t_cgo_argtype _cgo_a = _cgo_zero;\n")
if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) {
@@ -1094,7 +1109,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, "void _cgoexp%s_%s(void* p){}\n", cPrefix, exp.ExpName)
+ fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p __attribute__((unused))){}\n", cPrefix, exp.ExpName)
fmt.Fprintf(fgo2, "\t")
@@ -1804,7 +1819,7 @@ void _cgoPREFIX_Cfunc__Cmalloc(void *v) {
void *ret;
_cgo_tsan_acquire();
ret = malloc(a->p0);
- if (ret == 0 && a->p0 == 0) {
+ if (ret == NULL && a->p0 == 0) {
ret = malloc(1);
}
a->r1 = ret;
@@ -1938,13 +1953,15 @@ const builtinExportProlog = `
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
+extern size_t _GoStringLen(_GoString_ s);
+extern const char *_GoStringPtr(_GoString_ s);
#endif
#endif
`
func (p *Package) gccExportHeaderProlog() string {
- return strings.Replace(gccExportHeaderProlog, "GOINTBITS", fmt.Sprint(8*p.IntSize), -1)
+ return strings.ReplaceAll(gccExportHeaderProlog, "GOINTBITS", fmt.Sprint(8*p.IntSize))
}
// gccExportHeaderProlog is written to the exported header, after the
@@ -1981,10 +1998,16 @@ typedef size_t GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
#ifdef _MSC_VER
+#if !defined(__cplusplus) || _MSVC_LANG <= 201402L
#include
typedef _Fcomplex GoComplex64;
typedef _Dcomplex GoComplex128;
#else
+#include
+typedef std::complex GoComplex64;
+typedef std::complex GoComplex128;
+#endif
+#else
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
#endif
diff --git a/src/cmd/compile/README.md b/src/cmd/compile/README.md
index cffb4e7a80..02429d5688 100644
--- a/src/cmd/compile/README.md
+++ b/src/cmd/compile/README.md
@@ -57,7 +57,7 @@ terms of these, so the next step after type checking is to convert the syntax
and types2 representations to ir and types. This process is referred to as
"noding."
-Noding using a process called Unified IR, which builds a node representation
+Noding uses a process called Unified IR, which builds a node representation
using a serialized version of the typechecked code from step 2.
Unified IR is also involved in import/export of packages and inlining.
@@ -281,17 +281,17 @@ dependencies, so is not suitable for distributed build systems.)
```
$ go install golang.org/x/tools/cmd/toolstash@latest
$ git clone https://go.googlesource.com/go
- $ cd go
+ $ export PATH=$PWD/go/bin:$PATH
+ $ cd go/src
$ git checkout -b mybranch
- $ ./src/all.bash # build and confirm good starting point
- $ export PATH=$PWD/bin:$PATH
- $ toolstash save # save current tools
+ $ ./all.bash # build and confirm good starting point
+ $ toolstash save # save current tools
```
After that, your edit/compile/test cycle can be similar to:
```
- <... make edits to cmd/compile source ...>
+ [... make edits to cmd/compile source ...]
$ toolstash restore && go install cmd/compile # restore known good tools to build compiler
- <... 'go build', 'go test', etc. ...> # use freshly built compiler
+ [... 'go build', 'go test', etc. ...] # use freshly built compiler
```
* toolstash also allows comparing the installed vs. stashed copy of
diff --git a/src/cmd/compile/default.pgo b/src/cmd/compile/default.pgo
index 65c28706ea..2c2588704f 100644
Binary files a/src/cmd/compile/default.pgo and b/src/cmd/compile/default.pgo differ
diff --git a/src/cmd/compile/doc.go b/src/cmd/compile/doc.go
index 49abb857ad..81e6189a26 100644
--- a/src/cmd/compile/doc.go
+++ b/src/cmd/compile/doc.go
@@ -60,6 +60,11 @@ Flags:
Allow references to Go symbols in shared libraries (experimental).
-e
Remove the limit on the number of errors reported (default limit is 10).
+ -embedcfg file
+ Read go:embed configuration from file.
+ This is required if any //go:embed directives are used.
+ The file is a JSON file mapping patterns to lists of filenames
+ and filenames to full path names.
-goversion string
Specify required go tool version of the runtime.
Exits when the runtime go version does not match goversion.
@@ -248,6 +253,9 @@ The //go:nosplit directive must be followed by a function declaration.
It specifies that the function must omit its usual stack overflow check.
This is most commonly used by low-level runtime code invoked
at times when it is unsafe for the calling goroutine to be preempted.
+Using this directive outside of low-level runtime code is not safe,
+because it permits the nosplit function to overwrite the end of stack,
+leading to memory corruption and arbitrary program failure.
# Linkname Directive
diff --git a/src/cmd/compile/internal/abi/abiutils.go b/src/cmd/compile/internal/abi/abiutils.go
index e88a80d564..dacaad3f30 100644
--- a/src/cmd/compile/internal/abi/abiutils.go
+++ b/src/cmd/compile/internal/abi/abiutils.go
@@ -661,9 +661,7 @@ func (state *assignState) tryAllocRegs(typ *types.Type) []RegIndex {
func (pa *ABIParamAssignment) ComputePadding(storage []uint64) []uint64 {
nr := len(pa.Registers)
padding := storage[:nr]
- for i := 0; i < nr; i++ {
- padding[i] = 0
- }
+ clear(padding)
if pa.Type.Kind() != types.TSTRUCT || nr == 0 {
return padding
}
@@ -673,10 +671,9 @@ func (pa *ABIParamAssignment) ComputePadding(storage []uint64) []uint64 {
panic("internal error")
}
offsets, _ := appendParamOffsets([]int64{}, 0, pa.Type)
- off := int64(0)
for idx, t := range types {
ts := t.Size()
- off += int64(ts)
+ off := offsets[idx] + ts
if idx < len(types)-1 {
noff := offsets[idx+1]
if noff != off {
diff --git a/src/cmd/compile/internal/amd64/ggen.go b/src/cmd/compile/internal/amd64/ggen.go
index db98a22a1e..853a10cb9a 100644
--- a/src/cmd/compile/internal/amd64/ggen.go
+++ b/src/cmd/compile/internal/amd64/ggen.go
@@ -5,117 +5,23 @@
package amd64
import (
- "cmd/compile/internal/ir"
"cmd/compile/internal/objw"
- "cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/x86"
- "internal/buildcfg"
)
-// no floating point in note handlers on Plan 9
-var isPlan9 = buildcfg.GOOS == "plan9"
-
-// DUFFZERO consists of repeated blocks of 4 MOVUPSs + LEAQ,
-// See runtime/mkduff.go.
-const (
- dzBlocks = 16 // number of MOV/ADD blocks
- dzBlockLen = 4 // number of clears per block
- dzBlockSize = 23 // size of instructions in a single block
- dzMovSize = 5 // size of single MOV instruction w/ offset
- dzLeaqSize = 4 // size of single LEAQ instruction
- dzClearStep = 16 // number of bytes cleared by each MOV instruction
-
- dzClearLen = dzClearStep * dzBlockLen // bytes cleared by one block
- dzSize = dzBlocks * dzBlockSize
-)
-
-// dzOff returns the offset for a jump into DUFFZERO.
-// b is the number of bytes to zero.
-func dzOff(b int64) int64 {
- off := int64(dzSize)
- off -= b / dzClearLen * dzBlockSize
- tailLen := b % dzClearLen
- if tailLen >= dzClearStep {
- off -= dzLeaqSize + dzMovSize*(tailLen/dzClearStep)
- }
- return off
-}
-
-// duffzeroDI returns the pre-adjustment to DI for a call to DUFFZERO.
-// b is the number of bytes to zero.
-func dzDI(b int64) int64 {
- tailLen := b % dzClearLen
- if tailLen < dzClearStep {
- return 0
- }
- tailSteps := tailLen / dzClearStep
- return -dzClearStep * (dzBlockLen - tailSteps)
-}
-
func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.Prog {
- const (
- r13 = 1 << iota // if R13 is already zeroed.
- )
-
- if cnt == 0 {
- return p
+ if cnt%8 != 0 {
+ panic("zeroed region not aligned")
}
-
- if cnt == 8 {
+ for cnt >= 16 {
+ p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_MEM, x86.REG_SP, off)
+ off += 16
+ cnt -= 16
+ }
+ if cnt != 0 {
p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_MEM, x86.REG_SP, off)
- } else if !isPlan9 && cnt <= int64(8*types.RegSize) {
- for i := int64(0); i < cnt/16; i++ {
- p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_MEM, x86.REG_SP, off+i*16)
- }
-
- if cnt%16 != 0 {
- p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_MEM, x86.REG_SP, off+cnt-int64(16))
- }
- } else if !isPlan9 && (cnt <= int64(128*types.RegSize)) {
- // Save DI to r12. With the amd64 Go register abi, DI can contain
- // an incoming parameter, whereas R12 is always scratch.
- p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_DI, 0, obj.TYPE_REG, x86.REG_R12, 0)
- // Emit duffzero call
- p = pp.Append(p, leaptr, obj.TYPE_MEM, x86.REG_SP, off+dzDI(cnt), obj.TYPE_REG, x86.REG_DI, 0)
- p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, dzOff(cnt))
- p.To.Sym = ir.Syms.Duffzero
- if cnt%16 != 0 {
- p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_MEM, x86.REG_DI, -int64(8))
- }
- // Restore DI from r12
- p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_R12, 0, obj.TYPE_REG, x86.REG_DI, 0)
-
- } else {
- // When the register ABI is in effect, at this point in the
- // prolog we may have live values in all of RAX,RDI,RCX. Save
- // them off to registers before the REPSTOSQ below, then
- // restore. Note that R12 and R13 are always available as
- // scratch regs; here we also use R15 (this is safe to do
- // since there won't be any globals accessed in the prolog).
- // See rewriteToUseGot() in obj6.go for more on r15 use.
-
- // Save rax/rdi/rcx
- p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_DI, 0, obj.TYPE_REG, x86.REG_R12, 0)
- p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_REG, x86.REG_R13, 0)
- p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_CX, 0, obj.TYPE_REG, x86.REG_R15, 0)
-
- // Set up the REPSTOSQ and kick it off.
- p = pp.Append(p, x86.AXORL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_REG, x86.REG_AX, 0)
- p = pp.Append(p, x86.AMOVQ, obj.TYPE_CONST, 0, cnt/int64(types.RegSize), obj.TYPE_REG, x86.REG_CX, 0)
- p = pp.Append(p, leaptr, obj.TYPE_MEM, x86.REG_SP, off, obj.TYPE_REG, x86.REG_DI, 0)
- p = pp.Append(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
- p = pp.Append(p, x86.ASTOSQ, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
-
- // Restore rax/rdi/rcx
- p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_R12, 0, obj.TYPE_REG, x86.REG_DI, 0)
- p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_R13, 0, obj.TYPE_REG, x86.REG_AX, 0)
- p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_R15, 0, obj.TYPE_REG, x86.REG_CX, 0)
-
- // Record the fact that r13 is no longer zero.
- *state &= ^uint32(r13)
}
-
return p
}
diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go
index 493369af51..6331501dc9 100644
--- a/src/cmd/compile/internal/amd64/ssa.go
+++ b/src/cmd/compile/internal/amd64/ssa.go
@@ -6,7 +6,6 @@ package amd64
import (
"fmt"
- "internal/buildcfg"
"math"
"cmd/compile/internal/base"
@@ -18,6 +17,7 @@ import (
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/x86"
+ "internal/abi"
)
// ssaMarkMoves marks any MOVXconst ops that need to avoid clobbering flags.
@@ -142,36 +142,6 @@ func memIdx(a *obj.Addr, v *ssa.Value) {
a.Index = i
}
-// DUFFZERO consists of repeated blocks of 4 MOVUPSs + LEAQ,
-// See runtime/mkduff.go.
-func duffStart(size int64) int64 {
- x, _ := duff(size)
- return x
-}
-func duffAdj(size int64) int64 {
- _, x := duff(size)
- return x
-}
-
-// duff returns the offset (from duffzero, in bytes) and pointer adjust (in bytes)
-// required to use the duffzero mechanism for a block of the given size.
-func duff(size int64) (int64, int64) {
- if size < 32 || size > 1024 || size%dzClearStep != 0 {
- panic("bad duffzero size")
- }
- steps := size / dzClearStep
- blocks := steps / dzBlockLen
- steps %= dzBlockLen
- off := dzBlockSize * (dzBlocks - blocks)
- var adj int64
- if steps != 0 {
- off -= dzLeaqSize
- off -= dzMovSize * steps
- adj -= dzClearStep * (dzBlockLen - steps)
- }
- return off, adj
-}
-
func getgFromTLS(s *ssagen.State, r int16) {
// See the comments in cmd/internal/obj/x86/obj6.go
// near CanUse1InsnTLS for a detailed explanation of these instructions.
@@ -202,7 +172,7 @@ func getgFromTLS(s *ssagen.State, r int16) {
func ssaGenValue(s *ssagen.State, v *ssa.Value) {
switch v.Op {
- case ssa.OpAMD64VFMADD231SD:
+ case ssa.OpAMD64VFMADD231SD, ssa.OpAMD64VFMADD231SS:
p := s.Prog(v.Op.Asm())
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: v.Args[2].Reg()}
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: v.Reg()}
@@ -998,40 +968,207 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
- case ssa.OpAMD64DUFFZERO:
+
+ case ssa.OpAMD64LoweredZero:
if s.ABI != obj.ABIInternal {
// zero X15 manually
opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
}
- off := duffStart(v.AuxInt)
- adj := duffAdj(v.AuxInt)
- var p *obj.Prog
- if adj != 0 {
- p = s.Prog(x86.ALEAQ)
- p.From.Type = obj.TYPE_MEM
- p.From.Offset = adj
- p.From.Reg = x86.REG_DI
- p.To.Type = obj.TYPE_REG
- p.To.Reg = x86.REG_DI
+ ptrReg := v.Args[0].Reg()
+ n := v.AuxInt
+ if n < 16 {
+ v.Fatalf("Zero too small %d", n)
}
- p = s.Prog(obj.ADUFFZERO)
- p.To.Type = obj.TYPE_ADDR
- p.To.Sym = ir.Syms.Duffzero
- p.To.Offset = off
- case ssa.OpAMD64DUFFCOPY:
- p := s.Prog(obj.ADUFFCOPY)
- p.To.Type = obj.TYPE_ADDR
- p.To.Sym = ir.Syms.Duffcopy
- if v.AuxInt%16 != 0 {
- v.Fatalf("bad DUFFCOPY AuxInt %v", v.AuxInt)
+ zero16 := func(off int64) {
+ zero16(s, ptrReg, off)
+ }
+
+ // Generate zeroing instructions.
+ var off int64
+ for n >= 16 {
+ zero16(off)
+ off += 16
+ n -= 16
+ }
+ if n != 0 {
+ // use partially overlapped write.
+ // TODO: n <= 8, use smaller write?
+ zero16(off + n - 16)
+ }
+
+ case ssa.OpAMD64LoweredZeroLoop:
+ if s.ABI != obj.ABIInternal {
+ // zero X15 manually
+ opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
+ }
+ ptrReg := v.Args[0].Reg()
+ countReg := v.RegTmp()
+ n := v.AuxInt
+ loopSize := int64(64)
+ if n < 3*loopSize {
+ // - a loop count of 0 won't work.
+ // - a loop count of 1 is useless.
+ // - a loop count of 2 is a code size ~tie
+ // 4 instructions to implement the loop
+ // 4 instructions in the loop body
+ // vs
+ // 8 instructions in the straightline code
+ // Might as well use straightline code.
+ v.Fatalf("ZeroLoop size too small %d", n)
+ }
+ zero16 := func(off int64) {
+ zero16(s, ptrReg, off)
+ }
+
+ // Put iteration count in a register.
+ // MOVL $n, countReg
+ p := s.Prog(x86.AMOVL)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = n / loopSize
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = countReg
+ cntInit := p
+
+ // Zero loopSize bytes starting at ptrReg.
+ for i := range loopSize / 16 {
+ zero16(i * 16)
+ }
+ // ADDQ $loopSize, ptrReg
+ p = s.Prog(x86.AADDQ)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = loopSize
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = ptrReg
+ // DECL countReg
+ p = s.Prog(x86.ADECL)
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = countReg
+ // Jump to first instruction in loop if we're not done yet.
+ // JNE head
+ p = s.Prog(x86.AJNE)
+ p.To.Type = obj.TYPE_BRANCH
+ p.To.SetTarget(cntInit.Link)
+
+ // Multiples of the loop size are now done.
+ n %= loopSize
+
+ // Write any fractional portion.
+ var off int64
+ for n >= 16 {
+ zero16(off)
+ off += 16
+ n -= 16
+ }
+ if n != 0 {
+ // Use partially-overlapping write.
+ // TODO: n <= 8, use smaller write?
+ zero16(off + n - 16)
+ }
+
+ case ssa.OpAMD64LoweredMove:
+ dstReg := v.Args[0].Reg()
+ srcReg := v.Args[1].Reg()
+ if dstReg == srcReg {
+ break
+ }
+ tmpReg := int16(x86.REG_X14)
+ n := v.AuxInt
+ if n < 16 {
+ v.Fatalf("Move too small %d", n)
+ }
+ // move 16 bytes from srcReg+off to dstReg+off.
+ move16 := func(off int64) {
+ move16(s, srcReg, dstReg, tmpReg, off)
+ }
+
+ // Generate copying instructions.
+ var off int64
+ for n >= 16 {
+ move16(off)
+ off += 16
+ n -= 16
+ }
+ if n != 0 {
+ // use partially overlapped read/write.
+ // TODO: use smaller operations when we can?
+ move16(off + n - 16)
+ }
+
+ case ssa.OpAMD64LoweredMoveLoop:
+ dstReg := v.Args[0].Reg()
+ srcReg := v.Args[1].Reg()
+ if dstReg == srcReg {
+ break
+ }
+ countReg := v.RegTmp()
+ tmpReg := int16(x86.REG_X14)
+ n := v.AuxInt
+ loopSize := int64(64)
+ if n < 3*loopSize {
+ // - a loop count of 0 won't work.
+ // - a loop count of 1 is useless.
+ // - a loop count of 2 is a code size ~tie
+ // 4 instructions to implement the loop
+ // 4 instructions in the loop body
+ // vs
+ // 8 instructions in the straightline code
+ // Might as well use straightline code.
+ v.Fatalf("ZeroLoop size too small %d", n)
+ }
+ // move 16 bytes from srcReg+off to dstReg+off.
+ move16 := func(off int64) {
+ move16(s, srcReg, dstReg, tmpReg, off)
+ }
+
+ // Put iteration count in a register.
+ // MOVL $n, countReg
+ p := s.Prog(x86.AMOVL)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = n / loopSize
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = countReg
+ cntInit := p
+
+ // Copy loopSize bytes starting at srcReg to dstReg.
+ for i := range loopSize / 16 {
+ move16(i * 16)
+ }
+ // ADDQ $loopSize, srcReg
+ p = s.Prog(x86.AADDQ)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = loopSize
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = srcReg
+ // ADDQ $loopSize, dstReg
+ p = s.Prog(x86.AADDQ)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = loopSize
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = dstReg
+ // DECL countReg
+ p = s.Prog(x86.ADECL)
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = countReg
+ // Jump to loop header if we're not done yet.
+ // JNE head
+ p = s.Prog(x86.AJNE)
+ p.To.Type = obj.TYPE_BRANCH
+ p.To.SetTarget(cntInit.Link)
+
+ // Multiples of the loop size are now done.
+ n %= loopSize
+
+ // Copy any fractional portion.
+ var off int64
+ for n >= 16 {
+ move16(off)
+ off += 16
+ n -= 16
+ }
+ if n != 0 {
+ // Use partially-overlapping copy.
+ move16(off + n - 16)
}
- p.To.Offset = 14 * (64 - v.AuxInt/16)
- // 14 and 64 are magic constants. 14 is the number of bytes to encode:
- // MOVUPS (SI), X0
- // ADDQ $16, SI
- // MOVUPS X0, (DI)
- // ADDQ $16, DI
- // and 64 is the number of such blocks. See src/runtime/duff_amd64.s:duffcopy.
case ssa.OpCopy: // TODO: use MOVQreg for reg->reg copies instead of OpCopy?
if v.Type.IsMemory() {
@@ -1090,9 +1227,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
case ssa.OpAMD64CALLstatic, ssa.OpAMD64CALLtail:
if s.ABI == obj.ABI0 && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABIInternal {
// zeroing X15 when entering ABIInternal from ABI0
- if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9
- opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
- }
+ opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
// set G register from TLS
getgFromTLS(s, x86.REG_R14)
}
@@ -1103,9 +1238,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
s.Call(v)
if s.ABI == obj.ABIInternal && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABI0 {
// zeroing X15 when entering ABIInternal from ABI0
- if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9
- opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
- }
+ opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
// set G register from TLS
getgFromTLS(s, x86.REG_R14)
}
@@ -1140,12 +1273,91 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
// AuxInt encodes how many buffer entries we need.
p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
- case ssa.OpAMD64LoweredPanicBoundsA, ssa.OpAMD64LoweredPanicBoundsB, ssa.OpAMD64LoweredPanicBoundsC:
- p := s.Prog(obj.ACALL)
+ case ssa.OpAMD64LoweredPanicBoundsRR, ssa.OpAMD64LoweredPanicBoundsRC, ssa.OpAMD64LoweredPanicBoundsCR, ssa.OpAMD64LoweredPanicBoundsCC:
+ // Compute the constant we put in the PCData entry for this call.
+ code, signed := ssa.BoundsKind(v.AuxInt).Code()
+ xIsReg := false
+ yIsReg := false
+ xVal := 0
+ yVal := 0
+ switch v.Op {
+ case ssa.OpAMD64LoweredPanicBoundsRR:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - x86.REG_AX)
+ yIsReg = true
+ yVal = int(v.Args[1].Reg() - x86.REG_AX)
+ case ssa.OpAMD64LoweredPanicBoundsRC:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - x86.REG_AX)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ if yVal == xVal {
+ yVal = 1
+ }
+ p := s.Prog(x86.AMOVQ)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = x86.REG_AX + int16(yVal)
+ }
+ case ssa.OpAMD64LoweredPanicBoundsCR:
+ yIsReg = true
+ yVal = int(v.Args[0].Reg() - x86.REG_AX)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else {
+ // Move constant to a register
+ xIsReg = true
+ if xVal == yVal {
+ xVal = 1
+ }
+ p := s.Prog(x86.AMOVQ)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = x86.REG_AX + int16(xVal)
+ }
+ case ssa.OpAMD64LoweredPanicBoundsCC:
+ c := v.Aux.(ssa.PanicBoundsCC).Cx
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else {
+ // Move constant to a register
+ xIsReg = true
+ p := s.Prog(x86.AMOVQ)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = x86.REG_AX + int16(xVal)
+ }
+ c = v.Aux.(ssa.PanicBoundsCC).Cy
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ yVal = 1
+ p := s.Prog(x86.AMOVQ)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = x86.REG_AX + int16(yVal)
+ }
+ }
+ c := abi.BoundsEncode(code, signed, xIsReg, yIsReg, xVal, yVal)
+
+ p := s.Prog(obj.APCDATA)
+ p.From.SetConst(abi.PCDATA_PanicBounds)
+ p.To.SetConst(int64(c))
+ p = s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
- s.UseArgs(int64(2 * types.PtrSize)) // space used in callee args area by assembly stubs
+ p.To.Sym = ir.Syms.PanicBounds
case ssa.OpAMD64NEGQ, ssa.OpAMD64NEGL,
ssa.OpAMD64BSWAPQ, ssa.OpAMD64BSWAPL,
@@ -1159,6 +1371,31 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0()
+ case ssa.OpAMD64ADDQconstflags, ssa.OpAMD64ADDLconstflags:
+ p := s.Prog(v.Op.Asm())
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = v.AuxInt
+ // Note: the inc/dec instructions do not modify
+ // the carry flag like add$1 / sub$1 do.
+ // We currently never use the CF/OF flags from
+ // these instructions, so that is ok.
+ switch {
+ case p.As == x86.AADDQ && p.From.Offset == 1:
+ p.As = x86.AINCQ
+ p.From.Type = obj.TYPE_NONE
+ case p.As == x86.AADDQ && p.From.Offset == -1:
+ p.As = x86.ADECQ
+ p.From.Type = obj.TYPE_NONE
+ case p.As == x86.AADDL && p.From.Offset == 1:
+ p.As = x86.AINCL
+ p.From.Type = obj.TYPE_NONE
+ case p.As == x86.AADDL && p.From.Offset == -1:
+ p.As = x86.ADECL
+ p.From.Type = obj.TYPE_NONE
+ }
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = v.Reg0()
+
case ssa.OpAMD64BSFQ, ssa.OpAMD64BSRQ, ssa.OpAMD64BSFL, ssa.OpAMD64BSRL, ssa.OpAMD64SQRTSD, ssa.OpAMD64SQRTSS:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
@@ -1170,6 +1407,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
case ssa.OpAMD64BSFL, ssa.OpAMD64BSRL, ssa.OpAMD64SQRTSD, ssa.OpAMD64SQRTSS:
p.To.Reg = v.Reg()
}
+ case ssa.OpAMD64LoweredRound32F, ssa.OpAMD64LoweredRound64F:
+ // input is already rounded
case ssa.OpAMD64ROUNDSD:
p := s.Prog(v.Op.Asm())
val := v.AuxInt
@@ -1439,24 +1678,7 @@ var nefJumps = [2][2]ssagen.IndexJump{
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
- case ssa.BlockPlain:
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
- case ssa.BlockDefer:
- // defer returns in rax:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(x86.ATESTL)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x86.REG_AX
- p.To.Type = obj.TYPE_REG
- p.To.Reg = x86.REG_AX
- p = s.Prog(x86.AJNE)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
@@ -1527,3 +1749,32 @@ func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg in
p.Pos = p.Pos.WithNotStmt()
return p
}
+
+// zero 16 bytes at reg+off.
+func zero16(s *ssagen.State, reg int16, off int64) {
+ // MOVUPS X15, off(ptrReg)
+ p := s.Prog(x86.AMOVUPS)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = x86.REG_X15
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = reg
+ p.To.Offset = off
+}
+
+// move 16 bytes from src+off to dst+off using temporary register tmp.
+func move16(s *ssagen.State, src, dst, tmp int16, off int64) {
+ // MOVUPS off(srcReg), tmpReg
+ // MOVUPS tmpReg, off(dstReg)
+ p := s.Prog(x86.AMOVUPS)
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = src
+ p.From.Offset = off
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = tmp
+ p = s.Prog(x86.AMOVUPS)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = tmp
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = dst
+ p.To.Offset = off
+}
diff --git a/src/cmd/compile/internal/amd64/versions_test.go b/src/cmd/compile/internal/amd64/versions_test.go
index 92365fb365..15395fc5e5 100644
--- a/src/cmd/compile/internal/amd64/versions_test.go
+++ b/src/cmd/compile/internal/amd64/versions_test.go
@@ -75,7 +75,8 @@ func TestGoAMD64v1(t *testing.T) {
cmd := testenv.Command(t, dst.Name())
testenv.CleanCmdEnv(cmd)
cmd.Env = append(cmd.Env, "TESTGOAMD64V1=yes")
- cmd.Env = append(cmd.Env, fmt.Sprintf("GODEBUG=%s", strings.Join(features, ",")))
+ // Disable FIPS 140-3 mode, since it would detect the modified binary.
+ cmd.Env = append(cmd.Env, fmt.Sprintf("GODEBUG=%s,fips140=off", strings.Join(features, ",")))
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("couldn't execute test: %s\n%s", err, out)
diff --git a/src/cmd/compile/internal/arm/ssa.go b/src/cmd/compile/internal/arm/ssa.go
index 638ed3ed4e..a3bfb491b8 100644
--- a/src/cmd/compile/internal/arm/ssa.go
+++ b/src/cmd/compile/internal/arm/ssa.go
@@ -18,6 +18,7 @@ import (
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/arm"
+ "internal/abi"
)
// loadByType returns the load instruction of the given type.
@@ -712,18 +713,167 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Name = obj.NAME_EXTERN
// AuxInt encodes how many buffer entries we need.
p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
- case ssa.OpARMLoweredPanicBoundsA, ssa.OpARMLoweredPanicBoundsB, ssa.OpARMLoweredPanicBoundsC:
- p := s.Prog(obj.ACALL)
+
+ case ssa.OpARMLoweredPanicBoundsRR, ssa.OpARMLoweredPanicBoundsRC, ssa.OpARMLoweredPanicBoundsCR, ssa.OpARMLoweredPanicBoundsCC,
+ ssa.OpARMLoweredPanicExtendRR, ssa.OpARMLoweredPanicExtendRC:
+ // Compute the constant we put in the PCData entry for this call.
+ code, signed := ssa.BoundsKind(v.AuxInt).Code()
+ xIsReg := false
+ yIsReg := false
+ xVal := 0
+ yVal := 0
+ extend := false
+ switch v.Op {
+ case ssa.OpARMLoweredPanicBoundsRR:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - arm.REG_R0)
+ yIsReg = true
+ yVal = int(v.Args[1].Reg() - arm.REG_R0)
+ case ssa.OpARMLoweredPanicExtendRR:
+ extend = true
+ xIsReg = true
+ hi := int(v.Args[0].Reg() - arm.REG_R0)
+ lo := int(v.Args[1].Reg() - arm.REG_R0)
+ xVal = hi<<2 + lo // encode 2 register numbers
+ yIsReg = true
+ yVal = int(v.Args[2].Reg() - arm.REG_R0)
+ case ssa.OpARMLoweredPanicBoundsRC:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - arm.REG_R0)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ if yVal == xVal {
+ yVal = 1
+ }
+ p := s.Prog(arm.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = arm.REG_R0 + int16(yVal)
+ }
+ case ssa.OpARMLoweredPanicExtendRC:
+ extend = true
+ xIsReg = true
+ hi := int(v.Args[0].Reg() - arm.REG_R0)
+ lo := int(v.Args[1].Reg() - arm.REG_R0)
+ xVal = hi<<2 + lo // encode 2 register numbers
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ for yVal == hi || yVal == lo {
+ yVal++
+ }
+ p := s.Prog(arm.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = arm.REG_R0 + int16(yVal)
+ }
+ case ssa.OpARMLoweredPanicBoundsCR:
+ yIsReg = true
+ yVal = int(v.Args[0].Reg() - arm.REG_R0)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else if signed && int64(int32(c)) == c || !signed && int64(uint32(c)) == c {
+ // Move constant to a register
+ xIsReg = true
+ if xVal == yVal {
+ xVal = 1
+ }
+ p := s.Prog(arm.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = arm.REG_R0 + int16(xVal)
+ } else {
+ // Move constant to two registers
+ extend = true
+ xIsReg = true
+ hi := 0
+ lo := 1
+ if hi == yVal {
+ hi = 2
+ }
+ if lo == yVal {
+ lo = 2
+ }
+ xVal = hi<<2 + lo
+ p := s.Prog(arm.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c >> 32
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = arm.REG_R0 + int16(hi)
+ p = s.Prog(arm.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = int64(int32(c))
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = arm.REG_R0 + int16(lo)
+ }
+ case ssa.OpARMLoweredPanicBoundsCC:
+ c := v.Aux.(ssa.PanicBoundsCC).Cx
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else if signed && int64(int32(c)) == c || !signed && int64(uint32(c)) == c {
+ // Move constant to a register
+ xIsReg = true
+ p := s.Prog(arm.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = arm.REG_R0 + int16(xVal)
+ } else {
+ // Move constant to two registers
+ extend = true
+ xIsReg = true
+ hi := 0
+ lo := 1
+ xVal = hi<<2 + lo
+ p := s.Prog(arm.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c >> 32
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = arm.REG_R0 + int16(hi)
+ p = s.Prog(arm.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = int64(int32(c))
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = arm.REG_R0 + int16(lo)
+ }
+ c = v.Aux.(ssa.PanicBoundsCC).Cy
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ yVal = 2
+ p := s.Prog(arm.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = arm.REG_R0 + int16(yVal)
+ }
+ }
+ c := abi.BoundsEncode(code, signed, xIsReg, yIsReg, xVal, yVal)
+
+ p := s.Prog(obj.APCDATA)
+ p.From.SetConst(abi.PCDATA_PanicBounds)
+ p.To.SetConst(int64(c))
+ p = s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
- s.UseArgs(8) // space used in callee args area by assembly stubs
- case ssa.OpARMLoweredPanicExtendA, ssa.OpARMLoweredPanicExtendB, ssa.OpARMLoweredPanicExtendC:
- p := s.Prog(obj.ACALL)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ssagen.ExtendCheckFunc[v.AuxInt]
- s.UseArgs(12) // space used in callee args area by assembly stubs
+ if extend {
+ p.To.Sym = ir.Syms.PanicExtend
+ } else {
+ p.To.Sym = ir.Syms.PanicBounds
+ }
+
case ssa.OpARMDUFFZERO:
p := s.Prog(obj.ADUFFZERO)
p.To.Type = obj.TYPE_MEM
@@ -918,24 +1068,7 @@ var gtJumps = [2][2]ssagen.IndexJump{
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
- case ssa.BlockPlain:
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
-
- case ssa.BlockDefer:
- // defer returns in R0:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(arm.ACMP)
- p.From.Type = obj.TYPE_CONST
- p.From.Offset = 0
- p.Reg = arm.REG_R0
- p = s.Prog(arm.ABNE)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
diff --git a/src/cmd/compile/internal/arm64/ggen.go b/src/cmd/compile/internal/arm64/ggen.go
index a681adcb7f..1402746700 100644
--- a/src/cmd/compile/internal/arm64/ggen.go
+++ b/src/cmd/compile/internal/arm64/ggen.go
@@ -5,9 +5,7 @@
package arm64
import (
- "cmd/compile/internal/ir"
"cmd/compile/internal/objw"
- "cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/arm64"
)
@@ -22,47 +20,20 @@ func padframe(frame int64) int64 {
}
func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
- if cnt == 0 {
- return p
+ if cnt%8 != 0 {
+ panic("zeroed region not aligned")
}
- if cnt < int64(4*types.PtrSize) {
- for i := int64(0); i < cnt; i += int64(types.PtrSize) {
- p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+off+i)
- }
- } else if cnt <= int64(128*types.PtrSize) {
- if cnt%(2*int64(types.PtrSize)) != 0 {
- p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+off)
- off += int64(types.PtrSize)
- cnt -= int64(types.PtrSize)
- }
- p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REG_R20, 0)
- p = pp.Append(p, arm64.AADD, obj.TYPE_CONST, 0, 8+off, obj.TYPE_REG, arm64.REG_R20, 0)
- p.Reg = arm64.REG_R20
- p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ir.Syms.Duffzero
- p.To.Offset = 4 * (64 - cnt/(2*int64(types.PtrSize)))
- } else {
- // Not using REGTMP, so this is async preemptible (async preemption clobbers REGTMP).
- // We are at the function entry, where no register is live, so it is okay to clobber
- // other registers
- const rtmp = arm64.REG_R20
- p = pp.Append(p, arm64.AMOVD, obj.TYPE_CONST, 0, 8+off-8, obj.TYPE_REG, rtmp, 0)
- p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0)
- p = pp.Append(p, arm64.AADD, obj.TYPE_REG, rtmp, 0, obj.TYPE_REG, arm64.REGRT1, 0)
- p.Reg = arm64.REGRT1
- p = pp.Append(p, arm64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, rtmp, 0)
- p = pp.Append(p, arm64.AADD, obj.TYPE_REG, rtmp, 0, obj.TYPE_REG, arm64.REGRT2, 0)
- p.Reg = arm64.REGRT1
- p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGRT1, int64(types.PtrSize))
- p.Scond = arm64.C_XPRE
- p1 := p
- p = pp.Append(p, arm64.ACMP, obj.TYPE_REG, arm64.REGRT1, 0, obj.TYPE_NONE, 0, 0)
- p.Reg = arm64.REGRT2
- p = pp.Append(p, arm64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
- p.To.SetTarget(p1)
+ off += 8 // return address was ignored in offset calculation
+ for cnt >= 16 && off < 512 {
+ p = pp.Append(p, arm64.ASTP, obj.TYPE_REGREG, arm64.REGZERO, arm64.REGZERO, obj.TYPE_MEM, arm64.REGSP, off)
+ off += 16
+ cnt -= 16
+ }
+ for cnt != 0 {
+ p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, off)
+ off += 8
+ cnt -= 8
}
-
return p
}
diff --git a/src/cmd/compile/internal/arm64/ssa.go b/src/cmd/compile/internal/arm64/ssa.go
index adcabb1b95..43ecb6b4b7 100644
--- a/src/cmd/compile/internal/arm64/ssa.go
+++ b/src/cmd/compile/internal/arm64/ssa.go
@@ -16,6 +16,7 @@ import (
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/arm64"
+ "internal/abi"
)
// loadByType returns the load instruction of the given type.
@@ -147,13 +148,13 @@ func genIndexedOperand(op ssa.Op, base, idx int16) obj.Addr {
// Reg: base register, Index: (shifted) index register
mop := obj.Addr{Type: obj.TYPE_MEM, Reg: base}
switch op {
- case ssa.OpARM64MOVDloadidx8, ssa.OpARM64MOVDstoreidx8, ssa.OpARM64MOVDstorezeroidx8,
+ case ssa.OpARM64MOVDloadidx8, ssa.OpARM64MOVDstoreidx8,
ssa.OpARM64FMOVDloadidx8, ssa.OpARM64FMOVDstoreidx8:
mop.Index = arm64.REG_LSL | 3<<5 | idx&31
- case ssa.OpARM64MOVWloadidx4, ssa.OpARM64MOVWUloadidx4, ssa.OpARM64MOVWstoreidx4, ssa.OpARM64MOVWstorezeroidx4,
+ case ssa.OpARM64MOVWloadidx4, ssa.OpARM64MOVWUloadidx4, ssa.OpARM64MOVWstoreidx4,
ssa.OpARM64FMOVSloadidx4, ssa.OpARM64FMOVSstoreidx4:
mop.Index = arm64.REG_LSL | 2<<5 | idx&31
- case ssa.OpARM64MOVHloadidx2, ssa.OpARM64MOVHUloadidx2, ssa.OpARM64MOVHstoreidx2, ssa.OpARM64MOVHstorezeroidx2:
+ case ssa.OpARM64MOVHloadidx2, ssa.OpARM64MOVHUloadidx2, ssa.OpARM64MOVHstoreidx2:
mop.Index = arm64.REG_LSL | 1<<5 | idx&31
default: // not shifted
mop.Index = idx
@@ -188,7 +189,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Reg = x
p.To.Type = obj.TYPE_REG
p.To.Reg = y
- case ssa.OpARM64MOVDnop:
+ case ssa.OpARM64MOVDnop, ssa.OpARM64ZERO:
// nothing to do
case ssa.OpLoadReg:
if v.Type.IsFlags() {
@@ -516,7 +517,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
- case ssa.OpARM64LDP:
+ case ssa.OpARM64LDP, ssa.OpARM64LDPW, ssa.OpARM64LDPSW, ssa.OpARM64FLDPD, ssa.OpARM64FLDPS:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
@@ -583,7 +584,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To = genIndexedOperand(v.Op, v.Args[0].Reg(), v.Args[1].Reg())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[2].Reg()
- case ssa.OpARM64STP:
+ case ssa.OpARM64STP, ssa.OpARM64STPW, ssa.OpARM64FSTPD, ssa.OpARM64FSTPS:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REGREG
p.From.Reg = v.Args[1].Reg()
@@ -591,35 +592,6 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
ssagen.AddAux(&p.To, v)
- case ssa.OpARM64MOVBstorezero,
- ssa.OpARM64MOVHstorezero,
- ssa.OpARM64MOVWstorezero,
- ssa.OpARM64MOVDstorezero:
- p := s.Prog(v.Op.Asm())
- p.From.Type = obj.TYPE_REG
- p.From.Reg = arm64.REGZERO
- p.To.Type = obj.TYPE_MEM
- p.To.Reg = v.Args[0].Reg()
- ssagen.AddAux(&p.To, v)
- case ssa.OpARM64MOVBstorezeroidx,
- ssa.OpARM64MOVHstorezeroidx,
- ssa.OpARM64MOVWstorezeroidx,
- ssa.OpARM64MOVDstorezeroidx,
- ssa.OpARM64MOVHstorezeroidx2,
- ssa.OpARM64MOVWstorezeroidx4,
- ssa.OpARM64MOVDstorezeroidx8:
- p := s.Prog(v.Op.Asm())
- p.To = genIndexedOperand(v.Op, v.Args[0].Reg(), v.Args[1].Reg())
- p.From.Type = obj.TYPE_REG
- p.From.Reg = arm64.REGZERO
- case ssa.OpARM64MOVQstorezero:
- p := s.Prog(v.Op.Asm())
- p.From.Type = obj.TYPE_REGREG
- p.From.Reg = arm64.REGZERO
- p.From.Offset = int64(arm64.REGZERO)
- p.To.Type = obj.TYPE_MEM
- p.To.Reg = v.Args[0].Reg()
- ssagen.AddAux(&p.To, v)
case ssa.OpARM64BFI,
ssa.OpARM64BFXIL:
p := s.Prog(v.Op.Asm())
@@ -1078,68 +1050,268 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Offset = int64(condCode)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
- case ssa.OpARM64DUFFZERO:
- // runtime.duffzero expects start address in R20
- p := s.Prog(obj.ADUFFZERO)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ir.Syms.Duffzero
- p.To.Offset = v.AuxInt
+ case ssa.OpARM64CCMP,
+ ssa.OpARM64CCMN,
+ ssa.OpARM64CCMPconst,
+ ssa.OpARM64CCMNconst,
+ ssa.OpARM64CCMPW,
+ ssa.OpARM64CCMNW,
+ ssa.OpARM64CCMPWconst,
+ ssa.OpARM64CCMNWconst:
+ p := s.Prog(v.Op.Asm())
+ p.Reg = v.Args[0].Reg()
+ params := v.AuxArm64ConditionalParams()
+ p.From.Type = obj.TYPE_SPECIAL // assembler encodes conditional bits in Offset
+ p.From.Offset = int64(condBits[params.Cond()])
+ constValue, ok := params.ConstValue()
+ if ok {
+ p.AddRestSourceConst(constValue)
+ } else {
+ p.AddRestSourceReg(v.Args[1].Reg())
+ }
+ p.To.Type = obj.TYPE_CONST
+ p.To.Offset = params.Nzcv()
case ssa.OpARM64LoweredZero:
- // STP.P (ZR,ZR), 16(R16)
- // CMP Rarg1, R16
- // BLE -2(PC)
- // arg1 is the address of the last 16-byte unit to zero
- p := s.Prog(arm64.ASTP)
- p.Scond = arm64.C_XPOST
- p.From.Type = obj.TYPE_REGREG
- p.From.Reg = arm64.REGZERO
- p.From.Offset = int64(arm64.REGZERO)
- p.To.Type = obj.TYPE_MEM
- p.To.Reg = arm64.REG_R16
- p.To.Offset = 16
- p2 := s.Prog(arm64.ACMP)
- p2.From.Type = obj.TYPE_REG
- p2.From.Reg = v.Args[1].Reg()
- p2.Reg = arm64.REG_R16
- p3 := s.Prog(arm64.ABLE)
- p3.To.Type = obj.TYPE_BRANCH
- p3.To.SetTarget(p)
- case ssa.OpARM64DUFFCOPY:
- p := s.Prog(obj.ADUFFCOPY)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ir.Syms.Duffcopy
- p.To.Offset = v.AuxInt
+ ptrReg := v.Args[0].Reg()
+ n := v.AuxInt
+ if n < 16 {
+ v.Fatalf("Zero too small %d", n)
+ }
+
+ // Generate zeroing instructions.
+ var off int64
+ for n >= 16 {
+ // STP (ZR, ZR), off(ptrReg)
+ zero16(s, ptrReg, off, false)
+ off += 16
+ n -= 16
+ }
+ // Write any fractional portion.
+ // An overlapping 16-byte write can't be used here
+ // because STP's offsets must be a multiple of 8.
+ if n > 8 {
+ // MOVD ZR, off(ptrReg)
+ zero8(s, ptrReg, off)
+ off += 8
+ n -= 8
+ }
+ if n != 0 {
+ // MOVD ZR, off+n-8(ptrReg)
+ // TODO: for n<=4 we could use a smaller write.
+ zero8(s, ptrReg, off+n-8)
+ }
+ case ssa.OpARM64LoweredZeroLoop:
+ ptrReg := v.Args[0].Reg()
+ countReg := v.RegTmp()
+ n := v.AuxInt
+ loopSize := int64(64)
+ if n < 3*loopSize {
+ // - a loop count of 0 won't work.
+ // - a loop count of 1 is useless.
+ // - a loop count of 2 is a code size ~tie
+ // 3 instructions to implement the loop
+ // 4 instructions in the loop body
+ // vs
+ // 8 instructions in the straightline code
+ // Might as well use straightline code.
+ v.Fatalf("ZeroLoop size too small %d", n)
+ }
+
+ // Put iteration count in a register.
+ // MOVD $n, countReg
+ p := s.Prog(arm64.AMOVD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = n / loopSize
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = countReg
+ cntInit := p
+
+ // Zero loopSize bytes starting at ptrReg.
+ // Increment ptrReg by loopSize as a side effect.
+ for range loopSize / 16 {
+ // STP.P (ZR, ZR), 16(ptrReg)
+ zero16(s, ptrReg, 0, true)
+ // TODO: should we use the postincrement form,
+ // or use a separate += 64 instruction?
+ // postincrement saves an instruction, but maybe
+ // it requires more integer units to do the +=16s.
+ }
+ // Decrement loop count.
+ // SUB $1, countReg
+ p = s.Prog(arm64.ASUB)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = 1
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = countReg
+ // Jump to loop header if we're not done yet.
+ // CBNZ head
+ p = s.Prog(arm64.ACBNZ)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = countReg
+ p.To.Type = obj.TYPE_BRANCH
+ p.To.SetTarget(cntInit.Link)
+
+ // Multiples of the loop size are now done.
+ n %= loopSize
+
+ // Write any fractional portion.
+ var off int64
+ for n >= 16 {
+ // STP (ZR, ZR), off(ptrReg)
+ zero16(s, ptrReg, off, false)
+ off += 16
+ n -= 16
+ }
+ if n > 8 {
+ // Note: an overlapping 16-byte write can't be used
+ // here because STP's offsets must be a multiple of 8.
+ // MOVD ZR, off(ptrReg)
+ zero8(s, ptrReg, off)
+ off += 8
+ n -= 8
+ }
+ if n != 0 {
+ // MOVD ZR, off+n-8(ptrReg)
+ // TODO: for n<=4 we could use a smaller write.
+ zero8(s, ptrReg, off+n-8)
+ }
+ // TODO: maybe we should use the count register to instead
+ // hold an end pointer and compare against that?
+ // ADD $n, ptrReg, endReg
+ // then
+ // CMP ptrReg, endReg
+ // BNE loop
+ // There's a past-the-end pointer here, any problem with that?
+
case ssa.OpARM64LoweredMove:
- // LDP.P 16(R16), (R25, Rtmp)
- // STP.P (R25, Rtmp), 16(R17)
- // CMP Rarg2, R16
- // BLE -3(PC)
- // arg2 is the address of the last element of src
- p := s.Prog(arm64.ALDP)
- p.Scond = arm64.C_XPOST
- p.From.Type = obj.TYPE_MEM
- p.From.Reg = arm64.REG_R16
- p.From.Offset = 16
- p.To.Type = obj.TYPE_REGREG
- p.To.Reg = arm64.REG_R25
- p.To.Offset = int64(arm64.REGTMP)
- p2 := s.Prog(arm64.ASTP)
- p2.Scond = arm64.C_XPOST
- p2.From.Type = obj.TYPE_REGREG
- p2.From.Reg = arm64.REG_R25
- p2.From.Offset = int64(arm64.REGTMP)
- p2.To.Type = obj.TYPE_MEM
- p2.To.Reg = arm64.REG_R17
- p2.To.Offset = 16
- p3 := s.Prog(arm64.ACMP)
- p3.From.Type = obj.TYPE_REG
- p3.From.Reg = v.Args[2].Reg()
- p3.Reg = arm64.REG_R16
- p4 := s.Prog(arm64.ABLE)
- p4.To.Type = obj.TYPE_BRANCH
- p4.To.SetTarget(p)
+ dstReg := v.Args[0].Reg()
+ srcReg := v.Args[1].Reg()
+ if dstReg == srcReg {
+ break
+ }
+ tmpReg1 := int16(arm64.REG_R25)
+ tmpFReg1 := int16(arm64.REG_F16)
+ tmpFReg2 := int16(arm64.REG_F17)
+ n := v.AuxInt
+ if n < 16 {
+ v.Fatalf("Move too small %d", n)
+ }
+
+ // Generate copying instructions.
+ var off int64
+ for n >= 32 {
+ // FLDPQ off(srcReg), (tmpFReg1, tmpFReg2)
+ // FSTPQ (tmpFReg1, tmpFReg2), off(dstReg)
+ move32(s, srcReg, dstReg, tmpFReg1, tmpFReg2, off, false)
+ off += 32
+ n -= 32
+ }
+ for n >= 16 {
+ // FMOVQ off(src), tmpFReg1
+ // FMOVQ tmpFReg1, off(dst)
+ move16(s, srcReg, dstReg, tmpFReg1, off, false)
+ off += 16
+ n -= 16
+ }
+ if n > 8 {
+ // MOVD off(srcReg), tmpReg1
+ // MOVD tmpReg1, off(dstReg)
+ move8(s, srcReg, dstReg, tmpReg1, off)
+ off += 8
+ n -= 8
+ }
+ if n != 0 {
+ // MOVD off+n-8(srcReg), tmpReg1
+ // MOVD tmpReg1, off+n-8(dstReg)
+ move8(s, srcReg, dstReg, tmpReg1, off+n-8)
+ }
+ case ssa.OpARM64LoweredMoveLoop:
+ dstReg := v.Args[0].Reg()
+ srcReg := v.Args[1].Reg()
+ if dstReg == srcReg {
+ break
+ }
+ countReg := int16(arm64.REG_R24)
+ tmpReg1 := int16(arm64.REG_R25)
+ tmpFReg1 := int16(arm64.REG_F16)
+ tmpFReg2 := int16(arm64.REG_F17)
+ n := v.AuxInt
+ loopSize := int64(64)
+ if n < 3*loopSize {
+ // - a loop count of 0 won't work.
+ // - a loop count of 1 is useless.
+ // - a loop count of 2 is a code size ~tie
+ // 3 instructions to implement the loop
+ // 4 instructions in the loop body
+ // vs
+ // 8 instructions in the straightline code
+ // Might as well use straightline code.
+ v.Fatalf("ZeroLoop size too small %d", n)
+ }
+
+ // Put iteration count in a register.
+ // MOVD $n, countReg
+ p := s.Prog(arm64.AMOVD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = n / loopSize
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = countReg
+ cntInit := p
+
+ // Move loopSize bytes starting at srcReg to dstReg.
+ // Increment srcReg and destReg by loopSize as a side effect.
+ for range loopSize / 32 {
+ // FLDPQ.P 32(srcReg), (tmpFReg1, tmpFReg2)
+ // FSTPQ.P (tmpFReg1, tmpFReg2), 32(dstReg)
+ move32(s, srcReg, dstReg, tmpFReg1, tmpFReg2, 0, true)
+ }
+ // Decrement loop count.
+ // SUB $1, countReg
+ p = s.Prog(arm64.ASUB)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = 1
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = countReg
+ // Jump to loop header if we're not done yet.
+ // CBNZ head
+ p = s.Prog(arm64.ACBNZ)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = countReg
+ p.To.Type = obj.TYPE_BRANCH
+ p.To.SetTarget(cntInit.Link)
+
+ // Multiples of the loop size are now done.
+ n %= loopSize
+
+ // Copy any fractional portion.
+ var off int64
+ for n >= 32 {
+ // FLDPQ off(srcReg), (tmpFReg1, tmpFReg2)
+ // FSTPQ (tmpFReg1, tmpFReg2), off(dstReg)
+ move32(s, srcReg, dstReg, tmpFReg1, tmpFReg2, off, false)
+ off += 32
+ n -= 32
+ }
+ for n >= 16 {
+ // FMOVQ off(src), tmpFReg1
+ // FMOVQ tmpFReg1, off(dst)
+ move16(s, srcReg, dstReg, tmpFReg1, off, false)
+ off += 16
+ n -= 16
+ }
+ if n > 8 {
+ // MOVD off(srcReg), tmpReg1
+ // MOVD tmpReg1, off(dstReg)
+ move8(s, srcReg, dstReg, tmpReg1, off)
+ off += 8
+ n -= 8
+ }
+ if n != 0 {
+ // MOVD off+n-8(srcReg), tmpReg1
+ // MOVD tmpReg1, off+n-8(dstReg)
+ move8(s, srcReg, dstReg, tmpReg1, off+n-8)
+ }
+
case ssa.OpARM64CALLstatic, ssa.OpARM64CALLclosure, ssa.OpARM64CALLinter:
s.Call(v)
case ssa.OpARM64CALLtail:
@@ -1151,12 +1323,91 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
// AuxInt encodes how many buffer entries we need.
p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
- case ssa.OpARM64LoweredPanicBoundsA, ssa.OpARM64LoweredPanicBoundsB, ssa.OpARM64LoweredPanicBoundsC:
- p := s.Prog(obj.ACALL)
+ case ssa.OpARM64LoweredPanicBoundsRR, ssa.OpARM64LoweredPanicBoundsRC, ssa.OpARM64LoweredPanicBoundsCR, ssa.OpARM64LoweredPanicBoundsCC:
+ // Compute the constant we put in the PCData entry for this call.
+ code, signed := ssa.BoundsKind(v.AuxInt).Code()
+ xIsReg := false
+ yIsReg := false
+ xVal := 0
+ yVal := 0
+ switch v.Op {
+ case ssa.OpARM64LoweredPanicBoundsRR:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - arm64.REG_R0)
+ yIsReg = true
+ yVal = int(v.Args[1].Reg() - arm64.REG_R0)
+ case ssa.OpARM64LoweredPanicBoundsRC:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - arm64.REG_R0)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ if yVal == xVal {
+ yVal = 1
+ }
+ p := s.Prog(arm64.AMOVD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = arm64.REG_R0 + int16(yVal)
+ }
+ case ssa.OpARM64LoweredPanicBoundsCR:
+ yIsReg = true
+ yVal = int(v.Args[0].Reg() - arm64.REG_R0)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else {
+ // Move constant to a register
+ if xVal == yVal {
+ xVal = 1
+ }
+ p := s.Prog(arm64.AMOVD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = arm64.REG_R0 + int16(xVal)
+ }
+ case ssa.OpARM64LoweredPanicBoundsCC:
+ c := v.Aux.(ssa.PanicBoundsCC).Cx
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else {
+ // Move constant to a register
+ xIsReg = true
+ p := s.Prog(arm64.AMOVD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = arm64.REG_R0 + int16(xVal)
+ }
+ c = v.Aux.(ssa.PanicBoundsCC).Cy
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ yVal = 1
+ p := s.Prog(arm64.AMOVD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = arm64.REG_R0 + int16(yVal)
+ }
+ }
+ c := abi.BoundsEncode(code, signed, xIsReg, yIsReg, xVal, yVal)
+
+ p := s.Prog(obj.APCDATA)
+ p.From.SetConst(abi.PCDATA_PanicBounds)
+ p.To.SetConst(int64(c))
+ p = s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
- s.UseArgs(16) // space used in callee args area by assembly stubs
+ p.To.Sym = ir.Syms.PanicBounds
+
case ssa.OpARM64LoweredNilCheck:
// Issue a load which will fault if arg is nil.
p := s.Prog(arm64.AMOVB)
@@ -1327,24 +1578,7 @@ var gtJumps = [2][2]ssagen.IndexJump{
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
- case ssa.BlockPlain:
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
-
- case ssa.BlockDefer:
- // defer returns in R0:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(arm64.ACMP)
- p.From.Type = obj.TYPE_CONST
- p.From.Offset = 0
- p.Reg = arm64.REG_R0
- p = s.Prog(arm64.ABNE)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
@@ -1448,3 +1682,114 @@ func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg in
p.Pos = p.Pos.WithNotStmt()
return p
}
+
+// zero16 zeroes 16 bytes at reg+off.
+// If postInc is true, increment reg by 16.
+func zero16(s *ssagen.State, reg int16, off int64, postInc bool) {
+ // STP (ZR, ZR), off(reg)
+ p := s.Prog(arm64.ASTP)
+ p.From.Type = obj.TYPE_REGREG
+ p.From.Reg = arm64.REGZERO
+ p.From.Offset = int64(arm64.REGZERO)
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = reg
+ p.To.Offset = off
+ if postInc {
+ if off != 0 {
+ panic("can't postinc with non-zero offset")
+ }
+ // STP.P (ZR, ZR), 16(reg)
+ p.Scond = arm64.C_XPOST
+ p.To.Offset = 16
+ }
+}
+
+// zero8 zeroes 8 bytes at reg+off.
+func zero8(s *ssagen.State, reg int16, off int64) {
+ // MOVD ZR, off(reg)
+ p := s.Prog(arm64.AMOVD)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = arm64.REGZERO
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = reg
+ p.To.Offset = off
+}
+
+// move32 copies 32 bytes at src+off to dst+off.
+// Uses registers tmp1 and tmp2.
+// If postInc is true, increment src and dst by 32.
+func move32(s *ssagen.State, src, dst, tmp1, tmp2 int16, off int64, postInc bool) {
+ // FLDPQ off(src), (tmp1, tmp2)
+ ld := s.Prog(arm64.AFLDPQ)
+ ld.From.Type = obj.TYPE_MEM
+ ld.From.Reg = src
+ ld.From.Offset = off
+ ld.To.Type = obj.TYPE_REGREG
+ ld.To.Reg = tmp1
+ ld.To.Offset = int64(tmp2)
+ // FSTPQ (tmp1, tmp2), off(dst)
+ st := s.Prog(arm64.AFSTPQ)
+ st.From.Type = obj.TYPE_REGREG
+ st.From.Reg = tmp1
+ st.From.Offset = int64(tmp2)
+ st.To.Type = obj.TYPE_MEM
+ st.To.Reg = dst
+ st.To.Offset = off
+ if postInc {
+ if off != 0 {
+ panic("can't postinc with non-zero offset")
+ }
+ ld.Scond = arm64.C_XPOST
+ st.Scond = arm64.C_XPOST
+ ld.From.Offset = 32
+ st.To.Offset = 32
+ }
+}
+
+// move16 copies 16 bytes at src+off to dst+off.
+// Uses register tmp1
+// If postInc is true, increment src and dst by 16.
+func move16(s *ssagen.State, src, dst, tmp1 int16, off int64, postInc bool) {
+ // FMOVQ off(src), tmp1
+ ld := s.Prog(arm64.AFMOVQ)
+ ld.From.Type = obj.TYPE_MEM
+ ld.From.Reg = src
+ ld.From.Offset = off
+ ld.To.Type = obj.TYPE_REG
+ ld.To.Reg = tmp1
+ // FMOVQ tmp1, off(dst)
+ st := s.Prog(arm64.AFMOVQ)
+ st.From.Type = obj.TYPE_REG
+ st.From.Reg = tmp1
+ st.To.Type = obj.TYPE_MEM
+ st.To.Reg = dst
+ st.To.Offset = off
+ if postInc {
+ if off != 0 {
+ panic("can't postinc with non-zero offset")
+ }
+ ld.Scond = arm64.C_XPOST
+ st.Scond = arm64.C_XPOST
+ ld.From.Offset = 16
+ st.To.Offset = 16
+ }
+}
+
+// move8 copies 8 bytes at src+off to dst+off.
+// Uses register tmp.
+func move8(s *ssagen.State, src, dst, tmp int16, off int64) {
+ // MOVD off(src), tmp
+ ld := s.Prog(arm64.AMOVD)
+ ld.From.Type = obj.TYPE_MEM
+ ld.From.Reg = src
+ ld.From.Offset = off
+ ld.To.Type = obj.TYPE_REG
+ ld.To.Reg = tmp
+ // MOVD tmp, off(dst)
+ st := s.Prog(arm64.AMOVD)
+ st.From.Type = obj.TYPE_REG
+ st.From.Reg = tmp
+ st.To.Type = obj.TYPE_MEM
+ st.To.Reg = dst
+ st.To.Offset = off
+}
diff --git a/src/cmd/compile/internal/base/debug.go b/src/cmd/compile/internal/base/debug.go
index d42e11b2fa..9e8ab2f488 100644
--- a/src/cmd/compile/internal/base/debug.go
+++ b/src/cmd/compile/internal/base/debug.go
@@ -20,6 +20,7 @@ type DebugFlags struct {
Append int `help:"print information about append compilation"`
Checkptr int `help:"instrument unsafe pointer conversions\n0: instrumentation disabled\n1: conversions involving unsafe.Pointer are instrumented\n2: conversions to unsafe.Pointer force heap allocation" concurrent:"ok"`
Closure int `help:"print information about closure compilation"`
+ Converthash string `help:"hash value for use in debugging changes to platform-dependent float-to-[u]int conversion" concurrent:"ok"`
Defer int `help:"print information about defer compilation"`
DisableNil int `help:"disable nil checks" concurrent:"ok"`
DumpInlFuncProps string `help:"dump function properties from inl heuristics to specified file"`
@@ -29,6 +30,7 @@ type DebugFlags struct {
DumpPtrs int `help:"show Node pointers values in dump output"`
DwarfInl int `help:"print information about DWARF inlined function creation"`
EscapeMutationsCalls int `help:"print extra escape analysis diagnostics about mutations and calls" concurrent:"ok"`
+ EscapeDebug int `help:"print information about escape analysis and resulting optimizations" concurrent:"ok"`
Export int `help:"print export data"`
FIPSHash string `help:"hash value for FIPS debugging" concurrent:"ok"`
Fmahash string `help:"hash value for use in debugging platform-dependent multiply-add use" concurrent:"ok"`
@@ -39,6 +41,7 @@ type DebugFlags struct {
InlFuncsWithClosures int `help:"allow functions with closures to be inlined" concurrent:"ok"`
InlStaticInit int `help:"allow static initialization of inlined calls" concurrent:"ok"`
Libfuzzer int `help:"enable coverage instrumentation for libfuzzer"`
+ LiteralAllocHash string `help:"hash value for use in debugging literal allocation optimizations" concurrent:"ok"`
LoopVar int `help:"shared (0, default), 1 (private loop variables), 2, private + log"`
LoopVarHash string `help:"for debugging changes in loop behavior. Overrides experiment and loopvar flag."`
LocationLists int `help:"print information about DWARF location list creation"`
@@ -72,6 +75,8 @@ type DebugFlags struct {
PGOInlineBudget int `help:"inline budget for hot functions" concurrent:"ok"`
PGODevirtualize int `help:"enable profile-guided devirtualization; 0 to disable, 1 to enable interface devirtualization, 2 to enable function devirtualization" concurrent:"ok"`
RangeFuncCheck int `help:"insert code to check behavior of range iterator functions" concurrent:"ok"`
+ VariableMakeHash string `help:"hash value for debugging stack allocation of variable-sized make results" concurrent:"ok"`
+ VariableMakeThreshold int `help:"threshold in bytes for possible stack allocation of variable-sized make results" concurrent:"ok"`
WrapGlobalMapDbg int `help:"debug trace output for global map init wrapping"`
WrapGlobalMapCtl int `help:"global map init wrap control (0 => default, 1 => off, 2 => stress mode, no size cutoff)"`
ZeroCopy int `help:"enable zero-copy string->[]byte conversions" concurrent:"ok"`
diff --git a/src/cmd/compile/internal/base/flag.go b/src/cmd/compile/internal/base/flag.go
index 31ea8622b9..1d211e0a2d 100644
--- a/src/cmd/compile/internal/base/flag.go
+++ b/src/cmd/compile/internal/base/flag.go
@@ -183,7 +183,8 @@ func ParseFlags() {
Debug.InlStaticInit = 1
Debug.PGOInline = 1
Debug.PGODevirtualize = 2
- Debug.SyncFrames = -1 // disable sync markers by default
+ Debug.SyncFrames = -1 // disable sync markers by default
+ Debug.VariableMakeThreshold = 32 // 32 byte default for stack allocated make results
Debug.ZeroCopy = 1
Debug.RangeFuncCheck = 1
Debug.MergeLocals = 1
@@ -261,15 +262,28 @@ func ParseFlags() {
Debug.LoopVar = 1
}
+ if Debug.Converthash != "" {
+ ConvertHash = NewHashDebug("converthash", Debug.Converthash, nil)
+ } else {
+ // quietly disable the convert hash changes
+ ConvertHash = NewHashDebug("converthash", "qn", nil)
+ }
if Debug.Fmahash != "" {
FmaHash = NewHashDebug("fmahash", Debug.Fmahash, nil)
}
if Debug.PGOHash != "" {
PGOHash = NewHashDebug("pgohash", Debug.PGOHash, nil)
}
+ if Debug.LiteralAllocHash != "" {
+ LiteralAllocHash = NewHashDebug("literalalloc", Debug.LiteralAllocHash, nil)
+ }
+
if Debug.MergeLocalsHash != "" {
MergeLocalsHash = NewHashDebug("mergelocals", Debug.MergeLocalsHash, nil)
}
+ if Debug.VariableMakeHash != "" {
+ VariableMakeHash = NewHashDebug("variablemake", Debug.VariableMakeHash, nil)
+ }
if Flag.MSan && !platform.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH)
@@ -375,14 +389,14 @@ func ParseFlags() {
// See the comment on type CmdFlags for the rules.
func registerFlags() {
var (
- boolType = reflect.TypeOf(bool(false))
- intType = reflect.TypeOf(int(0))
- stringType = reflect.TypeOf(string(""))
- ptrBoolType = reflect.TypeOf(new(bool))
- ptrIntType = reflect.TypeOf(new(int))
- ptrStringType = reflect.TypeOf(new(string))
- countType = reflect.TypeOf(CountFlag(0))
- funcType = reflect.TypeOf((func(string))(nil))
+ boolType = reflect.TypeFor[bool]()
+ intType = reflect.TypeFor[int]()
+ stringType = reflect.TypeFor[string]()
+ ptrBoolType = reflect.TypeFor[*bool]()
+ ptrIntType = reflect.TypeFor[*int]()
+ ptrStringType = reflect.TypeFor[*string]()
+ countType = reflect.TypeFor[CountFlag]()
+ funcType = reflect.TypeFor[func(string)]()
)
v := reflect.ValueOf(&Flag).Elem()
@@ -562,7 +576,7 @@ func readEmbedCfg(file string) {
// parseSpectre parses the spectre configuration from the string s.
func parseSpectre(s string) {
- for _, f := range strings.Split(s, ",") {
+ for f := range strings.SplitSeq(s, ",") {
f = strings.TrimSpace(f)
switch f {
default:
diff --git a/src/cmd/compile/internal/base/hashdebug.go b/src/cmd/compile/internal/base/hashdebug.go
index 7a5cc42578..edf567457c 100644
--- a/src/cmd/compile/internal/base/hashdebug.go
+++ b/src/cmd/compile/internal/base/hashdebug.go
@@ -53,10 +53,13 @@ func (d *HashDebug) SetInlineSuffixOnly(b bool) *HashDebug {
// The default compiler-debugging HashDebug, for "-d=gossahash=..."
var hashDebug *HashDebug
-var FmaHash *HashDebug // for debugging fused-multiply-add floating point changes
-var LoopVarHash *HashDebug // for debugging shared/private loop variable changes
-var PGOHash *HashDebug // for debugging PGO optimization decisions
-var MergeLocalsHash *HashDebug // for debugging local stack slot merging changes
+var ConvertHash *HashDebug // for debugging float-to-[u]int conversion changes
+var FmaHash *HashDebug // for debugging fused-multiply-add floating point changes
+var LoopVarHash *HashDebug // for debugging shared/private loop variable changes
+var PGOHash *HashDebug // for debugging PGO optimization decisions
+var LiteralAllocHash *HashDebug // for debugging literal allocation optimizations
+var MergeLocalsHash *HashDebug // for debugging local stack slot merging changes
+var VariableMakeHash *HashDebug // for debugging variable-sized make optimizations
// DebugHashMatchPkgFunc reports whether debug variable Gossahash
//
diff --git a/src/cmd/compile/internal/base/print.go b/src/cmd/compile/internal/base/print.go
index 119f06fbc0..9e3348c1ec 100644
--- a/src/cmd/compile/internal/base/print.go
+++ b/src/cmd/compile/internal/base/print.go
@@ -220,7 +220,7 @@ func FatalfAt(pos src.XPos, format string, args ...interface{}) {
fmt.Printf("\n")
// If this is a released compiler version, ask for a bug report.
- if Debug.Panic == 0 && strings.HasPrefix(buildcfg.Version, "go") {
+ if Debug.Panic == 0 && strings.HasPrefix(buildcfg.Version, "go") && !strings.Contains(buildcfg.Version, "devel") {
fmt.Printf("\n")
fmt.Printf("Please file a bug report including a short program that triggers the error.\n")
fmt.Printf("https://go.dev/issue/new\n")
diff --git a/src/cmd/compile/internal/bitvec/bv.go b/src/cmd/compile/internal/bitvec/bv.go
index aab10433c8..9214aa6cd0 100644
--- a/src/cmd/compile/internal/bitvec/bv.go
+++ b/src/cmd/compile/internal/bitvec/bv.go
@@ -93,7 +93,7 @@ func (bv BitVec) Unset(i int32) {
bv.B[i/wordBits] &^= mask
}
-// bvnext returns the smallest index >= i for which bvget(bv, i) == 1.
+// Next returns the smallest index >= i for which bvget(bv, i) == 1.
// If there is no such index, bvnext returns -1.
func (bv BitVec) Next(i int32) int32 {
if i >= bv.N {
@@ -196,7 +196,5 @@ func (bv BitVec) String() string {
}
func (bv BitVec) Clear() {
- for i := range bv.B {
- bv.B[i] = 0
- }
+ clear(bv.B)
}
diff --git a/src/cmd/compile/internal/devirtualize/devirtualize.go b/src/cmd/compile/internal/devirtualize/devirtualize.go
index 372d058094..cb4608a024 100644
--- a/src/cmd/compile/internal/devirtualize/devirtualize.go
+++ b/src/cmd/compile/internal/devirtualize/devirtualize.go
@@ -18,9 +18,11 @@ import (
"cmd/compile/internal/types"
)
+const go126ImprovedConcreteTypeAnalysis = true
+
// StaticCall devirtualizes the given call if possible when the concrete callee
// is available statically.
-func StaticCall(call *ir.CallExpr) {
+func StaticCall(s *State, call *ir.CallExpr) {
// For promoted methods (including value-receiver methods promoted
// to pointer-receivers), the interface method wrapper may contain
// expressions that can panic (e.g., ODEREF, ODOTPTR,
@@ -40,15 +42,31 @@ func StaticCall(call *ir.CallExpr) {
}
sel := call.Fun.(*ir.SelectorExpr)
- r := ir.StaticValue(sel.X)
- if r.Op() != ir.OCONVIFACE {
- return
- }
- recv := r.(*ir.ConvExpr)
+ var typ *types.Type
+ if go126ImprovedConcreteTypeAnalysis {
+ typ = concreteType(s, sel.X)
+ if typ == nil {
+ return
+ }
- typ := recv.X.Type()
- if typ.IsInterface() {
- return
+ // Don't create type-assertions that would be impossible at compile-time.
+ // This can happen in such case: any(0).(interface {A()}).A(), this typechecks without
+ // any errors, but will cause a runtime panic. We statically know that int(0) does not
+ // implement that interface, thus we skip the devirtualization, as it is not possible
+ // to make an assertion: any(0).(interface{A()}).(int) (int does not implement interface{A()}).
+ if !typecheck.Implements(typ, sel.X.Type()) {
+ return
+ }
+ } else {
+ r := ir.StaticValue(sel.X)
+ if r.Op() != ir.OCONVIFACE {
+ return
+ }
+ recv := r.(*ir.ConvExpr)
+ typ = recv.X.Type()
+ if typ.IsInterface() {
+ return
+ }
}
// If typ is a shape type, then it was a type argument originally
@@ -99,8 +117,27 @@ func StaticCall(call *ir.CallExpr) {
return
}
- dt := ir.NewTypeAssertExpr(sel.Pos(), sel.X, nil)
- dt.SetType(typ)
+ dt := ir.NewTypeAssertExpr(sel.Pos(), sel.X, typ)
+
+ if go126ImprovedConcreteTypeAnalysis {
+ // Consider:
+ //
+ // var v Iface
+ // v.A()
+ // v = &Impl{}
+ //
+ // Here in the devirtualizer, we determine the concrete type of v as being an *Impl,
+ // but it can still be a nil interface, we have not detected that. The v.(*Impl)
+ // type assertion that we make here would also have failed, but with a different
+ // panic "pkg.Iface is nil, not *pkg.Impl", where previously we would get a nil panic.
+ // We fix this, by introducing an additional nilcheck on the itab.
+ // Calling a method on an nil interface (in most cases) is a bug in a program, so it is fine
+ // to devirtualize and further (possibly) inline them, even though we would never reach
+ // the called function.
+ dt.UseNilPanic = true
+ dt.SetPos(call.Pos())
+ }
+
x := typecheck.XDotMethod(sel.Pos(), dt, sel.Sel, true)
switch x.Op() {
case ir.ODOTMETH:
@@ -138,3 +175,413 @@ func StaticCall(call *ir.CallExpr) {
// Desugar OCALLMETH, if we created one (#57309).
typecheck.FixMethodCall(call)
}
+
+const concreteTypeDebug = false
+
+// concreteType determines the concrete type of n, following OCONVIFACEs and type asserts.
+// Returns nil when the concrete type could not be determined, or when there are multiple
+// (different) types assigned to an interface.
+func concreteType(s *State, n ir.Node) (typ *types.Type) {
+ typ = concreteType1(s, n, make(map[*ir.Name]struct{}))
+ if typ == &noType {
+ return nil
+ }
+ if typ != nil && typ.IsInterface() {
+ base.FatalfAt(n.Pos(), "typ.IsInterface() = true; want = false; typ = %v", typ)
+ }
+ return typ
+}
+
+// noType is a sentinel value returned by [concreteType1].
+var noType types.Type
+
+// concreteType1 analyzes the node n and returns its concrete type if it is statically known.
+// Otherwise, it returns a nil Type, indicating that a concrete type was not determined.
+// When n is known to be statically nil or a self-assignment is detected, in returns a sentinel [noType] type instead.
+func concreteType1(s *State, n ir.Node, seen map[*ir.Name]struct{}) (outT *types.Type) {
+ nn := n // for debug messages
+
+ if concreteTypeDebug {
+ defer func() {
+ t := "&noType"
+ if outT != &noType {
+ t = outT.String()
+ }
+ base.Warn("concreteType1(%v) -> %v", nn, t)
+ }()
+ }
+
+ for {
+ if concreteTypeDebug {
+ base.Warn("concreteType1(%v): analyzing %v", nn, n)
+ }
+
+ if !n.Type().IsInterface() {
+ return n.Type()
+ }
+
+ switch n1 := n.(type) {
+ case *ir.ConvExpr:
+ if n1.Op() == ir.OCONVNOP {
+ if !n1.Type().IsInterface() || !types.Identical(n1.Type().Underlying(), n1.X.Type().Underlying()) {
+ // As we check (directly before this switch) whether n is an interface, thus we should only reach
+ // here for iface conversions where both operands are the same.
+ base.FatalfAt(n1.Pos(), "not identical/interface types found n1.Type = %v; n1.X.Type = %v", n1.Type(), n1.X.Type())
+ }
+ n = n1.X
+ continue
+ }
+ if n1.Op() == ir.OCONVIFACE {
+ n = n1.X
+ continue
+ }
+ case *ir.InlinedCallExpr:
+ if n1.Op() == ir.OINLCALL {
+ n = n1.SingleResult()
+ continue
+ }
+ case *ir.ParenExpr:
+ n = n1.X
+ continue
+ case *ir.TypeAssertExpr:
+ n = n1.X
+ continue
+ }
+ break
+ }
+
+ if n.Op() != ir.ONAME {
+ return nil
+ }
+
+ name := n.(*ir.Name).Canonical()
+ if name.Class != ir.PAUTO {
+ return nil
+ }
+
+ if name.Op() != ir.ONAME {
+ base.FatalfAt(name.Pos(), "name.Op = %v; want = ONAME", n.Op())
+ }
+
+ // name.Curfn must be set, as we checked name.Class != ir.PAUTO before.
+ if name.Curfn == nil {
+ base.FatalfAt(name.Pos(), "name.Curfn = nil; want not nil")
+ }
+
+ if name.Addrtaken() {
+ return nil // conservatively assume it's reassigned with a different type indirectly
+ }
+
+ if _, ok := seen[name]; ok {
+ return &noType // Already analyzed assignments to name, no need to do that twice.
+ }
+ seen[name] = struct{}{}
+
+ if concreteTypeDebug {
+ base.Warn("concreteType1(%v): analyzing assignments to %v", nn, name)
+ }
+
+ var typ *types.Type
+ for _, v := range s.assignments(name) {
+ var t *types.Type
+ switch v := v.(type) {
+ case *types.Type:
+ t = v
+ case ir.Node:
+ t = concreteType1(s, v, seen)
+ if t == &noType {
+ continue
+ }
+ }
+ if t == nil || (typ != nil && !types.Identical(typ, t)) {
+ return nil
+ }
+ typ = t
+ }
+
+ if typ == nil {
+ // Variable either declared with zero value, or only assigned with nil.
+ return &noType
+ }
+
+ return typ
+}
+
+// assignment can be one of:
+// - nil - assignment from an interface type.
+// - *types.Type - assignment from a concrete type (non-interface).
+// - ir.Node - assignment from a ir.Node.
+//
+// In most cases assignment should be an [ir.Node], but in cases where we
+// do not follow the data-flow, we return either a concrete type (*types.Type) or a nil.
+// For example in range over a slice, if the slice elem is of an interface type, then we return
+// a nil, otherwise the elem's concrete type (We do so because we do not analyze assignment to the
+// slice being ranged-over).
+type assignment any
+
+// State holds precomputed state for use in [StaticCall].
+type State struct {
+ // ifaceAssignments maps interface variables to all their assignments
+ // defined inside functions stored in the analyzedFuncs set.
+ // Note: it does not include direct assignments to nil.
+ ifaceAssignments map[*ir.Name][]assignment
+
+ // ifaceCallExprAssigns stores every [*ir.CallExpr], which has an interface
+ // result, that is assigned to a variable.
+ ifaceCallExprAssigns map[*ir.CallExpr][]ifaceAssignRef
+
+ // analyzedFuncs is a set of Funcs that were analyzed for iface assignments.
+ analyzedFuncs map[*ir.Func]struct{}
+}
+
+type ifaceAssignRef struct {
+ name *ir.Name // ifaceAssignments[name]
+ assignmentIndex int // ifaceAssignments[name][assignmentIndex]
+ returnIndex int // (*ir.CallExpr).Result(returnIndex)
+}
+
+// InlinedCall updates the [State] to take into account a newly inlined call.
+func (s *State) InlinedCall(fun *ir.Func, origCall *ir.CallExpr, inlinedCall *ir.InlinedCallExpr) {
+ if _, ok := s.analyzedFuncs[fun]; !ok {
+ // Full analyze has not been yet executed for the provided function, so we can skip it for now.
+ // When no devirtualization happens in a function, it is unnecessary to analyze it.
+ return
+ }
+
+ // Analyze assignments in the newly inlined function.
+ s.analyze(inlinedCall.Init())
+ s.analyze(inlinedCall.Body)
+
+ refs, ok := s.ifaceCallExprAssigns[origCall]
+ if !ok {
+ return
+ }
+ delete(s.ifaceCallExprAssigns, origCall)
+
+ // Update assignments to reference the new ReturnVars of the inlined call.
+ for _, ref := range refs {
+ vt := &s.ifaceAssignments[ref.name][ref.assignmentIndex]
+ if *vt != nil {
+ base.Fatalf("unexpected non-nil assignment")
+ }
+ if concreteTypeDebug {
+ base.Warn(
+ "InlinedCall(%v, %v): replacing interface node in (%v,%v) to %v (typ %v)",
+ origCall, inlinedCall, ref.name, ref.assignmentIndex,
+ inlinedCall.ReturnVars[ref.returnIndex],
+ inlinedCall.ReturnVars[ref.returnIndex].Type(),
+ )
+ }
+
+ // Update ifaceAssignments with an ir.Node from the inlined function’s ReturnVars.
+ // This may enable future devirtualization of calls that reference ref.name.
+ // We will get calls to [StaticCall] from the interleaved package,
+ // to try devirtualize such calls afterwards.
+ *vt = inlinedCall.ReturnVars[ref.returnIndex]
+ }
+}
+
+// assignments returns all assignments to n.
+func (s *State) assignments(n *ir.Name) []assignment {
+ fun := n.Curfn
+ if fun == nil {
+ base.FatalfAt(n.Pos(), "n.Curfn = ")
+ }
+ if n.Class != ir.PAUTO {
+ base.FatalfAt(n.Pos(), "n.Class = %v; want = PAUTO", n.Class)
+ }
+
+ if !n.Type().IsInterface() {
+ base.FatalfAt(n.Pos(), "name passed to assignments is not of an interface type: %v", n.Type())
+ }
+
+ // Analyze assignments in func, if not analyzed before.
+ if _, ok := s.analyzedFuncs[fun]; !ok {
+ if concreteTypeDebug {
+ base.Warn("assignments(): analyzing assignments in %v func", fun)
+ }
+ if s.analyzedFuncs == nil {
+ s.ifaceAssignments = make(map[*ir.Name][]assignment)
+ s.ifaceCallExprAssigns = make(map[*ir.CallExpr][]ifaceAssignRef)
+ s.analyzedFuncs = make(map[*ir.Func]struct{})
+ }
+ s.analyzedFuncs[fun] = struct{}{}
+ s.analyze(fun.Init())
+ s.analyze(fun.Body)
+ }
+
+ return s.ifaceAssignments[n]
+}
+
+// analyze analyzes every assignment to interface variables in nodes, updating [State].
+func (s *State) analyze(nodes ir.Nodes) {
+ assign := func(name ir.Node, assignment assignment) (*ir.Name, int) {
+ if name == nil || name.Op() != ir.ONAME || ir.IsBlank(name) {
+ return nil, -1
+ }
+
+ n, ok := ir.OuterValue(name).(*ir.Name)
+ if !ok || n.Curfn == nil {
+ return nil, -1
+ }
+
+ // Do not track variables that are not of interface types.
+ // For devirtualization they are unnecessary, we will not even look them up.
+ if !n.Type().IsInterface() {
+ return nil, -1
+ }
+
+ n = n.Canonical()
+ if n.Op() != ir.ONAME {
+ base.FatalfAt(n.Pos(), "n.Op = %v; want = ONAME", n.Op())
+ }
+ if n.Class != ir.PAUTO {
+ return nil, -1
+ }
+
+ switch a := assignment.(type) {
+ case nil:
+ case *types.Type:
+ if a != nil && a.IsInterface() {
+ assignment = nil // non-concrete type
+ }
+ case ir.Node:
+ // nil assignment, we can safely ignore them, see [StaticCall].
+ if ir.IsNil(a) {
+ return nil, -1
+ }
+ default:
+ base.Fatalf("unexpected type: %v", assignment)
+ }
+
+ if concreteTypeDebug {
+ base.Warn("analyze(): assignment found %v = %v", name, assignment)
+ }
+
+ s.ifaceAssignments[n] = append(s.ifaceAssignments[n], assignment)
+ return n, len(s.ifaceAssignments[n]) - 1
+ }
+
+ var do func(n ir.Node)
+ do = func(n ir.Node) {
+ switch n.Op() {
+ case ir.OAS:
+ n := n.(*ir.AssignStmt)
+ if rhs := n.Y; rhs != nil {
+ for {
+ if r, ok := rhs.(*ir.ParenExpr); ok {
+ rhs = r.X
+ continue
+ }
+ break
+ }
+ if call, ok := rhs.(*ir.CallExpr); ok && call.Fun != nil {
+ retTyp := call.Fun.Type().Results()[0].Type
+ n, idx := assign(n.X, retTyp)
+ if n != nil && retTyp.IsInterface() {
+ // We have a call expression, that returns an interface, store it for later evaluation.
+ // In case this func gets inlined later, we will update the assignment (added before)
+ // with a reference to ReturnVars, see [State.InlinedCall], which might allow for future devirtualizing of n.X.
+ s.ifaceCallExprAssigns[call] = append(s.ifaceCallExprAssigns[call], ifaceAssignRef{n, idx, 0})
+ }
+ } else {
+ assign(n.X, rhs)
+ }
+ }
+ case ir.OAS2:
+ n := n.(*ir.AssignListStmt)
+ for i, p := range n.Lhs {
+ if n.Rhs[i] != nil {
+ assign(p, n.Rhs[i])
+ }
+ }
+ case ir.OAS2DOTTYPE:
+ n := n.(*ir.AssignListStmt)
+ if n.Rhs[0] == nil {
+ base.FatalfAt(n.Pos(), "n.Rhs[0] == nil; n = %v", n)
+ }
+ assign(n.Lhs[0], n.Rhs[0])
+ assign(n.Lhs[1], nil) // boolean does not have methods to devirtualize
+ case ir.OAS2MAPR, ir.OAS2RECV, ir.OSELRECV2:
+ n := n.(*ir.AssignListStmt)
+ if n.Rhs[0] == nil {
+ base.FatalfAt(n.Pos(), "n.Rhs[0] == nil; n = %v", n)
+ }
+ assign(n.Lhs[0], n.Rhs[0].Type())
+ assign(n.Lhs[1], nil) // boolean does not have methods to devirtualize
+ case ir.OAS2FUNC:
+ n := n.(*ir.AssignListStmt)
+ rhs := n.Rhs[0]
+ for {
+ if r, ok := rhs.(*ir.ParenExpr); ok {
+ rhs = r.X
+ continue
+ }
+ break
+ }
+ if call, ok := rhs.(*ir.CallExpr); ok {
+ for i, p := range n.Lhs {
+ retTyp := call.Fun.Type().Results()[i].Type
+ n, idx := assign(p, retTyp)
+ if n != nil && retTyp.IsInterface() {
+ // We have a call expression, that returns an interface, store it for later evaluation.
+ // In case this func gets inlined later, we will update the assignment (added before)
+ // with a reference to ReturnVars, see [State.InlinedCall], which might allow for future devirtualizing of n.X.
+ s.ifaceCallExprAssigns[call] = append(s.ifaceCallExprAssigns[call], ifaceAssignRef{n, idx, i})
+ }
+ }
+ } else if call, ok := rhs.(*ir.InlinedCallExpr); ok {
+ for i, p := range n.Lhs {
+ assign(p, call.ReturnVars[i])
+ }
+ } else {
+ base.FatalfAt(n.Pos(), "unexpected type %T in OAS2FUNC Rhs[0]", call)
+ }
+ case ir.ORANGE:
+ n := n.(*ir.RangeStmt)
+ xTyp := n.X.Type()
+
+ // Range over an array pointer.
+ if xTyp.IsPtr() && xTyp.Elem().IsArray() {
+ xTyp = xTyp.Elem()
+ }
+
+ if xTyp.IsArray() || xTyp.IsSlice() {
+ assign(n.Key, nil) // integer does not have methods to devirtualize
+ assign(n.Value, xTyp.Elem())
+ } else if xTyp.IsChan() {
+ assign(n.Key, xTyp.Elem())
+ base.AssertfAt(n.Value == nil, n.Pos(), "n.Value != nil in range over chan")
+ } else if xTyp.IsMap() {
+ assign(n.Key, xTyp.Key())
+ assign(n.Value, xTyp.Elem())
+ } else if xTyp.IsInteger() || xTyp.IsString() {
+ // Range over int/string, results do not have methods, so nothing to devirtualize.
+ assign(n.Key, nil)
+ assign(n.Value, nil)
+ } else {
+ // We will not reach here in case of an range-over-func, as it is
+ // rewrtten to function calls in the noder package.
+ base.FatalfAt(n.Pos(), "range over unexpected type %v", n.X.Type())
+ }
+ case ir.OSWITCH:
+ n := n.(*ir.SwitchStmt)
+ if guard, ok := n.Tag.(*ir.TypeSwitchGuard); ok {
+ for _, v := range n.Cases {
+ if v.Var == nil {
+ base.Assert(guard.Tag == nil)
+ continue
+ }
+ assign(v.Var, guard.X)
+ }
+ }
+ case ir.OCLOSURE:
+ n := n.(*ir.ClosureExpr)
+ if _, ok := s.analyzedFuncs[n.Func]; !ok {
+ s.analyzedFuncs[n.Func] = struct{}{}
+ ir.Visit(n.Func, do)
+ }
+ }
+ }
+ ir.VisitList(nodes, do)
+}
diff --git a/src/cmd/compile/internal/devirtualize/pgo.go b/src/cmd/compile/internal/devirtualize/pgo.go
index 96c9231be3..a8980bb86b 100644
--- a/src/cmd/compile/internal/devirtualize/pgo.go
+++ b/src/cmd/compile/internal/devirtualize/pgo.go
@@ -741,7 +741,7 @@ func findHotConcreteCallee(p *pgoir.Profile, caller *ir.Func, call *ir.CallExpr,
hottest = e
}
- if hottest == nil {
+ if hottest == nil || hottest.Weight == 0 {
if base.Debug.PGODebug >= 2 {
fmt.Printf("%v: call %s:%d: no hot callee\n", ir.Line(call), callerName, callOffset)
}
diff --git a/src/cmd/compile/internal/dwarfgen/dwarf.go b/src/cmd/compile/internal/dwarfgen/dwarf.go
index fa13f07fdf..9d975e0bc1 100644
--- a/src/cmd/compile/internal/dwarfgen/dwarf.go
+++ b/src/cmd/compile/internal/dwarfgen/dwarf.go
@@ -128,14 +128,29 @@ func Info(ctxt *obj.Link, fnsym *obj.LSym, infosym *obj.LSym, curfn obj.Func) (s
// already referenced by a dwarf var, attach an R_USETYPE relocation to
// the function symbol to insure that the type included in DWARF
// processing during linking.
+ // Do the same with R_USEIFACE relocations from the function symbol for the
+ // same reason.
+ // All these R_USETYPE relocations are only looked at if the function
+ // survives deadcode elimination in the linker.
typesyms := []*obj.LSym{}
for t := range fnsym.Func().Autot {
typesyms = append(typesyms, t)
}
+ for i := range fnsym.R {
+ if fnsym.R[i].Type == objabi.R_USEIFACE && !strings.HasPrefix(fnsym.R[i].Sym.Name, "go:itab.") {
+ // Types referenced through itab will be referenced from somewhere else
+ typesyms = append(typesyms, fnsym.R[i].Sym)
+ }
+ }
slices.SortFunc(typesyms, func(a, b *obj.LSym) int {
return strings.Compare(a.Name, b.Name)
})
+ var lastsym *obj.LSym
for _, sym := range typesyms {
+ if sym == lastsym {
+ continue
+ }
+ lastsym = sym
infosym.AddRel(ctxt, obj.Reloc{Type: objabi.R_USETYPE, Sym: sym})
}
fnsym.Func().Autot = nil
@@ -203,7 +218,7 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
continue
}
if n.Class != ir.PPARAMOUT || !n.IsOutputParamInRegisters() {
- panic("invalid ir.Name on debugInfo.RegOutputParams list")
+ base.Fatalf("invalid ir.Name on debugInfo.RegOutputParams list")
}
dcl = append(dcl, n)
}
@@ -248,11 +263,6 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
tag = dwarf.DW_TAG_formal_parameter
}
- if n.Esc() == ir.EscHeap {
- // The variable in question has been promoted to the heap.
- // Its address is in n.Heapaddr.
- // TODO(thanm): generate a better location expression
- }
inlIndex := 0
if base.Flag.GenDwarfInl > 1 {
if n.InlFormal() || n.InlLocal() {
@@ -263,7 +273,7 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
}
}
declpos := base.Ctxt.InnermostPos(n.Pos())
- vars = append(vars, &dwarf.Var{
+ dvar := &dwarf.Var{
Name: n.Sym().Name,
IsReturnValue: isReturnValue,
Tag: tag,
@@ -277,8 +287,19 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
ChildIndex: -1,
DictIndex: n.DictIndex,
ClosureOffset: closureOffset(n, closureVars),
- })
- // Record go type of to insure that it gets emitted by the linker.
+ }
+ if n.Esc() == ir.EscHeap {
+ if n.Heapaddr == nil {
+ base.Fatalf("invalid heap allocated var without Heapaddr")
+ }
+ debug := fn.DebugInfo.(*ssa.FuncDebug)
+ list := createHeapDerefLocationList(n, debug.EntryID)
+ dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
+ debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym))
+ }
+ }
+ vars = append(vars, dvar)
+ // Record go type to ensure that it gets emitted by the linker.
fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
}
@@ -550,11 +571,34 @@ func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID, closureVars
return dvar
}
+// createHeapDerefLocationList creates a location list for a heap-escaped variable
+// that describes "dereference pointer at stack offset"
+func createHeapDerefLocationList(n *ir.Name, entryID ssa.ID) []byte {
+ // Get the stack offset where the heap pointer is stored
+ heapPtrOffset := n.Heapaddr.FrameOffset()
+ if base.Ctxt.Arch.FixedFrameSize == 0 {
+ heapPtrOffset -= int64(types.PtrSize)
+ }
+ if buildcfg.FramePointerEnabled {
+ heapPtrOffset -= int64(types.PtrSize)
+ }
+
+ // Create a location expression: DW_OP_fbreg DW_OP_deref
+ var locExpr []byte
+ var sizeIdx int
+ locExpr, sizeIdx = ssa.SetupLocList(base.Ctxt, entryID, locExpr, ssa.BlockStart.ID, ssa.FuncEnd.ID)
+ locExpr = append(locExpr, dwarf.DW_OP_fbreg)
+ locExpr = dwarf.AppendSleb128(locExpr, heapPtrOffset)
+ locExpr = append(locExpr, dwarf.DW_OP_deref)
+ base.Ctxt.Arch.ByteOrder.PutUint16(locExpr[sizeIdx:], uint16(len(locExpr)-sizeIdx-2))
+ return locExpr
+}
+
// RecordFlags records the specified command-line flags to be placed
// in the DWARF info.
func RecordFlags(flags ...string) {
if base.Ctxt.Pkgpath == "" {
- panic("missing pkgpath")
+ base.Fatalf("missing pkgpath")
}
type BoolFlag interface {
diff --git a/src/cmd/compile/internal/dwarfgen/linenum_test.go b/src/cmd/compile/internal/dwarfgen/linenum_test.go
new file mode 100644
index 0000000000..05e72c2a06
--- /dev/null
+++ b/src/cmd/compile/internal/dwarfgen/linenum_test.go
@@ -0,0 +1,105 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package dwarfgen
+
+import (
+ "debug/dwarf"
+ "internal/platform"
+ "internal/testenv"
+ "io"
+ "runtime"
+ "testing"
+)
+
+func TestIssue75249(t *testing.T) {
+ testenv.MustHaveGoRun(t)
+ t.Parallel()
+
+ if !platform.ExecutableHasDWARF(runtime.GOOS, runtime.GOARCH) {
+ t.Skipf("skipping on %s/%s: no DWARF symbol table in executables", runtime.GOOS, runtime.GOARCH)
+ }
+
+ code := `
+package main
+
+type Data struct {
+ Field1 int
+ Field2 *int
+ Field3 int
+ Field4 *int
+ Field5 int
+ Field6 *int
+ Field7 int
+ Field8 *int
+}
+
+//go:noinline
+func InitializeData(d *Data) {
+ d.Field1++ // line 16
+ d.Field2 = d.Field4
+ d.Field3++
+ d.Field4 = d.Field6
+ d.Field5++
+ d.Field6 = d.Field8
+ d.Field7++
+ d.Field8 = d.Field2 // line 23
+}
+
+func main() {
+ var data Data
+ InitializeData(&data)
+}
+`
+
+ _, f := gobuild(t, t.TempDir(), true, []testline{{line: code}})
+ defer f.Close()
+
+ dwarfData, err := f.DWARF()
+ if err != nil {
+ t.Fatal(err)
+ }
+ dwarfReader := dwarfData.Reader()
+
+ for {
+ entry, err := dwarfReader.Next()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if entry == nil {
+ break
+ }
+ if entry.Tag != dwarf.TagCompileUnit {
+ continue
+ }
+ name := entry.AttrField(dwarf.AttrName)
+ if name == nil || name.Class != dwarf.ClassString || name.Val != "main" {
+ continue
+ }
+ lr, err := dwarfData.LineReader(entry)
+ if err != nil {
+ t.Fatal(err)
+ }
+ stmts := map[int]bool{}
+ for {
+ var le dwarf.LineEntry
+ err := lr.Next(&le)
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !le.IsStmt {
+ continue
+ }
+ stmts[le.Line] = true
+ }
+ for i := 16; i <= 23; i++ {
+ if !stmts[i] {
+ t.Errorf("missing statement at line %d", i)
+ }
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/dwarfgen/scope_test.go b/src/cmd/compile/internal/dwarfgen/scope_test.go
index feffb06e1f..a8d24a6973 100644
--- a/src/cmd/compile/internal/dwarfgen/scope_test.go
+++ b/src/cmd/compile/internal/dwarfgen/scope_test.go
@@ -226,7 +226,7 @@ func TestScopeRanges(t *testing.T) {
defer f.Close()
// the compiler uses forward slashes for paths even on windows
- src = strings.Replace(src, "\\", "/", -1)
+ src = strings.ReplaceAll(src, "\\", "/")
pcln, err := f.PCLineTable()
if err != nil {
diff --git a/src/cmd/compile/internal/escape/call.go b/src/cmd/compile/internal/escape/call.go
index 1d7a0c9089..f9351de975 100644
--- a/src/cmd/compile/internal/escape/call.go
+++ b/src/cmd/compile/internal/escape/call.go
@@ -40,6 +40,7 @@ func (e *escape) call(ks []hole, call ir.Node) {
var fn *ir.Name
switch call.Op() {
case ir.OCALLFUNC:
+ // TODO(thepudds): use an ir.ReassignOracle here.
v := ir.StaticValue(call.Fun)
fn = ir.StaticCalleeName(v)
}
@@ -83,15 +84,19 @@ func (e *escape) call(ks []hole, call ir.Node) {
argument(e.tagHole(ks, fn, param), arg)
}
- // hash/maphash.escapeForHash forces its argument to be on
- // the heap, if it contains a non-string pointer. We cannot
+ // internal/abi.EscapeNonString forces its argument to be on
+ // the heap, if it contains a non-string pointer.
+ // This is used in hash/maphash.Comparable, where we cannot
// hash pointers to local variables, as the address of the
// local variable might change on stack growth.
// Strings are okay as the hash depends on only the content,
// not the pointer.
+ // This is also used in unique.clone, to model the data flow
+ // edge on the value with strings excluded, because strings
+ // are cloned (by content).
// The actual call we match is
- // hash/maphash.escapeForHash[go.shape.T](dict, go.shape.T)
- if fn != nil && fn.Sym().Pkg.Path == "hash/maphash" && strings.HasPrefix(fn.Sym().Name, "escapeForHash[") {
+ // internal/abi.EscapeNonString[go.shape.T](dict, go.shape.T)
+ if fn != nil && fn.Sym().Pkg.Path == "internal/abi" && strings.HasPrefix(fn.Sym().Name, "EscapeNonString[") {
ps := fntype.Params()
if len(ps) == 2 && ps[1].Type.IsShape() {
if !hasNonStringPointers(ps[1].Type) {
@@ -159,6 +164,14 @@ func (e *escape) call(ks []hole, call ir.Node) {
}
e.discard(call.RType)
+ // Model the new backing store that might be allocated by append.
+ // Its address flows to the result.
+ // Users of escape analysis can look at the escape information for OAPPEND
+ // and use that to decide where to allocate the backing store.
+ backingStore := e.spill(ks[0], call)
+ // As we have a boolean to prevent reuse, we can treat these allocations as outside any loops.
+ backingStore.dst.loopDepth = 0
+
case ir.OCOPY:
call := call.(*ir.BinaryExpr)
argument(e.mutatorHole(), call.X)
@@ -179,7 +192,7 @@ func (e *escape) call(ks []hole, call ir.Node) {
e.discard(call.X)
e.discard(call.Y)
- case ir.ODELETE, ir.OPRINT, ir.OPRINTLN, ir.ORECOVERFP:
+ case ir.ODELETE, ir.OPRINT, ir.OPRINTLN, ir.ORECOVER:
call := call.(*ir.CallExpr)
for _, arg := range call.Args {
e.discard(arg)
diff --git a/src/cmd/compile/internal/escape/escape.go b/src/cmd/compile/internal/escape/escape.go
index d6f0708a7f..59250edfef 100644
--- a/src/cmd/compile/internal/escape/escape.go
+++ b/src/cmd/compile/internal/escape/escape.go
@@ -6,6 +6,8 @@ package escape
import (
"fmt"
+ "go/constant"
+ "go/token"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
@@ -86,8 +88,9 @@ import (
// A batch holds escape analysis state that's shared across an entire
// batch of functions being analyzed at once.
type batch struct {
- allLocs []*location
- closures []closure
+ allLocs []*location
+ closures []closure
+ reassignOracles map[*ir.Func]*ir.ReassignOracle
heapLoc location
mutatorLoc location
@@ -119,16 +122,24 @@ type escape struct {
}
func Funcs(all []*ir.Func) {
- ir.VisitFuncsBottomUp(all, Batch)
+ // Make a cache of ir.ReassignOracles. The cache is lazily populated.
+ // TODO(thepudds): consider adding a field on ir.Func instead. We might also be able
+ // to use that field elsewhere, like in walk. See discussion in https://go.dev/cl/688075.
+ reassignOracles := make(map[*ir.Func]*ir.ReassignOracle)
+
+ ir.VisitFuncsBottomUp(all, func(list []*ir.Func, recursive bool) {
+ Batch(list, reassignOracles)
+ })
}
// Batch performs escape analysis on a minimal batch of
// functions.
-func Batch(fns []*ir.Func, recursive bool) {
+func Batch(fns []*ir.Func, reassignOracles map[*ir.Func]*ir.ReassignOracle) {
var b batch
b.heapLoc.attrs = attrEscapes | attrPersists | attrMutates | attrCalls
b.mutatorLoc.attrs = attrMutates
b.calleeLoc.attrs = attrCalls
+ b.reassignOracles = reassignOracles
// Construct data-flow graph from syntax trees.
for _, fn := range fns {
@@ -154,6 +165,11 @@ func Batch(fns []*ir.Func, recursive bool) {
b.closures = nil
for _, loc := range b.allLocs {
+ // Try to replace some non-constant expressions with literals.
+ b.rewriteWithLiterals(loc.n, loc.curfn)
+
+ // Check if the node must be heap allocated for certain reasons
+ // such as OMAKESLICE for a large slice.
if why := HeapAllocReason(loc.n); why != "" {
b.flow(b.heapHole().addr(loc.n, why), loc)
}
@@ -306,7 +322,11 @@ func (b *batch) finish(fns []*ir.Func) {
}
} else {
if base.Flag.LowerM != 0 && !goDeferWrapper {
- base.WarnfAt(n.Pos(), "%v escapes to heap", n)
+ if n.Op() == ir.OAPPEND {
+ base.WarnfAt(n.Pos(), "append escapes to heap")
+ } else {
+ base.WarnfAt(n.Pos(), "%v escapes to heap", n)
+ }
}
if logopt.Enabled() {
var e_curfn *ir.Func // TODO(mdempsky): Fix.
@@ -316,7 +336,11 @@ func (b *batch) finish(fns []*ir.Func) {
n.SetEsc(ir.EscHeap)
} else {
if base.Flag.LowerM != 0 && n.Op() != ir.ONAME && !goDeferWrapper {
- base.WarnfAt(n.Pos(), "%v does not escape", n)
+ if n.Op() == ir.OAPPEND {
+ base.WarnfAt(n.Pos(), "append does not escape")
+ } else {
+ base.WarnfAt(n.Pos(), "%v does not escape", n)
+ }
}
n.SetEsc(ir.EscNone)
if !loc.hasAttr(attrPersists) {
@@ -507,3 +531,131 @@ func (b *batch) reportLeaks(pos src.XPos, name string, esc leaks, sig *types.Typ
base.WarnfAt(pos, "%v does not escape, mutate, or call", name)
}
}
+
+// rewriteWithLiterals attempts to replace certain non-constant expressions
+// within n with a literal if possible.
+func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
+ if n == nil || fn == nil {
+ return
+ }
+
+ assignTemp := func(pos src.XPos, n ir.Node, init *ir.Nodes) {
+ // Preserve any side effects of n by assigning it to an otherwise unused temp.
+ tmp := typecheck.TempAt(pos, fn, n.Type())
+ init.Append(typecheck.Stmt(ir.NewDecl(pos, ir.ODCL, tmp)))
+ init.Append(typecheck.Stmt(ir.NewAssignStmt(pos, tmp, n)))
+ }
+
+ switch n.Op() {
+ case ir.OMAKESLICE:
+ // Check if we can replace a non-constant argument to make with
+ // a literal to allow for this slice to be stack allocated if otherwise allowed.
+ n := n.(*ir.MakeExpr)
+
+ r := &n.Cap
+ if n.Cap == nil {
+ r = &n.Len
+ }
+
+ if (*r).Op() != ir.OLITERAL {
+ // Look up a cached ReassignOracle for the function, lazily computing one if needed.
+ ro := b.reassignOracle(fn)
+ if ro == nil {
+ base.Fatalf("no ReassignOracle for function %v with closure parent %v", fn, fn.ClosureParent)
+ }
+
+ s := ro.StaticValue(*r)
+ switch s.Op() {
+ case ir.OLITERAL:
+ lit, ok := s.(*ir.BasicLit)
+ if !ok || lit.Val().Kind() != constant.Int {
+ base.Fatalf("unexpected BasicLit Kind")
+ }
+ if constant.Compare(lit.Val(), token.GEQ, constant.MakeInt64(0)) {
+ if !base.LiteralAllocHash.MatchPos(n.Pos(), nil) {
+ // De-selected by literal alloc optimizations debug hash.
+ return
+ }
+ // Preserve any side effects of the original expression, then replace it.
+ assignTemp(n.Pos(), *r, n.PtrInit())
+ *r = ir.NewBasicLit(n.Pos(), (*r).Type(), lit.Val())
+ }
+ case ir.OLEN:
+ x := ro.StaticValue(s.(*ir.UnaryExpr).X)
+ if x.Op() == ir.OSLICELIT {
+ x := x.(*ir.CompLitExpr)
+ // Preserve any side effects of the original expression, then update the value.
+ assignTemp(n.Pos(), *r, n.PtrInit())
+ *r = ir.NewBasicLit(n.Pos(), types.Types[types.TINT], constant.MakeInt64(x.Len))
+ }
+ }
+ }
+ case ir.OCONVIFACE:
+ // Check if we can replace a non-constant expression in an interface conversion with
+ // a literal to avoid heap allocating the underlying interface value.
+ conv := n.(*ir.ConvExpr)
+ if conv.X.Op() != ir.OLITERAL && !conv.X.Type().IsInterface() {
+ // TODO(thepudds): likely could avoid some work by tightening the check of conv.X's type.
+ // Look up a cached ReassignOracle for the function, lazily computing one if needed.
+ ro := b.reassignOracle(fn)
+ if ro == nil {
+ base.Fatalf("no ReassignOracle for function %v with closure parent %v", fn, fn.ClosureParent)
+ }
+ v := ro.StaticValue(conv.X)
+ if v != nil && v.Op() == ir.OLITERAL && ir.ValidTypeForConst(conv.X.Type(), v.Val()) {
+ if !base.LiteralAllocHash.MatchPos(n.Pos(), nil) {
+ // De-selected by literal alloc optimizations debug hash.
+ return
+ }
+ if base.Debug.EscapeDebug >= 3 {
+ base.WarnfAt(n.Pos(), "rewriting OCONVIFACE value from %v (%v) to %v (%v)", conv.X, conv.X.Type(), v, v.Type())
+ }
+ // Preserve any side effects of the original expression, then replace it.
+ assignTemp(conv.Pos(), conv.X, conv.PtrInit())
+ v := v.(*ir.BasicLit)
+ conv.X = ir.NewBasicLit(conv.Pos(), conv.X.Type(), v.Val())
+ typecheck.Expr(conv)
+ }
+ }
+ }
+}
+
+// reassignOracle returns an initialized *ir.ReassignOracle for fn.
+// If fn is a closure, it returns the ReassignOracle for the ultimate parent.
+//
+// A new ReassignOracle is initialized lazily if needed, and the result
+// is cached to reduce duplicative work of preparing a ReassignOracle.
+func (b *batch) reassignOracle(fn *ir.Func) *ir.ReassignOracle {
+ if ro, ok := b.reassignOracles[fn]; ok {
+ return ro // Hit.
+ }
+
+ // For closures, we want the ultimate parent's ReassignOracle,
+ // so walk up the parent chain, if any.
+ f := fn
+ for f.ClosureParent != nil && !f.ClosureParent.IsPackageInit() {
+ f = f.ClosureParent
+ }
+
+ if f != fn {
+ // We found a parent.
+ ro := b.reassignOracles[f]
+ if ro != nil {
+ // Hit, via a parent. Before returning, store this ro for the original fn as well.
+ b.reassignOracles[fn] = ro
+ return ro
+ }
+ }
+
+ // Miss. We did not find a ReassignOracle for fn or a parent, so lazily create one.
+ ro := &ir.ReassignOracle{}
+ ro.Init(f)
+
+ // Cache the answer for the original fn.
+ b.reassignOracles[fn] = ro
+ if f != fn {
+ // Cache for the parent as well.
+ b.reassignOracles[f] = ro
+ }
+ return ro
+}
diff --git a/src/cmd/compile/internal/escape/expr.go b/src/cmd/compile/internal/escape/expr.go
index f479a2913a..1521c2edd1 100644
--- a/src/cmd/compile/internal/escape/expr.go
+++ b/src/cmd/compile/internal/escape/expr.go
@@ -139,7 +139,7 @@ func (e *escape) exprSkipInit(k hole, n ir.Node) {
e.discard(n.X)
case ir.OCALLMETH, ir.OCALLFUNC, ir.OCALLINTER, ir.OINLCALL,
- ir.OLEN, ir.OCAP, ir.OMIN, ir.OMAX, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY, ir.ORECOVERFP,
+ ir.OLEN, ir.OCAP, ir.OMIN, ir.OMAX, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY, ir.ORECOVER,
ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
e.call([]hole{k}, n)
diff --git a/src/cmd/compile/internal/escape/graph.go b/src/cmd/compile/internal/escape/graph.go
index 75e2546a7b..0ffb4a0bb5 100644
--- a/src/cmd/compile/internal/escape/graph.go
+++ b/src/cmd/compile/internal/escape/graph.go
@@ -62,9 +62,14 @@ type location struct {
dst *location
dstEdgeIdx int
- // queued is used by walkAll to track whether this location is
- // in the walk queue.
- queued bool
+ // queuedWalkAll is used by walkAll to track whether this location is
+ // in its work queue.
+ queuedWalkAll bool
+
+ // queuedWalkOne is used by walkOne to track whether this location is
+ // in its work queue. The value is the walkgen when this location was
+ // last queued for walkOne, or 0 if it's not currently queued.
+ queuedWalkOne uint32
// attrs is a bitset of location attributes.
attrs locAttr
@@ -75,6 +80,8 @@ type location struct {
captured bool // has a closure captured this variable?
reassigned bool // has this variable been reassigned?
addrtaken bool // has this variable's address been taken?
+ param bool // is this variable a parameter (ONAME of class ir.PPARAM)?
+ paramOut bool // is this variable an out parameter (ONAME of class ir.PPARAMOUT)?
}
type locAttr uint8
@@ -132,35 +139,6 @@ func (l *location) leakTo(sink *location, derefs int) {
l.paramEsc.AddHeap(derefs)
}
-// leakTo records that parameter l leaks to sink.
-func (b *batch) leakTo(l, sink *location, derefs int) {
- if (logopt.Enabled() || base.Flag.LowerM >= 2) && !l.hasAttr(attrEscapes) {
- if base.Flag.LowerM >= 2 {
- fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, b.explainLoc(sink), derefs)
- }
- explanation := b.explainPath(sink, l)
- if logopt.Enabled() {
- var e_curfn *ir.Func // TODO(mdempsky): Fix.
- logopt.LogOpt(l.n.Pos(), "leak", "escape", ir.FuncName(e_curfn),
- fmt.Sprintf("parameter %v leaks to %s with derefs=%d", l.n, b.explainLoc(sink), derefs), explanation)
- }
- }
-
- // If sink is a result parameter that doesn't escape (#44614)
- // and we can fit return bits into the escape analysis tag,
- // then record as a result leak.
- if !sink.hasAttr(attrEscapes) && sink.isName(ir.PPARAMOUT) && sink.curfn == l.curfn {
- if ri := sink.resultIndex - 1; ri < numEscResults {
- // Leak to result parameter.
- l.paramEsc.AddResult(ri, derefs)
- return
- }
- }
-
- // Otherwise, record as heap leak.
- l.paramEsc.AddHeap(derefs)
-}
-
func (l *location) isName(c ir.Class) bool {
return l.n != nil && l.n.Op() == ir.ONAME && l.n.(*ir.Name).Class == c
}
@@ -234,7 +212,7 @@ func (b *batch) flow(k hole, src *location) {
if base.Flag.LowerM >= 2 || logopt.Enabled() {
pos := base.FmtPos(src.n.Pos())
if base.Flag.LowerM >= 2 {
- fmt.Printf("%s: %v escapes to heap:\n", pos, src.n)
+ fmt.Printf("%s: %v escapes to heap in %v:\n", pos, src.n, ir.FuncName(src.curfn))
}
explanation := b.explainFlow(pos, dst, src, k.derefs, k.notes, []*logopt.LoggedOpt{})
if logopt.Enabled() {
@@ -281,6 +259,12 @@ func (e *escape) newLoc(n ir.Node, persists bool) *location {
curfn: e.curfn,
loopDepth: e.loopDepth,
}
+ if loc.isName(ir.PPARAM) {
+ loc.param = true
+ } else if loc.isName(ir.PPARAMOUT) {
+ loc.paramOut = true
+ }
+
if persists {
loc.attrs |= attrPersists
}
diff --git a/src/cmd/compile/internal/escape/solve.go b/src/cmd/compile/internal/escape/solve.go
index 32f5a771a3..e2ca3eabda 100644
--- a/src/cmd/compile/internal/escape/solve.go
+++ b/src/cmd/compile/internal/escape/solve.go
@@ -10,6 +10,7 @@ import (
"cmd/compile/internal/logopt"
"cmd/internal/src"
"fmt"
+ "math/bits"
"strings"
)
@@ -24,28 +25,41 @@ func (b *batch) walkAll() {
// !persists->persists and !escapes->escapes, which can each
// happen at most once. So we take Θ(len(e.allLocs)) walks.
- // LIFO queue, has enough room for e.allLocs and e.heapLoc.
- todo := make([]*location, 0, len(b.allLocs)+1)
+ // Queue of locations to walk. Has enough room for b.allLocs
+ // plus b.heapLoc, b.mutatorLoc, b.calleeLoc.
+ todo := newQueue(len(b.allLocs) + 3)
+
enqueue := func(loc *location) {
- if !loc.queued {
- todo = append(todo, loc)
- loc.queued = true
+ if !loc.queuedWalkAll {
+ loc.queuedWalkAll = true
+ if loc.hasAttr(attrEscapes) {
+ // Favor locations that escape to the heap,
+ // which in some cases allows attrEscape to
+ // propagate faster.
+ todo.pushFront(loc)
+ } else {
+ todo.pushBack(loc)
+ }
}
}
for _, loc := range b.allLocs {
- enqueue(loc)
+ todo.pushFront(loc)
+ // TODO(thepudds): clean up setting queuedWalkAll.
+ loc.queuedWalkAll = true
}
- enqueue(&b.mutatorLoc)
- enqueue(&b.calleeLoc)
- enqueue(&b.heapLoc)
+ todo.pushFront(&b.mutatorLoc)
+ todo.pushFront(&b.calleeLoc)
+ todo.pushFront(&b.heapLoc)
+
+ b.mutatorLoc.queuedWalkAll = true
+ b.calleeLoc.queuedWalkAll = true
+ b.heapLoc.queuedWalkAll = true
var walkgen uint32
- for len(todo) > 0 {
- root := todo[len(todo)-1]
- todo = todo[:len(todo)-1]
- root.queued = false
-
+ for todo.len() > 0 {
+ root := todo.popFront()
+ root.queuedWalkAll = false
walkgen++
b.walkOne(root, walkgen, enqueue)
}
@@ -77,10 +91,12 @@ func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location))
}
}
- todo := []*location{root} // LIFO queue
- for len(todo) > 0 {
- l := todo[len(todo)-1]
- todo = todo[:len(todo)-1]
+ todo := newQueue(1)
+ todo.pushFront(root)
+
+ for todo.len() > 0 {
+ l := todo.popFront()
+ l.queuedWalkOne = 0 // no longer queued for walkOne
derefs := l.derefs
var newAttrs locAttr
@@ -100,7 +116,7 @@ func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location))
if b.outlives(root, l) {
if !l.hasAttr(attrEscapes) && (logopt.Enabled() || base.Flag.LowerM >= 2) {
if base.Flag.LowerM >= 2 {
- fmt.Printf("%s: %v escapes to heap:\n", base.FmtPos(l.n.Pos()), l.n)
+ fmt.Printf("%s: %v escapes to heap in %v:\n", base.FmtPos(l.n.Pos()), l.n, ir.FuncName(l.curfn))
}
explanation := b.explainPath(root, l)
if logopt.Enabled() {
@@ -126,11 +142,11 @@ func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location))
// corresponding result parameter, then record
// that value flow for tagging the function
// later.
- if l.isName(ir.PPARAM) {
+ if l.param {
if b.outlives(root, l) {
if !l.hasAttr(attrEscapes) && (logopt.Enabled() || base.Flag.LowerM >= 2) {
if base.Flag.LowerM >= 2 {
- fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, b.explainLoc(root), derefs)
+ fmt.Printf("%s: parameter %v leaks to %s for %v with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, b.explainLoc(root), ir.FuncName(l.curfn), derefs)
}
explanation := b.explainPath(root, l)
if logopt.Enabled() {
@@ -167,7 +183,14 @@ func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location))
edge.src.derefs = d
edge.src.dst = l
edge.src.dstEdgeIdx = i
- todo = append(todo, edge.src)
+ // Check if already queued in todo.
+ if edge.src.queuedWalkOne != walkgen {
+ edge.src.queuedWalkOne = walkgen // Mark queued for this walkgen.
+
+ // Place at the back to possibly give time for
+ // other possible attribute changes to src.
+ todo.pushBack(edge.src)
+ }
}
}
}
@@ -211,7 +234,7 @@ func (b *batch) explainFlow(pos string, dst, srcloc *location, derefs int, notes
}
print := base.Flag.LowerM >= 2
- flow := fmt.Sprintf(" flow: %s = %s%v:", b.explainLoc(dst), ops, b.explainLoc(srcloc))
+ flow := fmt.Sprintf(" flow: %s ← %s%v:", b.explainLoc(dst), ops, b.explainLoc(srcloc))
if print {
fmt.Printf("%s:%s\n", pos, flow)
}
@@ -270,7 +293,7 @@ func (b *batch) outlives(l, other *location) bool {
// We don't know what callers do with returned values, so
// pessimistically we need to assume they flow to the heap and
// outlive everything too.
- if l.isName(ir.PPARAMOUT) {
+ if l.paramOut {
// Exception: Closures can return locations allocated outside of
// them without forcing them to the heap, if we can statically
// identify all call sites. For example:
@@ -278,7 +301,7 @@ func (b *batch) outlives(l, other *location) bool {
// var u int // okay to stack allocate
// fn := func() *int { return &u }()
// *fn() = 42
- if containsClosure(other.curfn, l.curfn) && !l.curfn.ClosureResultsLost() {
+ if ir.ContainsClosure(other.curfn, l.curfn) && !l.curfn.ClosureResultsLost() {
return false
}
@@ -304,24 +327,71 @@ func (b *batch) outlives(l, other *location) bool {
// func() {
// l = new(int) // must heap allocate: outlives call frame (if not inlined)
// }()
- if containsClosure(l.curfn, other.curfn) {
+ if ir.ContainsClosure(l.curfn, other.curfn) {
return true
}
return false
}
-// containsClosure reports whether c is a closure contained within f.
-func containsClosure(f, c *ir.Func) bool {
- // Common cases.
- if f == c || c.OClosure == nil {
- return false
- }
-
- for p := c.ClosureParent; p != nil; p = p.ClosureParent {
- if p == f {
- return true
- }
- }
- return false
+// queue implements a queue of locations for use in WalkAll and WalkOne.
+// It supports pushing to front & back, and popping from front.
+// TODO(thepudds): does cmd/compile have a deque or similar somewhere?
+type queue struct {
+ locs []*location
+ head int // index of front element
+ tail int // next back element
+ elems int
}
+
+func newQueue(capacity int) *queue {
+ capacity = max(capacity, 2)
+ capacity = 1 << bits.Len64(uint64(capacity-1)) // round up to a power of 2
+ return &queue{locs: make([]*location, capacity)}
+}
+
+// pushFront adds an element to the front of the queue.
+func (q *queue) pushFront(loc *location) {
+ if q.elems == len(q.locs) {
+ q.grow()
+ }
+ q.head = q.wrap(q.head - 1)
+ q.locs[q.head] = loc
+ q.elems++
+}
+
+// pushBack adds an element to the back of the queue.
+func (q *queue) pushBack(loc *location) {
+ if q.elems == len(q.locs) {
+ q.grow()
+ }
+ q.locs[q.tail] = loc
+ q.tail = q.wrap(q.tail + 1)
+ q.elems++
+}
+
+// popFront removes the front of the queue.
+func (q *queue) popFront() *location {
+ if q.elems == 0 {
+ return nil
+ }
+ loc := q.locs[q.head]
+ q.head = q.wrap(q.head + 1)
+ q.elems--
+ return loc
+}
+
+// grow doubles the capacity.
+func (q *queue) grow() {
+ newLocs := make([]*location, len(q.locs)*2)
+ for i := range q.elems {
+ // Copy over our elements in order.
+ newLocs[i] = q.locs[q.wrap(q.head+i)]
+ }
+ q.locs = newLocs
+ q.head = 0
+ q.tail = q.elems
+}
+
+func (q *queue) len() int { return q.elems }
+func (q *queue) wrap(i int) int { return i & (len(q.locs) - 1) }
diff --git a/src/cmd/compile/internal/escape/stmt.go b/src/cmd/compile/internal/escape/stmt.go
index b766864a30..2388873caf 100644
--- a/src/cmd/compile/internal/escape/stmt.go
+++ b/src/cmd/compile/internal/escape/stmt.go
@@ -183,7 +183,7 @@ func (e *escape) stmt(n ir.Node) {
dsts[i] = res.Nname.(*ir.Name)
}
e.assignList(dsts, n.Results, "return", n)
- case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OINLCALL, ir.OCLEAR, ir.OCLOSE, ir.OCOPY, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTLN, ir.ORECOVERFP:
+ case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OINLCALL, ir.OCLEAR, ir.OCLOSE, ir.OCOPY, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTLN, ir.ORECOVER:
e.call(nil, n)
case ir.OGO, ir.ODEFER:
n := n.(*ir.GoDeferStmt)
diff --git a/src/cmd/compile/internal/escape/utils.go b/src/cmd/compile/internal/escape/utils.go
index bd1d2c22a2..2718a7f841 100644
--- a/src/cmd/compile/internal/escape/utils.go
+++ b/src/cmd/compile/internal/escape/utils.go
@@ -206,14 +206,35 @@ func HeapAllocReason(n ir.Node) string {
if n.Op() == ir.OMAKESLICE {
n := n.(*ir.MakeExpr)
+
r := n.Cap
- if r == nil {
+ if n.Cap == nil {
r = n.Len
}
- if !ir.IsSmallIntConst(r) {
- return "non-constant size"
+
+ elem := n.Type().Elem()
+ if elem.Size() == 0 {
+ // TODO: stack allocate these? See #65685.
+ return "zero-sized element"
}
- if t := n.Type(); t.Elem().Size() != 0 && ir.Int64Val(r) > ir.MaxImplicitStackVarSize/t.Elem().Size() {
+ if !ir.IsSmallIntConst(r) {
+ // For non-constant sizes, we do a hybrid approach:
+ //
+ // if cap <= K {
+ // var backing [K]E
+ // s = backing[:len:cap]
+ // } else {
+ // s = makeslice(E, len, cap)
+ // }
+ //
+ // It costs a constant amount of stack space, but may
+ // avoid a heap allocation.
+ // Note we have to be careful that assigning s[i] = v
+ // still escapes v, because we forbid heap->stack pointers.
+ // Implementation is in ../walk/builtin.go:walkMakeSlice.
+ return ""
+ }
+ if ir.Int64Val(r) > ir.MaxImplicitStackVarSize/elem.Size() {
return "too large for stack"
}
}
diff --git a/src/cmd/compile/internal/gc/compile.go b/src/cmd/compile/internal/gc/compile.go
index 696c1f566e..1a40df9a84 100644
--- a/src/cmd/compile/internal/gc/compile.go
+++ b/src/cmd/compile/internal/gc/compile.go
@@ -114,6 +114,8 @@ func prepareFunc(fn *ir.Func) {
ir.CurFunc = fn
walk.Walk(fn)
ir.CurFunc = nil // enforce no further uses of CurFunc
+
+ base.Ctxt.DwTextCount++
}
// compileFunctions compiles all functions in compilequeue.
diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go
index c93f008ba2..9afbeb9d3b 100644
--- a/src/cmd/compile/internal/gc/export.go
+++ b/src/cmd/compile/internal/gc/export.go
@@ -47,5 +47,7 @@ func dumpasmhdr() {
}
}
- b.Close()
+ if err := b.Close(); err != nil {
+ base.Fatalf("%v", err)
+ }
}
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 253ec3257a..42e2afaee4 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -104,12 +104,10 @@ func Main(archInit func(*ssagen.ArchInfo)) {
ir.Pkgs.Runtime = types.NewPkg("go.runtime", "runtime")
ir.Pkgs.Runtime.Prefix = "runtime"
- if buildcfg.Experiment.SwissMap {
- // Pseudo-package that contains the compiler's builtin
- // declarations for maps.
- ir.Pkgs.InternalMaps = types.NewPkg("go.internal/runtime/maps", "internal/runtime/maps")
- ir.Pkgs.InternalMaps.Prefix = "internal/runtime/maps"
- }
+ // Pseudo-package that contains the compiler's builtin
+ // declarations for maps.
+ ir.Pkgs.InternalMaps = types.NewPkg("go.internal/runtime/maps", "internal/runtime/maps")
+ ir.Pkgs.InternalMaps.Prefix = "internal/runtime/maps"
// pseudo-packages used in symbol tables
ir.Pkgs.Itab = types.NewPkg("go.itab", "go.itab")
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index 4b42c81ef8..37bbce0318 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -57,7 +57,7 @@ func dumpobj1(outfile string, mode int) {
fmt.Printf("can't create %s: %v\n", outfile, err)
base.ErrorExit()
}
- defer bout.Close()
+
bout.WriteString("!\n")
if mode&modeCompilerObj != 0 {
@@ -70,6 +70,12 @@ func dumpobj1(outfile string, mode int) {
dumpLinkerObj(bout)
finishArchiveEntry(bout, start, "_go_.o")
}
+
+ if err := bout.Close(); err != nil {
+ base.FlushErrors()
+ fmt.Printf("error while writing to file %s: %v\n", outfile, err)
+ base.ErrorExit()
+ }
}
func printObjHeader(bout *bio.Writer) {
diff --git a/src/cmd/compile/internal/importer/gcimporter_test.go b/src/cmd/compile/internal/importer/gcimporter_test.go
index d913d3ca76..11e4ee6b58 100644
--- a/src/cmd/compile/internal/importer/gcimporter_test.go
+++ b/src/cmd/compile/internal/importer/gcimporter_test.go
@@ -164,7 +164,7 @@ func TestVersionHandling(t *testing.T) {
_, err := Import(make(map[string]*types2.Package), pkgpath, dir, nil)
if err != nil {
// ok to fail if it fails with a 'not the start of an archive file' error for select files
- if strings.Contains(err.Error(), "no longer supported") {
+ if strings.Contains(err.Error(), "not the start of an archive file") {
switch name {
case "test_go1.8_4.a",
"test_go1.8_5.a":
@@ -243,7 +243,7 @@ func TestImportStdLib(t *testing.T) {
// Get list of packages in stdlib. Filter out test-only packages with {{if .GoFiles}} check.
var stderr bytes.Buffer
- cmd := exec.Command("go", "list", "-f", "{{if .GoFiles}}{{.ImportPath}}{{end}}", "std")
+ cmd := exec.Command(testenv.GoToolPath(t), "list", "-f", "{{if .GoFiles}}{{.ImportPath}}{{end}}", "std")
cmd.Stderr = &stderr
out, err := cmd.Output()
if err != nil {
@@ -673,3 +673,50 @@ type S struct {
}
wg.Wait()
}
+
+func TestIssue63285(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ tmpdir := t.TempDir()
+ testoutdir := filepath.Join(tmpdir, "testdata")
+ if err := os.Mkdir(testoutdir, 0700); err != nil {
+ t.Fatalf("making output dir: %v", err)
+ }
+
+ compile(t, "testdata", "issue63285.go", testoutdir, nil)
+
+ issue63285, err := Import(make(map[string]*types2.Package), "./testdata/issue63285", tmpdir, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ check := func(pkgname, src string, imports importMap) (*types2.Package, error) {
+ f, err := syntax.Parse(syntax.NewFileBase(pkgname), strings.NewReader(src), nil, nil, 0)
+ if err != nil {
+ return nil, err
+ }
+ config := &types2.Config{
+ Importer: imports,
+ }
+ return config.Check(pkgname, []*syntax.File{f}, nil)
+ }
+
+ const pSrc = `package p
+
+import "issue63285"
+
+var _ issue63285.A[issue63285.B[any]]
+`
+
+ importer := importMap{
+ "issue63285": issue63285,
+ }
+ if _, err := check("p", pSrc, importer); err != nil {
+ t.Errorf("Check failed: %v", err)
+ }
+}
diff --git a/src/cmd/compile/internal/importer/support.go b/src/cmd/compile/internal/importer/support.go
index a443b4d862..6ce721557a 100644
--- a/src/cmd/compile/internal/importer/support.go
+++ b/src/cmd/compile/internal/importer/support.go
@@ -9,20 +9,14 @@ package importer
import (
"cmd/compile/internal/base"
"cmd/compile/internal/types2"
- "fmt"
"go/token"
"internal/pkgbits"
- "sync"
)
func assert(p bool) {
base.Assert(p)
}
-func errorf(format string, args ...interface{}) {
- panic(fmt.Sprintf(format, args...))
-}
-
const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go
// Synthesize a token.Pos
@@ -31,108 +25,6 @@ type fakeFileSet struct {
files map[string]*token.File
}
-func (s *fakeFileSet) pos(file string, line, column int) token.Pos {
- // TODO(mdempsky): Make use of column.
-
- // Since we don't know the set of needed file positions, we
- // reserve maxlines positions per file.
- const maxlines = 64 * 1024
- f := s.files[file]
- if f == nil {
- f = s.fset.AddFile(file, -1, maxlines)
- s.files[file] = f
- // Allocate the fake linebreak indices on first use.
- // TODO(adonovan): opt: save ~512KB using a more complex scheme?
- fakeLinesOnce.Do(func() {
- fakeLines = make([]int, maxlines)
- for i := range fakeLines {
- fakeLines[i] = i
- }
- })
- f.SetLines(fakeLines)
- }
-
- if line > maxlines {
- line = 1
- }
-
- // Treat the file as if it contained only newlines
- // and column=1: use the line number as the offset.
- return f.Pos(line - 1)
-}
-
-var (
- fakeLines []int
- fakeLinesOnce sync.Once
-)
-
-func chanDir(d int) types2.ChanDir {
- // tag values must match the constants in cmd/compile/internal/gc/go.go
- switch d {
- case 1 /* Crecv */ :
- return types2.RecvOnly
- case 2 /* Csend */ :
- return types2.SendOnly
- case 3 /* Cboth */ :
- return types2.SendRecv
- default:
- errorf("unexpected channel dir %d", d)
- return 0
- }
-}
-
-var predeclared = []types2.Type{
- // basic types
- types2.Typ[types2.Bool],
- types2.Typ[types2.Int],
- types2.Typ[types2.Int8],
- types2.Typ[types2.Int16],
- types2.Typ[types2.Int32],
- types2.Typ[types2.Int64],
- types2.Typ[types2.Uint],
- types2.Typ[types2.Uint8],
- types2.Typ[types2.Uint16],
- types2.Typ[types2.Uint32],
- types2.Typ[types2.Uint64],
- types2.Typ[types2.Uintptr],
- types2.Typ[types2.Float32],
- types2.Typ[types2.Float64],
- types2.Typ[types2.Complex64],
- types2.Typ[types2.Complex128],
- types2.Typ[types2.String],
-
- // basic type aliases
- types2.Universe.Lookup("byte").Type(),
- types2.Universe.Lookup("rune").Type(),
-
- // error
- types2.Universe.Lookup("error").Type(),
-
- // untyped types
- types2.Typ[types2.UntypedBool],
- types2.Typ[types2.UntypedInt],
- types2.Typ[types2.UntypedRune],
- types2.Typ[types2.UntypedFloat],
- types2.Typ[types2.UntypedComplex],
- types2.Typ[types2.UntypedString],
- types2.Typ[types2.UntypedNil],
-
- // package unsafe
- types2.Typ[types2.UnsafePointer],
-
- // invalid type
- types2.Typ[types2.Invalid], // only appears in packages with errors
-
- // used internally by gc; never used by this package or in .a files
- // not to be confused with the universe any
- anyType{},
-
- // comparable
- types2.Universe.Lookup("comparable").Type(),
-
- // "any" has special handling: see usage of predeclared.
-}
-
type anyType struct{}
func (t anyType) Underlying() types2.Type { return t }
diff --git a/src/cmd/compile/internal/importer/testdata/issue63285.go b/src/cmd/compile/internal/importer/testdata/issue63285.go
new file mode 100644
index 0000000000..54a06b7b88
--- /dev/null
+++ b/src/cmd/compile/internal/importer/testdata/issue63285.go
@@ -0,0 +1,11 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue63285
+
+type A[_ B[any]] struct{}
+
+type B[_ any] interface {
+ f() A[B[any]]
+}
diff --git a/src/cmd/compile/internal/importer/ureader.go b/src/cmd/compile/internal/importer/ureader.go
index 2f8f174a93..f6df56b70e 100644
--- a/src/cmd/compile/internal/importer/ureader.go
+++ b/src/cmd/compile/internal/importer/ureader.go
@@ -33,12 +33,12 @@ func ReadPackage(ctxt *types2.Context, imports map[string]*types2.Package, input
imports: imports,
enableAlias: true,
- posBases: make([]*syntax.PosBase, input.NumElems(pkgbits.RelocPosBase)),
- pkgs: make([]*types2.Package, input.NumElems(pkgbits.RelocPkg)),
- typs: make([]types2.Type, input.NumElems(pkgbits.RelocType)),
+ posBases: make([]*syntax.PosBase, input.NumElems(pkgbits.SectionPosBase)),
+ pkgs: make([]*types2.Package, input.NumElems(pkgbits.SectionPkg)),
+ typs: make([]types2.Type, input.NumElems(pkgbits.SectionType)),
}
- r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
+ r := pr.newReader(pkgbits.SectionMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
pkg := r.pkg()
if r.Version().Has(pkgbits.HasInit) {
@@ -52,7 +52,7 @@ func ReadPackage(ctxt *types2.Context, imports map[string]*types2.Package, input
if r.Version().Has(pkgbits.DerivedFuncInstance) {
assert(!r.Bool())
}
- r.p.objIdx(r.Reloc(pkgbits.RelocObj))
+ r.p.objIdx(r.Reloc(pkgbits.SectionObj))
assert(r.Len() == 0)
}
@@ -67,7 +67,8 @@ type reader struct {
p *pkgReader
- dict *readerDict
+ dict *readerDict
+ delayed []func()
}
type readerDict struct {
@@ -84,14 +85,14 @@ type readerTypeBound struct {
boundIdx int
}
-func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader {
+func (pr *pkgReader) newReader(k pkgbits.SectionKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader {
return &reader{
Decoder: pr.NewDecoder(k, idx, marker),
p: pr,
}
}
-func (pr *pkgReader) tempReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader {
+func (pr *pkgReader) tempReader(k pkgbits.SectionKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader {
return &reader{
Decoder: pr.TempDecoder(k, idx, marker),
p: pr,
@@ -118,7 +119,7 @@ func (r *reader) pos() syntax.Pos {
}
func (r *reader) posBase() *syntax.PosBase {
- return r.p.posBaseIdx(r.Reloc(pkgbits.RelocPosBase))
+ return r.p.posBaseIdx(r.Reloc(pkgbits.SectionPosBase))
}
func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) *syntax.PosBase {
@@ -127,7 +128,7 @@ func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) *syntax.PosBase {
}
var b *syntax.PosBase
{
- r := pr.tempReader(pkgbits.RelocPosBase, idx, pkgbits.SyncPosBase)
+ r := pr.tempReader(pkgbits.SectionPosBase, idx, pkgbits.SyncPosBase)
filename := r.String()
@@ -150,7 +151,7 @@ func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) *syntax.PosBase {
func (r *reader) pkg() *types2.Package {
r.Sync(pkgbits.SyncPkg)
- return r.p.pkgIdx(r.Reloc(pkgbits.RelocPkg))
+ return r.p.pkgIdx(r.Reloc(pkgbits.SectionPkg))
}
func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types2.Package {
@@ -160,7 +161,7 @@ func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types2.Package {
return pkg
}
- pkg := pr.newReader(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef).doPkg()
+ pkg := pr.newReader(pkgbits.SectionPkg, idx, pkgbits.SyncPkgDef).doPkg()
pr.pkgs[idx] = pkg
return pkg
}
@@ -206,7 +207,7 @@ func (r *reader) typInfo() typeInfo {
if r.Bool() {
return typeInfo{idx: pkgbits.Index(r.Len()), derived: true}
}
- return typeInfo{idx: r.Reloc(pkgbits.RelocType), derived: false}
+ return typeInfo{idx: r.Reloc(pkgbits.SectionType), derived: false}
}
func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict) types2.Type {
@@ -225,7 +226,7 @@ func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict) types2.Type {
var typ types2.Type
{
- r := pr.tempReader(pkgbits.RelocType, idx, pkgbits.SyncTypeIdx)
+ r := pr.tempReader(pkgbits.SectionType, idx, pkgbits.SyncTypeIdx)
r.dict = dict
typ = r.doTyp()
@@ -376,7 +377,7 @@ func (r *reader) obj() (types2.Object, []types2.Type) {
assert(!r.Bool())
}
- pkg, name := r.p.objIdx(r.Reloc(pkgbits.RelocObj))
+ pkg, name := r.p.objIdx(r.Reloc(pkgbits.SectionObj))
obj := pkg.Scope().Lookup(name)
targs := make([]types2.Type, r.Len())
@@ -392,7 +393,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types2.Package, string) {
var objName string
var tag pkgbits.CodeObj
{
- rname := pr.tempReader(pkgbits.RelocName, idx, pkgbits.SyncObject1)
+ rname := pr.tempReader(pkgbits.SectionName, idx, pkgbits.SyncObject1)
objPkg, objName = rname.qualifiedIdent()
assert(objName != "")
@@ -409,7 +410,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types2.Package, string) {
objPkg.Scope().InsertLazy(objName, func() types2.Object {
dict := pr.objDictIdx(idx)
- r := pr.newReader(pkgbits.RelocObj, idx, pkgbits.SyncObject1)
+ r := pr.newReader(pkgbits.SectionObj, idx, pkgbits.SyncObject1)
r.dict = dict
switch tag {
@@ -420,7 +421,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types2.Package, string) {
pos := r.pos()
var tparams []*types2.TypeParam
if r.Version().Has(pkgbits.AliasTypeParamNames) {
- tparams = r.typeParamNames()
+ tparams = r.typeParamNames(false)
}
typ := r.typ()
return newAliasTypeName(pr.enableAlias, pos, objPkg, objName, typ, tparams)
@@ -433,28 +434,28 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types2.Package, string) {
case pkgbits.ObjFunc:
pos := r.pos()
- tparams := r.typeParamNames()
+ tparams := r.typeParamNames(false)
sig := r.signature(nil, nil, tparams)
return types2.NewFunc(pos, objPkg, objName, sig)
case pkgbits.ObjType:
pos := r.pos()
- return types2.NewTypeNameLazy(pos, objPkg, objName, func(named *types2.Named) (tparams []*types2.TypeParam, underlying types2.Type, methods []*types2.Func) {
- tparams = r.typeParamNames()
+ return types2.NewTypeNameLazy(pos, objPkg, objName, func(_ *types2.Named) ([]*types2.TypeParam, types2.Type, []*types2.Func, []func()) {
+ tparams := r.typeParamNames(true)
// TODO(mdempsky): Rewrite receiver types to underlying is an
// Interface? The go/types importer does this (I think because
// unit tests expected that), but cmd/compile doesn't care
// about it, so maybe we can avoid worrying about that here.
- underlying = r.typ().Underlying()
+ underlying := r.typ().Underlying()
- methods = make([]*types2.Func, r.Len())
+ methods := make([]*types2.Func, r.Len())
for i := range methods {
- methods[i] = r.method()
+ methods[i] = r.method(true)
}
- return
+ return tparams, underlying, methods, r.delayed
})
case pkgbits.ObjVar:
@@ -470,7 +471,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types2.Package, string) {
func (pr *pkgReader) objDictIdx(idx pkgbits.Index) *readerDict {
var dict readerDict
{
- r := pr.tempReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1)
+ r := pr.tempReader(pkgbits.SectionObjDict, idx, pkgbits.SyncObject1)
if implicits := r.Len(); implicits != 0 {
base.Fatalf("unexpected object with %v implicit type parameter(s)", implicits)
@@ -484,7 +485,7 @@ func (pr *pkgReader) objDictIdx(idx pkgbits.Index) *readerDict {
dict.derived = make([]derivedInfo, r.Len())
dict.derivedTypes = make([]types2.Type, len(dict.derived))
for i := range dict.derived {
- dict.derived[i] = derivedInfo{idx: r.Reloc(pkgbits.RelocType)}
+ dict.derived[i] = derivedInfo{idx: r.Reloc(pkgbits.SectionType)}
if r.Version().Has(pkgbits.DerivedInfoNeeded) {
assert(!r.Bool())
}
@@ -497,7 +498,7 @@ func (pr *pkgReader) objDictIdx(idx pkgbits.Index) *readerDict {
return &dict
}
-func (r *reader) typeParamNames() []*types2.TypeParam {
+func (r *reader) typeParamNames(isLazy bool) []*types2.TypeParam {
r.Sync(pkgbits.SyncTypeParamNames)
// Note: This code assumes it only processes objects without
@@ -523,19 +524,38 @@ func (r *reader) typeParamNames() []*types2.TypeParam {
r.dict.tparams[i] = types2.NewTypeParam(tname, nil)
}
- for i, bound := range r.dict.bounds {
- r.dict.tparams[i].SetConstraint(r.p.typIdx(bound, r.dict))
+ // Type parameters that are read by lazy loaders cannot have their
+ // constraints set eagerly; do them after loading (go.dev/issue/63285).
+ if isLazy {
+ // The reader dictionary will continue mutating before we have time
+ // to call delayed functions; must make a local copy of both the type
+ // parameters and their (unexpanded) constraints.
+ bounds := make([]types2.Type, len(r.dict.bounds))
+ for i, bound := range r.dict.bounds {
+ bounds[i] = r.p.typIdx(bound, r.dict)
+ }
+
+ tparams := r.dict.tparams
+ r.delayed = append(r.delayed, func() {
+ for i, bound := range bounds {
+ tparams[i].SetConstraint(bound)
+ }
+ })
+ } else {
+ for i, bound := range r.dict.bounds {
+ r.dict.tparams[i].SetConstraint(r.p.typIdx(bound, r.dict))
+ }
}
return r.dict.tparams
}
-func (r *reader) method() *types2.Func {
+func (r *reader) method(isLazy bool) *types2.Func {
r.Sync(pkgbits.SyncMethod)
pos := r.pos()
pkg, name := r.selector()
- rtparams := r.typeParamNames()
+ rtparams := r.typeParamNames(isLazy)
sig := r.signature(r.param(), rtparams, nil)
_ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go.
diff --git a/src/cmd/compile/internal/inline/inl.go b/src/cmd/compile/internal/inline/inl.go
index f298f69ec1..9f03f6404a 100644
--- a/src/cmd/compile/internal/inline/inl.go
+++ b/src/cmd/compile/internal/inline/inl.go
@@ -42,6 +42,7 @@ import (
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/pgo"
+ "cmd/internal/src"
)
// Inlining budget parameters, gathered in one place
@@ -169,19 +170,8 @@ func CanInlineFuncs(funcs []*ir.Func, profile *pgoir.Profile) {
}
ir.VisitFuncsBottomUp(funcs, func(funcs []*ir.Func, recursive bool) {
- numfns := numNonClosures(funcs)
-
for _, fn := range funcs {
- if !recursive || numfns > 1 {
- // We allow inlining if there is no
- // recursion, or the recursion cycle is
- // across more than one function.
- CanInline(fn, profile)
- } else {
- if base.Flag.LowerM > 1 && fn.OClosure == nil {
- fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(fn), fn.Nname)
- }
- }
+ CanInline(fn, profile)
if inlheur.Enabled() {
analyzeFuncProps(fn, profile)
}
@@ -464,6 +454,11 @@ opSwitch:
// generate code.
cheap = true
}
+ if strings.HasPrefix(fn, "EscapeNonString[") {
+ // internal/abi.EscapeNonString[T] is a compiler intrinsic
+ // implemented in the escape analysis phase.
+ cheap = true
+ }
case "internal/runtime/sys":
switch fn {
case "GetCallerPC", "GetCallerSP":
@@ -482,12 +477,6 @@ opSwitch:
case "panicrangestate":
cheap = true
}
- case "hash/maphash":
- if strings.HasPrefix(fn, "escapeForHash[") {
- // hash/maphash.escapeForHash[T] is a compiler intrinsic
- // implemented in the escape analysis phase.
- cheap = true
- }
}
}
// Special case for coverage counter updates; although
@@ -616,10 +605,7 @@ opSwitch:
v.budget -= inlineExtraPanicCost
case ir.ORECOVER:
- base.FatalfAt(n.Pos(), "ORECOVER missed typecheck")
- case ir.ORECOVERFP:
- // recover matches the argument frame pointer to find
- // the right panic value, so it needs an argument frame.
+ // TODO: maybe we could allow inlining of recover() now?
v.reason = "call to recover"
return true
@@ -752,6 +738,17 @@ opSwitch:
if n.X.Op() == ir.OINDEX && isIndexingCoverageCounter(n.X) {
return false
}
+
+ case ir.OSLICE, ir.OSLICEARR, ir.OSLICESTR, ir.OSLICE3, ir.OSLICE3ARR:
+ n := n.(*ir.SliceExpr)
+
+ // Ignore superfluous slicing.
+ if n.Low != nil && n.Low.Op() == ir.OLITERAL && ir.Int64Val(n.Low) == 0 {
+ v.budget++
+ }
+ if n.High != nil && n.High.Op() == ir.OLEN && n.High.(*ir.UnaryExpr).X == n.X {
+ v.budget += 2
+ }
}
v.budget--
@@ -811,10 +808,10 @@ func inlineCallCheck(callerfn *ir.Func, call *ir.CallExpr) (bool, bool) {
}
}
- // hash/maphash.escapeForHash[T] is a compiler intrinsic implemented
+ // internal/abi.EscapeNonString[T] is a compiler intrinsic implemented
// in the escape analysis phase.
- if fn := ir.StaticCalleeName(call.Fun); fn != nil && fn.Sym().Pkg.Path == "hash/maphash" &&
- strings.HasPrefix(fn.Sym().Name, "escapeForHash[") {
+ if fn := ir.StaticCalleeName(call.Fun); fn != nil && fn.Sym().Pkg.Path == "internal/abi" &&
+ strings.HasPrefix(fn.Sym().Name, "EscapeNonString[") {
return false, true
}
@@ -974,6 +971,16 @@ func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller, closureCal
return true, 0, metric, hot
}
+// parsePos returns all the inlining positions and the innermost position.
+func parsePos(pos src.XPos, posTmp []src.Pos) ([]src.Pos, src.Pos) {
+ ctxt := base.Ctxt
+ ctxt.AllPos(pos, func(p src.Pos) {
+ posTmp = append(posTmp, p)
+ })
+ l := len(posTmp) - 1
+ return posTmp[:l], posTmp[l]
+}
+
// canInlineCallExpr returns true if the call n from caller to callee
// can be inlined, plus the score computed for the call expr in question,
// and whether the callee is hot according to PGO.
@@ -1001,12 +1008,15 @@ func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCa
return false, 0, false
}
- if callee == callerfn {
- // Can't recursively inline a function into itself.
- if log && logopt.Enabled() {
- logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", fmt.Sprintf("recursive call to %s", ir.FuncName(callerfn)))
+ callees, calleeInner := parsePos(n.Pos(), make([]src.Pos, 0, 10))
+
+ for _, p := range callees {
+ if p.Line() == calleeInner.Line() && p.Col() == calleeInner.Col() && p.AbsFilename() == calleeInner.AbsFilename() {
+ if log && logopt.Enabled() {
+ logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", fmt.Sprintf("recursive call to %s", ir.FuncName(callerfn)))
+ }
+ return false, 0, false
}
- return false, 0, false
}
if base.Flag.Cfg.Instrumenting && types.IsNoInstrumentPkg(callee.Sym().Pkg) {
@@ -1114,12 +1124,18 @@ func mkinlcall(callerfn *ir.Func, n *ir.CallExpr, fn *ir.Func, bigCaller, closur
// Not a standard call.
return
}
- if n.Fun.Op() != ir.OCLOSURE {
- // Not a direct closure call.
+
+ var nf = n.Fun
+ // Skips ir.OCONVNOPs, see issue #73716.
+ for nf.Op() == ir.OCONVNOP {
+ nf = nf.(*ir.ConvExpr).X
+ }
+ if nf.Op() != ir.OCLOSURE {
+ // Not a direct closure call or one with type conversion.
return
}
- clo := n.Fun.(*ir.ClosureExpr)
+ clo := nf.(*ir.ClosureExpr)
if !clo.Func.IsClosure() {
// enqueueFunc will handle non closures anyways.
return
@@ -1206,17 +1222,6 @@ func pruneUnusedAutos(ll []*ir.Name, vis *hairyVisitor) []*ir.Name {
return s
}
-// numNonClosures returns the number of functions in list which are not closures.
-func numNonClosures(list []*ir.Func) int {
- count := 0
- for _, fn := range list {
- if fn.OClosure == nil {
- count++
- }
- }
- return count
-}
-
func doList(list []ir.Node, do func(ir.Node) bool) bool {
for _, x := range list {
if x != nil {
diff --git a/src/cmd/compile/internal/inline/inlheur/analyze_func_flags.go b/src/cmd/compile/internal/inline/inlheur/analyze_func_flags.go
index d86fd7d71b..22312e2241 100644
--- a/src/cmd/compile/internal/inline/inlheur/analyze_func_flags.go
+++ b/src/cmd/compile/internal/inline/inlheur/analyze_func_flags.go
@@ -335,7 +335,7 @@ func (ffa *funcFlagsAnalyzer) nodeVisitPost(n ir.Node) {
ir.OPRINTLN, ir.OPRINT, ir.OLABEL, ir.OCALLINTER, ir.ODEFER,
ir.OSEND, ir.ORECV, ir.OSELRECV2, ir.OGO, ir.OAPPEND, ir.OAS2DOTTYPE,
ir.OAS2MAPR, ir.OGETG, ir.ODELETE, ir.OINLMARK, ir.OAS2RECV,
- ir.OMIN, ir.OMAX, ir.OMAKE, ir.ORECOVERFP, ir.OGETCALLERSP:
+ ir.OMIN, ir.OMAX, ir.OMAKE, ir.OGETCALLERSP:
// these should all be benign/uninteresting
case ir.OTAILCALL, ir.OJUMPTABLE, ir.OTYPESW:
// don't expect to see these at all.
diff --git a/src/cmd/compile/internal/inline/inlheur/dumpscores_test.go b/src/cmd/compile/internal/inline/inlheur/dumpscores_test.go
index 438b70096f..2e1bcf98f9 100644
--- a/src/cmd/compile/internal/inline/inlheur/dumpscores_test.go
+++ b/src/cmd/compile/internal/inline/inlheur/dumpscores_test.go
@@ -105,5 +105,5 @@ func gatherInlCallSitesScoresForFile(t *testing.T, testcase string, td string) (
if err := os.WriteFile(dumpfile, out, 0666); err != nil {
return "", err
}
- return dumpfile, err
+ return dumpfile, nil
}
diff --git a/src/cmd/compile/internal/inline/inlheur/scoring.go b/src/cmd/compile/internal/inline/inlheur/scoring.go
index 28fa643132..1396c4d800 100644
--- a/src/cmd/compile/internal/inline/inlheur/scoring.go
+++ b/src/cmd/compile/internal/inline/inlheur/scoring.go
@@ -399,14 +399,6 @@ func LargestNegativeScoreAdjustment(fn *ir.Func, props *FuncProps) int {
return score
}
-// LargestPositiveScoreAdjustment tries to estimate the largest possible
-// positive score adjustment that could be applied to a given callsite.
-// At the moment we don't have very many positive score adjustments, so
-// this is just hard-coded, not table-driven.
-func LargestPositiveScoreAdjustment(fn *ir.Func) int {
- return adjValues[panicPathAdj] + adjValues[initFuncAdj]
-}
-
// callSiteTab contains entries for each call in the function
// currently being processed by InlineCalls; this variable will either
// be set to 'cstabCache' below (for non-inlinable routines) or to the
diff --git a/src/cmd/compile/internal/inline/interleaved/interleaved.go b/src/cmd/compile/internal/inline/interleaved/interleaved.go
index a35121517a..c83bbdb718 100644
--- a/src/cmd/compile/internal/inline/interleaved/interleaved.go
+++ b/src/cmd/compile/internal/inline/interleaved/interleaved.go
@@ -45,6 +45,8 @@ func DevirtualizeAndInlinePackage(pkg *ir.Package, profile *pgoir.Profile) {
inlState := make(map[*ir.Func]*inlClosureState)
calleeUseCounts := make(map[*ir.Func]int)
+ var state devirtualize.State
+
// Pre-process all the functions, adding parentheses around call sites and starting their "inl state".
for _, fn := range typecheck.Target.Funcs {
bigCaller := base.Flag.LowerL != 0 && inline.IsBigFunc(fn)
@@ -58,7 +60,7 @@ func DevirtualizeAndInlinePackage(pkg *ir.Package, profile *pgoir.Profile) {
// Do a first pass at counting call sites.
for i := range s.parens {
- s.resolve(i)
+ s.resolve(&state, i)
}
}
@@ -102,10 +104,11 @@ func DevirtualizeAndInlinePackage(pkg *ir.Package, profile *pgoir.Profile) {
for {
for i := l0; i < l1; i++ { // can't use "range parens" here
paren := s.parens[i]
- if new := s.edit(i); new != nil {
+ if origCall, inlinedCall := s.edit(&state, i); inlinedCall != nil {
// Update AST and recursively mark nodes.
- paren.X = new
- ir.EditChildren(new, s.mark) // mark may append to parens
+ paren.X = inlinedCall
+ ir.EditChildren(inlinedCall, s.mark) // mark may append to parens
+ state.InlinedCall(s.fn, origCall, inlinedCall)
done = false
}
}
@@ -114,7 +117,7 @@ func DevirtualizeAndInlinePackage(pkg *ir.Package, profile *pgoir.Profile) {
break
}
for i := l0; i < l1; i++ {
- s.resolve(i)
+ s.resolve(&state, i)
}
}
@@ -188,7 +191,7 @@ type inlClosureState struct {
// resolve attempts to resolve a call to a potentially inlineable callee
// and updates use counts on the callees. Returns the call site count
// for that callee.
-func (s *inlClosureState) resolve(i int) (*ir.Func, int) {
+func (s *inlClosureState) resolve(state *devirtualize.State, i int) (*ir.Func, int) {
p := s.parens[i]
if i < len(s.resolved) {
if callee := s.resolved[i]; callee != nil {
@@ -200,7 +203,7 @@ func (s *inlClosureState) resolve(i int) (*ir.Func, int) {
if !ok { // previously inlined
return nil, -1
}
- devirtualize.StaticCall(call)
+ devirtualize.StaticCall(state, call)
if callee := inline.InlineCallTarget(s.fn, call, s.profile); callee != nil {
for len(s.resolved) <= i {
s.resolved = append(s.resolved, nil)
@@ -213,23 +216,23 @@ func (s *inlClosureState) resolve(i int) (*ir.Func, int) {
return nil, 0
}
-func (s *inlClosureState) edit(i int) ir.Node {
+func (s *inlClosureState) edit(state *devirtualize.State, i int) (*ir.CallExpr, *ir.InlinedCallExpr) {
n := s.parens[i].X
call, ok := n.(*ir.CallExpr)
if !ok {
- return nil
+ return nil, nil
}
// This is redundant with earlier calls to
// resolve, but because things can change it
// must be re-checked.
- callee, count := s.resolve(i)
+ callee, count := s.resolve(state, i)
if count <= 0 {
- return nil
+ return nil, nil
}
if inlCall := inline.TryInlineCall(s.fn, call, s.bigCaller, s.profile, count == 1 && callee.ClosureParent != nil); inlCall != nil {
- return inlCall
+ return call, inlCall
}
- return nil
+ return nil, nil
}
// Mark inserts parentheses, and is called repeatedly.
@@ -253,7 +256,7 @@ func (s *inlClosureState) mark(n ir.Node) ir.Node {
if isTestingBLoop(n) {
// No inlining nor devirtualization performed on b.Loop body
- if base.Flag.LowerM > 1 {
+ if base.Flag.LowerM > 0 {
fmt.Printf("%v: skip inlining within testing.B.loop for %v\n", ir.Line(n), n)
}
// We still want to explore inlining opportunities in other parts of ForStmt.
@@ -338,16 +341,18 @@ func (s *inlClosureState) unparenthesize() {
// returns.
func (s *inlClosureState) fixpoint() bool {
changed := false
+ var state devirtualize.State
ir.WithFunc(s.fn, func() {
done := false
for !done {
done = true
for i := 0; i < len(s.parens); i++ { // can't use "range parens" here
paren := s.parens[i]
- if new := s.edit(i); new != nil {
+ if origCall, inlinedCall := s.edit(&state, i); inlinedCall != nil {
// Update AST and recursively mark nodes.
- paren.X = new
- ir.EditChildren(new, s.mark) // mark may append to parens
+ paren.X = inlinedCall
+ ir.EditChildren(inlinedCall, s.mark) // mark may append to parens
+ state.InlinedCall(s.fn, origCall, inlinedCall)
done = false
changed = true
}
diff --git a/src/cmd/compile/internal/ir/copy.go b/src/cmd/compile/internal/ir/copy.go
index d30f7bc688..4ec887d397 100644
--- a/src/cmd/compile/internal/ir/copy.go
+++ b/src/cmd/compile/internal/ir/copy.go
@@ -32,12 +32,3 @@ func DeepCopy(pos src.XPos, n Node) Node {
}
return edit(n)
}
-
-// DeepCopyList returns a list of deep copies (using DeepCopy) of the nodes in list.
-func DeepCopyList(pos src.XPos, list []Node) []Node {
- var out []Node
- for _, n := range list {
- out = append(out, DeepCopy(pos, n))
- }
- return out
-}
diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go
index 4a2e996569..7a75ff40f2 100644
--- a/src/cmd/compile/internal/ir/expr.go
+++ b/src/cmd/compile/internal/ir/expr.go
@@ -24,12 +24,12 @@ type Expr interface {
// A miniExpr is a miniNode with extra fields common to expressions.
// TODO(rsc): Once we are sure about the contents, compact the bools
// into a bit field and leave extra bits available for implementations
-// embedding miniExpr. Right now there are ~60 unused bits sitting here.
+// embedding miniExpr. Right now there are ~24 unused bits sitting here.
type miniExpr struct {
miniNode
+ flags bitset8
typ *types.Type
init Nodes // TODO(rsc): Don't require every Node to have an init
- flags bitset8
}
const (
@@ -213,7 +213,7 @@ func (n *CallExpr) SetOp(op Op) {
ODELETE,
OGETG, OGETCALLERSP,
OMAKE, OMAX, OMIN, OPRINT, OPRINTLN,
- ORECOVER, ORECOVERFP:
+ ORECOVER:
n.op = op
}
}
@@ -617,7 +617,7 @@ func (o Op) IsSlice3() bool {
return false
}
-// A SliceHeader expression constructs a slice header from its parts.
+// A SliceHeaderExpr constructs a slice header from its parts.
type SliceHeaderExpr struct {
miniExpr
Ptr Node
@@ -665,7 +665,7 @@ func NewStarExpr(pos src.XPos, x Node) *StarExpr {
func (n *StarExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 }
func (n *StarExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }
-// A TypeAssertionExpr is a selector expression X.(Type).
+// A TypeAssertExpr is a selector expression X.(Type).
// Before type-checking, the type is Ntype.
type TypeAssertExpr struct {
miniExpr
@@ -677,6 +677,11 @@ type TypeAssertExpr struct {
// An internal/abi.TypeAssert descriptor to pass to the runtime.
Descriptor *obj.LSym
+
+ // When set to true, if this assert would panic, then use a nil pointer panic
+ // instead of an interface conversion panic.
+ // It must not be set for type asserts using the commaok form.
+ UseNilPanic bool
}
func NewTypeAssertExpr(pos src.XPos, x Node, typ *types.Type) *TypeAssertExpr {
@@ -854,6 +859,10 @@ func IsAddressable(n Node) bool {
//
// calling StaticValue on the "int(y)" expression returns the outer
// "g()" expression.
+//
+// NOTE: StaticValue can return a result with a different type than
+// n's type because it can traverse through OCONVNOP operations.
+// TODO: consider reapplying OCONVNOP operations to the result. See https://go.dev/cl/676517.
func StaticValue(n Node) Node {
for {
switch n1 := n.(type) {
@@ -908,12 +917,12 @@ FindRHS:
break FindRHS
}
}
- base.Fatalf("%v missing from LHS of %v", n, defn)
+ base.FatalfAt(defn.Pos(), "%v missing from LHS of %v", n, defn)
default:
return nil
}
if rhs == nil {
- base.Fatalf("RHS is nil: %v", defn)
+ base.FatalfAt(defn.Pos(), "RHS is nil: %v", defn)
}
if Reassigned(n) {
diff --git a/src/cmd/compile/internal/ir/fmt.go b/src/cmd/compile/internal/ir/fmt.go
index 31c610348b..ae4ff62652 100644
--- a/src/cmd/compile/internal/ir/fmt.go
+++ b/src/cmd/compile/internal/ir/fmt.go
@@ -1194,7 +1194,7 @@ func dumpNode(w io.Writer, n Node, depth int) {
}
}
-var nodeType = reflect.TypeOf((*Node)(nil)).Elem()
+var nodeType = reflect.TypeFor[Node]()
func dumpNodes(w io.Writer, list Nodes, depth int) {
if len(list) == 0 {
diff --git a/src/cmd/compile/internal/ir/func.go b/src/cmd/compile/internal/ir/func.go
index 6354da3556..668537c90e 100644
--- a/src/cmd/compile/internal/ir/func.go
+++ b/src/cmd/compile/internal/ir/func.go
@@ -627,3 +627,18 @@ func (fn *Func) DeclareParams(setNname bool) {
declareParams(params, PPARAM, "~p", 0)
declareParams(results, PPARAMOUT, "~r", len(params))
}
+
+// ContainsClosure reports whether c is a closure contained within f.
+func ContainsClosure(f, c *Func) bool {
+ // Common cases.
+ if f == c || c.OClosure == nil {
+ return false
+ }
+
+ for p := c.ClosureParent; p != nil; p = p.ClosureParent {
+ if p == f {
+ return true
+ }
+ }
+ return false
+}
diff --git a/src/cmd/compile/internal/ir/mini.go b/src/cmd/compile/internal/ir/mini.go
index 52c622df23..6c91560e52 100644
--- a/src/cmd/compile/internal/ir/mini.go
+++ b/src/cmd/compile/internal/ir/mini.go
@@ -15,7 +15,7 @@ import (
// A miniNode is a minimal node implementation,
// meant to be embedded as the first field in a larger node implementation,
-// at a cost of 8 bytes.
+// at a cost of 12 bytes.
//
// A miniNode is NOT a valid Node by itself: the embedding struct
// must at the least provide:
@@ -28,21 +28,12 @@ import (
// for more useful panic messages when invalid methods are called,
// instead of implementing Op itself.
type miniNode struct {
- pos src.XPos // uint32
- op Op // uint8
+ pos src.XPos
+ op Op
bits bitset8
esc uint16
}
-// posOr returns pos if known, or else n.pos.
-// For use in DeepCopy.
-func (n *miniNode) posOr(pos src.XPos) src.XPos {
- if pos.IsKnown() {
- return pos
- }
- return n.pos
-}
-
// op can be read, but not written.
// An embedding implementation can provide a SetOp if desired.
// (The panicking SetOp is with the other panics below.)
diff --git a/src/cmd/compile/internal/ir/mknode.go b/src/cmd/compile/internal/ir/mknode.go
index e5df481a2d..2f439b9996 100644
--- a/src/cmd/compile/internal/ir/mknode.go
+++ b/src/cmd/compile/internal/ir/mknode.go
@@ -258,6 +258,7 @@ func processType(t *ast.TypeSpec) {
var doChildrenWithHiddenBody strings.Builder
var editChildrenBody strings.Builder
var editChildrenWithHiddenBody strings.Builder
+ var hasHidden bool
for _, f := range fields {
names := f.Names
ft := f.Type
@@ -309,6 +310,7 @@ func processType(t *ast.TypeSpec) {
"if n.%s != nil {\nn.%s = edit(n.%s).(%s%s)\n}\n", name, name, name, ptr, ft)
}
if hidden {
+ hasHidden = true
continue
}
if isSlice {
@@ -327,19 +329,27 @@ func processType(t *ast.TypeSpec) {
}
fmt.Fprintf(&buf, "func (n *%s) copy() Node {\nc := *n\n", name)
buf.WriteString(copyBody.String())
- fmt.Fprintf(&buf, "return &c\n}\n")
+ buf.WriteString("return &c\n}\n")
fmt.Fprintf(&buf, "func (n *%s) doChildren(do func(Node) bool) bool {\n", name)
buf.WriteString(doChildrenBody.String())
- fmt.Fprintf(&buf, "return false\n}\n")
+ buf.WriteString("return false\n}\n")
fmt.Fprintf(&buf, "func (n *%s) doChildrenWithHidden(do func(Node) bool) bool {\n", name)
- buf.WriteString(doChildrenWithHiddenBody.String())
- fmt.Fprintf(&buf, "return false\n}\n")
+ if hasHidden {
+ buf.WriteString(doChildrenWithHiddenBody.String())
+ buf.WriteString("return false\n}\n")
+ } else {
+ buf.WriteString("return n.doChildren(do)\n}\n")
+ }
fmt.Fprintf(&buf, "func (n *%s) editChildren(edit func(Node) Node) {\n", name)
buf.WriteString(editChildrenBody.String())
- fmt.Fprintf(&buf, "}\n")
+ buf.WriteString("}\n")
fmt.Fprintf(&buf, "func (n *%s) editChildrenWithHidden(edit func(Node) Node) {\n", name)
- buf.WriteString(editChildrenWithHiddenBody.String())
- fmt.Fprintf(&buf, "}\n")
+ if hasHidden {
+ buf.WriteString(editChildrenWithHiddenBody.String())
+ } else {
+ buf.WriteString("n.editChildren(edit)\n")
+ }
+ buf.WriteString("}\n")
}
func generateHelpers() {
diff --git a/src/cmd/compile/internal/ir/node.go b/src/cmd/compile/internal/ir/node.go
index 058ada5ac3..8c61bb6ed5 100644
--- a/src/cmd/compile/internal/ir/node.go
+++ b/src/cmd/compile/internal/ir/node.go
@@ -215,7 +215,7 @@ const (
ORSH // X >> Y
OAND // X & Y
OANDNOT // X &^ Y
- ONEW // new(X); corresponds to calls to new in source code
+ ONEW // new(X); corresponds to calls to new(T) in source code
ONOT // !X
OBITNOT // ^X
OPLUS // +X
@@ -234,7 +234,6 @@ const (
OSLICEHEADER // sliceheader{Ptr, Len, Cap} (Ptr is unsafe.Pointer, Len is length, Cap is capacity)
OSTRINGHEADER // stringheader{Ptr, Len} (Ptr is unsafe.Pointer, Len is length)
ORECOVER // recover()
- ORECOVERFP // recover(Args) w/ explicit FP argument
ORECV // <-X
ORUNESTR // Type(X) (Type is string, X is rune)
OSELRECV2 // like OAS2: Lhs = Rhs where len(Lhs)=2, len(Rhs)=1, Rhs[0].Op = ORECV (appears as .Var of OCASE)
diff --git a/src/cmd/compile/internal/ir/node_gen.go b/src/cmd/compile/internal/ir/node_gen.go
index 026acbf9dd..2221045c93 100644
--- a/src/cmd/compile/internal/ir/node_gen.go
+++ b/src/cmd/compile/internal/ir/node_gen.go
@@ -24,16 +24,7 @@ func (n *AddStringExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *AddStringExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if doNodes(n.List, do) {
- return true
- }
- if n.Prealloc != nil && do(n.Prealloc) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *AddStringExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -43,11 +34,7 @@ func (n *AddStringExpr) editChildren(edit func(Node) Node) {
}
}
func (n *AddStringExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- editNodes(n.List, edit)
- if n.Prealloc != nil {
- n.Prealloc = edit(n.Prealloc).(*Name)
- }
+ n.editChildren(edit)
}
func (n *AddrExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -69,16 +56,7 @@ func (n *AddrExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *AddrExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.X != nil && do(n.X) {
- return true
- }
- if n.Prealloc != nil && do(n.Prealloc) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *AddrExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -90,13 +68,7 @@ func (n *AddrExpr) editChildren(edit func(Node) Node) {
}
}
func (n *AddrExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.X != nil {
- n.X = edit(n.X).(Node)
- }
- if n.Prealloc != nil {
- n.Prealloc = edit(n.Prealloc).(*Name)
- }
+ n.editChildren(edit)
}
func (n *AssignListStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -120,16 +92,7 @@ func (n *AssignListStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *AssignListStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if doNodes(n.Lhs, do) {
- return true
- }
- if doNodes(n.Rhs, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *AssignListStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -137,9 +100,7 @@ func (n *AssignListStmt) editChildren(edit func(Node) Node) {
editNodes(n.Rhs, edit)
}
func (n *AssignListStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- editNodes(n.Lhs, edit)
- editNodes(n.Rhs, edit)
+ n.editChildren(edit)
}
func (n *AssignOpStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -161,16 +122,7 @@ func (n *AssignOpStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *AssignOpStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.X != nil && do(n.X) {
- return true
- }
- if n.Y != nil && do(n.Y) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *AssignOpStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -182,13 +134,7 @@ func (n *AssignOpStmt) editChildren(edit func(Node) Node) {
}
}
func (n *AssignOpStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.X != nil {
- n.X = edit(n.X).(Node)
- }
- if n.Y != nil {
- n.Y = edit(n.Y).(Node)
- }
+ n.editChildren(edit)
}
func (n *AssignStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -210,16 +156,7 @@ func (n *AssignStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *AssignStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.X != nil && do(n.X) {
- return true
- }
- if n.Y != nil && do(n.Y) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *AssignStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -231,13 +168,7 @@ func (n *AssignStmt) editChildren(edit func(Node) Node) {
}
}
func (n *AssignStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.X != nil {
- n.X = edit(n.X).(Node)
- }
- if n.Y != nil {
- n.Y = edit(n.Y).(Node)
- }
+ n.editChildren(edit)
}
func (n *BasicLit) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -253,16 +184,13 @@ func (n *BasicLit) doChildren(do func(Node) bool) bool {
return false
}
func (n *BasicLit) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *BasicLit) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
}
func (n *BasicLit) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
+ n.editChildren(edit)
}
func (n *BinaryExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -337,21 +265,14 @@ func (n *BlockStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *BlockStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if doNodes(n.List, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *BlockStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
editNodes(n.List, edit)
}
func (n *BlockStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- editNodes(n.List, edit)
+ n.editChildren(edit)
}
func (n *BranchStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -367,16 +288,13 @@ func (n *BranchStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *BranchStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *BranchStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
}
func (n *BranchStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
+ n.editChildren(edit)
}
func (n *CallExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -480,22 +398,7 @@ func (n *CaseClause) doChildren(do func(Node) bool) bool {
return false
}
func (n *CaseClause) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.Var != nil && do(n.Var) {
- return true
- }
- if doNodes(n.List, do) {
- return true
- }
- if doNodes(n.RTypes, do) {
- return true
- }
- if doNodes(n.Body, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *CaseClause) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -507,13 +410,7 @@ func (n *CaseClause) editChildren(edit func(Node) Node) {
editNodes(n.Body, edit)
}
func (n *CaseClause) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.Var != nil {
- n.Var = edit(n.Var).(*Name)
- }
- editNodes(n.List, edit)
- editNodes(n.RTypes, edit)
- editNodes(n.Body, edit)
+ n.editChildren(edit)
}
func (n *ClosureExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -532,13 +429,7 @@ func (n *ClosureExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *ClosureExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.Prealloc != nil && do(n.Prealloc) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *ClosureExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -547,10 +438,7 @@ func (n *ClosureExpr) editChildren(edit func(Node) Node) {
}
}
func (n *ClosureExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.Prealloc != nil {
- n.Prealloc = edit(n.Prealloc).(*Name)
- }
+ n.editChildren(edit)
}
func (n *CommClause) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -573,16 +461,7 @@ func (n *CommClause) doChildren(do func(Node) bool) bool {
return false
}
func (n *CommClause) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.Comm != nil && do(n.Comm) {
- return true
- }
- if doNodes(n.Body, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *CommClause) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -592,11 +471,7 @@ func (n *CommClause) editChildren(edit func(Node) Node) {
editNodes(n.Body, edit)
}
func (n *CommClause) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.Comm != nil {
- n.Comm = edit(n.Comm).(Node)
- }
- editNodes(n.Body, edit)
+ n.editChildren(edit)
}
func (n *CompLitExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -724,10 +599,7 @@ func (n *Decl) doChildren(do func(Node) bool) bool {
return false
}
func (n *Decl) doChildrenWithHidden(do func(Node) bool) bool {
- if n.X != nil && do(n.X) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *Decl) editChildren(edit func(Node) Node) {
if n.X != nil {
@@ -735,9 +607,7 @@ func (n *Decl) editChildren(edit func(Node) Node) {
}
}
func (n *Decl) editChildrenWithHidden(edit func(Node) Node) {
- if n.X != nil {
- n.X = edit(n.X).(*Name)
- }
+ n.editChildren(edit)
}
func (n *DynamicType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -759,16 +629,7 @@ func (n *DynamicType) doChildren(do func(Node) bool) bool {
return false
}
func (n *DynamicType) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.RType != nil && do(n.RType) {
- return true
- }
- if n.ITab != nil && do(n.ITab) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *DynamicType) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -780,13 +641,7 @@ func (n *DynamicType) editChildren(edit func(Node) Node) {
}
}
func (n *DynamicType) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.RType != nil {
- n.RType = edit(n.RType).(Node)
- }
- if n.ITab != nil {
- n.ITab = edit(n.ITab).(Node)
- }
+ n.editChildren(edit)
}
func (n *DynamicTypeAssertExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -814,22 +669,7 @@ func (n *DynamicTypeAssertExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *DynamicTypeAssertExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.X != nil && do(n.X) {
- return true
- }
- if n.SrcRType != nil && do(n.SrcRType) {
- return true
- }
- if n.RType != nil && do(n.RType) {
- return true
- }
- if n.ITab != nil && do(n.ITab) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *DynamicTypeAssertExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -847,19 +687,7 @@ func (n *DynamicTypeAssertExpr) editChildren(edit func(Node) Node) {
}
}
func (n *DynamicTypeAssertExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.X != nil {
- n.X = edit(n.X).(Node)
- }
- if n.SrcRType != nil {
- n.SrcRType = edit(n.SrcRType).(Node)
- }
- if n.RType != nil {
- n.RType = edit(n.RType).(Node)
- }
- if n.ITab != nil {
- n.ITab = edit(n.ITab).(Node)
- }
+ n.editChildren(edit)
}
func (n *ForStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -885,19 +713,7 @@ func (n *ForStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *ForStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.Cond != nil && do(n.Cond) {
- return true
- }
- if n.Post != nil && do(n.Post) {
- return true
- }
- if doNodes(n.Body, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *ForStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -910,14 +726,7 @@ func (n *ForStmt) editChildren(edit func(Node) Node) {
editNodes(n.Body, edit)
}
func (n *ForStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.Cond != nil {
- n.Cond = edit(n.Cond).(Node)
- }
- if n.Post != nil {
- n.Post = edit(n.Post).(Node)
- }
- editNodes(n.Body, edit)
+ n.editChildren(edit)
}
func (n *Func) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -941,16 +750,7 @@ func (n *GoDeferStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *GoDeferStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.Call != nil && do(n.Call) {
- return true
- }
- if n.DeferAt != nil && do(n.DeferAt) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *GoDeferStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -962,13 +762,7 @@ func (n *GoDeferStmt) editChildren(edit func(Node) Node) {
}
}
func (n *GoDeferStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.Call != nil {
- n.Call = edit(n.Call).(Node)
- }
- if n.DeferAt != nil {
- n.DeferAt = edit(n.DeferAt).(Expr)
- }
+ n.editChildren(edit)
}
func (n *Ident) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -984,16 +778,13 @@ func (n *Ident) doChildren(do func(Node) bool) bool {
return false
}
func (n *Ident) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *Ident) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
}
func (n *Ident) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
+ n.editChildren(edit)
}
func (n *IfStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1020,19 +811,7 @@ func (n *IfStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *IfStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.Cond != nil && do(n.Cond) {
- return true
- }
- if doNodes(n.Body, do) {
- return true
- }
- if doNodes(n.Else, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *IfStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -1043,12 +822,7 @@ func (n *IfStmt) editChildren(edit func(Node) Node) {
editNodes(n.Else, edit)
}
func (n *IfStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.Cond != nil {
- n.Cond = edit(n.Cond).(Node)
- }
- editNodes(n.Body, edit)
- editNodes(n.Else, edit)
+ n.editChildren(edit)
}
func (n *IndexExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1119,16 +893,13 @@ func (n *InlineMarkStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *InlineMarkStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *InlineMarkStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
}
func (n *InlineMarkStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
+ n.editChildren(edit)
}
func (n *InlinedCallExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1152,16 +923,7 @@ func (n *InlinedCallExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *InlinedCallExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if doNodes(n.Body, do) {
- return true
- }
- if doNodes(n.ReturnVars, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *InlinedCallExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -1169,9 +931,7 @@ func (n *InlinedCallExpr) editChildren(edit func(Node) Node) {
editNodes(n.ReturnVars, edit)
}
func (n *InlinedCallExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- editNodes(n.Body, edit)
- editNodes(n.ReturnVars, edit)
+ n.editChildren(edit)
}
func (n *InterfaceSwitchStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1199,22 +959,7 @@ func (n *InterfaceSwitchStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *InterfaceSwitchStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.Case != nil && do(n.Case) {
- return true
- }
- if n.Itab != nil && do(n.Itab) {
- return true
- }
- if n.RuntimeType != nil && do(n.RuntimeType) {
- return true
- }
- if n.Hash != nil && do(n.Hash) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *InterfaceSwitchStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -1232,19 +977,7 @@ func (n *InterfaceSwitchStmt) editChildren(edit func(Node) Node) {
}
}
func (n *InterfaceSwitchStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.Case != nil {
- n.Case = edit(n.Case).(Node)
- }
- if n.Itab != nil {
- n.Itab = edit(n.Itab).(Node)
- }
- if n.RuntimeType != nil {
- n.RuntimeType = edit(n.RuntimeType).(Node)
- }
- if n.Hash != nil {
- n.Hash = edit(n.Hash).(Node)
- }
+ n.editChildren(edit)
}
func (n *JumpTableStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1263,13 +996,7 @@ func (n *JumpTableStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *JumpTableStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.Idx != nil && do(n.Idx) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *JumpTableStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -1278,10 +1005,7 @@ func (n *JumpTableStmt) editChildren(edit func(Node) Node) {
}
}
func (n *JumpTableStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.Idx != nil {
- n.Idx = edit(n.Idx).(Node)
- }
+ n.editChildren(edit)
}
func (n *KeyExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1303,16 +1027,7 @@ func (n *KeyExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *KeyExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.Key != nil && do(n.Key) {
- return true
- }
- if n.Value != nil && do(n.Value) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *KeyExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -1324,13 +1039,7 @@ func (n *KeyExpr) editChildren(edit func(Node) Node) {
}
}
func (n *KeyExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.Key != nil {
- n.Key = edit(n.Key).(Node)
- }
- if n.Value != nil {
- n.Value = edit(n.Value).(Node)
- }
+ n.editChildren(edit)
}
func (n *LabelStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1346,16 +1055,13 @@ func (n *LabelStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *LabelStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *LabelStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
}
func (n *LabelStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
+ n.editChildren(edit)
}
func (n *LinksymOffsetExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1371,16 +1077,13 @@ func (n *LinksymOffsetExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *LinksymOffsetExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *LinksymOffsetExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
}
func (n *LinksymOffsetExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
+ n.editChildren(edit)
}
func (n *LogicalExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1402,16 +1105,7 @@ func (n *LogicalExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *LogicalExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.X != nil && do(n.X) {
- return true
- }
- if n.Y != nil && do(n.Y) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *LogicalExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -1423,13 +1117,7 @@ func (n *LogicalExpr) editChildren(edit func(Node) Node) {
}
}
func (n *LogicalExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.X != nil {
- n.X = edit(n.X).(Node)
- }
- if n.Y != nil {
- n.Y = edit(n.Y).(Node)
- }
+ n.editChildren(edit)
}
func (n *MakeExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1502,16 +1190,13 @@ func (n *NilExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *NilExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *NilExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
}
func (n *NilExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
+ n.editChildren(edit)
}
func (n *ParenExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1530,13 +1215,7 @@ func (n *ParenExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *ParenExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.X != nil && do(n.X) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *ParenExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -1545,10 +1224,7 @@ func (n *ParenExpr) editChildren(edit func(Node) Node) {
}
}
func (n *ParenExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.X != nil {
- n.X = edit(n.X).(Node)
- }
+ n.editChildren(edit)
}
func (n *RangeStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1676,16 +1352,13 @@ func (n *ResultExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *ResultExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *ResultExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
}
func (n *ResultExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
+ n.editChildren(edit)
}
func (n *ReturnStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1705,21 +1378,14 @@ func (n *ReturnStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *ReturnStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if doNodes(n.Results, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *ReturnStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
editNodes(n.Results, edit)
}
func (n *ReturnStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- editNodes(n.Results, edit)
+ n.editChildren(edit)
}
func (n *SelectStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1743,16 +1409,7 @@ func (n *SelectStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *SelectStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if doCommClauses(n.Cases, do) {
- return true
- }
- if doNodes(n.Compiled, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *SelectStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -1760,9 +1417,7 @@ func (n *SelectStmt) editChildren(edit func(Node) Node) {
editNodes(n.Compiled, edit)
}
func (n *SelectStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- editCommClauses(n.Cases, edit)
- editNodes(n.Compiled, edit)
+ n.editChildren(edit)
}
func (n *SelectorExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1784,16 +1439,7 @@ func (n *SelectorExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *SelectorExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.X != nil && do(n.X) {
- return true
- }
- if n.Prealloc != nil && do(n.Prealloc) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *SelectorExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -1805,13 +1451,7 @@ func (n *SelectorExpr) editChildren(edit func(Node) Node) {
}
}
func (n *SelectorExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.X != nil {
- n.X = edit(n.X).(Node)
- }
- if n.Prealloc != nil {
- n.Prealloc = edit(n.Prealloc).(*Name)
- }
+ n.editChildren(edit)
}
func (n *SendStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1833,16 +1473,7 @@ func (n *SendStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *SendStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.Chan != nil && do(n.Chan) {
- return true
- }
- if n.Value != nil && do(n.Value) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *SendStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -1854,13 +1485,7 @@ func (n *SendStmt) editChildren(edit func(Node) Node) {
}
}
func (n *SendStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.Chan != nil {
- n.Chan = edit(n.Chan).(Node)
- }
- if n.Value != nil {
- n.Value = edit(n.Value).(Node)
- }
+ n.editChildren(edit)
}
func (n *SliceExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1888,22 +1513,7 @@ func (n *SliceExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *SliceExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.X != nil && do(n.X) {
- return true
- }
- if n.Low != nil && do(n.Low) {
- return true
- }
- if n.High != nil && do(n.High) {
- return true
- }
- if n.Max != nil && do(n.Max) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *SliceExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -1921,19 +1531,7 @@ func (n *SliceExpr) editChildren(edit func(Node) Node) {
}
}
func (n *SliceExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.X != nil {
- n.X = edit(n.X).(Node)
- }
- if n.Low != nil {
- n.Low = edit(n.Low).(Node)
- }
- if n.High != nil {
- n.High = edit(n.High).(Node)
- }
- if n.Max != nil {
- n.Max = edit(n.Max).(Node)
- }
+ n.editChildren(edit)
}
func (n *SliceHeaderExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1958,19 +1556,7 @@ func (n *SliceHeaderExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *SliceHeaderExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.Ptr != nil && do(n.Ptr) {
- return true
- }
- if n.Len != nil && do(n.Len) {
- return true
- }
- if n.Cap != nil && do(n.Cap) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *SliceHeaderExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -1985,16 +1571,7 @@ func (n *SliceHeaderExpr) editChildren(edit func(Node) Node) {
}
}
func (n *SliceHeaderExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.Ptr != nil {
- n.Ptr = edit(n.Ptr).(Node)
- }
- if n.Len != nil {
- n.Len = edit(n.Len).(Node)
- }
- if n.Cap != nil {
- n.Cap = edit(n.Cap).(Node)
- }
+ n.editChildren(edit)
}
func (n *StarExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -2013,13 +1590,7 @@ func (n *StarExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *StarExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.X != nil && do(n.X) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *StarExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -2028,10 +1599,7 @@ func (n *StarExpr) editChildren(edit func(Node) Node) {
}
}
func (n *StarExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.X != nil {
- n.X = edit(n.X).(Node)
- }
+ n.editChildren(edit)
}
func (n *StringHeaderExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -2053,16 +1621,7 @@ func (n *StringHeaderExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *StringHeaderExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.Ptr != nil && do(n.Ptr) {
- return true
- }
- if n.Len != nil && do(n.Len) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *StringHeaderExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -2074,13 +1633,7 @@ func (n *StringHeaderExpr) editChildren(edit func(Node) Node) {
}
}
func (n *StringHeaderExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.Ptr != nil {
- n.Ptr = edit(n.Ptr).(Node)
- }
- if n.Len != nil {
- n.Len = edit(n.Len).(Node)
- }
+ n.editChildren(edit)
}
func (n *StructKeyExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -2099,13 +1652,7 @@ func (n *StructKeyExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *StructKeyExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.Value != nil && do(n.Value) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *StructKeyExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -2114,10 +1661,7 @@ func (n *StructKeyExpr) editChildren(edit func(Node) Node) {
}
}
func (n *StructKeyExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.Value != nil {
- n.Value = edit(n.Value).(Node)
- }
+ n.editChildren(edit)
}
func (n *SwitchStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -2144,19 +1688,7 @@ func (n *SwitchStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *SwitchStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.Tag != nil && do(n.Tag) {
- return true
- }
- if doCaseClauses(n.Cases, do) {
- return true
- }
- if doNodes(n.Compiled, do) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *SwitchStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -2167,12 +1699,7 @@ func (n *SwitchStmt) editChildren(edit func(Node) Node) {
editNodes(n.Compiled, edit)
}
func (n *SwitchStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.Tag != nil {
- n.Tag = edit(n.Tag).(Node)
- }
- editCaseClauses(n.Cases, edit)
- editNodes(n.Compiled, edit)
+ n.editChildren(edit)
}
func (n *TailCallStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -2191,13 +1718,7 @@ func (n *TailCallStmt) doChildren(do func(Node) bool) bool {
return false
}
func (n *TailCallStmt) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.Call != nil && do(n.Call) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *TailCallStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -2206,10 +1727,7 @@ func (n *TailCallStmt) editChildren(edit func(Node) Node) {
}
}
func (n *TailCallStmt) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.Call != nil {
- n.Call = edit(n.Call).(*CallExpr)
- }
+ n.editChildren(edit)
}
func (n *TypeAssertExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -2270,13 +1788,7 @@ func (n *TypeSwitchGuard) doChildren(do func(Node) bool) bool {
return false
}
func (n *TypeSwitchGuard) doChildrenWithHidden(do func(Node) bool) bool {
- if n.Tag != nil && do(n.Tag) {
- return true
- }
- if n.X != nil && do(n.X) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *TypeSwitchGuard) editChildren(edit func(Node) Node) {
if n.Tag != nil {
@@ -2287,12 +1799,7 @@ func (n *TypeSwitchGuard) editChildren(edit func(Node) Node) {
}
}
func (n *TypeSwitchGuard) editChildrenWithHidden(edit func(Node) Node) {
- if n.Tag != nil {
- n.Tag = edit(n.Tag).(*Ident)
- }
- if n.X != nil {
- n.X = edit(n.X).(Node)
- }
+ n.editChildren(edit)
}
func (n *UnaryExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -2311,13 +1818,7 @@ func (n *UnaryExpr) doChildren(do func(Node) bool) bool {
return false
}
func (n *UnaryExpr) doChildrenWithHidden(do func(Node) bool) bool {
- if doNodes(n.init, do) {
- return true
- }
- if n.X != nil && do(n.X) {
- return true
- }
- return false
+ return n.doChildren(do)
}
func (n *UnaryExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
@@ -2326,10 +1827,7 @@ func (n *UnaryExpr) editChildren(edit func(Node) Node) {
}
}
func (n *UnaryExpr) editChildrenWithHidden(edit func(Node) Node) {
- editNodes(n.init, edit)
- if n.X != nil {
- n.X = edit(n.X).(Node)
- }
+ n.editChildren(edit)
}
func (n *typeNode) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -2341,11 +1839,12 @@ func (n *typeNode) doChildren(do func(Node) bool) bool {
return false
}
func (n *typeNode) doChildrenWithHidden(do func(Node) bool) bool {
- return false
+ return n.doChildren(do)
}
func (n *typeNode) editChildren(edit func(Node) Node) {
}
func (n *typeNode) editChildrenWithHidden(edit func(Node) Node) {
+ n.editChildren(edit)
}
func copyCaseClauses(list []*CaseClause) []*CaseClause {
diff --git a/src/cmd/compile/internal/ir/op_string.go b/src/cmd/compile/internal/ir/op_string.go
index a1806d1349..7494beee4c 100644
--- a/src/cmd/compile/internal/ir/op_string.go
+++ b/src/cmd/compile/internal/ir/op_string.go
@@ -108,62 +108,61 @@ func _() {
_ = x[OSLICEHEADER-97]
_ = x[OSTRINGHEADER-98]
_ = x[ORECOVER-99]
- _ = x[ORECOVERFP-100]
- _ = x[ORECV-101]
- _ = x[ORUNESTR-102]
- _ = x[OSELRECV2-103]
- _ = x[OMIN-104]
- _ = x[OMAX-105]
- _ = x[OREAL-106]
- _ = x[OIMAG-107]
- _ = x[OCOMPLEX-108]
- _ = x[OUNSAFEADD-109]
- _ = x[OUNSAFESLICE-110]
- _ = x[OUNSAFESLICEDATA-111]
- _ = x[OUNSAFESTRING-112]
- _ = x[OUNSAFESTRINGDATA-113]
- _ = x[OMETHEXPR-114]
- _ = x[OMETHVALUE-115]
- _ = x[OBLOCK-116]
- _ = x[OBREAK-117]
- _ = x[OCASE-118]
- _ = x[OCONTINUE-119]
- _ = x[ODEFER-120]
- _ = x[OFALL-121]
- _ = x[OFOR-122]
- _ = x[OGOTO-123]
- _ = x[OIF-124]
- _ = x[OLABEL-125]
- _ = x[OGO-126]
- _ = x[ORANGE-127]
- _ = x[ORETURN-128]
- _ = x[OSELECT-129]
- _ = x[OSWITCH-130]
- _ = x[OTYPESW-131]
- _ = x[OINLCALL-132]
- _ = x[OMAKEFACE-133]
- _ = x[OITAB-134]
- _ = x[OIDATA-135]
- _ = x[OSPTR-136]
- _ = x[OCFUNC-137]
- _ = x[OCHECKNIL-138]
- _ = x[ORESULT-139]
- _ = x[OINLMARK-140]
- _ = x[OLINKSYMOFFSET-141]
- _ = x[OJUMPTABLE-142]
- _ = x[OINTERFACESWITCH-143]
- _ = x[ODYNAMICDOTTYPE-144]
- _ = x[ODYNAMICDOTTYPE2-145]
- _ = x[ODYNAMICTYPE-146]
- _ = x[OTAILCALL-147]
- _ = x[OGETG-148]
- _ = x[OGETCALLERSP-149]
- _ = x[OEND-150]
+ _ = x[ORECV-100]
+ _ = x[ORUNESTR-101]
+ _ = x[OSELRECV2-102]
+ _ = x[OMIN-103]
+ _ = x[OMAX-104]
+ _ = x[OREAL-105]
+ _ = x[OIMAG-106]
+ _ = x[OCOMPLEX-107]
+ _ = x[OUNSAFEADD-108]
+ _ = x[OUNSAFESLICE-109]
+ _ = x[OUNSAFESLICEDATA-110]
+ _ = x[OUNSAFESTRING-111]
+ _ = x[OUNSAFESTRINGDATA-112]
+ _ = x[OMETHEXPR-113]
+ _ = x[OMETHVALUE-114]
+ _ = x[OBLOCK-115]
+ _ = x[OBREAK-116]
+ _ = x[OCASE-117]
+ _ = x[OCONTINUE-118]
+ _ = x[ODEFER-119]
+ _ = x[OFALL-120]
+ _ = x[OFOR-121]
+ _ = x[OGOTO-122]
+ _ = x[OIF-123]
+ _ = x[OLABEL-124]
+ _ = x[OGO-125]
+ _ = x[ORANGE-126]
+ _ = x[ORETURN-127]
+ _ = x[OSELECT-128]
+ _ = x[OSWITCH-129]
+ _ = x[OTYPESW-130]
+ _ = x[OINLCALL-131]
+ _ = x[OMAKEFACE-132]
+ _ = x[OITAB-133]
+ _ = x[OIDATA-134]
+ _ = x[OSPTR-135]
+ _ = x[OCFUNC-136]
+ _ = x[OCHECKNIL-137]
+ _ = x[ORESULT-138]
+ _ = x[OINLMARK-139]
+ _ = x[OLINKSYMOFFSET-140]
+ _ = x[OJUMPTABLE-141]
+ _ = x[OINTERFACESWITCH-142]
+ _ = x[ODYNAMICDOTTYPE-143]
+ _ = x[ODYNAMICDOTTYPE2-144]
+ _ = x[ODYNAMICTYPE-145]
+ _ = x[OTAILCALL-146]
+ _ = x[OGETG-147]
+ _ = x[OGETCALLERSP-148]
+ _ = x[OEND-149]
}
-const _Op_name = "XXXNAMENONAMETYPELITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLEARCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTLNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERSTRINGHEADERRECOVERRECOVERFPRECVRUNESTRSELRECV2MINMAXREALIMAGCOMPLEXUNSAFEADDUNSAFESLICEUNSAFESLICEDATAUNSAFESTRINGUNSAFESTRINGDATAMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWINLCALLMAKEFACEITABIDATASPTRCFUNCCHECKNILRESULTINLMARKLINKSYMOFFSETJUMPTABLEINTERFACESWITCHDYNAMICDOTTYPEDYNAMICDOTTYPE2DYNAMICTYPETAILCALLGETGGETCALLERSPEND"
+const _Op_name = "XXXNAMENONAMETYPELITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLEARCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTLNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERSTRINGHEADERRECOVERRECVRUNESTRSELRECV2MINMAXREALIMAGCOMPLEXUNSAFEADDUNSAFESLICEUNSAFESLICEDATAUNSAFESTRINGUNSAFESTRINGDATAMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWINLCALLMAKEFACEITABIDATASPTRCFUNCCHECKNILRESULTINLMARKLINKSYMOFFSETJUMPTABLEINTERFACESWITCHDYNAMICDOTTYPEDYNAMICDOTTYPE2DYNAMICTYPETAILCALLGETGGETCALLERSPEND"
-var _Op_index = [...]uint16{0, 3, 7, 13, 17, 24, 27, 30, 33, 35, 38, 44, 48, 54, 60, 69, 81, 90, 99, 111, 120, 129, 141, 143, 146, 156, 163, 170, 177, 181, 185, 193, 201, 210, 213, 218, 223, 230, 237, 243, 252, 260, 268, 274, 278, 287, 294, 298, 301, 308, 314, 317, 323, 330, 338, 342, 349, 357, 359, 361, 363, 365, 367, 369, 374, 379, 387, 390, 399, 402, 406, 414, 421, 430, 443, 446, 449, 452, 455, 458, 461, 467, 470, 473, 479, 483, 486, 490, 495, 500, 507, 512, 516, 521, 529, 537, 543, 552, 563, 575, 582, 591, 595, 602, 610, 613, 616, 620, 624, 631, 640, 651, 666, 678, 694, 702, 711, 716, 721, 725, 733, 738, 742, 745, 749, 751, 756, 758, 763, 769, 775, 781, 787, 794, 802, 806, 811, 815, 820, 828, 834, 841, 854, 863, 878, 892, 907, 918, 926, 930, 941, 944}
+var _Op_index = [...]uint16{0, 3, 7, 13, 17, 24, 27, 30, 33, 35, 38, 44, 48, 54, 60, 69, 81, 90, 99, 111, 120, 129, 141, 143, 146, 156, 163, 170, 177, 181, 185, 193, 201, 210, 213, 218, 223, 230, 237, 243, 252, 260, 268, 274, 278, 287, 294, 298, 301, 308, 314, 317, 323, 330, 338, 342, 349, 357, 359, 361, 363, 365, 367, 369, 374, 379, 387, 390, 399, 402, 406, 414, 421, 430, 443, 446, 449, 452, 455, 458, 461, 467, 470, 473, 479, 483, 486, 490, 495, 500, 507, 512, 516, 521, 529, 537, 543, 552, 563, 575, 582, 586, 593, 601, 604, 607, 611, 615, 622, 631, 642, 657, 669, 685, 693, 702, 707, 712, 716, 724, 729, 733, 736, 740, 742, 747, 749, 754, 760, 766, 772, 778, 785, 793, 797, 802, 806, 811, 819, 825, 832, 845, 854, 869, 883, 898, 909, 917, 921, 932, 935}
func (i Op) String() string {
if i >= Op(len(_Op_index)-1) {
diff --git a/src/cmd/compile/internal/ir/reassignment.go b/src/cmd/compile/internal/ir/reassignment.go
index ff54f708c2..ba14d078a2 100644
--- a/src/cmd/compile/internal/ir/reassignment.go
+++ b/src/cmd/compile/internal/ir/reassignment.go
@@ -178,12 +178,12 @@ FindRHS:
break FindRHS
}
}
- base.Fatalf("%v missing from LHS of %v", n, defn)
+ base.FatalfAt(defn.Pos(), "%v missing from LHS of %v", n, defn)
default:
return nil
}
if rhs == nil {
- base.Fatalf("RHS is nil: %v", defn)
+ base.FatalfAt(defn.Pos(), "RHS is nil: %v", defn)
}
if _, ok := ro.singleDef[n]; !ok {
diff --git a/src/cmd/compile/internal/ir/scc.go b/src/cmd/compile/internal/ir/scc.go
index b6056040f7..7beacc7849 100644
--- a/src/cmd/compile/internal/ir/scc.go
+++ b/src/cmd/compile/internal/ir/scc.go
@@ -22,7 +22,7 @@ package ir
// Second, each function becomes two virtual nodes in the graph,
// with numbers n and n+1. We record the function's node number as n
// but search from node n+1. If the search tells us that the component
-// number (min) is n+1, we know that this is a trivial component: one function
+// number (minVisitGen) is n+1, we know that this is a trivial component: one function
// plus its closures. If the search tells us that the component number is
// n, then there was a path from node n+1 back to node n, meaning that
// the function set is mutually recursive. The escape analysis can be
@@ -70,13 +70,13 @@ func (v *bottomUpVisitor) visit(n *Func) uint32 {
id := v.visitgen
v.nodeID[n] = id
v.visitgen++
- min := v.visitgen
+ minVisitGen := v.visitgen
v.stack = append(v.stack, n)
do := func(defn Node) {
if defn != nil {
- if m := v.visit(defn.(*Func)); m < min {
- min = m
+ if m := v.visit(defn.(*Func)); m < minVisitGen {
+ minVisitGen = m
}
}
}
@@ -97,13 +97,13 @@ func (v *bottomUpVisitor) visit(n *Func) uint32 {
}
})
- if (min == id || min == id+1) && !n.IsClosure() {
+ if (minVisitGen == id || minVisitGen == id+1) && !n.IsClosure() {
// This node is the root of a strongly connected component.
- // The original min was id+1. If the bottomUpVisitor found its way
+ // The original minVisitGen was id+1. If the bottomUpVisitor found its way
// back to id, then this block is a set of mutually recursive functions.
// Otherwise, it's just a lone function that does not recurse.
- recursive := min == id
+ recursive := minVisitGen == id
// Remove connected component from stack and mark v.nodeID so that future
// visits return a large number, which will not affect the caller's min.
@@ -121,5 +121,5 @@ func (v *bottomUpVisitor) visit(n *Func) uint32 {
v.analyze(block, recursive)
}
- return min
+ return minVisitGen
}
diff --git a/src/cmd/compile/internal/ir/sizeof_test.go b/src/cmd/compile/internal/ir/sizeof_test.go
index ea74296315..14b6b4f3cd 100644
--- a/src/cmd/compile/internal/ir/sizeof_test.go
+++ b/src/cmd/compile/internal/ir/sizeof_test.go
@@ -21,7 +21,9 @@ func TestSizeof(t *testing.T) {
_64bit uintptr // size on 64bit platforms
}{
{Func{}, 184, 312},
- {Name{}, 96, 168},
+ {Name{}, 96, 160},
+ {miniExpr{}, 32, 48},
+ {miniNode{}, 12, 12},
}
for _, tt := range tests {
diff --git a/src/cmd/compile/internal/ir/symtab.go b/src/cmd/compile/internal/ir/symtab.go
index 1cc8d93f10..f8eb457880 100644
--- a/src/cmd/compile/internal/ir/symtab.go
+++ b/src/cmd/compile/internal/ir/symtab.go
@@ -13,44 +13,50 @@ import (
var Syms symsStruct
type symsStruct struct {
- AssertE2I *obj.LSym
- AssertE2I2 *obj.LSym
- Asanread *obj.LSym
- Asanwrite *obj.LSym
- CgoCheckMemmove *obj.LSym
- CgoCheckPtrWrite *obj.LSym
- CheckPtrAlignment *obj.LSym
- Deferproc *obj.LSym
- Deferprocat *obj.LSym
- DeferprocStack *obj.LSym
- Deferreturn *obj.LSym
- Duffcopy *obj.LSym
- Duffzero *obj.LSym
- GCWriteBarrier [8]*obj.LSym
- Goschedguarded *obj.LSym
- Growslice *obj.LSym
- InterfaceSwitch *obj.LSym
- Memmove *obj.LSym
- Msanread *obj.LSym
- Msanwrite *obj.LSym
- Msanmove *obj.LSym
- Newobject *obj.LSym
- Newproc *obj.LSym
- Panicdivide *obj.LSym
- Panicshift *obj.LSym
- PanicdottypeE *obj.LSym
- PanicdottypeI *obj.LSym
- Panicnildottype *obj.LSym
- Panicoverflow *obj.LSym
- Racefuncenter *obj.LSym
- Racefuncexit *obj.LSym
- Raceread *obj.LSym
- Racereadrange *obj.LSym
- Racewrite *obj.LSym
- Racewriterange *obj.LSym
- TypeAssert *obj.LSym
- WBZero *obj.LSym
- WBMove *obj.LSym
+ AssertE2I *obj.LSym
+ AssertE2I2 *obj.LSym
+ Asanread *obj.LSym
+ Asanwrite *obj.LSym
+ CgoCheckMemmove *obj.LSym
+ CgoCheckPtrWrite *obj.LSym
+ CheckPtrAlignment *obj.LSym
+ Deferproc *obj.LSym
+ Deferprocat *obj.LSym
+ DeferprocStack *obj.LSym
+ Deferreturn *obj.LSym
+ Duffcopy *obj.LSym
+ Duffzero *obj.LSym
+ GCWriteBarrier [8]*obj.LSym
+ Goschedguarded *obj.LSym
+ Growslice *obj.LSym
+ InterfaceSwitch *obj.LSym
+ MallocGC *obj.LSym
+ MallocGCSmallNoScan [27]*obj.LSym
+ MallocGCSmallScanNoHeader [27]*obj.LSym
+ MallocGCTiny [16]*obj.LSym
+ Memmove *obj.LSym
+ Msanread *obj.LSym
+ Msanwrite *obj.LSym
+ Msanmove *obj.LSym
+ Newobject *obj.LSym
+ Newproc *obj.LSym
+ PanicBounds *obj.LSym
+ PanicExtend *obj.LSym
+ Panicdivide *obj.LSym
+ Panicshift *obj.LSym
+ PanicdottypeE *obj.LSym
+ PanicdottypeI *obj.LSym
+ Panicnildottype *obj.LSym
+ Panicoverflow *obj.LSym
+ Racefuncenter *obj.LSym
+ Racefuncexit *obj.LSym
+ Raceread *obj.LSym
+ Racereadrange *obj.LSym
+ Racewrite *obj.LSym
+ Racewriterange *obj.LSym
+ TypeAssert *obj.LSym
+ WBZero *obj.LSym
+ WBMove *obj.LSym
// Wasm
SigPanic *obj.LSym
Staticuint64s *obj.LSym
@@ -58,11 +64,13 @@ type symsStruct struct {
Udiv *obj.LSym
WriteBarrier *obj.LSym
Zerobase *obj.LSym
+ ZeroVal *obj.LSym
ARM64HasATOMICS *obj.LSym
ARMHasVFPv4 *obj.LSym
Loong64HasLAMCAS *obj.LSym
Loong64HasLAM_BH *obj.LSym
Loong64HasLSX *obj.LSym
+ RISCV64HasZbb *obj.LSym
X86HasFMA *obj.LSym
X86HasPOPCNT *obj.LSym
X86HasSSE41 *obj.LSym
diff --git a/src/cmd/compile/internal/ir/type.go b/src/cmd/compile/internal/ir/type.go
index 6daca856a6..0f44cf8d04 100644
--- a/src/cmd/compile/internal/ir/type.go
+++ b/src/cmd/compile/internal/ir/type.go
@@ -42,6 +42,10 @@ func TypeNode(t *types.Type) Node {
// A DynamicType represents a type expression whose exact type must be
// computed dynamically.
+//
+// TODO(adonovan): I think "dynamic" is a misnomer here; it's really a
+// type with free type parameters that needs to be instantiated to obtain
+// a ground type for which an rtype can exist.
type DynamicType struct {
miniExpr
diff --git a/src/cmd/compile/internal/ir/visit.go b/src/cmd/compile/internal/ir/visit.go
index 8dff11af33..c68bb5d033 100644
--- a/src/cmd/compile/internal/ir/visit.go
+++ b/src/cmd/compile/internal/ir/visit.go
@@ -155,19 +155,6 @@ func Any(n Node, cond func(Node) bool) bool {
return do(n)
}
-// AnyList calls Any(x, cond) for each node x in the list, in order.
-// If any call returns true, AnyList stops and returns true.
-// Otherwise, AnyList returns false after calling Any(x, cond)
-// for every x in the list.
-func AnyList(list Nodes, cond func(Node) bool) bool {
- for _, x := range list {
- if Any(x, cond) {
- return true
- }
- }
- return false
-}
-
// EditChildren edits the child nodes of n, replacing each child x with edit(x).
//
// Note that EditChildren(n, edit) only calls edit(x) for n's immediate children.
diff --git a/src/cmd/compile/internal/liveness/mergelocals.go b/src/cmd/compile/internal/liveness/mergelocals.go
index cbe49aa655..6967ee016e 100644
--- a/src/cmd/compile/internal/liveness/mergelocals.go
+++ b/src/cmd/compile/internal/liveness/mergelocals.go
@@ -56,7 +56,7 @@ type candRegion struct {
type cstate struct {
fn *ir.Func
f *ssa.Func
- lv *liveness
+ lv *Liveness
cands []*ir.Name
nameToSlot map[*ir.Name]int32
regions []candRegion
diff --git a/src/cmd/compile/internal/liveness/plive.go b/src/cmd/compile/internal/liveness/plive.go
index a20d856aa2..9b8f15e9c2 100644
--- a/src/cmd/compile/internal/liveness/plive.go
+++ b/src/cmd/compile/internal/liveness/plive.go
@@ -102,8 +102,8 @@ type blockEffects struct {
liveout bitvec.BitVec
}
-// A collection of global state used by liveness analysis.
-type liveness struct {
+// A collection of global state used by Liveness analysis.
+type Liveness struct {
fn *ir.Func
f *ssa.Func
vars []*ir.Name
@@ -235,7 +235,7 @@ func getvariables(fn *ir.Func) ([]*ir.Name, map[*ir.Name]int32) {
return vars, idx
}
-func (lv *liveness) initcache() {
+func (lv *Liveness) initcache() {
if lv.cache.initialized {
base.Fatalf("liveness cache initialized twice")
return
@@ -281,7 +281,7 @@ const (
// valueEffects returns the index of a variable in lv.vars and the
// liveness effects v has on that variable.
// If v does not affect any tracked variables, it returns -1, 0.
-func (lv *liveness) valueEffects(v *ssa.Value) (int32, liveEffect) {
+func (lv *Liveness) valueEffects(v *ssa.Value) (int32, liveEffect) {
n, e := affectedVar(v)
if e == 0 || n == nil { // cheapest checks first
return -1, 0
@@ -392,8 +392,8 @@ type livenessFuncCache struct {
// Constructs a new liveness structure used to hold the global state of the
// liveness computation. The cfg argument is a slice of *BasicBlocks and the
// vars argument is a slice of *Nodes.
-func newliveness(fn *ir.Func, f *ssa.Func, vars []*ir.Name, idx map[*ir.Name]int32, stkptrsize int64) *liveness {
- lv := &liveness{
+func newliveness(fn *ir.Func, f *ssa.Func, vars []*ir.Name, idx map[*ir.Name]int32, stkptrsize int64) *Liveness {
+ lv := &Liveness{
fn: fn,
f: f,
vars: vars,
@@ -447,14 +447,14 @@ func newliveness(fn *ir.Func, f *ssa.Func, vars []*ir.Name, idx map[*ir.Name]int
return lv
}
-func (lv *liveness) blockEffects(b *ssa.Block) *blockEffects {
+func (lv *Liveness) blockEffects(b *ssa.Block) *blockEffects {
return &lv.be[b.ID]
}
// Generates live pointer value maps for arguments and local variables. The
// this argument and the in arguments are always assumed live. The vars
// argument is a slice of *Nodes.
-func (lv *liveness) pointerMap(liveout bitvec.BitVec, vars []*ir.Name, args, locals bitvec.BitVec) {
+func (lv *Liveness) pointerMap(liveout bitvec.BitVec, vars []*ir.Name, args, locals bitvec.BitVec) {
var slotsSeen map[int64]*ir.Name
checkForDuplicateSlots := base.Debug.MergeLocals != 0
if checkForDuplicateSlots {
@@ -504,7 +504,7 @@ func IsUnsafe(f *ssa.Func) bool {
}
// markUnsafePoints finds unsafe points and computes lv.unsafePoints.
-func (lv *liveness) markUnsafePoints() {
+func (lv *Liveness) markUnsafePoints() {
if IsUnsafe(lv.f) {
// No complex analysis necessary.
lv.allUnsafe = true
@@ -647,7 +647,7 @@ func (lv *liveness) markUnsafePoints() {
// This does not necessarily mean the instruction is a safe-point. In
// particular, call Values can have a stack map in case the callee
// grows the stack, but not themselves be a safe-point.
-func (lv *liveness) hasStackMap(v *ssa.Value) bool {
+func (lv *Liveness) hasStackMap(v *ssa.Value) bool {
if !v.Op.IsCall() {
return false
}
@@ -663,7 +663,7 @@ func (lv *liveness) hasStackMap(v *ssa.Value) bool {
// Initializes the sets for solving the live variables. Visits all the
// instructions in each basic block to summarizes the information at each basic
// block
-func (lv *liveness) prologue() {
+func (lv *Liveness) prologue() {
lv.initcache()
for _, b := range lv.f.Blocks {
@@ -685,7 +685,7 @@ func (lv *liveness) prologue() {
}
// Solve the liveness dataflow equations.
-func (lv *liveness) solve() {
+func (lv *Liveness) solve() {
// These temporary bitvectors exist to avoid successive allocations and
// frees within the loop.
nvars := int32(len(lv.vars))
@@ -745,7 +745,7 @@ func (lv *liveness) solve() {
// Visits all instructions in a basic block and computes a bit vector of live
// variables at each safe point locations.
-func (lv *liveness) epilogue() {
+func (lv *Liveness) epilogue() {
nvars := int32(len(lv.vars))
liveout := bitvec.New(nvars)
livedefer := bitvec.New(nvars) // always-live variables
@@ -769,7 +769,7 @@ func (lv *liveness) epilogue() {
// its stack copy is not live.
continue
}
- // Note: zeroing is handled by zeroResults in walk.go.
+ // Note: zeroing is handled by zeroResults in ../ssagen/ssa.go.
livedefer.Set(int32(i))
}
if n.IsOutputParamHeapAddr() {
@@ -914,7 +914,7 @@ func (lv *liveness) epilogue() {
// is actually a net loss: we save about 50k of argument bitmaps but the new
// PCDATA tables cost about 100k. So for now we keep using a single index for
// both bitmap lists.
-func (lv *liveness) compact(b *ssa.Block) {
+func (lv *Liveness) compact(b *ssa.Block) {
pos := 0
if b == lv.f.Entry {
// Handle entry stack map.
@@ -939,7 +939,7 @@ func (lv *liveness) compact(b *ssa.Block) {
lv.livevars = lv.livevars[:0]
}
-func (lv *liveness) enableClobber() {
+func (lv *Liveness) enableClobber() {
// The clobberdead experiment inserts code to clobber pointer slots in all
// the dead variables (locals and args) at every synchronous safepoint.
if !base.Flag.ClobberDead {
@@ -981,7 +981,7 @@ func (lv *liveness) enableClobber() {
// Clobber only functions where the hash of the function name matches a pattern.
// Useful for binary searching for a miscompiled function.
hstr := ""
- for _, b := range hash.Sum20([]byte(lv.f.Name)) {
+ for _, b := range hash.Sum32([]byte(lv.f.Name)) {
hstr += fmt.Sprintf("%08b", b)
}
if !strings.HasSuffix(hstr, h) {
@@ -994,7 +994,7 @@ func (lv *liveness) enableClobber() {
// Inserts code to clobber pointer slots in all the dead variables (locals and args)
// at every synchronous safepoint in b.
-func (lv *liveness) clobber(b *ssa.Block) {
+func (lv *Liveness) clobber(b *ssa.Block) {
// Copy block's values to a temporary.
oldSched := append([]*ssa.Value{}, b.Values...)
b.Values = b.Values[:0]
@@ -1029,7 +1029,7 @@ func (lv *liveness) clobber(b *ssa.Block) {
// clobber generates code to clobber pointer slots in all dead variables
// (those not marked in live). Clobbering instructions are added to the end
// of b.Values.
-func clobber(lv *liveness, b *ssa.Block, live bitvec.BitVec) {
+func clobber(lv *Liveness, b *ssa.Block, live bitvec.BitVec) {
for i, n := range lv.vars {
if !live.Get(int32(i)) && !n.Addrtaken() && !n.OpenDeferSlot() && !n.IsOutputParamHeapAddr() {
// Don't clobber stack objects (address-taken). They are
@@ -1102,7 +1102,7 @@ func clobberPtr(b *ssa.Block, v *ir.Name, offset int64) {
b.NewValue0IA(src.NoXPos, ssa.OpClobber, types.TypeVoid, offset, v)
}
-func (lv *liveness) showlive(v *ssa.Value, live bitvec.BitVec) {
+func (lv *Liveness) showlive(v *ssa.Value, live bitvec.BitVec) {
if base.Flag.Live == 0 || ir.FuncName(lv.fn) == "init" || strings.HasPrefix(ir.FuncName(lv.fn), ".") {
return
}
@@ -1119,6 +1119,24 @@ func (lv *liveness) showlive(v *ssa.Value, live bitvec.BitVec) {
return
}
+ pos, s := lv.format(v, live)
+
+ base.WarnfAt(pos, "%s", s)
+}
+
+func (lv *Liveness) Format(v *ssa.Value) string {
+ if v == nil {
+ _, s := lv.format(nil, lv.stackMaps[0])
+ return s
+ }
+ if idx := lv.livenessMap.Get(v); idx.StackMapValid() {
+ _, s := lv.format(v, lv.stackMaps[idx])
+ return s
+ }
+ return ""
+}
+
+func (lv *Liveness) format(v *ssa.Value, live bitvec.BitVec) (src.XPos, string) {
pos := lv.fn.Nname.Pos()
if v != nil {
pos = v.Pos
@@ -1149,11 +1167,10 @@ func (lv *liveness) showlive(v *ssa.Value, live bitvec.BitVec) {
for _, v := range names {
s += " " + v
}
-
- base.WarnfAt(pos, "%s", s)
+ return pos, s
}
-func (lv *liveness) printbvec(printed bool, name string, live bitvec.BitVec) bool {
+func (lv *Liveness) printbvec(printed bool, name string, live bitvec.BitVec) bool {
if live.IsEmpty() {
return printed
}
@@ -1177,7 +1194,7 @@ func (lv *liveness) printbvec(printed bool, name string, live bitvec.BitVec) boo
}
// printeffect is like printbvec, but for valueEffects.
-func (lv *liveness) printeffect(printed bool, name string, pos int32, x bool) bool {
+func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool) bool {
if !x {
return printed
}
@@ -1197,7 +1214,7 @@ func (lv *liveness) printeffect(printed bool, name string, pos int32, x bool) bo
// Prints the computed liveness information and inputs, for debugging.
// This format synthesizes the information used during the multiple passes
// into a single presentation.
-func (lv *liveness) printDebug() {
+func (lv *Liveness) printDebug() {
fmt.Printf("liveness: %s\n", ir.FuncName(lv.fn))
for i, b := range lv.f.Blocks {
@@ -1309,7 +1326,7 @@ func (lv *liveness) printDebug() {
// first word dumped is the total number of bitmaps. The second word is the
// length of the bitmaps. All bitmaps are assumed to be of equal length. The
// remaining bytes are the raw bitmaps.
-func (lv *liveness) emit() (argsSym, liveSym *obj.LSym) {
+func (lv *Liveness) emit() (argsSym, liveSym *obj.LSym) {
// Size args bitmaps to be just large enough to hold the largest pointer.
// First, find the largest Xoffset node we care about.
// (Nodes without pointers aren't in lv.vars; see ShouldTrack.)
@@ -1370,7 +1387,7 @@ func (lv *liveness) emit() (argsSym, liveSym *obj.LSym) {
// structure read by the garbage collector.
// Returns a map from GC safe points to their corresponding stack map index,
// and a map that contains all input parameters that may be partially live.
-func Compute(curfn *ir.Func, f *ssa.Func, stkptrsize int64, pp *objw.Progs) (Map, map[*ir.Name]bool) {
+func Compute(curfn *ir.Func, f *ssa.Func, stkptrsize int64, pp *objw.Progs, retLiveness bool) (Map, map[*ir.Name]bool, *Liveness) {
// Construct the global liveness state.
vars, idx := getvariables(curfn)
lv := newliveness(curfn, f, vars, idx, stkptrsize)
@@ -1397,9 +1414,7 @@ func Compute(curfn *ir.Func, f *ssa.Func, stkptrsize int64, pp *objw.Progs) (Map
{
cache := f.Cache.Liveness.(*livenessFuncCache)
if cap(lv.be) < 2000 { // Threshold from ssa.Cache slices.
- for i := range lv.be {
- lv.be[i] = blockEffects{}
- }
+ clear(lv.be)
cache.be = lv.be
}
if len(lv.livenessMap.Vals) < 2000 {
@@ -1432,10 +1447,15 @@ func Compute(curfn *ir.Func, f *ssa.Func, stkptrsize int64, pp *objw.Progs) (Map
p.To.Sym = x
}
- return lv.livenessMap, lv.partLiveArgs
+ retLv := lv
+ if !retLiveness {
+ retLv = nil
+ }
+
+ return lv.livenessMap, lv.partLiveArgs, retLv
}
-func (lv *liveness) emitStackObjects() *obj.LSym {
+func (lv *Liveness) emitStackObjects() *obj.LSym {
var vars []*ir.Name
for _, n := range lv.fn.Dcl {
if shouldTrack(n) && n.Addrtaken() && n.Esc() != ir.EscHeap {
@@ -1473,7 +1493,7 @@ func (lv *liveness) emitStackObjects() *obj.LSym {
if sz != int64(int32(sz)) {
base.Fatalf("stack object too big: %v of type %v, size %d", v, t, sz)
}
- lsym, ptrBytes := reflectdata.GCSym(t)
+ lsym, ptrBytes := reflectdata.GCSym(t, false)
off = objw.Uint32(x, off, uint32(sz))
off = objw.Uint32(x, off, uint32(ptrBytes))
off = objw.SymPtrOff(x, off, lsym)
diff --git a/src/cmd/compile/internal/logopt/log_opts.go b/src/cmd/compile/internal/logopt/log_opts.go
index 3e7d86aac5..d08f6fb5d6 100644
--- a/src/cmd/compile/internal/logopt/log_opts.go
+++ b/src/cmd/compile/internal/logopt/log_opts.go
@@ -326,7 +326,7 @@ var mu = sync.Mutex{} // mu protects loggedOpts.
// funcName is the name of the function
// A typical use for this to accumulate an explanation for a missed optimization, for example, why did something escape?
func NewLoggedOpt(pos, lastPos src.XPos, what, pass, funcName string, args ...interface{}) *LoggedOpt {
- pass = strings.Replace(pass, " ", "_", -1)
+ pass = strings.ReplaceAll(pass, " ", "_")
return &LoggedOpt{pos, lastPos, pass, funcName, what, args}
}
@@ -405,7 +405,7 @@ func fixSlash(f string) string {
if os.PathSeparator == '/' {
return f
}
- return strings.Replace(f, string(os.PathSeparator), "/", -1)
+ return strings.ReplaceAll(f, string(os.PathSeparator), "/")
}
func uriIfy(f string) DocumentURI {
diff --git a/src/cmd/compile/internal/logopt/logopt_test.go b/src/cmd/compile/internal/logopt/logopt_test.go
index c7debd9897..1edabf9fb7 100644
--- a/src/cmd/compile/internal/logopt/logopt_test.go
+++ b/src/cmd/compile/internal/logopt/logopt_test.go
@@ -203,16 +203,16 @@ func s15a8(x *[15]int64) [15]int64 {
// escape analysis explanation
want(t, slogged, `{"range":{"start":{"line":7,"character":13},"end":{"line":7,"character":13}},"severity":3,"code":"leak","source":"go compiler","message":"parameter z leaks to ~r0 with derefs=0",`+
`"relatedInformation":[`+
- `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: y = z:"},`+
+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: y ← z:"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y := z (assign-pair)"},`+
- `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: ~r0 = y:"},`+
+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: ~r0 ← y:"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y.b (dot of pointer)"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from \u0026y.b (address-of)"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":9},"end":{"line":4,"character":9}}},"message":"inlineLoc"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from ~r0 = \u0026y.b (assign-pair)"},`+
- `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: flow: ~r0 = ~r0:"},`+
+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: flow: ~r0 ← ~r0:"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: from return ~r0 (return)"}]}`)
})
}
diff --git a/src/cmd/compile/internal/loong64/ggen.go b/src/cmd/compile/internal/loong64/ggen.go
index 27d318a8bb..bb1b146346 100644
--- a/src/cmd/compile/internal/loong64/ggen.go
+++ b/src/cmd/compile/internal/loong64/ggen.go
@@ -6,49 +6,23 @@ package loong64
import (
"cmd/compile/internal/base"
- "cmd/compile/internal/ir"
"cmd/compile/internal/objw"
- "cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/loong64"
)
func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
- if cnt == 0 {
- return p
+ if cnt%8 != 0 {
+ panic("zeroed region not aligned")
}
// Adjust the frame to account for LR.
off += base.Ctxt.Arch.FixedFrameSize
- if cnt < int64(4*types.PtrSize) {
- for i := int64(0); i < cnt; i += int64(types.PtrSize) {
- p = pp.Append(p, loong64.AMOVV, obj.TYPE_REG, loong64.REGZERO, 0, obj.TYPE_MEM, loong64.REGSP, off+i)
- }
- } else if cnt <= int64(128*types.PtrSize) {
- p = pp.Append(p, loong64.AADDV, obj.TYPE_CONST, 0, off, obj.TYPE_REG, loong64.REGRT1, 0)
- p.Reg = loong64.REGSP
- p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ir.Syms.Duffzero
- p.To.Offset = 8 * (128 - cnt/int64(types.PtrSize))
- } else {
- // ADDV $(off), SP, r1
- // ADDV $cnt, r1, r2
- // loop:
- // MOVV R0, (r1)
- // ADDV $Widthptr, r1
- // BNE r1, r2, loop
- p = pp.Append(p, loong64.AADDV, obj.TYPE_CONST, 0, off, obj.TYPE_REG, loong64.REGRT1, 0)
- p.Reg = loong64.REGSP
- p = pp.Append(p, loong64.AADDV, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, loong64.REGRT2, 0)
- p.Reg = loong64.REGRT1
- p = pp.Append(p, loong64.AMOVV, obj.TYPE_REG, loong64.REGZERO, 0, obj.TYPE_MEM, loong64.REGRT1, 0)
- loop := p
- p = pp.Append(p, loong64.AADDV, obj.TYPE_CONST, 0, int64(types.PtrSize), obj.TYPE_REG, loong64.REGRT1, 0)
- p = pp.Append(p, loong64.ABNE, obj.TYPE_REG, loong64.REGRT1, 0, obj.TYPE_BRANCH, 0, 0)
- p.Reg = loong64.REGRT2
- p.To.SetTarget(loop)
+ for cnt != 0 {
+ p = pp.Append(p, loong64.AMOVV, obj.TYPE_REG, loong64.REGZERO, 0, obj.TYPE_MEM, loong64.REGSP, off)
+ off += 8
+ cnt -= 8
}
return p
diff --git a/src/cmd/compile/internal/loong64/ssa.go b/src/cmd/compile/internal/loong64/ssa.go
index 0ba9efa1d3..bd0d96a695 100644
--- a/src/cmd/compile/internal/loong64/ssa.go
+++ b/src/cmd/compile/internal/loong64/ssa.go
@@ -16,6 +16,7 @@ import (
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/loong64"
+ "internal/abi"
)
// isFPreg reports whether r is an FP register.
@@ -124,6 +125,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = y
case ssa.OpLOONG64MOVVnop,
+ ssa.OpLOONG64ZERO,
ssa.OpLOONG64LoweredRound32F,
ssa.OpLOONG64LoweredRound64F:
// nothing to do
@@ -165,8 +167,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpLOONG64OR,
ssa.OpLOONG64XOR,
ssa.OpLOONG64NOR,
+ ssa.OpLOONG64ANDN,
+ ssa.OpLOONG64ORN,
+ ssa.OpLOONG64SLL,
ssa.OpLOONG64SLLV,
+ ssa.OpLOONG64SRL,
ssa.OpLOONG64SRLV,
+ ssa.OpLOONG64SRA,
ssa.OpLOONG64SRAV,
ssa.OpLOONG64ROTR,
ssa.OpLOONG64ROTRV,
@@ -178,7 +185,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpLOONG64MULD,
ssa.OpLOONG64DIVF,
ssa.OpLOONG64DIVD,
- ssa.OpLOONG64MULV, ssa.OpLOONG64MULHV, ssa.OpLOONG64MULHVU,
+ ssa.OpLOONG64MULV, ssa.OpLOONG64MULHV, ssa.OpLOONG64MULHVU, ssa.OpLOONG64MULH, ssa.OpLOONG64MULHU,
ssa.OpLOONG64DIVV, ssa.OpLOONG64REMV, ssa.OpLOONG64DIVVU, ssa.OpLOONG64REMVU,
ssa.OpLOONG64FCOPYSGD:
p := s.Prog(v.Op.Asm())
@@ -269,13 +276,16 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpLOONG64ADDVconst,
+ ssa.OpLOONG64ADDV16const,
ssa.OpLOONG64SUBVconst,
ssa.OpLOONG64ANDconst,
ssa.OpLOONG64ORconst,
ssa.OpLOONG64XORconst,
- ssa.OpLOONG64NORconst,
+ ssa.OpLOONG64SLLconst,
ssa.OpLOONG64SLLVconst,
+ ssa.OpLOONG64SRLconst,
ssa.OpLOONG64SRLVconst,
+ ssa.OpLOONG64SRAconst,
ssa.OpLOONG64SRAVconst,
ssa.OpLOONG64ROTRconst,
ssa.OpLOONG64ROTRVconst,
@@ -287,6 +297,23 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
+
+ case ssa.OpLOONG64NORconst:
+ // MOVV $const, Rtmp
+ // NOR Rtmp, Rarg0, Rout
+ p := s.Prog(loong64.AMOVV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = v.AuxInt
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = loong64.REGTMP
+
+ p2 := s.Prog(v.Op.Asm())
+ p2.From.Type = obj.TYPE_REG
+ p2.From.Reg = loong64.REGTMP
+ p2.Reg = v.Args[0].Reg()
+ p2.To.Type = obj.TYPE_REG
+ p2.To.Reg = v.Reg()
+
case ssa.OpLOONG64MOVVconst:
r := v.Reg()
p := s.Prog(v.Op.Asm())
@@ -407,18 +434,6 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Reg = v.Args[0].Reg()
p.To.Index = v.Args[1].Reg()
- case ssa.OpLOONG64MOVBstorezeroidx,
- ssa.OpLOONG64MOVHstorezeroidx,
- ssa.OpLOONG64MOVWstorezeroidx,
- ssa.OpLOONG64MOVVstorezeroidx:
- p := s.Prog(v.Op.Asm())
- p.From.Type = obj.TYPE_REG
- p.From.Reg = loong64.REGZERO
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_NONE
- p.To.Reg = v.Args[0].Reg()
- p.To.Index = v.Args[1].Reg()
-
case ssa.OpLOONG64MOVBload,
ssa.OpLOONG64MOVBUload,
ssa.OpLOONG64MOVHload,
@@ -446,16 +461,6 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
ssagen.AddAux(&p.To, v)
- case ssa.OpLOONG64MOVBstorezero,
- ssa.OpLOONG64MOVHstorezero,
- ssa.OpLOONG64MOVWstorezero,
- ssa.OpLOONG64MOVVstorezero:
- p := s.Prog(v.Op.Asm())
- p.From.Type = obj.TYPE_REG
- p.From.Reg = loong64.REGZERO
- p.To.Type = obj.TYPE_MEM
- p.To.Reg = v.Args[0].Reg()
- ssagen.AddAux(&p.To, v)
case ssa.OpLOONG64MOVBreg,
ssa.OpLOONG64MOVBUreg,
ssa.OpLOONG64MOVHreg,
@@ -518,6 +523,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpLOONG64SQRTF,
ssa.OpLOONG64REVB2H,
ssa.OpLOONG64REVB2W,
+ ssa.OpLOONG64REVB4H,
ssa.OpLOONG64REVBV,
ssa.OpLOONG64BITREV4B,
ssa.OpLOONG64BITREVW,
@@ -547,80 +553,213 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
- case ssa.OpLOONG64DUFFZERO:
- // runtime.duffzero expects start address in R20
- p := s.Prog(obj.ADUFFZERO)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ir.Syms.Duffzero
- p.To.Offset = v.AuxInt
case ssa.OpLOONG64LoweredZero:
- // MOVx R0, (Rarg0)
- // ADDV $sz, Rarg0
- // BGEU Rarg1, Rarg0, -2(PC)
- mov, sz := largestMove(v.AuxInt)
- p := s.Prog(mov)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = loong64.REGZERO
- p.To.Type = obj.TYPE_MEM
- p.To.Reg = v.Args[0].Reg()
+ ptrReg := v.Args[0].Reg()
+ n := v.AuxInt
+ if n < 16 {
+ v.Fatalf("Zero too small %d", n)
+ }
- p2 := s.Prog(loong64.AADDVU)
- p2.From.Type = obj.TYPE_CONST
- p2.From.Offset = sz
- p2.To.Type = obj.TYPE_REG
- p2.To.Reg = v.Args[0].Reg()
+ // Generate Zeroing instructions.
+ var off int64
+ for n >= 8 {
+ // MOVV ZR, off(ptrReg)
+ zero8(s, ptrReg, off)
+ off += 8
+ n -= 8
+ }
+ if n != 0 {
+ // MOVV ZR, off+n-8(ptrReg)
+ zero8(s, ptrReg, off+n-8)
+ }
+ case ssa.OpLOONG64LoweredZeroLoop:
+ ptrReg := v.Args[0].Reg()
+ countReg := v.RegTmp()
+ var off int64
+ n := v.AuxInt
+ loopSize := int64(64)
+ if n < 3*loopSize {
+ // - a loop count of 0 won't work.
+ // - a loop count of 1 is useless.
+ // - a loop count of 2 is a code size ~tie
+ // 4 instructions to implement the loop
+ // 8 instructions in the loop body
+ // vs
+ // 16 instuctions in the straightline code
+ // Might as well use straightline code.
+ v.Fatalf("ZeroLoop size tool small %d", n)
+ }
- p3 := s.Prog(loong64.ABGEU)
- p3.From.Type = obj.TYPE_REG
- p3.From.Reg = v.Args[1].Reg()
- p3.Reg = v.Args[0].Reg()
- p3.To.Type = obj.TYPE_BRANCH
- p3.To.SetTarget(p)
-
- case ssa.OpLOONG64DUFFCOPY:
- p := s.Prog(obj.ADUFFCOPY)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ir.Syms.Duffcopy
- p.To.Offset = v.AuxInt
- case ssa.OpLOONG64LoweredMove:
- // MOVx (Rarg1), Rtmp
- // MOVx Rtmp, (Rarg0)
- // ADDV $sz, Rarg1
- // ADDV $sz, Rarg0
- // BGEU Rarg2, Rarg0, -4(PC)
- mov, sz := largestMove(v.AuxInt)
- p := s.Prog(mov)
- p.From.Type = obj.TYPE_MEM
- p.From.Reg = v.Args[1].Reg()
+ // Put iteration count in a register.
+ // MOVV $n/loopSize, countReg
+ p := s.Prog(loong64.AMOVV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = n / loopSize
p.To.Type = obj.TYPE_REG
- p.To.Reg = loong64.REGTMP
+ p.To.Reg = countReg
+ cntInit := p
- p2 := s.Prog(mov)
- p2.From.Type = obj.TYPE_REG
- p2.From.Reg = loong64.REGTMP
- p2.To.Type = obj.TYPE_MEM
- p2.To.Reg = v.Args[0].Reg()
+ // Zero loopSize bytes starting at ptrReg.
+ for range loopSize / 8 {
+ // MOVV ZR, off(ptrReg)
+ zero8(s, ptrReg, off)
+ off += 8
+ }
- p3 := s.Prog(loong64.AADDVU)
- p3.From.Type = obj.TYPE_CONST
- p3.From.Offset = sz
- p3.To.Type = obj.TYPE_REG
- p3.To.Reg = v.Args[1].Reg()
+ // Increment ptrReg by loopSize.
+ // ADDV $loopSize, ptrReg
+ p = s.Prog(loong64.AADDV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = loopSize
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = ptrReg
- p4 := s.Prog(loong64.AADDVU)
- p4.From.Type = obj.TYPE_CONST
- p4.From.Offset = sz
- p4.To.Type = obj.TYPE_REG
- p4.To.Reg = v.Args[0].Reg()
+ // Decrement loop count.
+ // SUBV $1, countReg
+ p = s.Prog(loong64.ASUBV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = 1
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = countReg
- p5 := s.Prog(loong64.ABGEU)
- p5.From.Type = obj.TYPE_REG
- p5.From.Reg = v.Args[2].Reg()
- p5.Reg = v.Args[1].Reg()
- p5.To.Type = obj.TYPE_BRANCH
- p5.To.SetTarget(p)
+ // Jump to loop header if we're not done yet.
+ // BNE countReg, loop header
+ p = s.Prog(loong64.ABNE)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = countReg
+ p.To.Type = obj.TYPE_BRANCH
+ p.To.SetTarget(cntInit.Link)
+
+ // Multiples of the loop size are now done.
+ n %= loopSize
+
+ off = 0
+ // Write any fractional portion.
+ for n >= 8 {
+ // MOVV ZR, off(ptrReg)
+ zero8(s, ptrReg, off)
+ off += 8
+ n -= 8
+ }
+
+ if n != 0 {
+ zero8(s, ptrReg, off+n-8)
+ }
+
+ case ssa.OpLOONG64LoweredMove:
+ dstReg := v.Args[0].Reg()
+ srcReg := v.Args[1].Reg()
+ if dstReg == srcReg {
+ break
+ }
+ tmpReg := int16(loong64.REG_R20)
+ n := v.AuxInt
+ if n < 16 {
+ v.Fatalf("Move too small %d", n)
+ }
+
+ var off int64
+ for n >= 8 {
+ // MOVV off(srcReg), tmpReg
+ // MOVV tmpReg, off(dstReg)
+ move8(s, srcReg, dstReg, tmpReg, off)
+ off += 8
+ n -= 8
+ }
+
+ if n != 0 {
+ // MOVV off+n-8(srcReg), tmpReg
+ // MOVV tmpReg, off+n-8(srcReg)
+ move8(s, srcReg, dstReg, tmpReg, off+n-8)
+ }
+ case ssa.OpLOONG64LoweredMoveLoop:
+ dstReg := v.Args[0].Reg()
+ srcReg := v.Args[1].Reg()
+ if dstReg == srcReg {
+ break
+ }
+ countReg := int16(loong64.REG_R20)
+ tmpReg := int16(loong64.REG_R21)
+ var off int64
+ n := v.AuxInt
+ loopSize := int64(64)
+ if n < 3*loopSize {
+ // - a loop count of 0 won't work.
+ // - a loop count of 1 is useless.
+ // - a loop count of 2 is a code size ~tie
+ // 4 instructions to implement the loop
+ // 8 instructions in the loop body
+ // vs
+ // 16 instructions in the straightline code
+ // Might as well use straightline code.
+ v.Fatalf("ZeroLoop size too small %d", n)
+ }
+
+ // Put iteration count in a register.
+ // MOVV $n/loopSize, countReg
+ p := s.Prog(loong64.AMOVV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = n / loopSize
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = countReg
+ cntInit := p
+
+ // Move loopSize bytes starting at srcReg to dstReg.
+ for range loopSize / 8 {
+ // MOVV off(srcReg), tmpReg
+ // MOVV tmpReg, off(dstReg)
+ move8(s, srcReg, dstReg, tmpReg, off)
+ off += 8
+ }
+
+ // Increment srcReg and destReg by loopSize.
+ // ADDV $loopSize, srcReg
+ p = s.Prog(loong64.AADDV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = loopSize
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = srcReg
+ // ADDV $loopSize, dstReg
+ p = s.Prog(loong64.AADDV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = loopSize
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = dstReg
+
+ // Decrement loop count.
+ // SUBV $1, countReg
+ p = s.Prog(loong64.ASUBV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = 1
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = countReg
+
+ // Jump to loop header if we're not done yet.
+ // BNE countReg, loop header
+ p = s.Prog(loong64.ABNE)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = countReg
+ p.To.Type = obj.TYPE_BRANCH
+ p.To.SetTarget(cntInit.Link)
+
+ // Multiples of the loop size are now done.
+ n %= loopSize
+
+ off = 0
+ // Copy any fractional portion.
+ for n >= 8 {
+ // MOVV off(srcReg), tmpReg
+ // MOVV tmpReg, off(dstReg)
+ move8(s, srcReg, dstReg, tmpReg, off)
+ off += 8
+ n -= 8
+ }
+
+ if n != 0 {
+ // MOVV off+n-8(srcReg), tmpReg
+ // MOVV tmpReg, off+n-8(srcReg)
+ move8(s, srcReg, dstReg, tmpReg, off+n-8)
+ }
case ssa.OpLOONG64CALLstatic, ssa.OpLOONG64CALLclosure, ssa.OpLOONG64CALLinter:
s.Call(v)
@@ -639,12 +778,92 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Type = obj.TYPE_CONST
p.From.Offset = 0x1A
- case ssa.OpLOONG64LoweredPanicBoundsA, ssa.OpLOONG64LoweredPanicBoundsB, ssa.OpLOONG64LoweredPanicBoundsC:
- p := s.Prog(obj.ACALL)
+ case ssa.OpLOONG64LoweredPanicBoundsRR, ssa.OpLOONG64LoweredPanicBoundsRC, ssa.OpLOONG64LoweredPanicBoundsCR, ssa.OpLOONG64LoweredPanicBoundsCC:
+ // Compute the constant we put in the PCData entry for this call.
+ code, signed := ssa.BoundsKind(v.AuxInt).Code()
+ xIsReg := false
+ yIsReg := false
+ xVal := 0
+ yVal := 0
+ switch v.Op {
+ case ssa.OpLOONG64LoweredPanicBoundsRR:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - loong64.REG_R4)
+ yIsReg = true
+ yVal = int(v.Args[1].Reg() - loong64.REG_R4)
+ case ssa.OpLOONG64LoweredPanicBoundsRC:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - loong64.REG_R4)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ if yVal == xVal {
+ yVal = 1
+ }
+ p := s.Prog(loong64.AMOVV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = loong64.REG_R4 + int16(yVal)
+ }
+ case ssa.OpLOONG64LoweredPanicBoundsCR:
+ yIsReg = true
+ yVal = int(v.Args[0].Reg() - loong64.REG_R4)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else {
+ // Move constant to a register
+ xIsReg = true
+ if xVal == yVal {
+ xVal = 1
+ }
+ p := s.Prog(loong64.AMOVV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = loong64.REG_R4 + int16(xVal)
+ }
+ case ssa.OpLOONG64LoweredPanicBoundsCC:
+ c := v.Aux.(ssa.PanicBoundsCC).Cx
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else {
+ // Move constant to a register
+ xIsReg = true
+ p := s.Prog(loong64.AMOVV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = loong64.REG_R4 + int16(xVal)
+ }
+ c = v.Aux.(ssa.PanicBoundsCC).Cy
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ yVal = 1
+ p := s.Prog(loong64.AMOVV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = loong64.REG_R4 + int16(yVal)
+ }
+ }
+ c := abi.BoundsEncode(code, signed, xIsReg, yIsReg, xVal, yVal)
+
+ p := s.Prog(obj.APCDATA)
+ p.From.SetConst(abi.PCDATA_PanicBounds)
+ p.To.SetConst(int64(c))
+ p = s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
- s.UseArgs(16) // space used in callee args area by assembly stubs
+ p.To.Sym = ir.Syms.PanicBounds
+
case ssa.OpLOONG64LoweredAtomicLoad8, ssa.OpLOONG64LoweredAtomicLoad32, ssa.OpLOONG64LoweredAtomicLoad64:
// MOVB (Rarg0), Rout
// DBAR 0x14
@@ -942,6 +1161,35 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
+
+ case ssa.OpLOONG64PRELD:
+ // PRELD (Rarg0), hint
+ p := s.Prog(v.Op.Asm())
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = v.Args[0].Reg()
+ p.AddRestSourceConst(v.AuxInt & 0x1f)
+
+ case ssa.OpLOONG64PRELDX:
+ // PRELDX (Rarg0), $n, $hint
+ p := s.Prog(v.Op.Asm())
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = v.Args[0].Reg()
+ p.AddRestSourceArgs([]obj.Addr{
+ {Type: obj.TYPE_CONST, Offset: int64((v.AuxInt >> 5) & 0x1fffffffff)},
+ {Type: obj.TYPE_CONST, Offset: int64((v.AuxInt >> 0) & 0x1f)},
+ })
+
+ case ssa.OpLOONG64ADDshiftLLV:
+ // ADDshiftLLV Rarg0, Rarg1, $shift
+ // ALSLV $shift, Rarg1, Rarg0, Rtmp
+ p := s.Prog(v.Op.Asm())
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = v.AuxInt
+ p.Reg = v.Args[1].Reg()
+ p.AddRestSourceReg(v.Args[0].Reg())
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = v.Reg()
+
case ssa.OpClobber, ssa.OpClobberReg:
// TODO: implement for clobberdead experiment. Nop is ok for now.
default:
@@ -952,8 +1200,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
var blockJump = map[ssa.BlockKind]struct {
asm, invasm obj.As
}{
- ssa.BlockLOONG64EQ: {loong64.ABEQ, loong64.ABNE},
- ssa.BlockLOONG64NE: {loong64.ABNE, loong64.ABEQ},
+ ssa.BlockLOONG64EQZ: {loong64.ABEQ, loong64.ABNE},
+ ssa.BlockLOONG64NEZ: {loong64.ABNE, loong64.ABEQ},
ssa.BlockLOONG64LTZ: {loong64.ABLTZ, loong64.ABGEZ},
ssa.BlockLOONG64GEZ: {loong64.ABGEZ, loong64.ABLTZ},
ssa.BlockLOONG64LEZ: {loong64.ABLEZ, loong64.ABGTZ},
@@ -970,22 +1218,7 @@ var blockJump = map[ssa.BlockKind]struct {
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
- case ssa.BlockPlain:
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
- case ssa.BlockDefer:
- // defer returns in R19:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(loong64.ABNE)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = loong64.REGZERO
- p.Reg = loong64.REG_R19
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
@@ -994,7 +1227,7 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
case ssa.BlockExit, ssa.BlockRetJmp:
case ssa.BlockRet:
s.Prog(obj.ARET)
- case ssa.BlockLOONG64EQ, ssa.BlockLOONG64NE,
+ case ssa.BlockLOONG64EQZ, ssa.BlockLOONG64NEZ,
ssa.BlockLOONG64LTZ, ssa.BlockLOONG64GEZ,
ssa.BlockLOONG64LEZ, ssa.BlockLOONG64GTZ,
ssa.BlockLOONG64BEQ, ssa.BlockLOONG64BNE,
@@ -1024,7 +1257,7 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
p.From.Type = obj.TYPE_REG
p.From.Reg = b.Controls[0].Reg()
p.Reg = b.Controls[1].Reg()
- case ssa.BlockLOONG64EQ, ssa.BlockLOONG64NE,
+ case ssa.BlockLOONG64EQZ, ssa.BlockLOONG64NEZ,
ssa.BlockLOONG64LTZ, ssa.BlockLOONG64GEZ,
ssa.BlockLOONG64LEZ, ssa.BlockLOONG64GTZ,
ssa.BlockLOONG64FPT, ssa.BlockLOONG64FPF:
@@ -1033,6 +1266,29 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
p.From.Reg = b.Controls[0].Reg()
}
}
+ case ssa.BlockLOONG64JUMPTABLE:
+ // ALSLV $3, Rarg0, Rarg1, REGTMP
+ // MOVV (REGTMP), REGTMP
+ // JMP (REGTMP)
+ p := s.Prog(loong64.AALSLV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = 3 // idx*8
+ p.Reg = b.Controls[0].Reg()
+ p.AddRestSourceReg(b.Controls[1].Reg())
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = loong64.REGTMP
+ p1 := s.Prog(loong64.AMOVV)
+ p1.From.Type = obj.TYPE_MEM
+ p1.From.Reg = loong64.REGTMP
+ p1.From.Offset = 0
+ p1.To.Type = obj.TYPE_REG
+ p1.To.Reg = loong64.REGTMP
+ p2 := s.Prog(obj.AJMP)
+ p2.To.Type = obj.TYPE_MEM
+ p2.To.Reg = loong64.REGTMP
+ // Save jump tables for later resolution of the target blocks.
+ s.JumpTables = append(s.JumpTables, b)
+
default:
b.Fatalf("branch not implemented: %s", b.LongString())
}
@@ -1056,3 +1312,32 @@ func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg in
p.Pos = p.Pos.WithNotStmt()
return p
}
+
+// move8 copies 8 bytes at src+off to dst+off.
+func move8(s *ssagen.State, src, dst, tmp int16, off int64) {
+ // MOVV off(src), tmp
+ ld := s.Prog(loong64.AMOVV)
+ ld.From.Type = obj.TYPE_MEM
+ ld.From.Reg = src
+ ld.From.Offset = off
+ ld.To.Type = obj.TYPE_REG
+ ld.To.Reg = tmp
+ // MOVV tmp, off(dst)
+ st := s.Prog(loong64.AMOVV)
+ st.From.Type = obj.TYPE_REG
+ st.From.Reg = tmp
+ st.To.Type = obj.TYPE_MEM
+ st.To.Reg = dst
+ st.To.Offset = off
+}
+
+// zero8 zeroes 8 bytes at reg+off.
+func zero8(s *ssagen.State, reg int16, off int64) {
+ // MOVV ZR, off(reg)
+ p := s.Prog(loong64.AMOVV)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = loong64.REGZERO
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = reg
+ p.To.Offset = off
+}
diff --git a/src/cmd/compile/internal/loopvar/loopvar.go b/src/cmd/compile/internal/loopvar/loopvar.go
index 030fc04c13..5a4590d299 100644
--- a/src/cmd/compile/internal/loopvar/loopvar.go
+++ b/src/cmd/compile/internal/loopvar/loopvar.go
@@ -305,6 +305,7 @@ func ForCapture(fn *ir.Func) []VarAndLoop {
as := ir.NewAssignStmt(x.Pos(), z, tz)
as.Def = true
as.SetTypecheck(1)
+ z.Defn = as
preBody.Append(as)
dclFixups[z] = as
diff --git a/src/cmd/compile/internal/loopvar/loopvar_test.go b/src/cmd/compile/internal/loopvar/loopvar_test.go
index b19962f0fd..0265dd06ee 100644
--- a/src/cmd/compile/internal/loopvar/loopvar_test.go
+++ b/src/cmd/compile/internal/loopvar/loopvar_test.go
@@ -50,7 +50,7 @@ var cases = []testcase{
{"1", "", 0, []string{"for_nested.go"}},
}
-// TestLoopVar checks that the GOEXPERIMENT and debug flags behave as expected.
+// TestLoopVarGo1_21 checks that the GOEXPERIMENT and debug flags behave as expected.
func TestLoopVarGo1_21(t *testing.T) {
switch runtime.GOOS {
case "linux", "darwin":
diff --git a/src/cmd/compile/internal/mips/ggen.go b/src/cmd/compile/internal/mips/ggen.go
index e235ef9567..394f015589 100644
--- a/src/cmd/compile/internal/mips/ggen.go
+++ b/src/cmd/compile/internal/mips/ggen.go
@@ -12,34 +12,15 @@ import (
"cmd/internal/obj/mips"
)
-// TODO(mips): implement DUFFZERO
func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
-
- if cnt == 0 {
- return p
+ if cnt%int64(types.PtrSize) != 0 {
+ panic("zeroed region not aligned")
}
- if cnt < int64(4*types.PtrSize) {
- for i := int64(0); i < cnt; i += int64(types.PtrSize) {
- p = pp.Append(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, base.Ctxt.Arch.FixedFrameSize+off+i)
- }
- } else {
- //fmt.Printf("zerorange frame:%v, lo: %v, hi:%v \n", frame ,lo, hi)
- // ADD $(FIXED_FRAME+frame+lo-4), SP, r1
- // ADD $cnt, r1, r2
- // loop:
- // MOVW R0, (Widthptr)r1
- // ADD $Widthptr, r1
- // BNE r1, r2, loop
- p = pp.Append(p, mips.AADD, obj.TYPE_CONST, 0, base.Ctxt.Arch.FixedFrameSize+off-4, obj.TYPE_REG, mips.REGRT1, 0)
- p.Reg = mips.REGSP
- p = pp.Append(p, mips.AADD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, mips.REGRT2, 0)
- p.Reg = mips.REGRT1
- p = pp.Append(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGRT1, int64(types.PtrSize))
- p1 := p
- p = pp.Append(p, mips.AADD, obj.TYPE_CONST, 0, int64(types.PtrSize), obj.TYPE_REG, mips.REGRT1, 0)
- p = pp.Append(p, mips.ABNE, obj.TYPE_REG, mips.REGRT1, 0, obj.TYPE_BRANCH, 0, 0)
- p.Reg = mips.REGRT2
- p.To.SetTarget(p1)
+
+ for cnt != 0 {
+ p = pp.Append(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, base.Ctxt.Arch.FixedFrameSize+off)
+ cnt -= int64(types.PtrSize)
+ off += int64(types.PtrSize)
}
return p
diff --git a/src/cmd/compile/internal/mips/ssa.go b/src/cmd/compile/internal/mips/ssa.go
index bfccafd8e5..c338fdc6b3 100644
--- a/src/cmd/compile/internal/mips/ssa.go
+++ b/src/cmd/compile/internal/mips/ssa.go
@@ -15,6 +15,7 @@ import (
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/mips"
+ "internal/abi"
)
// isFPreg reports whether r is an FP register.
@@ -486,18 +487,167 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Name = obj.NAME_EXTERN
// AuxInt encodes how many buffer entries we need.
p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
- case ssa.OpMIPSLoweredPanicBoundsA, ssa.OpMIPSLoweredPanicBoundsB, ssa.OpMIPSLoweredPanicBoundsC:
- p := s.Prog(obj.ACALL)
+
+ case ssa.OpMIPSLoweredPanicBoundsRR, ssa.OpMIPSLoweredPanicBoundsRC, ssa.OpMIPSLoweredPanicBoundsCR, ssa.OpMIPSLoweredPanicBoundsCC,
+ ssa.OpMIPSLoweredPanicExtendRR, ssa.OpMIPSLoweredPanicExtendRC:
+ // Compute the constant we put in the PCData entry for this call.
+ code, signed := ssa.BoundsKind(v.AuxInt).Code()
+ xIsReg := false
+ yIsReg := false
+ xVal := 0
+ yVal := 0
+ extend := false
+ switch v.Op {
+ case ssa.OpMIPSLoweredPanicBoundsRR:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - mips.REG_R1)
+ yIsReg = true
+ yVal = int(v.Args[1].Reg() - mips.REG_R1)
+ case ssa.OpMIPSLoweredPanicExtendRR:
+ extend = true
+ xIsReg = true
+ hi := int(v.Args[0].Reg() - mips.REG_R1)
+ lo := int(v.Args[1].Reg() - mips.REG_R1)
+ xVal = hi<<2 + lo // encode 2 register numbers
+ yIsReg = true
+ yVal = int(v.Args[2].Reg() - mips.REG_R1)
+ case ssa.OpMIPSLoweredPanicBoundsRC:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - mips.REG_R1)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ if yVal == xVal {
+ yVal = 1
+ }
+ p := s.Prog(mips.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = mips.REG_R1 + int16(yVal)
+ }
+ case ssa.OpMIPSLoweredPanicExtendRC:
+ extend = true
+ xIsReg = true
+ hi := int(v.Args[0].Reg() - mips.REG_R1)
+ lo := int(v.Args[1].Reg() - mips.REG_R1)
+ xVal = hi<<2 + lo // encode 2 register numbers
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ for yVal == hi || yVal == lo {
+ yVal++
+ }
+ p := s.Prog(mips.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = mips.REG_R1 + int16(yVal)
+ }
+ case ssa.OpMIPSLoweredPanicBoundsCR:
+ yIsReg = true
+ yVal = int(v.Args[0].Reg() - mips.REG_R1)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else if signed && int64(int32(c)) == c || !signed && int64(uint32(c)) == c {
+ // Move constant to a register
+ xIsReg = true
+ if xVal == yVal {
+ xVal = 1
+ }
+ p := s.Prog(mips.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = mips.REG_R1 + int16(xVal)
+ } else {
+ // Move constant to two registers
+ extend = true
+ xIsReg = true
+ hi := 0
+ lo := 1
+ if hi == yVal {
+ hi = 2
+ }
+ if lo == yVal {
+ lo = 2
+ }
+ xVal = hi<<2 + lo
+ p := s.Prog(mips.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c >> 32
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = mips.REG_R1 + int16(hi)
+ p = s.Prog(mips.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = int64(int32(c))
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = mips.REG_R1 + int16(lo)
+ }
+ case ssa.OpMIPSLoweredPanicBoundsCC:
+ c := v.Aux.(ssa.PanicBoundsCC).Cx
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else if signed && int64(int32(c)) == c || !signed && int64(uint32(c)) == c {
+ // Move constant to a register
+ xIsReg = true
+ p := s.Prog(mips.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = mips.REG_R1 + int16(xVal)
+ } else {
+ // Move constant to two registers
+ extend = true
+ xIsReg = true
+ hi := 0
+ lo := 1
+ xVal = hi<<2 + lo
+ p := s.Prog(mips.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c >> 32
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = mips.REG_R1 + int16(hi)
+ p = s.Prog(mips.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = int64(int32(c))
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = mips.REG_R1 + int16(lo)
+ }
+ c = v.Aux.(ssa.PanicBoundsCC).Cy
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ yVal = 2
+ p := s.Prog(mips.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = mips.REG_R1 + int16(yVal)
+ }
+ }
+ c := abi.BoundsEncode(code, signed, xIsReg, yIsReg, xVal, yVal)
+
+ p := s.Prog(obj.APCDATA)
+ p.From.SetConst(abi.PCDATA_PanicBounds)
+ p.To.SetConst(int64(c))
+ p = s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
- s.UseArgs(8) // space used in callee args area by assembly stubs
- case ssa.OpMIPSLoweredPanicExtendA, ssa.OpMIPSLoweredPanicExtendB, ssa.OpMIPSLoweredPanicExtendC:
- p := s.Prog(obj.ACALL)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ssagen.ExtendCheckFunc[v.AuxInt]
- s.UseArgs(12) // space used in callee args area by assembly stubs
+ if extend {
+ p.To.Sym = ir.Syms.PanicExtend
+ } else {
+ p.To.Sym = ir.Syms.PanicBounds
+ }
+
case ssa.OpMIPSLoweredAtomicLoad8,
ssa.OpMIPSLoweredAtomicLoad32:
s.Prog(mips.ASYNC)
@@ -804,6 +954,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p := s.Prog(obj.AGETCALLERPC)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
+ case ssa.OpMIPSLoweredPubBarrier:
+ // SYNC
+ s.Prog(v.Op.Asm())
case ssa.OpClobber, ssa.OpClobberReg:
// TODO: implement for clobberdead experiment. Nop is ok for now.
default:
@@ -826,22 +979,7 @@ var blockJump = map[ssa.BlockKind]struct {
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
- case ssa.BlockPlain:
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
- case ssa.BlockDefer:
- // defer returns in R1:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(mips.ABNE)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = mips.REGZERO
- p.Reg = mips.REG_R1
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
diff --git a/src/cmd/compile/internal/mips64/ggen.go b/src/cmd/compile/internal/mips64/ggen.go
index 5f3f3e64d9..740d68e335 100644
--- a/src/cmd/compile/internal/mips64/ggen.go
+++ b/src/cmd/compile/internal/mips64/ggen.go
@@ -5,7 +5,6 @@
package mips64
import (
- "cmd/compile/internal/ir"
"cmd/compile/internal/objw"
"cmd/compile/internal/types"
"cmd/internal/obj"
@@ -13,37 +12,14 @@ import (
)
func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
- if cnt == 0 {
- return p
+ if cnt%int64(types.PtrSize) != 0 {
+ panic("zeroed region not aligned")
}
- if cnt < int64(4*types.PtrSize) {
- for i := int64(0); i < cnt; i += int64(types.PtrSize) {
- p = pp.Append(p, mips.AMOVV, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, 8+off+i)
- }
- } else if cnt <= int64(128*types.PtrSize) {
- p = pp.Append(p, mips.AADDV, obj.TYPE_CONST, 0, 8+off-8, obj.TYPE_REG, mips.REGRT1, 0)
- p.Reg = mips.REGSP
- p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ir.Syms.Duffzero
- p.To.Offset = 8 * (128 - cnt/int64(types.PtrSize))
- } else {
- // ADDV $(8+frame+lo-8), SP, r1
- // ADDV $cnt, r1, r2
- // loop:
- // MOVV R0, (Widthptr)r1
- // ADDV $Widthptr, r1
- // BNE r1, r2, loop
- p = pp.Append(p, mips.AADDV, obj.TYPE_CONST, 0, 8+off-8, obj.TYPE_REG, mips.REGRT1, 0)
- p.Reg = mips.REGSP
- p = pp.Append(p, mips.AADDV, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, mips.REGRT2, 0)
- p.Reg = mips.REGRT1
- p = pp.Append(p, mips.AMOVV, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGRT1, int64(types.PtrSize))
- p1 := p
- p = pp.Append(p, mips.AADDV, obj.TYPE_CONST, 0, int64(types.PtrSize), obj.TYPE_REG, mips.REGRT1, 0)
- p = pp.Append(p, mips.ABNE, obj.TYPE_REG, mips.REGRT1, 0, obj.TYPE_BRANCH, 0, 0)
- p.Reg = mips.REGRT2
- p.To.SetTarget(p1)
+
+ for cnt != 0 {
+ p = pp.Append(p, mips.AMOVV, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, off+8)
+ cnt -= int64(types.PtrSize)
+ off += int64(types.PtrSize)
}
return p
diff --git a/src/cmd/compile/internal/mips64/ssa.go b/src/cmd/compile/internal/mips64/ssa.go
index 0c0dc6e495..6eae8fe0dd 100644
--- a/src/cmd/compile/internal/mips64/ssa.go
+++ b/src/cmd/compile/internal/mips64/ssa.go
@@ -15,6 +15,7 @@ import (
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/mips"
+ "internal/abi"
)
// isFPreg reports whether r is an FP register.
@@ -114,7 +115,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = y
}
- case ssa.OpMIPS64MOVVnop:
+ case ssa.OpMIPS64MOVVnop, ssa.OpMIPS64ZERO:
// nothing to do
case ssa.OpLoadReg:
if v.Type.IsFlags() {
@@ -300,16 +301,6 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
ssagen.AddAux(&p.To, v)
- case ssa.OpMIPS64MOVBstorezero,
- ssa.OpMIPS64MOVHstorezero,
- ssa.OpMIPS64MOVWstorezero,
- ssa.OpMIPS64MOVVstorezero:
- p := s.Prog(v.Op.Asm())
- p.From.Type = obj.TYPE_REG
- p.From.Reg = mips.REGZERO
- p.To.Type = obj.TYPE_MEM
- p.To.Reg = v.Args[0].Reg()
- ssagen.AddAux(&p.To, v)
case ssa.OpMIPS64MOVBreg,
ssa.OpMIPS64MOVBUreg,
ssa.OpMIPS64MOVHreg,
@@ -507,12 +498,93 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Name = obj.NAME_EXTERN
// AuxInt encodes how many buffer entries we need.
p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
- case ssa.OpMIPS64LoweredPanicBoundsA, ssa.OpMIPS64LoweredPanicBoundsB, ssa.OpMIPS64LoweredPanicBoundsC:
- p := s.Prog(obj.ACALL)
+
+ case ssa.OpMIPS64LoweredPanicBoundsRR, ssa.OpMIPS64LoweredPanicBoundsRC, ssa.OpMIPS64LoweredPanicBoundsCR, ssa.OpMIPS64LoweredPanicBoundsCC:
+ // Compute the constant we put in the PCData entry for this call.
+ code, signed := ssa.BoundsKind(v.AuxInt).Code()
+ xIsReg := false
+ yIsReg := false
+ xVal := 0
+ yVal := 0
+ switch v.Op {
+ case ssa.OpMIPS64LoweredPanicBoundsRR:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - mips.REG_R1)
+ yIsReg = true
+ yVal = int(v.Args[1].Reg() - mips.REG_R1)
+ case ssa.OpMIPS64LoweredPanicBoundsRC:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - mips.REG_R1)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ if yVal == xVal {
+ yVal = 1
+ }
+ p := s.Prog(mips.AMOVV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = mips.REG_R1 + int16(yVal)
+ }
+ case ssa.OpMIPS64LoweredPanicBoundsCR:
+ yIsReg = true
+ yVal = int(v.Args[0].Reg() - mips.REG_R1)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else {
+ // Move constant to a register
+ xIsReg = true
+ if xVal == yVal {
+ xVal = 1
+ }
+ p := s.Prog(mips.AMOVV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = mips.REG_R1 + int16(xVal)
+ }
+ case ssa.OpMIPS64LoweredPanicBoundsCC:
+ c := v.Aux.(ssa.PanicBoundsCC).Cx
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else {
+ // Move constant to a register
+ xIsReg = true
+ p := s.Prog(mips.AMOVV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = mips.REG_R1 + int16(xVal)
+ }
+ c = v.Aux.(ssa.PanicBoundsCC).Cy
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ yVal = 1
+ p := s.Prog(mips.AMOVV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = mips.REG_R1 + int16(yVal)
+ }
+ }
+ c := abi.BoundsEncode(code, signed, xIsReg, yIsReg, xVal, yVal)
+
+ p := s.Prog(obj.APCDATA)
+ p.From.SetConst(abi.PCDATA_PanicBounds)
+ p.To.SetConst(int64(c))
+ p = s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
- s.UseArgs(16) // space used in callee args area by assembly stubs
+ p.To.Sym = ir.Syms.PanicBounds
+
case ssa.OpMIPS64LoweredAtomicLoad8, ssa.OpMIPS64LoweredAtomicLoad32, ssa.OpMIPS64LoweredAtomicLoad64:
as := mips.AMOVV
switch v.Op {
@@ -813,6 +885,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p := s.Prog(obj.AGETCALLERPC)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
+ case ssa.OpMIPS64LoweredPubBarrier:
+ // SYNC
+ s.Prog(v.Op.Asm())
case ssa.OpClobber, ssa.OpClobberReg:
// TODO: implement for clobberdead experiment. Nop is ok for now.
default:
@@ -835,22 +910,7 @@ var blockJump = map[ssa.BlockKind]struct {
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
- case ssa.BlockPlain:
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
- case ssa.BlockDefer:
- // defer returns in R1:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(mips.ABNE)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = mips.REGZERO
- p.Reg = mips.REG_R1
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
diff --git a/src/cmd/compile/internal/noder/doc.go b/src/cmd/compile/internal/noder/doc.go
new file mode 100644
index 0000000000..8eb67e92f0
--- /dev/null
+++ b/src/cmd/compile/internal/noder/doc.go
@@ -0,0 +1,273 @@
+// Copyright 2025 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.
+
+/*
+The Unified IR (UIR) format is implicitly defined by the package noder.
+
+At the highest level, a package encoded in UIR follows the grammar
+below.
+
+ File = Header Payload fingerprint .
+ Header = version [ flags ] sectionEnds elementEnds .
+
+ version = uint32 . // used for backward compatibility
+ flags = uint32 . // feature flags used across versions
+ sectionEnds = [10]uint32 . // defines section boundaries
+ elementEnds = []uint32 . // defines element boundaries
+ fingerprint = [8]byte . // sha256 fingerprint
+
+The payload is a series of sections. Each section has a kind which
+determines its index in the series.
+
+ SectionKind = Uint64 .
+ Payload = SectionString
+ SectionMeta
+ SectionPosBase
+ SectionPkg
+ SectionName
+ SectionType
+ SectionObj
+ SectionObjExt // TODO(markfreeman) Define.
+ SectionObjDict // TODO(markfreeman) Define.
+ SectionBody // TODO(markfreeman) Define.
+ .
+
+# Sections
+A section is a series of elements of a type determined by the section's
+kind. Go constructs are mapped onto one or more elements with possibly
+different types; in that case, the elements are in different sections.
+
+Elements are accessed using an element index relative to the start of
+the section.
+
+ RelElemIdx = Uint64 .
+
+## String Section
+String values are stored as elements in the string section. Elements
+outside the string section access string values by reference.
+
+ SectionString = { String } .
+
+Note that despite being an element, a string does not begin with a
+reference table.
+
+## Meta Section
+The meta section provides fundamental information for a package. It
+contains exactly two elements — a public root and a private root.
+
+ SectionMeta = PublicRoot
+ PrivateRoot // TODO(markfreeman): Define.
+ .
+
+The public root element identifies the package and provides references
+for all exported objects it contains.
+
+ PublicRoot = RefTable
+ [ Sync ]
+ PkgRef
+ [ HasInit ]
+ ObjectRefCount // TODO(markfreeman): Define.
+ { ObjectRef } // TODO(markfreeman): Define.
+ .
+ HasInit = Bool . // Whether the package uses any
+ // initialization functions.
+
+## PosBase Section
+This section provides position information. It is a series of PosBase
+elements.
+
+ SectionPosBase = { PosBase } .
+
+A base is either a file base or line base (produced by a line
+directive). Every base has a position, line, and column; these are
+constant for file bases and hence not encoded.
+
+ PosBase = RefTable
+ [ Sync ]
+ StringRef // the (absolute) file name for the base
+ Bool // true if a file base, else a line base
+ // The below is omitted for file bases.
+ [ Pos
+ Uint64 // line
+ Uint64 ] // column
+ .
+
+A source position Pos represents a file-absolute (line, column) pair
+and a PosBase indicating the position Pos is relative to. Positions
+without a PosBase have no line or column.
+
+ Pos = [ Sync ]
+ Bool // true if the position has a base
+ // The below is omitted if the position has no base.
+ [ Ref[PosBase]
+ Uint64 // line
+ Uint64 ] // column
+ .
+
+## Package Section
+The package section holds package information. It is a series of Pkg
+elements.
+
+ SectionPkg = { Pkg } .
+
+A Pkg element contains a (path, name) pair and a series of imported
+packages. The below package paths have special meaning.
+
+ +--------------+-----------------------------------+
+ | package path | indicates |
+ +--------------+-----------------------------------+
+ | "" | the current package |
+ | "builtin" | the fake builtin package |
+ | "unsafe" | the compiler-known unsafe package |
+ +--------------+-----------------------------------+
+
+ Pkg = RefTable
+ [ Sync ]
+ StringRef // path
+ // The below is omitted for the special package paths
+ // "builtin" and "unsafe".
+ [ StringRef // name
+ Imports ]
+ .
+ Imports = Uint64 // the number of declared imports
+ { PkgRef } // references to declared imports
+ .
+
+Note, a PkgRef is *not* equivalent to Ref[Pkg] due to an extra marker.
+
+ PkgRef = [ Sync ]
+ Ref[Pkg]
+ .
+
+## Type Section
+The type section is a series of type definition elements.
+
+ SectionType = { TypeDef } .
+
+A type definition can be in one of several formats, which are identified
+by their TypeSpec code.
+
+ TypeDef = RefTable
+ [ Sync ]
+ [ Sync ]
+ Uint64 // denotes which TypeSpec to use
+ TypeSpec
+ .
+
+ TypeSpec = TypeSpecBasic // TODO(markfreeman): Define.
+ | TypeSpecNamed // TODO(markfreeman): Define.
+ | TypeSpecPointer // TODO(markfreeman): Define.
+ | TypeSpecSlice // TODO(markfreeman): Define.
+ | TypeSpecArray // TODO(markfreeman): Define.
+ | TypeSpecChan // TODO(markfreeman): Define.
+ | TypeSpecMap // TODO(markfreeman): Define.
+ | TypeSpecSignature // TODO(markfreeman): Define.
+ | TypeSpecStruct // TODO(markfreeman): Define.
+ | TypeSpecInterface // TODO(markfreeman): Define.
+ | TypeSpecUnion // TODO(markfreeman): Define.
+ | TypeSpecTypeParam // TODO(markfreeman): Define.
+ .
+
+// TODO(markfreeman): Document the reader dictionary once we understand it more.
+To use a type elsewhere, a TypeUse is encoded.
+
+ TypeUse = [ Sync ]
+ Bool // whether it is a derived type
+ [ Uint64 ] // if derived, an index into the reader dictionary
+ [ Ref[TypeDef] ] // else, a reference to the type
+ .
+
+## Object Sections
+Information about an object (e.g. variable, function, type name, etc.)
+is split into multiple elements in different sections. Those elements
+have the same section-relative element index.
+
+### Name Section
+The name section holds a series of names.
+
+ SectionName = { Name } .
+
+Names are elements holding qualified identifiers and type information
+for objects.
+
+ Name = RefTable
+ [ Sync ]
+ [ Sync ]
+ PkgRef // the object's package
+ StringRef // the object's package-local name
+ [ Sync ]
+ Uint64 // the object's type (e.g. Var, Func, etc.)
+ .
+
+### Definition Section
+The definition section holds definitions for objects defined by the target
+package; it does not contain definitions for imported objects.
+
+ SectionObj = { ObjectDef } .
+
+Object definitions can be in one of several formats. To determine the correct
+format, the name section must be referenced; it contains a code indicating
+the object's type.
+
+ ObjectDef = RefTable
+ [ Sync ]
+ ObjectSpec
+ .
+
+ ObjectSpec = ObjectSpecConst // TODO(markfreeman) Define.
+ | ObjectSpecFunc // TODO(markfreeman) Define.
+ | ObjectSpecAlias // TODO(markfreeman) Define.
+ | ObjectSpecNamedType // TODO(markfreeman) Define.
+ | ObjectSpecVar // TODO(markfreeman) Define.
+ .
+
+To use an object definition elsewhere, an ObjectUse is encoded.
+
+ ObjectUse = [ Sync ]
+ [ Bool ]
+ Ref[ObjectDef]
+ Uint64 // the number of type arguments
+ { TypeUse } // references to the type arguments
+ .
+
+# References
+A reference table precedes every element. Each entry in the table
+contains a (section, index) pair denoting the location of the
+referenced element.
+
+ RefTable = [ Sync ]
+ Uint64 // the number of table entries
+ { RefTableEntry }
+ .
+ RefTableEntry = [ Sync ]
+ SectionKind
+ RelElemIdx
+ .
+
+Elements encode references to other elements as an index in the
+reference table — not the location of the referenced element directly.
+
+ RefTableIdx = Uint64 .
+
+To do this, the Ref[T] primitive is used as below; note that this is
+the same shape as provided by package pkgbits, just with new
+interpretation applied.
+
+ Ref[T] = [ Sync ]
+ RefTableIdx // the Uint64
+ .
+
+# Primitives
+Primitive encoding is handled separately by the pkgbits package. Check
+there for definitions of the below productions.
+
+ * Bool
+ * Int64
+ * Uint64
+ * String
+ * Ref[T]
+ * Sync
+*/
+
+package noder
diff --git a/src/cmd/compile/internal/noder/linker.go b/src/cmd/compile/internal/noder/linker.go
index 4f1cc7b612..51b03a1897 100644
--- a/src/cmd/compile/internal/noder/linker.go
+++ b/src/cmd/compile/internal/noder/linker.go
@@ -47,8 +47,8 @@ type linker struct {
// relocAll ensures that all elements specified by pr and relocs are
// copied into the output export data file, and returns the
// corresponding indices in the output.
-func (l *linker) relocAll(pr *pkgReader, relocs []pkgbits.RelocEnt) []pkgbits.RelocEnt {
- res := make([]pkgbits.RelocEnt, len(relocs))
+func (l *linker) relocAll(pr *pkgReader, relocs []pkgbits.RefTableEntry) []pkgbits.RefTableEntry {
+ res := make([]pkgbits.RefTableEntry, len(relocs))
for i, rent := range relocs {
rent.Idx = l.relocIdx(pr, rent.Kind, rent.Idx)
res[i] = rent
@@ -58,7 +58,7 @@ func (l *linker) relocAll(pr *pkgReader, relocs []pkgbits.RelocEnt) []pkgbits.Re
// relocIdx ensures a single element is copied into the output export
// data file, and returns the corresponding index in the output.
-func (l *linker) relocIdx(pr *pkgReader, k pkgbits.RelocKind, idx index) index {
+func (l *linker) relocIdx(pr *pkgReader, k pkgbits.SectionKind, idx index) index {
assert(pr != nil)
absIdx := pr.AbsIdx(k, idx)
@@ -69,11 +69,11 @@ func (l *linker) relocIdx(pr *pkgReader, k pkgbits.RelocKind, idx index) index {
var newidx index
switch k {
- case pkgbits.RelocString:
+ case pkgbits.SectionString:
newidx = l.relocString(pr, idx)
- case pkgbits.RelocPkg:
+ case pkgbits.SectionPkg:
newidx = l.relocPkg(pr, idx)
- case pkgbits.RelocObj:
+ case pkgbits.SectionObj:
newidx = l.relocObj(pr, idx)
default:
@@ -84,7 +84,7 @@ func (l *linker) relocIdx(pr *pkgReader, k pkgbits.RelocKind, idx index) index {
// if we do external relocations.
w := l.pw.NewEncoderRaw(k)
- l.relocCommon(pr, &w, k, idx)
+ l.relocCommon(pr, w, k, idx)
newidx = w.Idx
}
@@ -113,8 +113,8 @@ func (l *linker) relocPkg(pr *pkgReader, idx index) index {
return newidx
}
- r := pr.NewDecoder(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef)
- w := l.pw.NewEncoder(pkgbits.RelocPkg, pkgbits.SyncPkgDef)
+ r := pr.NewDecoder(pkgbits.SectionPkg, idx, pkgbits.SyncPkgDef)
+ w := l.pw.NewEncoder(pkgbits.SectionPkg, pkgbits.SyncPkgDef)
l.pkgs[path] = w.Idx
// TODO(mdempsky): We end up leaving an empty string reference here
@@ -158,19 +158,19 @@ func (l *linker) relocObj(pr *pkgReader, idx index) index {
assert(tag2 != pkgbits.ObjStub)
}
- w := l.pw.NewEncoderRaw(pkgbits.RelocObj)
- wext := l.pw.NewEncoderRaw(pkgbits.RelocObjExt)
- wname := l.pw.NewEncoderRaw(pkgbits.RelocName)
- wdict := l.pw.NewEncoderRaw(pkgbits.RelocObjDict)
+ w := l.pw.NewEncoderRaw(pkgbits.SectionObj)
+ wext := l.pw.NewEncoderRaw(pkgbits.SectionObjExt)
+ wname := l.pw.NewEncoderRaw(pkgbits.SectionName)
+ wdict := l.pw.NewEncoderRaw(pkgbits.SectionObjDict)
l.decls[sym] = w.Idx
assert(wext.Idx == w.Idx)
assert(wname.Idx == w.Idx)
assert(wdict.Idx == w.Idx)
- l.relocCommon(pr, &w, pkgbits.RelocObj, idx)
- l.relocCommon(pr, &wname, pkgbits.RelocName, idx)
- l.relocCommon(pr, &wdict, pkgbits.RelocObjDict, idx)
+ l.relocCommon(pr, w, pkgbits.SectionObj, idx)
+ l.relocCommon(pr, wname, pkgbits.SectionName, idx)
+ l.relocCommon(pr, wdict, pkgbits.SectionObjDict, idx)
// Generic types and functions won't have definitions, and imported
// objects may not either.
@@ -181,15 +181,15 @@ func (l *linker) relocObj(pr *pkgReader, idx index) index {
wext.Sync(pkgbits.SyncObject1)
switch tag {
case pkgbits.ObjFunc:
- l.relocFuncExt(&wext, obj)
+ l.relocFuncExt(wext, obj)
case pkgbits.ObjType:
- l.relocTypeExt(&wext, obj)
+ l.relocTypeExt(wext, obj)
case pkgbits.ObjVar:
- l.relocVarExt(&wext, obj)
+ l.relocVarExt(wext, obj)
}
wext.Flush()
} else {
- l.relocCommon(pr, &wext, pkgbits.RelocObjExt, idx)
+ l.relocCommon(pr, wext, pkgbits.SectionObjExt, idx)
}
// Check if we need to export the inline bodies for functions and
@@ -247,12 +247,12 @@ func (l *linker) exportBody(obj *ir.Name, local bool) {
pri, ok := bodyReaderFor(fn)
assert(ok)
- l.bodies[sym] = l.relocIdx(pri.pr, pkgbits.RelocBody, pri.idx)
+ l.bodies[sym] = l.relocIdx(pri.pr, pkgbits.SectionBody, pri.idx)
}
// relocCommon copies the specified element from pr into w,
// recursively relocating any referenced elements as well.
-func (l *linker) relocCommon(pr *pkgReader, w *pkgbits.Encoder, k pkgbits.RelocKind, idx index) {
+func (l *linker) relocCommon(pr *pkgReader, w *pkgbits.Encoder, k pkgbits.SectionKind, idx index) {
r := pr.NewDecoderRaw(k, idx)
w.Relocs = l.relocAll(pr, r.Relocs)
io.Copy(&w.Data, &r.Data)
diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go
index 7905c374c5..79a9078333 100644
--- a/src/cmd/compile/internal/noder/noder.go
+++ b/src/cmd/compile/internal/noder/noder.go
@@ -162,6 +162,7 @@ var allowedStdPragmas = map[string]bool{
"go:cgo_ldflag": true,
"go:cgo_dynamic_linker": true,
"go:embed": true,
+ "go:fix": true,
"go:generate": true,
}
@@ -457,7 +458,7 @@ func Renameinit() *types.Sym {
func checkEmbed(decl *syntax.VarDecl, haveEmbed, withinFunc bool) error {
switch {
case !haveEmbed:
- return errors.New("go:embed only allowed in Go files that import \"embed\"")
+ return errors.New("go:embed requires import \"embed\" (or import _ \"embed\", if package is not used)")
case len(decl.NameList) > 1:
return errors.New("go:embed cannot apply to multiple vars")
case decl.Values != nil:
diff --git a/src/cmd/compile/internal/noder/posmap.go b/src/cmd/compile/internal/noder/posmap.go
index 439daf454e..9b02765e95 100644
--- a/src/cmd/compile/internal/noder/posmap.go
+++ b/src/cmd/compile/internal/noder/posmap.go
@@ -23,7 +23,6 @@ type poser interface{ Pos() syntax.Pos }
type ender interface{ End() syntax.Pos }
func (m *posMap) pos(p poser) src.XPos { return m.makeXPos(p.Pos()) }
-func (m *posMap) end(p ender) src.XPos { return m.makeXPos(p.End()) }
func (m *posMap) makeXPos(pos syntax.Pos) src.XPos {
// Predeclared objects (e.g., the result parameter for error.Error)
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go
index eca66487fa..a8a45b0269 100644
--- a/src/cmd/compile/internal/noder/reader.go
+++ b/src/cmd/compile/internal/noder/reader.go
@@ -55,9 +55,9 @@ func newPkgReader(pr pkgbits.PkgDecoder) *pkgReader {
return &pkgReader{
PkgDecoder: pr,
- posBases: make([]*src.PosBase, pr.NumElems(pkgbits.RelocPosBase)),
- pkgs: make([]*types.Pkg, pr.NumElems(pkgbits.RelocPkg)),
- typs: make([]*types.Type, pr.NumElems(pkgbits.RelocType)),
+ posBases: make([]*src.PosBase, pr.NumElems(pkgbits.SectionPosBase)),
+ pkgs: make([]*types.Pkg, pr.NumElems(pkgbits.SectionPkg)),
+ typs: make([]*types.Type, pr.NumElems(pkgbits.SectionType)),
newindex: make([]index, pr.TotalElems()),
}
@@ -74,7 +74,7 @@ type pkgReaderIndex struct {
synthetic func(pos src.XPos, r *reader)
}
-func (pri pkgReaderIndex) asReader(k pkgbits.RelocKind, marker pkgbits.SyncMarker) *reader {
+func (pri pkgReaderIndex) asReader(k pkgbits.SectionKind, marker pkgbits.SyncMarker) *reader {
if pri.synthetic != nil {
return &reader{synthetic: pri.synthetic}
}
@@ -85,7 +85,7 @@ func (pri pkgReaderIndex) asReader(k pkgbits.RelocKind, marker pkgbits.SyncMarke
return r
}
-func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx index, marker pkgbits.SyncMarker) *reader {
+func (pr *pkgReader) newReader(k pkgbits.SectionKind, idx index, marker pkgbits.SyncMarker) *reader {
return &reader{
Decoder: pr.NewDecoder(k, idx, marker),
p: pr,
@@ -255,7 +255,7 @@ func (r *reader) pos0() src.Pos {
// posBase reads a position base from the bitstream.
func (r *reader) posBase() *src.PosBase {
- return r.inlPosBase(r.p.posBaseIdx(r.Reloc(pkgbits.RelocPosBase)))
+ return r.inlPosBase(r.p.posBaseIdx(r.Reloc(pkgbits.SectionPosBase)))
}
// posBaseIdx returns the specified position base, reading it first if
@@ -265,7 +265,7 @@ func (pr *pkgReader) posBaseIdx(idx index) *src.PosBase {
return b
}
- r := pr.newReader(pkgbits.RelocPosBase, idx, pkgbits.SyncPosBase)
+ r := pr.newReader(pkgbits.SectionPosBase, idx, pkgbits.SyncPosBase)
var b *src.PosBase
absFilename := r.String()
@@ -336,7 +336,7 @@ func (r *reader) inlPos(xpos src.XPos) src.XPos {
// pkg reads a package reference from the bitstream.
func (r *reader) pkg() *types.Pkg {
r.Sync(pkgbits.SyncPkg)
- return r.p.pkgIdx(r.Reloc(pkgbits.RelocPkg))
+ return r.p.pkgIdx(r.Reloc(pkgbits.SectionPkg))
}
// pkgIdx returns the specified package from the export data, reading
@@ -346,7 +346,7 @@ func (pr *pkgReader) pkgIdx(idx index) *types.Pkg {
return pkg
}
- pkg := pr.newReader(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef).doPkg()
+ pkg := pr.newReader(pkgbits.SectionPkg, idx, pkgbits.SyncPkgDef).doPkg()
pr.pkgs[idx] = pkg
return pkg
}
@@ -393,7 +393,7 @@ func (r *reader) typInfo() typeInfo {
if r.Bool() {
return typeInfo{idx: index(r.Len()), derived: true}
}
- return typeInfo{idx: r.Reloc(pkgbits.RelocType), derived: false}
+ return typeInfo{idx: r.Reloc(pkgbits.SectionType), derived: false}
}
// typListIdx returns a list of the specified types, resolving derived
@@ -423,7 +423,7 @@ func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict, wrapped bool) *type
return typ
}
- r := pr.newReader(pkgbits.RelocType, idx, pkgbits.SyncTypeIdx)
+ r := pr.newReader(pkgbits.SectionType, idx, pkgbits.SyncTypeIdx)
r.dict = dict
typ := r.doTyp()
@@ -643,7 +643,7 @@ func (r *reader) objInfo() objInfo {
if r.Version().Has(pkgbits.DerivedFuncInstance) {
assert(!r.Bool())
}
- idx := r.Reloc(pkgbits.RelocObj)
+ idx := r.Reloc(pkgbits.SectionObj)
explicits := make([]typeInfo, r.Len())
for i := range explicits {
@@ -685,7 +685,7 @@ func (pr *pkgReader) objIdx(idx index, implicits, explicits []*types.Type, shape
// Other sources of internal failure (such as duplicate definitions) still fail
// the build.
func (pr *pkgReader) objIdxMayFail(idx index, implicits, explicits []*types.Type, shaped bool) (ir.Node, error) {
- rname := pr.newReader(pkgbits.RelocName, idx, pkgbits.SyncObject1)
+ rname := pr.newReader(pkgbits.SectionName, idx, pkgbits.SyncObject1)
_, sym := rname.qualifiedIdent()
tag := pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj))
@@ -714,8 +714,8 @@ func (pr *pkgReader) objIdxMayFail(idx index, implicits, explicits []*types.Type
return sym.Def.(*ir.Name), nil
}
- r := pr.newReader(pkgbits.RelocObj, idx, pkgbits.SyncObject1)
- rext := pr.newReader(pkgbits.RelocObjExt, idx, pkgbits.SyncObject1)
+ r := pr.newReader(pkgbits.SectionObj, idx, pkgbits.SyncObject1)
+ rext := pr.newReader(pkgbits.SectionObjExt, idx, pkgbits.SyncObject1)
r.dict = dict
rext.dict = dict
@@ -762,7 +762,7 @@ func (pr *pkgReader) objIdxMayFail(idx index, implicits, explicits []*types.Type
if hack {
if sym.Def != nil {
name = sym.Def.(*ir.Name)
- assert(name.Type() == typ)
+ assert(types.IdenticalStrict(name.Type(), typ))
return name, nil
}
sym.Def = name
@@ -930,8 +930,19 @@ func shapify(targ *types.Type, basic bool) *types.Type {
// types, and discarding struct field names and tags. However, we'll
// need to start tracking how type parameters are actually used to
// implement some of these optimizations.
+ pointerShaping := basic && targ.IsPtr() && !targ.Elem().NotInHeap()
+ // The exception is when the type parameter is a pointer to a type
+ // which `Type.HasShape()` returns true, but `Type.IsShape()` returns
+ // false, like `*[]go.shape.T`. This is because the type parameter is
+ // used to instantiate a generic function inside another generic function.
+ // In this case, we want to keep the targ as-is, otherwise, we may lose the
+ // original type after `*[]go.shape.T` is shapified to `*go.shape.uint8`.
+ // See issue #54535, #71184.
+ if pointerShaping && !targ.Elem().IsShape() && targ.Elem().HasShape() {
+ return targ
+ }
under := targ.Underlying()
- if basic && targ.IsPtr() && !targ.Elem().NotInHeap() {
+ if pointerShaping {
under = types.NewPtr(types.Types[types.TUINT8])
}
@@ -959,7 +970,7 @@ func shapify(targ *types.Type, basic bool) *types.Type {
// objDictIdx reads and returns the specified object dictionary.
func (pr *pkgReader) objDictIdx(sym *types.Sym, idx index, implicits, explicits []*types.Type, shaped bool) (*readerDict, error) {
- r := pr.newReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1)
+ r := pr.newReader(pkgbits.SectionObjDict, idx, pkgbits.SyncObject1)
dict := readerDict{
shaped: shaped,
@@ -984,7 +995,7 @@ func (pr *pkgReader) objDictIdx(sym *types.Sym, idx index, implicits, explicits
dict.derived = make([]derivedInfo, r.Len())
dict.derivedTypes = make([]*types.Type, len(dict.derived))
for i := range dict.derived {
- dict.derived[i] = derivedInfo{idx: r.Reloc(pkgbits.RelocType)}
+ dict.derived[i] = derivedInfo{idx: r.Reloc(pkgbits.SectionType)}
if r.Version().Has(pkgbits.DerivedInfoNeeded) {
assert(!r.Bool())
}
@@ -1278,7 +1289,7 @@ func (r *reader) addBody(fn *ir.Func, method *types.Sym) {
// generic functions; see comment in funcExt.
assert(fn.Nname.Defn != nil)
- idx := r.Reloc(pkgbits.RelocBody)
+ idx := r.Reloc(pkgbits.SectionBody)
pri := pkgReaderIndex{r.p, idx, r.dict, method, nil}
bodyReader[fn] = pri
@@ -1292,7 +1303,7 @@ func (r *reader) addBody(fn *ir.Func, method *types.Sym) {
}
func (pri pkgReaderIndex) funcBody(fn *ir.Func) {
- r := pri.asReader(pkgbits.RelocBody, pkgbits.SyncFuncBody)
+ r := pri.asReader(pkgbits.SectionBody, pkgbits.SyncFuncBody)
r.funcBody(fn)
}
@@ -2420,8 +2431,17 @@ func (r *reader) expr() (res ir.Node) {
case exprNew:
pos := r.pos()
- typ := r.exprType()
- return typecheck.Expr(ir.NewUnaryExpr(pos, ir.ONEW, typ))
+ if r.Bool() {
+ // new(expr) -> tmp := expr; &tmp
+ x := r.expr()
+ x = typecheck.DefaultLit(x, nil) // See TODO in exprConvert case.
+ var init ir.Nodes
+ addr := ir.NewAddrExpr(pos, r.tempCopy(pos, x, &init))
+ addr.SetInit(init)
+ return typecheck.Expr(addr)
+ }
+ // new(T)
+ return typecheck.Expr(ir.NewUnaryExpr(pos, ir.ONEW, r.exprType()))
case exprSizeof:
return ir.NewUintptr(r.pos(), r.typ().Size())
@@ -2591,7 +2611,7 @@ func (r *reader) funcInst(pos src.XPos) (wrapperFn, baseFn, dictPtr ir.Node) {
}
func (pr *pkgReader) objDictName(idx index, implicits, explicits []*types.Type) *ir.Name {
- rname := pr.newReader(pkgbits.RelocName, idx, pkgbits.SyncObject1)
+ rname := pr.newReader(pkgbits.SectionName, idx, pkgbits.SyncObject1)
_, sym := rname.qualifiedIdent()
tag := pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj))
@@ -2941,6 +2961,7 @@ func (r *reader) multiExpr() []ir.Node {
as.Def = true
for i := range results {
tmp := r.temp(pos, r.typ())
+ tmp.Defn = as
as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, tmp))
as.Lhs.Append(tmp)
@@ -3228,6 +3249,7 @@ func (r *reader) exprType() ir.Node {
var rtype, itab ir.Node
if r.Bool() {
+ // non-empty interface
typ, rtype, _, _, itab = r.itab(pos)
if !typ.IsInterface() {
rtype = nil // TODO(mdempsky): Leave set?
@@ -3437,7 +3459,7 @@ func unifiedInlineCall(callerfn *ir.Func, call *ir.CallExpr, fn *ir.Func, inlInd
expandInline(fn, pri)
}
- r := pri.asReader(pkgbits.RelocBody, pkgbits.SyncFuncBody)
+ r := pri.asReader(pkgbits.SectionBody, pkgbits.SyncFuncBody)
tmpfn := ir.NewFunc(fn.Pos(), fn.Nname.Pos(), callerfn.Sym(), fn.Type())
@@ -3626,7 +3648,7 @@ func expandInline(fn *ir.Func, pri pkgReaderIndex) {
tmpfn.ClosureVars = fn.ClosureVars
{
- r := pri.asReader(pkgbits.RelocBody, pkgbits.SyncFuncBody)
+ r := pri.asReader(pkgbits.SectionBody, pkgbits.SyncFuncBody)
// Don't change parameter's Sym/Nname fields.
r.funarghack = true
@@ -3650,17 +3672,6 @@ func expandInline(fn *ir.Func, pri pkgReaderIndex) {
typecheck.Target.Funcs = typecheck.Target.Funcs[:topdcls]
}
-// usedLocals returns a set of local variables that are used within body.
-func usedLocals(body []ir.Node) ir.NameSet {
- var used ir.NameSet
- ir.VisitList(body, func(n ir.Node) {
- if n, ok := n.(*ir.Name); ok && n.Op() == ir.ONAME && n.Class == ir.PAUTO {
- used.Add(n)
- }
- })
- return used
-}
-
// @@@ Method wrappers
//
// Here we handle constructing "method wrappers," alternative entry
diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go
index 59e8c1013f..05f4483d0d 100644
--- a/src/cmd/compile/internal/noder/unified.go
+++ b/src/cmd/compile/internal/noder/unified.go
@@ -7,7 +7,6 @@ package noder
import (
"cmp"
"fmt"
- "internal/buildcfg"
"internal/pkgbits"
"internal/types/errors"
"io"
@@ -199,7 +198,7 @@ func unified(m posMap, noders []*noder) {
localPkgReader = newPkgReader(pkgbits.NewPkgDecoder(types.LocalPkg.Path, data))
readPackage(localPkgReader, types.LocalPkg, true)
- r := localPkgReader.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate)
+ r := localPkgReader.newReader(pkgbits.SectionMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate)
r.pkgInit(types.LocalPkg, target)
readBodies(target, false)
@@ -322,8 +321,8 @@ func writePkgStub(m posMap, noders []*noder) string {
pw.collectDecls(noders)
- publicRootWriter := pw.newWriter(pkgbits.RelocMeta, pkgbits.SyncPublic)
- privateRootWriter := pw.newWriter(pkgbits.RelocMeta, pkgbits.SyncPrivate)
+ publicRootWriter := pw.newWriter(pkgbits.SectionMeta, pkgbits.SyncPublic)
+ privateRootWriter := pw.newWriter(pkgbits.SectionMeta, pkgbits.SyncPrivate)
assert(publicRootWriter.Idx == pkgbits.PublicRootIdx)
assert(privateRootWriter.Idx == pkgbits.PrivateRootIdx)
@@ -406,7 +405,7 @@ func freePackage(pkg *types2.Package) {
// import.
func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) {
{
- r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
+ r := pr.newReader(pkgbits.SectionMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
pkg := r.pkg()
// This error can happen if "go tool compile" is called with wrong "-p" flag, see issue #54542.
@@ -424,7 +423,7 @@ func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) {
if r.Version().Has(pkgbits.DerivedFuncInstance) {
assert(!r.Bool())
}
- idx := r.Reloc(pkgbits.RelocObj)
+ idx := r.Reloc(pkgbits.SectionObj)
assert(r.Len() == 0)
path, name, code := r.p.PeekObj(idx)
@@ -437,7 +436,7 @@ func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) {
}
if !localStub {
- r := pr.newReader(pkgbits.RelocMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate)
+ r := pr.newReader(pkgbits.SectionMeta, pkgbits.PrivateRootIdx, pkgbits.SyncPrivate)
if r.Bool() {
sym := importpkg.Lookup(".inittask")
@@ -449,7 +448,7 @@ func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) {
for i, n := 0, r.Len(); i < n; i++ {
path := r.String()
name := r.String()
- idx := r.Reloc(pkgbits.RelocBody)
+ idx := r.Reloc(pkgbits.SectionBody)
sym := types.NewPkg(path, "").Lookup(name)
if _, ok := importBodyReader[sym]; !ok {
@@ -464,11 +463,8 @@ func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) {
// writeUnifiedExport writes to `out` the finalized, self-contained
// Unified IR export data file for the current compilation unit.
func writeUnifiedExport(out io.Writer) {
- // Use V2 as the encoded version aliastypeparams GOEXPERIMENT is enabled.
- version := pkgbits.V1
- if buildcfg.Experiment.AliasTypeParams {
- version = pkgbits.V2
- }
+ // Use V2 as the encoded version for aliastypeparams.
+ version := pkgbits.V2
l := linker{
pw: pkgbits.NewPkgEncoder(version, base.Debug.SyncFrames),
@@ -477,8 +473,8 @@ func writeUnifiedExport(out io.Writer) {
bodies: make(map[*types.Sym]index),
}
- publicRootWriter := l.pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPublic)
- privateRootWriter := l.pw.NewEncoder(pkgbits.RelocMeta, pkgbits.SyncPrivate)
+ publicRootWriter := l.pw.NewEncoder(pkgbits.SectionMeta, pkgbits.SyncPublic)
+ privateRootWriter := l.pw.NewEncoder(pkgbits.SectionMeta, pkgbits.SyncPrivate)
assert(publicRootWriter.Idx == pkgbits.PublicRootIdx)
assert(privateRootWriter.Idx == pkgbits.PrivateRootIdx)
@@ -486,10 +482,10 @@ func writeUnifiedExport(out io.Writer) {
{
pr := localPkgReader
- r := pr.NewDecoder(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
+ r := pr.NewDecoder(pkgbits.SectionMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
r.Sync(pkgbits.SyncPkg)
- selfPkgIdx = l.relocIdx(pr, pkgbits.RelocPkg, r.Reloc(pkgbits.RelocPkg))
+ selfPkgIdx = l.relocIdx(pr, pkgbits.SectionPkg, r.Reloc(pkgbits.SectionPkg))
if r.Version().Has(pkgbits.HasInit) {
r.Bool()
@@ -500,7 +496,7 @@ func writeUnifiedExport(out io.Writer) {
if r.Version().Has(pkgbits.DerivedFuncInstance) {
assert(!r.Bool())
}
- idx := r.Reloc(pkgbits.RelocObj)
+ idx := r.Reloc(pkgbits.SectionObj)
assert(r.Len() == 0)
xpath, xname, xtag := pr.PeekObj(idx)
@@ -508,7 +504,7 @@ func writeUnifiedExport(out io.Writer) {
assert(xtag != pkgbits.ObjStub)
if types.IsExported(xname) {
- l.relocIdx(pr, pkgbits.RelocObj, idx)
+ l.relocIdx(pr, pkgbits.SectionObj, idx)
}
}
@@ -525,7 +521,7 @@ func writeUnifiedExport(out io.Writer) {
w := publicRootWriter
w.Sync(pkgbits.SyncPkg)
- w.Reloc(pkgbits.RelocPkg, selfPkgIdx)
+ w.Reloc(pkgbits.SectionPkg, selfPkgIdx)
if w.Version().Has(pkgbits.HasInit) {
w.Bool(false)
@@ -537,7 +533,7 @@ func writeUnifiedExport(out io.Writer) {
if w.Version().Has(pkgbits.DerivedFuncInstance) {
w.Bool(false)
}
- w.Reloc(pkgbits.RelocObj, idx)
+ w.Reloc(pkgbits.SectionObj, idx)
w.Len(0)
}
@@ -564,7 +560,7 @@ func writeUnifiedExport(out io.Writer) {
for _, body := range bodies {
w.String(body.sym.Pkg.Path)
w.String(body.sym.Name)
- w.Reloc(pkgbits.RelocBody, body.idx)
+ w.Reloc(pkgbits.SectionBody, body.idx)
}
w.Sync(pkgbits.SyncEOF)
diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go
index f4b02f279d..9c90d221c2 100644
--- a/src/cmd/compile/internal/noder/writer.go
+++ b/src/cmd/compile/internal/noder/writer.go
@@ -96,11 +96,8 @@ type pkgWriter struct {
// newPkgWriter returns an initialized pkgWriter for the specified
// package.
func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info, otherInfo map[*syntax.FuncLit]bool) *pkgWriter {
- // Use V2 as the encoded version aliastypeparams GOEXPERIMENT is enabled.
- version := pkgbits.V1
- if buildcfg.Experiment.AliasTypeParams {
- version = pkgbits.V2
- }
+ // Use V2 as the encoded version for aliastypeparams.
+ version := pkgbits.V2
return &pkgWriter{
PkgEncoder: pkgbits.NewPkgEncoder(version, base.Debug.SyncFrames),
@@ -174,7 +171,7 @@ func (pw *pkgWriter) typeOf(expr syntax.Expr) types2.Type {
type writer struct {
p *pkgWriter
- pkgbits.Encoder
+ *pkgbits.Encoder
// sig holds the signature for the current function body, if any.
sig *types2.Signature
@@ -367,7 +364,7 @@ func (dict *writerDict) itabIdx(typInfo, ifaceInfo typeInfo) int {
return idx
}
-func (pw *pkgWriter) newWriter(k pkgbits.RelocKind, marker pkgbits.SyncMarker) *writer {
+func (pw *pkgWriter) newWriter(k pkgbits.SectionKind, marker pkgbits.SyncMarker) *writer {
return &writer{
Encoder: pw.NewEncoder(k, marker),
p: pw,
@@ -395,7 +392,7 @@ func (w *writer) pos(p poser) {
// posBase writes a reference to the given PosBase into the element
// bitstream.
func (w *writer) posBase(b *syntax.PosBase) {
- w.Reloc(pkgbits.RelocPosBase, w.p.posBaseIdx(b))
+ w.Reloc(pkgbits.SectionPosBase, w.p.posBaseIdx(b))
}
// posBaseIdx returns the index for the given PosBase.
@@ -404,7 +401,7 @@ func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) index {
return idx
}
- w := pw.newWriter(pkgbits.RelocPosBase, pkgbits.SyncPosBase)
+ w := pw.newWriter(pkgbits.SectionPosBase, pkgbits.SyncPosBase)
w.p.posBasesIdx[b] = w.Idx
w.String(trimFilename(b))
@@ -427,7 +424,7 @@ func (w *writer) pkg(pkg *types2.Package) {
func (w *writer) pkgRef(idx index) {
w.Sync(pkgbits.SyncPkg)
- w.Reloc(pkgbits.RelocPkg, idx)
+ w.Reloc(pkgbits.SectionPkg, idx)
}
// pkgIdx returns the index for the given package, adding it to the
@@ -437,7 +434,7 @@ func (pw *pkgWriter) pkgIdx(pkg *types2.Package) index {
return idx
}
- w := pw.newWriter(pkgbits.RelocPkg, pkgbits.SyncPkgDef)
+ w := pw.newWriter(pkgbits.SectionPkg, pkgbits.SyncPkgDef)
pw.pkgsIdx[pkg] = w.Idx
// The universe and package unsafe need to be handled specially by
@@ -489,7 +486,7 @@ func (w *writer) typInfo(info typeInfo) {
w.Len(int(info.idx))
w.derived = true
} else {
- w.Reloc(pkgbits.RelocType, info.idx)
+ w.Reloc(pkgbits.SectionType, info.idx)
}
}
@@ -520,7 +517,7 @@ func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo {
}
}
- w := pw.newWriter(pkgbits.RelocType, pkgbits.SyncTypeIdx)
+ w := pw.newWriter(pkgbits.SectionType, pkgbits.SyncTypeIdx)
w.dict = dict
switch typ := typ.(type) {
@@ -737,7 +734,7 @@ func (w *writer) objInfo(info objInfo) {
if w.Version().Has(pkgbits.DerivedFuncInstance) {
w.Bool(false)
}
- w.Reloc(pkgbits.RelocObj, info.idx)
+ w.Reloc(pkgbits.SectionObj, info.idx)
w.Len(len(info.explicits))
for _, info := range info.explicits {
@@ -799,10 +796,10 @@ func (pw *pkgWriter) objIdx(obj types2.Object) index {
// TODO(mdempsky): Re-evaluate whether RelocName still makes sense
// to keep separate from RelocObj.
- w := pw.newWriter(pkgbits.RelocObj, pkgbits.SyncObject1)
- wext := pw.newWriter(pkgbits.RelocObjExt, pkgbits.SyncObject1)
- wname := pw.newWriter(pkgbits.RelocName, pkgbits.SyncObject1)
- wdict := pw.newWriter(pkgbits.RelocObjDict, pkgbits.SyncObject1)
+ w := pw.newWriter(pkgbits.SectionObj, pkgbits.SyncObject1)
+ wext := pw.newWriter(pkgbits.SectionObjExt, pkgbits.SyncObject1)
+ wname := pw.newWriter(pkgbits.SectionName, pkgbits.SyncObject1)
+ wdict := pw.newWriter(pkgbits.SectionObjDict, pkgbits.SyncObject1)
pw.objsIdx[obj] = w.Idx // break cycles
assert(wext.Idx == w.Idx)
@@ -917,7 +914,7 @@ func (w *writer) objDict(obj types2.Object, dict *writerDict) {
nderived := len(dict.derived)
w.Len(nderived)
for _, typ := range dict.derived {
- w.Reloc(pkgbits.RelocType, typ.idx)
+ w.Reloc(pkgbits.SectionType, typ.idx)
if w.Version().Has(pkgbits.DerivedInfoNeeded) {
w.Bool(false)
}
@@ -1132,7 +1129,7 @@ func (w *writer) funcExt(obj *types2.Func) {
}
w.Bool(false) // stub extension
- w.Reloc(pkgbits.RelocBody, body)
+ w.Reloc(pkgbits.SectionBody, body)
w.Sync(pkgbits.SyncEOF)
}
@@ -1170,7 +1167,7 @@ func (w *writer) pragmaFlag(p ir.PragmaFlag) {
// bodyIdx returns the index for the given function body (specified by
// block), adding it to the export data
func (pw *pkgWriter) bodyIdx(sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx index, closureVars []posVar) {
- w := pw.newWriter(pkgbits.RelocBody, pkgbits.SyncFuncBody)
+ w := pw.newWriter(pkgbits.SectionBody, pkgbits.SyncFuncBody)
w.sig = sig
w.dict = dict
@@ -2038,10 +2035,16 @@ func (w *writer) expr(expr syntax.Expr) {
case "new":
assert(len(expr.ArgList) == 1)
assert(!expr.HasDots)
+ arg := expr.ArgList[0]
w.Code(exprNew)
w.pos(expr)
- w.exprType(nil, expr.ArgList[0])
+ tv := w.p.typeAndValue(arg)
+ if w.Bool(!tv.IsType()) {
+ w.expr(arg) // new(expr), go1.26
+ } else {
+ w.exprType(nil, arg) // new(T)
+ }
return
case "Sizeof":
@@ -2401,7 +2404,7 @@ func (w *writer) funcLit(expr *syntax.FuncLit) {
w.useLocal(cv.pos, cv.var_)
}
- w.Reloc(pkgbits.RelocBody, body)
+ w.Reloc(pkgbits.SectionBody, body)
}
type posVar struct {
@@ -2413,11 +2416,6 @@ func (p posVar) String() string {
return p.pos.String() + ":" + p.var_.String()
}
-func (w *writer) exprList(expr syntax.Expr) {
- w.Sync(pkgbits.SyncExprList)
- w.exprs(syntax.UnpackListExpr(expr))
-}
-
func (w *writer) exprs(exprs []syntax.Expr) {
w.Sync(pkgbits.SyncExprs)
w.Len(len(exprs))
diff --git a/src/cmd/compile/internal/objw/prog.go b/src/cmd/compile/internal/objw/prog.go
index 84fb996723..753fd8615c 100644
--- a/src/cmd/compile/internal/objw/prog.go
+++ b/src/cmd/compile/internal/objw/prog.go
@@ -116,10 +116,7 @@ func (pp *Progs) Flush() {
func (pp *Progs) Free() {
if base.Ctxt.CanReuseProgs() {
// Clear progs to enable GC and avoid abuse.
- s := pp.Cache[:pp.CacheIndex]
- for i := range s {
- s[i] = obj.Prog{}
- }
+ clear(pp.Cache[:pp.CacheIndex])
}
// Clear pp to avoid abuse.
*pp = Progs{}
diff --git a/src/cmd/compile/internal/pgoir/irgraph.go b/src/cmd/compile/internal/pgoir/irgraph.go
index 914a4da8b5..e879d438a6 100644
--- a/src/cmd/compile/internal/pgoir/irgraph.go
+++ b/src/cmd/compile/internal/pgoir/irgraph.go
@@ -158,7 +158,7 @@ func New(profileFile string) (*Profile, error) {
}, nil
}
-// initializeIRGraph builds the IRGraph by visiting all the ir.Func in decl list
+// createIRGraph builds the IRGraph by visiting all the ir.Func in decl list
// of a package.
func createIRGraph(namedEdgeMap pgo.NamedEdgeMap) *IRGraph {
g := &IRGraph{
diff --git a/src/cmd/compile/internal/pkginit/initAsanGlobals.go b/src/cmd/compile/internal/pkginit/initAsanGlobals.go
index 42db0eaf1b..96c052204a 100644
--- a/src/cmd/compile/internal/pkginit/initAsanGlobals.go
+++ b/src/cmd/compile/internal/pkginit/initAsanGlobals.go
@@ -227,6 +227,12 @@ func canInstrumentGlobal(g ir.Node) bool {
return false
}
+ // Do not instrument counter globals in internal/fuzz. These globals are replaced by the linker.
+ // See go.dev/issue/72766 for more details.
+ if n.Sym().Pkg.Path == "internal/fuzz" && (n.Sym().Name == "_counters" || n.Sym().Name == "_ecounters") {
+ return false
+ }
+
// Do not instrument globals that are linknamed, because their home package will do the work.
if n.Sym().Linkname != "" {
return false
diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go
index 53ec4289c7..ace3024480 100644
--- a/src/cmd/compile/internal/ppc64/ssa.go
+++ b/src/cmd/compile/internal/ppc64/ssa.go
@@ -14,6 +14,7 @@ import (
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/ppc64"
+ "internal/abi"
"internal/buildcfg"
"math"
"strings"
@@ -1913,12 +1914,90 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
// AuxInt encodes how many buffer entries we need.
p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
- case ssa.OpPPC64LoweredPanicBoundsA, ssa.OpPPC64LoweredPanicBoundsB, ssa.OpPPC64LoweredPanicBoundsC:
- p := s.Prog(obj.ACALL)
+ case ssa.OpPPC64LoweredPanicBoundsRR, ssa.OpPPC64LoweredPanicBoundsRC, ssa.OpPPC64LoweredPanicBoundsCR, ssa.OpPPC64LoweredPanicBoundsCC:
+ // Compute the constant we put in the PCData entry for this call.
+ code, signed := ssa.BoundsKind(v.AuxInt).Code()
+ xIsReg := false
+ yIsReg := false
+ xVal := 0
+ yVal := 0
+ switch v.Op {
+ case ssa.OpPPC64LoweredPanicBoundsRR:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - ppc64.REG_R3)
+ yIsReg = true
+ yVal = int(v.Args[1].Reg() - ppc64.REG_R3)
+ case ssa.OpPPC64LoweredPanicBoundsRC:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - ppc64.REG_R3)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ if yVal == xVal {
+ yVal = 1
+ }
+ p := s.Prog(ppc64.AMOVD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = ppc64.REG_R3 + int16(yVal)
+ }
+ case ssa.OpPPC64LoweredPanicBoundsCR:
+ yIsReg = true
+ yVal = int(v.Args[0].Reg() - ppc64.REG_R3)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else {
+ // Move constant to a register
+ if xVal == yVal {
+ xVal = 1
+ }
+ p := s.Prog(ppc64.AMOVD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = ppc64.REG_R3 + int16(xVal)
+ }
+ case ssa.OpPPC64LoweredPanicBoundsCC:
+ c := v.Aux.(ssa.PanicBoundsCC).Cx
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else {
+ // Move constant to a register
+ xIsReg = true
+ p := s.Prog(ppc64.AMOVD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = ppc64.REG_R3 + int16(xVal)
+ }
+ c = v.Aux.(ssa.PanicBoundsCC).Cy
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ yVal = 1
+ p := s.Prog(ppc64.AMOVD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = ppc64.REG_R3 + int16(yVal)
+ }
+ }
+ c := abi.BoundsEncode(code, signed, xIsReg, yIsReg, xVal, yVal)
+
+ p := s.Prog(obj.APCDATA)
+ p.From.SetConst(abi.PCDATA_PanicBounds)
+ p.To.SetConst(int64(c))
+ p = s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
- s.UseArgs(16) // space used in callee args area by assembly stubs
+ p.To.Sym = ir.Syms.PanicBounds
case ssa.OpPPC64LoweredNilCheck:
if buildcfg.GOOS == "aix" {
@@ -2003,26 +2082,7 @@ var blockJump = [...]struct {
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
- case ssa.BlockDefer:
- // defer returns in R3:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(ppc64.ACMP)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = ppc64.REG_R3
- p.To.Type = obj.TYPE_CONST
- p.To.Offset = 0
-
- p = s.Prog(ppc64.ABNE)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
-
- case ssa.BlockPlain:
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
diff --git a/src/cmd/compile/internal/reflectdata/map_swiss.go b/src/cmd/compile/internal/reflectdata/map.go
similarity index 80%
rename from src/cmd/compile/internal/reflectdata/map_swiss.go
rename to src/cmd/compile/internal/reflectdata/map.go
index 074c36a453..2b43d4af27 100644
--- a/src/cmd/compile/internal/reflectdata/map_swiss.go
+++ b/src/cmd/compile/internal/reflectdata/map.go
@@ -15,10 +15,10 @@ import (
"internal/abi"
)
-// SwissMapGroupType makes the map slot group type given the type of the map.
-func SwissMapGroupType(t *types.Type) *types.Type {
- if t.MapType().SwissGroup != nil {
- return t.MapType().SwissGroup
+// MapGroupType makes the map slot group type given the type of the map.
+func MapGroupType(t *types.Type) *types.Type {
+ if t.MapType().Group != nil {
+ return t.MapType().Group
}
// Builds a type representing a group structure for the given map type.
@@ -29,7 +29,7 @@ func SwissMapGroupType(t *types.Type) *types.Type {
//
// type group struct {
// ctrl uint64
- // slots [abi.SwissMapGroupSlots]struct {
+ // slots [abi.MapGroupSlots]struct {
// key keyType
// elem elemType
// }
@@ -39,10 +39,10 @@ func SwissMapGroupType(t *types.Type) *types.Type {
elemtype := t.Elem()
types.CalcSize(keytype)
types.CalcSize(elemtype)
- if keytype.Size() > abi.SwissMapMaxKeyBytes {
+ if keytype.Size() > abi.MapMaxKeyBytes {
keytype = types.NewPtr(keytype)
}
- if elemtype.Size() > abi.SwissMapMaxElemBytes {
+ if elemtype.Size() > abi.MapMaxElemBytes {
elemtype = types.NewPtr(elemtype)
}
@@ -53,7 +53,7 @@ func SwissMapGroupType(t *types.Type) *types.Type {
slot := types.NewStruct(slotFields)
slot.SetNoalg(true)
- slotArr := types.NewArray(slot, abi.SwissMapGroupSlots)
+ slotArr := types.NewArray(slot, abi.MapGroupSlots)
slotArr.SetNoalg(true)
fields := []*types.Field{
@@ -76,25 +76,25 @@ func SwissMapGroupType(t *types.Type) *types.Type {
// the end to ensure pointers are valid.
base.Fatalf("bad group size for %v", t)
}
- if t.Key().Size() > abi.SwissMapMaxKeyBytes && !keytype.IsPtr() {
+ if t.Key().Size() > abi.MapMaxKeyBytes && !keytype.IsPtr() {
base.Fatalf("key indirect incorrect for %v", t)
}
- if t.Elem().Size() > abi.SwissMapMaxElemBytes && !elemtype.IsPtr() {
+ if t.Elem().Size() > abi.MapMaxElemBytes && !elemtype.IsPtr() {
base.Fatalf("elem indirect incorrect for %v", t)
}
- t.MapType().SwissGroup = group
+ t.MapType().Group = group
group.StructType().Map = t
return group
}
-var cachedSwissTableType *types.Type
+var cachedMapTableType *types.Type
-// swissTableType returns a type interchangeable with internal/runtime/maps.table.
+// mapTableType returns a type interchangeable with internal/runtime/maps.table.
// Make sure this stays in sync with internal/runtime/maps/table.go.
-func swissTableType() *types.Type {
- if cachedSwissTableType != nil {
- return cachedSwissTableType
+func mapTableType() *types.Type {
+ if cachedMapTableType != nil {
+ return cachedMapTableType
}
// type table struct {
@@ -135,17 +135,17 @@ func swissTableType() *types.Type {
base.Fatalf("internal/runtime/maps.table size not correct: got %d, want %d", table.Size(), size)
}
- cachedSwissTableType = table
+ cachedMapTableType = table
return table
}
-var cachedSwissMapType *types.Type
+var cachedMapType *types.Type
-// SwissMapType returns a type interchangeable with internal/runtime/maps.Map.
+// MapType returns a type interchangeable with internal/runtime/maps.Map.
// Make sure this stays in sync with internal/runtime/maps/map.go.
-func SwissMapType() *types.Type {
- if cachedSwissMapType != nil {
- return cachedSwissMapType
+func MapType() *types.Type {
+ if cachedMapType != nil {
+ return cachedMapType
}
// type Map struct {
@@ -159,6 +159,7 @@ func SwissMapType() *types.Type {
// globalShift uint8
//
// writing uint8
+ // tombstonePossible bool
// // N.B Padding
//
// clearSeq uint64
@@ -172,6 +173,7 @@ func SwissMapType() *types.Type {
makefield("globalDepth", types.Types[types.TUINT8]),
makefield("globalShift", types.Types[types.TUINT8]),
makefield("writing", types.Types[types.TUINT8]),
+ makefield("tombstonePossible", types.Types[types.TBOOL]),
makefield("clearSeq", types.Types[types.TUINT64]),
}
@@ -189,23 +191,23 @@ func SwissMapType() *types.Type {
base.Fatalf("internal/runtime/maps.Map size not correct: got %d, want %d", m.Size(), size)
}
- cachedSwissMapType = m
+ cachedMapType = m
return m
}
-var cachedSwissIterType *types.Type
+var cachedMapIterType *types.Type
-// SwissMapIterType returns a type interchangeable with runtime.hiter.
-// Make sure this stays in sync with runtime/map.go.
-func SwissMapIterType() *types.Type {
- if cachedSwissIterType != nil {
- return cachedSwissIterType
+// MapIterType returns a type interchangeable with internal/runtime/maps.Iter.
+// Make sure this stays in sync with internal/runtime/maps/table.go.
+func MapIterType() *types.Type {
+ if cachedMapIterType != nil {
+ return cachedMapIterType
}
// type Iter struct {
// key unsafe.Pointer // *Key
// elem unsafe.Pointer // *Elem
- // typ unsafe.Pointer // *SwissMapType
+ // typ unsafe.Pointer // *MapType
// m *Map
//
// groupSlotOffset uint64
@@ -229,13 +231,13 @@ func SwissMapIterType() *types.Type {
makefield("key", types.Types[types.TUNSAFEPTR]), // Used in range.go for TMAP.
makefield("elem", types.Types[types.TUNSAFEPTR]), // Used in range.go for TMAP.
makefield("typ", types.Types[types.TUNSAFEPTR]),
- makefield("m", types.NewPtr(SwissMapType())),
+ makefield("m", types.NewPtr(MapType())),
makefield("groupSlotOffset", types.Types[types.TUINT64]),
makefield("dirOffset", types.Types[types.TUINT64]),
makefield("clearSeq", types.Types[types.TUINT64]),
makefield("globalDepth", types.Types[types.TUINT8]),
makefield("dirIdx", types.Types[types.TINT]),
- makefield("tab", types.NewPtr(swissTableType())),
+ makefield("tab", types.NewPtr(mapTableType())),
makefield("group", types.Types[types.TUNSAFEPTR]),
makefield("entryIdx", types.Types[types.TUINT64]),
}
@@ -255,13 +257,13 @@ func SwissMapIterType() *types.Type {
base.Fatalf("internal/runtime/maps.Iter size not correct: got %d, want %d", iter.Size(), size)
}
- cachedSwissIterType = iter
+ cachedMapIterType = iter
return iter
}
-func writeSwissMapType(t *types.Type, lsym *obj.LSym, c rttype.Cursor) {
- // internal/abi.SwissMapType
- gtyp := SwissMapGroupType(t)
+func writeMapType(t *types.Type, lsym *obj.LSym, c rttype.Cursor) {
+ // internal/abi.MapType
+ gtyp := MapGroupType(t)
s1 := writeType(t.Key())
s2 := writeType(t.Elem())
s3 := writeType(gtyp)
@@ -285,16 +287,16 @@ func writeSwissMapType(t *types.Type, lsym *obj.LSym, c rttype.Cursor) {
c.Field("ElemOff").WriteUintptr(uint64(elemOff))
var flags uint32
if needkeyupdate(t.Key()) {
- flags |= abi.SwissMapNeedKeyUpdate
+ flags |= abi.MapNeedKeyUpdate
}
if hashMightPanic(t.Key()) {
- flags |= abi.SwissMapHashMightPanic
+ flags |= abi.MapHashMightPanic
}
- if t.Key().Size() > abi.SwissMapMaxKeyBytes {
- flags |= abi.SwissMapIndirectKey
+ if t.Key().Size() > abi.MapMaxKeyBytes {
+ flags |= abi.MapIndirectKey
}
- if t.Elem().Size() > abi.SwissMapMaxKeyBytes {
- flags |= abi.SwissMapIndirectElem
+ if t.Elem().Size() > abi.MapMaxKeyBytes {
+ flags |= abi.MapIndirectElem
}
c.Field("Flags").WriteUint32(flags)
diff --git a/src/cmd/compile/internal/reflectdata/map_noswiss.go b/src/cmd/compile/internal/reflectdata/map_noswiss.go
deleted file mode 100644
index a6fab4cbac..0000000000
--- a/src/cmd/compile/internal/reflectdata/map_noswiss.go
+++ /dev/null
@@ -1,305 +0,0 @@
-// Copyright 2024 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package reflectdata
-
-import (
- "internal/abi"
-
- "cmd/compile/internal/base"
- "cmd/compile/internal/ir"
- "cmd/compile/internal/rttype"
- "cmd/compile/internal/types"
- "cmd/internal/obj"
- "cmd/internal/objabi"
- "cmd/internal/src"
-)
-
-// OldMapBucketType makes the map bucket type given the type of the map.
-func OldMapBucketType(t *types.Type) *types.Type {
- // Builds a type representing a Bucket structure for
- // the given map type. This type is not visible to users -
- // we include only enough information to generate a correct GC
- // program for it.
- // Make sure this stays in sync with runtime/map.go.
- //
- // A "bucket" is a "struct" {
- // tophash [abi.OldMapBucketCount]uint8
- // keys [abi.OldMapBucketCount]keyType
- // elems [abi.OldMapBucketCount]elemType
- // overflow *bucket
- // }
- if t.MapType().OldBucket != nil {
- return t.MapType().OldBucket
- }
-
- keytype := t.Key()
- elemtype := t.Elem()
- types.CalcSize(keytype)
- types.CalcSize(elemtype)
- if keytype.Size() > abi.OldMapMaxKeyBytes {
- keytype = types.NewPtr(keytype)
- }
- if elemtype.Size() > abi.OldMapMaxElemBytes {
- elemtype = types.NewPtr(elemtype)
- }
-
- field := make([]*types.Field, 0, 5)
-
- // The first field is: uint8 topbits[BUCKETSIZE].
- arr := types.NewArray(types.Types[types.TUINT8], abi.OldMapBucketCount)
- field = append(field, makefield("topbits", arr))
-
- arr = types.NewArray(keytype, abi.OldMapBucketCount)
- arr.SetNoalg(true)
- keys := makefield("keys", arr)
- field = append(field, keys)
-
- arr = types.NewArray(elemtype, abi.OldMapBucketCount)
- arr.SetNoalg(true)
- elems := makefield("elems", arr)
- field = append(field, elems)
-
- // If keys and elems have no pointers, the map implementation
- // can keep a list of overflow pointers on the side so that
- // buckets can be marked as having no pointers.
- // Arrange for the bucket to have no pointers by changing
- // the type of the overflow field to uintptr in this case.
- // See comment on hmap.overflow in runtime/map.go.
- otyp := types.Types[types.TUNSAFEPTR]
- if !elemtype.HasPointers() && !keytype.HasPointers() {
- otyp = types.Types[types.TUINTPTR]
- }
- overflow := makefield("overflow", otyp)
- field = append(field, overflow)
-
- // link up fields
- bucket := types.NewStruct(field[:])
- bucket.SetNoalg(true)
- types.CalcSize(bucket)
-
- // Check invariants that map code depends on.
- if !types.IsComparable(t.Key()) {
- base.Fatalf("unsupported map key type for %v", t)
- }
- if abi.OldMapBucketCount < 8 {
- base.Fatalf("bucket size %d too small for proper alignment %d", abi.OldMapBucketCount, 8)
- }
- if uint8(keytype.Alignment()) > abi.OldMapBucketCount {
- base.Fatalf("key align too big for %v", t)
- }
- if uint8(elemtype.Alignment()) > abi.OldMapBucketCount {
- base.Fatalf("elem align %d too big for %v, BUCKETSIZE=%d", elemtype.Alignment(), t, abi.OldMapBucketCount)
- }
- if keytype.Size() > abi.OldMapMaxKeyBytes {
- base.Fatalf("key size too large for %v", t)
- }
- if elemtype.Size() > abi.OldMapMaxElemBytes {
- base.Fatalf("elem size too large for %v", t)
- }
- if t.Key().Size() > abi.OldMapMaxKeyBytes && !keytype.IsPtr() {
- base.Fatalf("key indirect incorrect for %v", t)
- }
- if t.Elem().Size() > abi.OldMapMaxElemBytes && !elemtype.IsPtr() {
- base.Fatalf("elem indirect incorrect for %v", t)
- }
- if keytype.Size()%keytype.Alignment() != 0 {
- base.Fatalf("key size not a multiple of key align for %v", t)
- }
- if elemtype.Size()%elemtype.Alignment() != 0 {
- base.Fatalf("elem size not a multiple of elem align for %v", t)
- }
- if uint8(bucket.Alignment())%uint8(keytype.Alignment()) != 0 {
- base.Fatalf("bucket align not multiple of key align %v", t)
- }
- if uint8(bucket.Alignment())%uint8(elemtype.Alignment()) != 0 {
- base.Fatalf("bucket align not multiple of elem align %v", t)
- }
- if keys.Offset%keytype.Alignment() != 0 {
- base.Fatalf("bad alignment of keys in bmap for %v", t)
- }
- if elems.Offset%elemtype.Alignment() != 0 {
- base.Fatalf("bad alignment of elems in bmap for %v", t)
- }
-
- // Double-check that overflow field is final memory in struct,
- // with no padding at end.
- if overflow.Offset != bucket.Size()-int64(types.PtrSize) {
- base.Fatalf("bad offset of overflow in bmap for %v, overflow.Offset=%d, bucket.Size()-int64(types.PtrSize)=%d",
- t, overflow.Offset, bucket.Size()-int64(types.PtrSize))
- }
-
- t.MapType().OldBucket = bucket
-
- bucket.StructType().Map = t
- return bucket
-}
-
-var oldHmapType *types.Type
-
-// OldMapType returns a type interchangeable with runtime.hmap.
-// Make sure this stays in sync with runtime/map.go.
-func OldMapType() *types.Type {
- if oldHmapType != nil {
- return oldHmapType
- }
-
- // build a struct:
- // type hmap struct {
- // count int
- // flags uint8
- // B uint8
- // noverflow uint16
- // hash0 uint32
- // buckets unsafe.Pointer
- // oldbuckets unsafe.Pointer
- // nevacuate uintptr
- // clearSeq uint64
- // extra unsafe.Pointer // *mapextra
- // }
- // must match runtime/map.go:hmap.
- fields := []*types.Field{
- makefield("count", types.Types[types.TINT]),
- makefield("flags", types.Types[types.TUINT8]),
- makefield("B", types.Types[types.TUINT8]),
- makefield("noverflow", types.Types[types.TUINT16]),
- makefield("hash0", types.Types[types.TUINT32]), // Used in walk.go for OMAKEMAP.
- makefield("buckets", types.Types[types.TUNSAFEPTR]), // Used in walk.go for OMAKEMAP.
- makefield("oldbuckets", types.Types[types.TUNSAFEPTR]),
- makefield("nevacuate", types.Types[types.TUINTPTR]),
- makefield("clearSeq", types.Types[types.TUINT64]),
- makefield("extra", types.Types[types.TUNSAFEPTR]),
- }
-
- n := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, ir.Pkgs.Runtime.Lookup("hmap"))
- hmap := types.NewNamed(n)
- n.SetType(hmap)
- n.SetTypecheck(1)
-
- hmap.SetUnderlying(types.NewStruct(fields))
- types.CalcSize(hmap)
-
- // The size of hmap should be 56 bytes on 64 bit
- // and 36 bytes on 32 bit platforms.
- if size := int64(2*8 + 5*types.PtrSize); hmap.Size() != size {
- base.Fatalf("hmap size not correct: got %d, want %d", hmap.Size(), size)
- }
-
- oldHmapType = hmap
- return hmap
-}
-
-var oldHiterType *types.Type
-
-// OldMapIterType returns a type interchangeable with runtime.hiter.
-// Make sure this stays in sync with runtime/map.go.
-func OldMapIterType() *types.Type {
- if oldHiterType != nil {
- return oldHiterType
- }
-
- hmap := OldMapType()
-
- // build a struct:
- // type hiter struct {
- // key unsafe.Pointer // *Key
- // elem unsafe.Pointer // *Elem
- // t unsafe.Pointer // *OldMapType
- // h *hmap
- // buckets unsafe.Pointer
- // bptr unsafe.Pointer // *bmap
- // overflow unsafe.Pointer // *[]*bmap
- // oldoverflow unsafe.Pointer // *[]*bmap
- // startBucket uintptr
- // offset uint8
- // wrapped bool
- // B uint8
- // i uint8
- // bucket uintptr
- // checkBucket uintptr
- // clearSeq uint64
- // }
- // must match runtime/map.go:hiter.
- fields := []*types.Field{
- makefield("key", types.Types[types.TUNSAFEPTR]), // Used in range.go for TMAP.
- makefield("elem", types.Types[types.TUNSAFEPTR]), // Used in range.go for TMAP.
- makefield("t", types.Types[types.TUNSAFEPTR]),
- makefield("h", types.NewPtr(hmap)),
- makefield("buckets", types.Types[types.TUNSAFEPTR]),
- makefield("bptr", types.Types[types.TUNSAFEPTR]),
- makefield("overflow", types.Types[types.TUNSAFEPTR]),
- makefield("oldoverflow", types.Types[types.TUNSAFEPTR]),
- makefield("startBucket", types.Types[types.TUINTPTR]),
- makefield("offset", types.Types[types.TUINT8]),
- makefield("wrapped", types.Types[types.TBOOL]),
- makefield("B", types.Types[types.TUINT8]),
- makefield("i", types.Types[types.TUINT8]),
- makefield("bucket", types.Types[types.TUINTPTR]),
- makefield("checkBucket", types.Types[types.TUINTPTR]),
- makefield("clearSeq", types.Types[types.TUINT64]),
- }
-
- // build iterator struct holding the above fields
- n := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, ir.Pkgs.Runtime.Lookup("hiter"))
- hiter := types.NewNamed(n)
- n.SetType(hiter)
- n.SetTypecheck(1)
-
- hiter.SetUnderlying(types.NewStruct(fields))
- types.CalcSize(hiter)
- if hiter.Size() != int64(8+12*types.PtrSize) {
- base.Fatalf("hash_iter size not correct %d %d", hiter.Size(), 8+12*types.PtrSize)
- }
-
- oldHiterType = hiter
- return hiter
-}
-
-func writeOldMapType(t *types.Type, lsym *obj.LSym, c rttype.Cursor) {
- // internal/abi.OldMapType
- s1 := writeType(t.Key())
- s2 := writeType(t.Elem())
- s3 := writeType(OldMapBucketType(t))
- hasher := genhash(t.Key())
-
- c.Field("Key").WritePtr(s1)
- c.Field("Elem").WritePtr(s2)
- c.Field("Bucket").WritePtr(s3)
- c.Field("Hasher").WritePtr(hasher)
- var flags uint32
- // Note: flags must match maptype accessors in ../../../../runtime/type.go
- // and maptype builder in ../../../../reflect/type.go:MapOf.
- if t.Key().Size() > abi.OldMapMaxKeyBytes {
- c.Field("KeySize").WriteUint8(uint8(types.PtrSize))
- flags |= 1 // indirect key
- } else {
- c.Field("KeySize").WriteUint8(uint8(t.Key().Size()))
- }
-
- if t.Elem().Size() > abi.OldMapMaxElemBytes {
- c.Field("ValueSize").WriteUint8(uint8(types.PtrSize))
- flags |= 2 // indirect value
- } else {
- c.Field("ValueSize").WriteUint8(uint8(t.Elem().Size()))
- }
- c.Field("BucketSize").WriteUint16(uint16(OldMapBucketType(t).Size()))
- if types.IsReflexive(t.Key()) {
- flags |= 4 // reflexive key
- }
- if needkeyupdate(t.Key()) {
- flags |= 8 // need key update
- }
- if hashMightPanic(t.Key()) {
- flags |= 16 // hash might panic
- }
- c.Field("Flags").WriteUint32(flags)
-
- 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.
- lsym.AddRel(base.Ctxt, obj.Reloc{Type: objabi.R_KEEP, Sym: writeType(u)})
- }
-}
diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go
index a4a701c9a2..38b9391c5f 100644
--- a/src/cmd/compile/internal/reflectdata/reflect.go
+++ b/src/cmd/compile/internal/reflectdata/reflect.go
@@ -8,7 +8,6 @@ import (
"encoding/binary"
"fmt"
"internal/abi"
- "internal/buildcfg"
"slices"
"sort"
"strings"
@@ -415,6 +414,10 @@ var kinds = []abi.Kind{
types.TUNSAFEPTR: abi.UnsafePointer,
}
+func ABIKindOfType(t *types.Type) abi.Kind {
+ return kinds[t.Kind()]
+}
+
var (
memhashvarlen *obj.LSym
memequalvarlen *obj.LSym
@@ -491,6 +494,9 @@ func dcommontype(c rttype.Cursor, t *types.Type) {
exported = types.IsExported(t.Elem().Sym().Name)
}
}
+ if types.IsDirectIface(t) {
+ tflag |= abi.TFlagDirectIface
+ }
if tflag != abi.TFlag(uint8(tflag)) {
// this should optimize away completely
@@ -510,11 +516,7 @@ func dcommontype(c rttype.Cursor, t *types.Type) {
c.Field("Align_").WriteUint8(uint8(t.Alignment()))
c.Field("FieldAlign_").WriteUint8(uint8(t.Alignment()))
- kind := kinds[t.Kind()]
- if types.IsDirectIface(t) {
- kind |= abi.KindDirectIface
- }
- c.Field("Kind_").WriteUint8(uint8(kind))
+ c.Field("Kind_").WriteUint8(uint8(ABIKindOfType(t)))
c.Field("Equal").WritePtr(eqfunc)
c.Field("GCData").WritePtr(gcsym)
@@ -592,11 +594,21 @@ func TypePtrAt(pos src.XPos, t *types.Type) *ir.AddrExpr {
// it may sometimes, but not always, be a type that can't implement the specified
// interface.
func ITabLsym(typ, iface *types.Type) *obj.LSym {
+ return itabLsym(typ, iface, true)
+}
+
+func itabLsym(typ, iface *types.Type, allowNonImplement bool) *obj.LSym {
s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString())
lsym := s.Linksym()
+ signatmu.Lock()
+ if lsym.Extra == nil {
+ ii := lsym.NewItabInfo()
+ ii.Type = typ
+ }
+ signatmu.Unlock()
if !existed {
- writeITab(lsym, typ, iface, true)
+ writeITab(lsym, typ, iface, allowNonImplement)
}
return lsym
}
@@ -605,13 +617,7 @@ func ITabLsym(typ, iface *types.Type) *obj.LSym {
// *runtime.itab value for concrete type typ implementing interface
// iface.
func ITabAddrAt(pos src.XPos, typ, iface *types.Type) *ir.AddrExpr {
- s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString())
- lsym := s.Linksym()
-
- if !existed {
- writeITab(lsym, typ, iface, false)
- }
-
+ lsym := itabLsym(typ, iface, false)
return typecheck.LinksymAddr(pos, lsym, types.Types[types.TUINT8])
}
@@ -711,6 +717,10 @@ func writeType(t *types.Type) *obj.LSym {
}
s.SetSiggen(true)
+ if !tbase.HasShape() {
+ TypeLinksym(t) // ensure lsym.Extra is set
+ }
+
if !NeedEmit(tbase) {
if i := typecheck.BaseTypeIndex(t); i >= 0 {
lsym.Pkg = tbase.Sym().Pkg.Prefix
@@ -769,11 +779,7 @@ func writeType(t *types.Type) *obj.LSym {
rt = rttype.InterfaceType
dataAdd = len(imethods(t)) * int(rttype.IMethod.Size())
case types.TMAP:
- if buildcfg.Experiment.SwissMap {
- rt = rttype.SwissMapType
- } else {
- rt = rttype.OldMapType
- }
+ rt = rttype.MapType
case types.TPTR:
rt = rttype.PtrType
// TODO: use rttype.Type for Elem() is ANY?
@@ -873,11 +879,7 @@ func writeType(t *types.Type) *obj.LSym {
}
case types.TMAP:
- if buildcfg.Experiment.SwissMap {
- writeSwissMapType(t, lsym, c)
- } else {
- writeOldMapType(t, lsym, c)
- }
+ writeMapType(t, lsym, c)
case types.TPTR:
// internal/abi.PtrType
@@ -1227,7 +1229,7 @@ func typesStrCmp(a, b typeAndStr) int {
// GC information is always a bitmask, never a gc program.
// GCSym may be called in concurrent backend, so it does not emit the symbol
// content.
-func GCSym(t *types.Type) (lsym *obj.LSym, ptrdata int64) {
+func GCSym(t *types.Type, onDemandAllowed bool) (lsym *obj.LSym, ptrdata int64) {
// Record that we need to emit the GC symbol.
gcsymmu.Lock()
if _, ok := gcsymset[t]; !ok {
@@ -1235,7 +1237,7 @@ func GCSym(t *types.Type) (lsym *obj.LSym, ptrdata int64) {
}
gcsymmu.Unlock()
- lsym, _, ptrdata = dgcsym(t, false, false)
+ lsym, _, ptrdata = dgcsym(t, false, onDemandAllowed)
return
}
@@ -1280,9 +1282,7 @@ func dgcptrmask(t *types.Type, write bool) *obj.LSym {
// word offsets in t that hold pointers.
// ptrmask is assumed to fit at least types.PtrDataSize(t)/PtrSize bits.
func fillptrmask(t *types.Type, ptrmask []byte) {
- for i := range ptrmask {
- ptrmask[i] = 0
- }
+ clear(ptrmask)
if !t.HasPointers() {
return
}
@@ -1475,10 +1475,3 @@ func MarkUsedIfaceMethod(n *ir.CallExpr) {
Add: InterfaceMethodOffset(ityp, midx),
})
}
-
-func deref(t *types.Type) *types.Type {
- if t.IsPtr() {
- return t.Elem()
- }
- return t
-}
diff --git a/src/cmd/compile/internal/riscv64/ggen.go b/src/cmd/compile/internal/riscv64/ggen.go
index 44488e4327..d8afb01f3b 100644
--- a/src/cmd/compile/internal/riscv64/ggen.go
+++ b/src/cmd/compile/internal/riscv64/ggen.go
@@ -6,7 +6,6 @@ package riscv64
import (
"cmd/compile/internal/base"
- "cmd/compile/internal/ir"
"cmd/compile/internal/objw"
"cmd/compile/internal/types"
"cmd/internal/obj"
@@ -14,46 +13,19 @@ import (
)
func zeroRange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
- if cnt == 0 {
- return p
+
+ if cnt%int64(types.PtrSize) != 0 {
+ panic("zeroed region not aligned")
}
// Adjust the frame to account for LR.
off += base.Ctxt.Arch.FixedFrameSize
- if cnt < int64(4*types.PtrSize) {
- for i := int64(0); i < cnt; i += int64(types.PtrSize) {
- p = pp.Append(p, riscv.AMOV, obj.TYPE_REG, riscv.REG_ZERO, 0, obj.TYPE_MEM, riscv.REG_SP, off+i)
- }
- return p
+ for cnt != 0 {
+ p = pp.Append(p, riscv.AMOV, obj.TYPE_REG, riscv.REG_ZERO, 0, obj.TYPE_MEM, riscv.REG_SP, off)
+ cnt -= int64(types.PtrSize)
+ off += int64(types.PtrSize)
}
- if cnt <= int64(128*types.PtrSize) {
- p = pp.Append(p, riscv.AADDI, obj.TYPE_CONST, 0, off, obj.TYPE_REG, riscv.REG_X25, 0)
- p.Reg = riscv.REG_SP
- p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ir.Syms.Duffzero
- p.To.Offset = 8 * (128 - cnt/int64(types.PtrSize))
- return p
- }
-
- // Loop, zeroing pointer width bytes at a time.
- // ADD $(off), SP, T0
- // ADD $(cnt), T0, T1
- // loop:
- // MOV ZERO, (T0)
- // ADD $Widthptr, T0
- // BNE T0, T1, loop
- p = pp.Append(p, riscv.AADD, obj.TYPE_CONST, 0, off, obj.TYPE_REG, riscv.REG_T0, 0)
- p.Reg = riscv.REG_SP
- p = pp.Append(p, riscv.AADD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, riscv.REG_T1, 0)
- p.Reg = riscv.REG_T0
- p = pp.Append(p, riscv.AMOV, obj.TYPE_REG, riscv.REG_ZERO, 0, obj.TYPE_MEM, riscv.REG_T0, 0)
- loop := p
- p = pp.Append(p, riscv.AADD, obj.TYPE_CONST, 0, int64(types.PtrSize), obj.TYPE_REG, riscv.REG_T0, 0)
- p = pp.Append(p, riscv.ABNE, obj.TYPE_REG, riscv.REG_T0, 0, obj.TYPE_BRANCH, 0, 0)
- p.Reg = riscv.REG_T1
- p.To.SetTarget(loop)
return p
}
diff --git a/src/cmd/compile/internal/riscv64/ssa.go b/src/cmd/compile/internal/riscv64/ssa.go
index 759d8d7cf4..61de983bb0 100644
--- a/src/cmd/compile/internal/riscv64/ssa.go
+++ b/src/cmd/compile/internal/riscv64/ssa.go
@@ -14,6 +14,7 @@ import (
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/riscv"
+ "internal/abi"
)
// ssaRegToReg maps ssa register numbers to obj register numbers.
@@ -180,6 +181,8 @@ func largestMove(alignment int64) (obj.As, int64) {
}
}
+var fracMovOps = []obj.As{riscv.AMOVB, riscv.AMOVH, riscv.AMOVW, riscv.AMOV}
+
// ssaMarkMoves marks any MOVXconst ops that need to avoid clobbering flags.
// RISC-V has no flags, so this is a no-op.
func ssaMarkMoves(s *ssagen.State, b *ssa.Block) {}
@@ -416,10 +419,12 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = r
case ssa.OpRISCV64FSQRTS, ssa.OpRISCV64FNEGS, ssa.OpRISCV64FABSD, ssa.OpRISCV64FSQRTD, ssa.OpRISCV64FNEGD,
- ssa.OpRISCV64FMVSX, ssa.OpRISCV64FMVDX,
+ ssa.OpRISCV64FMVSX, ssa.OpRISCV64FMVXS, ssa.OpRISCV64FMVDX, ssa.OpRISCV64FMVXD,
ssa.OpRISCV64FCVTSW, ssa.OpRISCV64FCVTSL, ssa.OpRISCV64FCVTWS, ssa.OpRISCV64FCVTLS,
ssa.OpRISCV64FCVTDW, ssa.OpRISCV64FCVTDL, ssa.OpRISCV64FCVTWD, ssa.OpRISCV64FCVTLD, ssa.OpRISCV64FCVTDS, ssa.OpRISCV64FCVTSD,
- ssa.OpRISCV64NOT, ssa.OpRISCV64NEG, ssa.OpRISCV64NEGW:
+ ssa.OpRISCV64FCLASSS, ssa.OpRISCV64FCLASSD,
+ ssa.OpRISCV64NOT, ssa.OpRISCV64NEG, ssa.OpRISCV64NEGW, ssa.OpRISCV64CLZ, ssa.OpRISCV64CLZW, ssa.OpRISCV64CTZ, ssa.OpRISCV64CTZW,
+ ssa.OpRISCV64REV8, ssa.OpRISCV64CPOP, ssa.OpRISCV64CPOPW:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
@@ -507,12 +512,91 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Name = obj.NAME_EXTERN
// AuxInt encodes how many buffer entries we need.
p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
- case ssa.OpRISCV64LoweredPanicBoundsA, ssa.OpRISCV64LoweredPanicBoundsB, ssa.OpRISCV64LoweredPanicBoundsC:
- p := s.Prog(obj.ACALL)
+
+ case ssa.OpRISCV64LoweredPanicBoundsRR, ssa.OpRISCV64LoweredPanicBoundsRC, ssa.OpRISCV64LoweredPanicBoundsCR, ssa.OpRISCV64LoweredPanicBoundsCC:
+ // Compute the constant we put in the PCData entry for this call.
+ code, signed := ssa.BoundsKind(v.AuxInt).Code()
+ xIsReg := false
+ yIsReg := false
+ xVal := 0
+ yVal := 0
+ switch v.Op {
+ case ssa.OpRISCV64LoweredPanicBoundsRR:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - riscv.REG_X5)
+ yIsReg = true
+ yVal = int(v.Args[1].Reg() - riscv.REG_X5)
+ case ssa.OpRISCV64LoweredPanicBoundsRC:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - riscv.REG_X5)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ if yVal == xVal {
+ yVal = 1
+ }
+ p := s.Prog(riscv.AMOV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = riscv.REG_X5 + int16(yVal)
+ }
+ case ssa.OpRISCV64LoweredPanicBoundsCR:
+ yIsReg = true
+ yVal = int(v.Args[0].Reg() - riscv.REG_X5)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else {
+ // Move constant to a register
+ if xVal == yVal {
+ xVal = 1
+ }
+ p := s.Prog(riscv.AMOV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = riscv.REG_X5 + int16(xVal)
+ }
+ case ssa.OpRISCV64LoweredPanicBoundsCC:
+ c := v.Aux.(ssa.PanicBoundsCC).Cx
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else {
+ // Move constant to a register
+ xIsReg = true
+ p := s.Prog(riscv.AMOV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = riscv.REG_X5 + int16(xVal)
+ }
+ c = v.Aux.(ssa.PanicBoundsCC).Cy
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ yVal = 1
+ p := s.Prog(riscv.AMOV)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = riscv.REG_X5 + int16(yVal)
+ }
+ }
+ c := abi.BoundsEncode(code, signed, xIsReg, yIsReg, xVal, yVal)
+
+ p := s.Prog(obj.APCDATA)
+ p.From.SetConst(abi.PCDATA_PanicBounds)
+ p.To.SetConst(int64(c))
+ p = s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
- s.UseArgs(16) // space used in callee args area by assembly stubs
+ p.To.Sym = ir.Syms.PanicBounds
case ssa.OpRISCV64LoweredAtomicLoad8:
s.Prog(riscv.AFENCE)
@@ -656,70 +740,181 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.RegTo2 = riscv.REG_ZERO
case ssa.OpRISCV64LoweredZero:
- mov, sz := largestMove(v.AuxInt)
+ ptr := v.Args[0].Reg()
+ sc := v.AuxValAndOff()
+ n := sc.Val64()
- // mov ZERO, (Rarg0)
- // ADD $sz, Rarg0
- // BGEU Rarg1, Rarg0, -2(PC)
+ mov, sz := largestMove(sc.Off64())
- p := s.Prog(mov)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = riscv.REG_ZERO
- p.To.Type = obj.TYPE_MEM
- p.To.Reg = v.Args[0].Reg()
+ // mov ZERO, (offset)(Rarg0)
+ var off int64
+ for n >= sz {
+ zeroOp(s, mov, ptr, off)
+ off += sz
+ n -= sz
+ }
+
+ for i := len(fracMovOps) - 1; i >= 0; i-- {
+ tsz := int64(1 << i)
+ if n < tsz {
+ continue
+ }
+ zeroOp(s, fracMovOps[i], ptr, off)
+ off += tsz
+ n -= tsz
+ }
+
+ case ssa.OpRISCV64LoweredZeroLoop:
+ ptr := v.Args[0].Reg()
+ sc := v.AuxValAndOff()
+ n := sc.Val64()
+ mov, sz := largestMove(sc.Off64())
+ chunk := 8 * sz
+
+ if n <= 3*chunk {
+ v.Fatalf("ZeroLoop too small:%d, expect:%d", n, 3*chunk)
+ }
+
+ tmp := v.RegTmp()
+
+ p := s.Prog(riscv.AADD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = n - n%chunk
+ p.Reg = ptr
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = tmp
+
+ for i := int64(0); i < 8; i++ {
+ zeroOp(s, mov, ptr, sz*i)
+ }
p2 := s.Prog(riscv.AADD)
p2.From.Type = obj.TYPE_CONST
- p2.From.Offset = sz
+ p2.From.Offset = chunk
p2.To.Type = obj.TYPE_REG
- p2.To.Reg = v.Args[0].Reg()
+ p2.To.Reg = ptr
- p3 := s.Prog(riscv.ABGEU)
- p3.To.Type = obj.TYPE_BRANCH
- p3.Reg = v.Args[0].Reg()
+ p3 := s.Prog(riscv.ABNE)
+ p3.From.Reg = tmp
p3.From.Type = obj.TYPE_REG
- p3.From.Reg = v.Args[1].Reg()
- p3.To.SetTarget(p)
+ p3.Reg = ptr
+ p3.To.Type = obj.TYPE_BRANCH
+ p3.To.SetTarget(p.Link)
+
+ n %= chunk
+
+ // mov ZERO, (offset)(Rarg0)
+ var off int64
+ for n >= sz {
+ zeroOp(s, mov, ptr, off)
+ off += sz
+ n -= sz
+ }
+
+ for i := len(fracMovOps) - 1; i >= 0; i-- {
+ tsz := int64(1 << i)
+ if n < tsz {
+ continue
+ }
+ zeroOp(s, fracMovOps[i], ptr, off)
+ off += tsz
+ n -= tsz
+ }
case ssa.OpRISCV64LoweredMove:
- mov, sz := largestMove(v.AuxInt)
+ dst := v.Args[0].Reg()
+ src := v.Args[1].Reg()
+ if dst == src {
+ break
+ }
- // mov (Rarg1), T2
- // mov T2, (Rarg0)
- // ADD $sz, Rarg0
- // ADD $sz, Rarg1
- // BGEU Rarg2, Rarg0, -4(PC)
+ sa := v.AuxValAndOff()
+ n := sa.Val64()
+ mov, sz := largestMove(sa.Off64())
- p := s.Prog(mov)
- p.From.Type = obj.TYPE_MEM
- p.From.Reg = v.Args[1].Reg()
+ var off int64
+ tmp := int16(riscv.REG_X5)
+ for n >= sz {
+ moveOp(s, mov, dst, src, tmp, off)
+ off += sz
+ n -= sz
+ }
+
+ for i := len(fracMovOps) - 1; i >= 0; i-- {
+ tsz := int64(1 << i)
+ if n < tsz {
+ continue
+ }
+ moveOp(s, fracMovOps[i], dst, src, tmp, off)
+ off += tsz
+ n -= tsz
+ }
+
+ case ssa.OpRISCV64LoweredMoveLoop:
+ dst := v.Args[0].Reg()
+ src := v.Args[1].Reg()
+ if dst == src {
+ break
+ }
+
+ sc := v.AuxValAndOff()
+ n := sc.Val64()
+ mov, sz := largestMove(sc.Off64())
+ chunk := 8 * sz
+
+ if n <= 3*chunk {
+ v.Fatalf("MoveLoop too small:%d, expect:%d", n, 3*chunk)
+ }
+ tmp := int16(riscv.REG_X5)
+
+ p := s.Prog(riscv.AADD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = n - n%chunk
+ p.Reg = src
p.To.Type = obj.TYPE_REG
- p.To.Reg = riscv.REG_T2
+ p.To.Reg = riscv.REG_X6
- p2 := s.Prog(mov)
- p2.From.Type = obj.TYPE_REG
- p2.From.Reg = riscv.REG_T2
- p2.To.Type = obj.TYPE_MEM
- p2.To.Reg = v.Args[0].Reg()
+ for i := int64(0); i < 8; i++ {
+ moveOp(s, mov, dst, src, tmp, sz*i)
+ }
- p3 := s.Prog(riscv.AADD)
- p3.From.Type = obj.TYPE_CONST
- p3.From.Offset = sz
- p3.To.Type = obj.TYPE_REG
- p3.To.Reg = v.Args[0].Reg()
+ p1 := s.Prog(riscv.AADD)
+ p1.From.Type = obj.TYPE_CONST
+ p1.From.Offset = chunk
+ p1.To.Type = obj.TYPE_REG
+ p1.To.Reg = src
- p4 := s.Prog(riscv.AADD)
- p4.From.Type = obj.TYPE_CONST
- p4.From.Offset = sz
- p4.To.Type = obj.TYPE_REG
- p4.To.Reg = v.Args[1].Reg()
+ p2 := s.Prog(riscv.AADD)
+ p2.From.Type = obj.TYPE_CONST
+ p2.From.Offset = chunk
+ p2.To.Type = obj.TYPE_REG
+ p2.To.Reg = dst
- p5 := s.Prog(riscv.ABGEU)
- p5.To.Type = obj.TYPE_BRANCH
- p5.Reg = v.Args[1].Reg()
- p5.From.Type = obj.TYPE_REG
- p5.From.Reg = v.Args[2].Reg()
- p5.To.SetTarget(p)
+ p3 := s.Prog(riscv.ABNE)
+ p3.From.Reg = riscv.REG_X6
+ p3.From.Type = obj.TYPE_REG
+ p3.Reg = src
+ p3.To.Type = obj.TYPE_BRANCH
+ p3.To.SetTarget(p.Link)
+
+ n %= chunk
+
+ var off int64
+ for n >= sz {
+ moveOp(s, mov, dst, src, tmp, off)
+ off += sz
+ n -= sz
+ }
+
+ for i := len(fracMovOps) - 1; i >= 0; i-- {
+ tsz := int64(1 << i)
+ if n < tsz {
+ continue
+ }
+ moveOp(s, fracMovOps[i], dst, src, tmp, off)
+ off += tsz
+ n -= tsz
+ }
case ssa.OpRISCV64LoweredNilCheck:
// Issue a load which will fault if arg is nil.
@@ -754,20 +949,6 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
- case ssa.OpRISCV64DUFFZERO:
- p := s.Prog(obj.ADUFFZERO)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ir.Syms.Duffzero
- p.To.Offset = v.AuxInt
-
- case ssa.OpRISCV64DUFFCOPY:
- p := s.Prog(obj.ADUFFCOPY)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ir.Syms.Duffcopy
- p.To.Offset = v.AuxInt
-
case ssa.OpRISCV64LoweredPubBarrier:
// FENCE
s.Prog(v.Op.Asm())
@@ -802,22 +983,7 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
s.SetPos(b.Pos)
switch b.Kind {
- case ssa.BlockDefer:
- // defer returns in A0:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Prog(riscv.ABNE)
- p.To.Type = obj.TYPE_BRANCH
- p.From.Type = obj.TYPE_REG
- p.From.Reg = riscv.REG_ZERO
- p.Reg = riscv.REG_A0
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
- if b.Succs[0].Block() != next {
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_BRANCH
- s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
- }
- case ssa.BlockPlain:
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
@@ -888,3 +1054,31 @@ func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg in
p.Pos = p.Pos.WithNotStmt()
return p
}
+
+func zeroOp(s *ssagen.State, mov obj.As, reg int16, off int64) {
+ p := s.Prog(mov)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = riscv.REG_ZERO
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = reg
+ p.To.Offset = off
+ return
+}
+
+func moveOp(s *ssagen.State, mov obj.As, dst int16, src int16, tmp int16, off int64) {
+ p := s.Prog(mov)
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = src
+ p.From.Offset = off
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = tmp
+
+ p1 := s.Prog(mov)
+ p1.From.Type = obj.TYPE_REG
+ p1.From.Reg = tmp
+ p1.To.Type = obj.TYPE_MEM
+ p1.To.Reg = dst
+ p1.To.Offset = off
+
+ return
+}
diff --git a/src/cmd/compile/internal/rttype/rttype.go b/src/cmd/compile/internal/rttype/rttype.go
index a5aecb2535..b8c9533991 100644
--- a/src/cmd/compile/internal/rttype/rttype.go
+++ b/src/cmd/compile/internal/rttype/rttype.go
@@ -27,8 +27,7 @@ var ArrayType *types.Type
var ChanType *types.Type
var FuncType *types.Type
var InterfaceType *types.Type
-var OldMapType *types.Type
-var SwissMapType *types.Type
+var MapType *types.Type
var PtrType *types.Type
var SliceType *types.Type
var StructType *types.Type
@@ -50,26 +49,25 @@ func Init() {
// Note: this has to be called explicitly instead of being
// an init function so it runs after the types package has
// been properly initialized.
- Type = fromReflect(reflect.TypeOf(abi.Type{}))
- ArrayType = fromReflect(reflect.TypeOf(abi.ArrayType{}))
- ChanType = fromReflect(reflect.TypeOf(abi.ChanType{}))
- FuncType = fromReflect(reflect.TypeOf(abi.FuncType{}))
- InterfaceType = fromReflect(reflect.TypeOf(abi.InterfaceType{}))
- OldMapType = fromReflect(reflect.TypeOf(abi.OldMapType{}))
- SwissMapType = fromReflect(reflect.TypeOf(abi.SwissMapType{}))
- PtrType = fromReflect(reflect.TypeOf(abi.PtrType{}))
- SliceType = fromReflect(reflect.TypeOf(abi.SliceType{}))
- StructType = fromReflect(reflect.TypeOf(abi.StructType{}))
+ Type = FromReflect(reflect.TypeFor[abi.Type]())
+ ArrayType = FromReflect(reflect.TypeFor[abi.ArrayType]())
+ ChanType = FromReflect(reflect.TypeFor[abi.ChanType]())
+ FuncType = FromReflect(reflect.TypeFor[abi.FuncType]())
+ InterfaceType = FromReflect(reflect.TypeFor[abi.InterfaceType]())
+ MapType = FromReflect(reflect.TypeFor[abi.MapType]())
+ PtrType = FromReflect(reflect.TypeFor[abi.PtrType]())
+ SliceType = FromReflect(reflect.TypeFor[abi.SliceType]())
+ StructType = FromReflect(reflect.TypeFor[abi.StructType]())
- IMethod = fromReflect(reflect.TypeOf(abi.Imethod{}))
- Method = fromReflect(reflect.TypeOf(abi.Method{}))
- StructField = fromReflect(reflect.TypeOf(abi.StructField{}))
- UncommonType = fromReflect(reflect.TypeOf(abi.UncommonType{}))
+ IMethod = FromReflect(reflect.TypeFor[abi.Imethod]())
+ Method = FromReflect(reflect.TypeFor[abi.Method]())
+ StructField = FromReflect(reflect.TypeFor[abi.StructField]())
+ UncommonType = FromReflect(reflect.TypeFor[abi.UncommonType]())
- InterfaceSwitch = fromReflect(reflect.TypeOf(abi.InterfaceSwitch{}))
- TypeAssert = fromReflect(reflect.TypeOf(abi.TypeAssert{}))
+ InterfaceSwitch = FromReflect(reflect.TypeFor[abi.InterfaceSwitch]())
+ TypeAssert = FromReflect(reflect.TypeFor[abi.TypeAssert]())
- ITab = fromReflect(reflect.TypeOf(abi.ITab{}))
+ ITab = FromReflect(reflect.TypeFor[abi.ITab]())
// Make sure abi functions are correct. These functions are used
// by the linker which doesn't have the ability to do type layout,
@@ -92,8 +90,8 @@ func Init() {
}
}
-// fromReflect translates from a host type to the equivalent target type.
-func fromReflect(rt reflect.Type) *types.Type {
+// FromReflect translates from a host type to the equivalent target type.
+func FromReflect(rt reflect.Type) *types.Type {
t := reflectToType(rt)
types.CalcSize(t)
return t
@@ -108,6 +106,10 @@ func reflectToType(rt reflect.Type) *types.Type {
return types.Types[types.TBOOL]
case reflect.Int:
return types.Types[types.TINT]
+ case reflect.Int8:
+ return types.Types[types.TINT8]
+ case reflect.Int16:
+ return types.Types[types.TINT16]
case reflect.Int32:
return types.Types[types.TINT32]
case reflect.Uint8:
@@ -116,9 +118,15 @@ func reflectToType(rt reflect.Type) *types.Type {
return types.Types[types.TUINT16]
case reflect.Uint32:
return types.Types[types.TUINT32]
+ case reflect.Float32:
+ return types.Types[types.TFLOAT32]
+ case reflect.Float64:
+ return types.Types[types.TFLOAT64]
case reflect.Uintptr:
return types.Types[types.TUINTPTR]
- case reflect.Ptr, reflect.Func, reflect.UnsafePointer:
+ case reflect.Ptr:
+ return types.NewPtr(reflectToType(rt.Elem()))
+ case reflect.Func, reflect.UnsafePointer:
// TODO: there's no mechanism to distinguish different pointer types,
// so we treat them all as unsafe.Pointer.
return types.Types[types.TUNSAFEPTR]
@@ -134,6 +142,12 @@ func reflectToType(rt reflect.Type) *types.Type {
fields[i] = &types.Field{Sym: &types.Sym{Name: f.Name}, Type: ft}
}
return types.NewStruct(fields)
+ case reflect.Chan:
+ return types.NewChan(reflectToType(rt.Elem()), types.ChanDir(rt.ChanDir()))
+ case reflect.String:
+ return types.Types[types.TSTRING]
+ case reflect.Complex128:
+ return types.Types[types.TCOMPLEX128]
default:
base.Fatalf("unhandled kind %s", rt.Kind())
return nil
@@ -155,7 +169,7 @@ func NewCursor(lsym *obj.LSym, off int64, t *types.Type) Cursor {
// WritePtr writes a pointer "target" to the component at the location specified by c.
func (c Cursor) WritePtr(target *obj.LSym) {
- if c.typ.Kind() != types.TUNSAFEPTR {
+ if c.typ.Kind() != types.TUNSAFEPTR && c.typ.Kind() != types.TPTR {
base.Fatalf("can't write ptr, it has kind %s", c.typ.Kind())
}
if target == nil {
diff --git a/src/cmd/compile/internal/s390x/ggen.go b/src/cmd/compile/internal/s390x/ggen.go
index 70e4031224..c4f88e7826 100644
--- a/src/cmd/compile/internal/s390x/ggen.go
+++ b/src/cmd/compile/internal/s390x/ggen.go
@@ -11,7 +11,7 @@ import (
"cmd/internal/obj/s390x"
)
-// clearLoopCutOff is the (somewhat arbitrary) value above which it is better
+// clearLoopCutoff is the (somewhat arbitrary) value above which it is better
// to have a loop of clear instructions (e.g. XCs) rather than just generating
// multiple instructions (i.e. loop unrolling).
// Must be between 256 and 4096.
diff --git a/src/cmd/compile/internal/s390x/ssa.go b/src/cmd/compile/internal/s390x/ssa.go
index a97c1569c1..de00f1ef8c 100644
--- a/src/cmd/compile/internal/s390x/ssa.go
+++ b/src/cmd/compile/internal/s390x/ssa.go
@@ -15,6 +15,7 @@ import (
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/s390x"
+ "internal/abi"
)
// ssaMarkMoves marks any MOVXconst ops that need to avoid clobbering flags.
@@ -281,6 +282,10 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
case ssa.OpS390XCPSDR:
p := opregreg(s, v.Op.Asm(), v.Reg(), v.Args[1].Reg())
p.Reg = v.Args[0].Reg()
+ case ssa.OpS390XWFMAXDB, ssa.OpS390XWFMAXSB,
+ ssa.OpS390XWFMINDB, ssa.OpS390XWFMINSB:
+ p := opregregimm(s, v.Op.Asm(), v.Reg(), v.Args[0].Reg(), 1 /* Java Math.Max() */)
+ p.AddRestSource(obj.Addr{Type: obj.TYPE_REG, Reg: v.Args[1].Reg()})
case ssa.OpS390XDIVD, ssa.OpS390XDIVW,
ssa.OpS390XDIVDU, ssa.OpS390XDIVWU,
ssa.OpS390XMODD, ssa.OpS390XMODW,
@@ -569,12 +574,92 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Name = obj.NAME_EXTERN
// AuxInt encodes how many buffer entries we need.
p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
- case ssa.OpS390XLoweredPanicBoundsA, ssa.OpS390XLoweredPanicBoundsB, ssa.OpS390XLoweredPanicBoundsC:
- p := s.Prog(obj.ACALL)
+
+ case ssa.OpS390XLoweredPanicBoundsRR, ssa.OpS390XLoweredPanicBoundsRC, ssa.OpS390XLoweredPanicBoundsCR, ssa.OpS390XLoweredPanicBoundsCC:
+ // Compute the constant we put in the PCData entry for this call.
+ code, signed := ssa.BoundsKind(v.AuxInt).Code()
+ xIsReg := false
+ yIsReg := false
+ xVal := 0
+ yVal := 0
+ switch v.Op {
+ case ssa.OpS390XLoweredPanicBoundsRR:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - s390x.REG_R0)
+ yIsReg = true
+ yVal = int(v.Args[1].Reg() - s390x.REG_R0)
+ case ssa.OpS390XLoweredPanicBoundsRC:
+ xIsReg = true
+ xVal = int(v.Args[0].Reg() - s390x.REG_R0)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ if yVal == xVal {
+ yVal = 1
+ }
+ p := s.Prog(s390x.AMOVD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = s390x.REG_R0 + int16(yVal)
+ }
+ case ssa.OpS390XLoweredPanicBoundsCR:
+ yIsReg = true
+ yVal = int(v.Args[0].Reg() - s390x.REG_R0)
+ c := v.Aux.(ssa.PanicBoundsC).C
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else {
+ // Move constant to a register
+ if xVal == yVal {
+ xVal = 1
+ }
+ p := s.Prog(s390x.AMOVD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = s390x.REG_R0 + int16(xVal)
+ }
+ case ssa.OpS390XLoweredPanicBoundsCC:
+ c := v.Aux.(ssa.PanicBoundsCC).Cx
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ xVal = int(c)
+ } else {
+ // Move constant to a register
+ xIsReg = true
+ p := s.Prog(s390x.AMOVD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = s390x.REG_R0 + int16(xVal)
+ }
+ c = v.Aux.(ssa.PanicBoundsCC).Cy
+ if c >= 0 && c <= abi.BoundsMaxConst {
+ yVal = int(c)
+ } else {
+ // Move constant to a register
+ yIsReg = true
+ yVal = 1
+ p := s.Prog(s390x.AMOVD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = c
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = s390x.REG_R0 + int16(yVal)
+ }
+ }
+ c := abi.BoundsEncode(code, signed, xIsReg, yIsReg, xVal, yVal)
+
+ p := s.Prog(obj.APCDATA)
+ p.From.SetConst(abi.PCDATA_PanicBounds)
+ p.To.SetConst(int64(c))
+ p = s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
- p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
- s.UseArgs(16) // space used in callee args area by assembly stubs
+ p.To.Sym = ir.Syms.PanicBounds
+
case ssa.OpS390XFLOGR, ssa.OpS390XPOPCNT,
ssa.OpS390XNEG, ssa.OpS390XNEGW,
ssa.OpS390XMOVWBR, ssa.OpS390XMOVDBR:
@@ -887,26 +972,13 @@ func blockAsm(b *ssa.Block) obj.As {
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
// Handle generic blocks first.
switch b.Kind {
- case ssa.BlockPlain:
+ case ssa.BlockPlain, ssa.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(s390x.ABR)
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
return
- case ssa.BlockDefer:
- // defer returns in R3:
- // 0 if we should continue executing
- // 1 if we should jump to deferreturn call
- p := s.Br(s390x.ACIJ, b.Succs[1].Block())
- p.From.Type = obj.TYPE_CONST
- p.From.Offset = int64(s390x.NotEqual & s390x.NotUnordered) // unordered is not possible
- p.Reg = s390x.REG_R3
- p.AddRestSourceConst(0)
- if b.Succs[0].Block() != next {
- s.Br(s390x.ABR, b.Succs[0].Block())
- }
- return
case ssa.BlockExit, ssa.BlockRetJmp:
return
case ssa.BlockRet:
diff --git a/src/cmd/compile/internal/ssa/README.md b/src/cmd/compile/internal/ssa/README.md
index 2c602f160c..3626f5bb7b 100644
--- a/src/cmd/compile/internal/ssa/README.md
+++ b/src/cmd/compile/internal/ssa/README.md
@@ -48,8 +48,7 @@ However, certain types don't come from Go and are special; below we will cover
Some operators contain an auxiliary field. The aux fields are usually printed as
enclosed in `[]` or `{}`, and could be the constant op argument, argument type,
-etc.
-for example:
+etc. For example:
v13 (?) = Const64 [1]
@@ -206,6 +205,16 @@ name, e.g.
This will match any function named "Foo" within a package whose final
suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).
+The users may also print the Control Flow Graph(CFG) by specifying in
+`GOSSAFUNC` value in the following format:
+
+ GOSSAFUNC="$FunctionName:$PassName1,$PassName2,..." go build
+
+For example, the following command will print SSA with CFGs attached to the
+`sccp` and `generic deadcode` pass columns:
+
+ GOSSAFUNC="blah.Foo:sccp,generic deadcode" go build
+
If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value
and dumps will be written to stdout:
diff --git a/src/cmd/compile/internal/ssa/_gen/386.rules b/src/cmd/compile/internal/ssa/_gen/386.rules
index 67cfa3460a..5f11502419 100644
--- a/src/cmd/compile/internal/ssa/_gen/386.rules
+++ b/src/cmd/compile/internal/ssa/_gen/386.rules
@@ -246,7 +246,7 @@
// Medium copying uses a duff device.
(Move [s] dst src mem)
&& s > 8 && s <= 4*128 && s%4 == 0
- && !config.noDuffDevice && logLargeCopy(v, s) =>
+ && logLargeCopy(v, s) =>
(DUFFCOPY [10*(128-s/4)] dst src mem)
// 10 and 128 are magic constants. 10 is the number of bytes to encode:
// MOVL (SI), CX
@@ -256,7 +256,7 @@
// and 128 is the number of such blocks. See src/runtime/duff_386.s:duffcopy.
// Large copying uses REP MOVSL.
-(Move [s] dst src mem) && (s > 4*128 || config.noDuffDevice) && s%4 == 0 && logLargeCopy(v, s) =>
+(Move [s] dst src mem) && s > 4*128 && s%4 == 0 && logLargeCopy(v, s) =>
(REPMOVSL dst src (MOVLconst [int32(s/4)]) mem)
// Lowering Zero instructions
@@ -299,8 +299,7 @@
// Medium zeroing uses a duff device.
(Zero [s] destptr mem)
- && s > 16 && s <= 4*128 && s%4 == 0
- && !config.noDuffDevice =>
+ && s > 16 && s <= 4*128 && s%4 == 0 =>
(DUFFZERO [1*(128-s/4)] destptr (MOVLconst [0]) mem)
// 1 and 128 are magic constants. 1 is the number of bytes to encode STOSL.
// 128 is the number of STOSL instructions in duffzero.
@@ -308,7 +307,7 @@
// Large zeroing uses REP STOSQ.
(Zero [s] destptr mem)
- && (s > 4*128 || (config.noDuffDevice && s > 16))
+ && s > 4*128
&& s%4 == 0 =>
(REPSTOSL destptr (MOVLconst [int32(s/4)]) (MOVLconst [0]) mem)
@@ -364,13 +363,16 @@
// Write barrier.
(WB ...) => (LoweredWB ...)
-(PanicBounds [kind] x y mem) && boundsABI(kind) == 0 => (LoweredPanicBoundsA [kind] x y mem)
-(PanicBounds [kind] x y mem) && boundsABI(kind) == 1 => (LoweredPanicBoundsB [kind] x y mem)
-(PanicBounds [kind] x y mem) && boundsABI(kind) == 2 => (LoweredPanicBoundsC [kind] x y mem)
+(PanicBounds ...) => (LoweredPanicBoundsRR ...)
+(PanicExtend ...) => (LoweredPanicExtendRR ...)
-(PanicExtend [kind] hi lo y mem) && boundsABI(kind) == 0 => (LoweredPanicExtendA [kind] hi lo y mem)
-(PanicExtend [kind] hi lo y mem) && boundsABI(kind) == 1 => (LoweredPanicExtendB [kind] hi lo y mem)
-(PanicExtend [kind] hi lo y mem) && boundsABI(kind) == 2 => (LoweredPanicExtendC [kind] hi lo y mem)
+(LoweredPanicBoundsRR [kind] x (MOVLconst [c]) mem) => (LoweredPanicBoundsRC [kind] x {PanicBoundsC{C:int64(c)}} mem)
+(LoweredPanicBoundsRR [kind] (MOVLconst [c]) y mem) => (LoweredPanicBoundsCR [kind] {PanicBoundsC{C:int64(c)}} y mem)
+(LoweredPanicBoundsRC [kind] {p} (MOVLconst [c]) mem) => (LoweredPanicBoundsCC [kind] {PanicBoundsCC{Cx:int64(c), Cy:p.C}} mem)
+
+(LoweredPanicExtendRR [kind] hi lo (MOVLconst [c]) mem) => (LoweredPanicExtendRC [kind] hi lo {PanicBoundsC{C:int64(c)}} mem)
+(LoweredPanicExtendRR [kind] (MOVLconst [hi]) (MOVLconst [lo]) y mem) => (LoweredPanicBoundsCR [kind] {PanicBoundsC{C:int64(hi)<<32 + int64(uint32(lo))}} y mem)
+(LoweredPanicExtendRC [kind] {p} (MOVLconst [hi]) (MOVLconst [lo]) mem) => (LoweredPanicBoundsCC [kind] {PanicBoundsCC{Cx:int64(hi)<<32+int64(uint32(lo)), Cy:p.C}} mem)
// ***************************
// Above: lowering rules
@@ -940,3 +942,5 @@
(MOVBload [off] {sym} (SB) _) && symIsRO(sym) => (MOVLconst [int32(read8(sym, int64(off)))])
(MOVWload [off] {sym} (SB) _) && symIsRO(sym) => (MOVLconst [int32(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))])
(MOVLload [off] {sym} (SB) _) && symIsRO(sym) => (MOVLconst [int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))])
+(MOVBLSXload [off] {sym} (SB) _) && symIsRO(sym) => (MOVLconst [int32(int8(read8(sym, int64(off))))])
+(MOVWLSXload [off] {sym} (SB) _) && symIsRO(sym) => (MOVLconst [int32(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))])
diff --git a/src/cmd/compile/internal/ssa/_gen/386Ops.go b/src/cmd/compile/internal/ssa/_gen/386Ops.go
index a976a91fb8..60599a33ab 100644
--- a/src/cmd/compile/internal/ssa/_gen/386Ops.go
+++ b/src/cmd/compile/internal/ssa/_gen/386Ops.go
@@ -76,7 +76,6 @@ func init() {
cx = buildReg("CX")
dx = buildReg("DX")
bx = buildReg("BX")
- si = buildReg("SI")
gp = buildReg("AX CX DX BX BP SI DI")
fp = buildReg("X0 X1 X2 X3 X4 X5 X6 X7")
gpsp = gp | buildReg("SP")
@@ -523,16 +522,19 @@ func init() {
// Returns a pointer to a write barrier buffer in DI.
{name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: callerSave &^ gp, outputs: []regMask{buildReg("DI")}}, clobberFlags: true, aux: "Int64"},
- // There are three of these functions so that they can have three different register inputs.
- // When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
- // default registers to match so we don't need to copy registers around unnecessarily.
- {name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{dx, bx}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go).
- {name: "LoweredPanicBoundsB", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{cx, dx}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go).
- {name: "LoweredPanicBoundsC", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{ax, cx}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go).
- // Extend ops are the same as Bounds ops except the indexes are 64-bit.
- {name: "LoweredPanicExtendA", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{si, dx, bx}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
- {name: "LoweredPanicExtendB", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{si, cx, dx}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
- {name: "LoweredPanicExtendC", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{si, ax, cx}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
+ // LoweredPanicBoundsRR takes x and y, two values that caused a bounds check to fail.
+ // the RC and CR versions are used when one of the arguments is a constant. CC is used
+ // when both are constant (normally both 0, as prove derives the fact that a [0] bounds
+ // failure means the length must have also been 0).
+ // AuxInt contains a report code (see PanicBounds in genericOps.go).
+ {name: "LoweredPanicBoundsRR", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{gp, gp}}, typ: "Mem", call: true}, // arg0=x, arg1=y, arg2=mem, returns memory.
+ {name: "LoweredPanicBoundsRC", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{gp}}, typ: "Mem", call: true}, // arg0=x, arg1=mem, returns memory.
+ {name: "LoweredPanicBoundsCR", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{gp}}, typ: "Mem", call: true}, // arg0=y, arg1=mem, returns memory.
+ {name: "LoweredPanicBoundsCC", argLength: 1, aux: "PanicBoundsCC", reg: regInfo{}, typ: "Mem", call: true}, // arg0=mem, returns memory.
+
+ // Same as above, but the x value is 64 bits.
+ {name: "LoweredPanicExtendRR", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{ax | cx | dx | bx, ax | cx | dx | bx, gp}}, typ: "Mem", call: true}, // arg0=x_hi, arg1=x_lo, arg2=y, arg3=mem, returns memory.
+ {name: "LoweredPanicExtendRC", argLength: 3, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{ax | cx | dx | bx, ax | cx | dx | bx}}, typ: "Mem", call: true}, // arg0=x_hi, arg1=x_lo, arg2=mem, returns memory.
// Constant flag values. For any comparison, there are 5 possible
// outcomes: the three from the signed total order (<,==,>) and the
diff --git a/src/cmd/compile/internal/ssa/_gen/AMD64.rules b/src/cmd/compile/internal/ssa/_gen/AMD64.rules
index ce9a6e9914..0bea99e38d 100644
--- a/src/cmd/compile/internal/ssa/_gen/AMD64.rules
+++ b/src/cmd/compile/internal/ssa/_gen/AMD64.rules
@@ -162,15 +162,24 @@
(Cvt64to32F ...) => (CVTSQ2SS ...)
(Cvt64to64F ...) => (CVTSQ2SD ...)
-(Cvt32Fto32 ...) => (CVTTSS2SL ...)
-(Cvt32Fto64 ...) => (CVTTSS2SQ ...)
-(Cvt64Fto32 ...) => (CVTTSD2SL ...)
-(Cvt64Fto64 ...) => (CVTTSD2SQ ...)
+// Float, to int.
+// To make AMD64 "overflow" return max positive instead of max negative, compute
+// y and not x, smear the sign bit, and xor.
+(Cvt32Fto32 x) && base.ConvertHash.MatchPos(v.Pos, nil) => (XORL y (SARLconst [31] (ANDL y:(CVTTSS2SL x) (NOTL (MOVLf2i x)))))
+(Cvt64Fto32 x) && base.ConvertHash.MatchPos(v.Pos, nil) => (XORL y (SARLconst [31] (ANDL y:(CVTTSD2SL x) (NOTL (MOVLf2i (CVTSD2SS x))))))
+
+(Cvt32Fto64 x) && base.ConvertHash.MatchPos(v.Pos, nil) => (XORQ y (SARQconst [63] (ANDQ y:(CVTTSS2SQ x) (NOTQ (MOVQf2i (CVTSS2SD x))) )))
+(Cvt64Fto64 x) && base.ConvertHash.MatchPos(v.Pos, nil) => (XORQ y (SARQconst [63] (ANDQ y:(CVTTSD2SQ x) (NOTQ (MOVQf2i x)))))
+
+(Cvt32Fto32 x) && !base.ConvertHash.MatchPos(v.Pos, nil) => (CVTTSS2SL x)
+(Cvt32Fto64 x) && !base.ConvertHash.MatchPos(v.Pos, nil) => (CVTTSS2SQ x)
+(Cvt64Fto32 x) && !base.ConvertHash.MatchPos(v.Pos, nil) => (CVTTSD2SL x)
+(Cvt64Fto64 x) && !base.ConvertHash.MatchPos(v.Pos, nil) => (CVTTSD2SQ x)
(Cvt32Fto64F ...) => (CVTSS2SD ...)
(Cvt64Fto32F ...) => (CVTSD2SS ...)
-(Round(32|64)F ...) => (Copy ...)
+(Round(32|64)F ...) => (LoweredRound(32|64)F ...)
// Floating-point min is tricky, as the hardware op isn't right for various special
// cases (-0 and NaN). We use two hardware ops organized just right to make the
@@ -262,28 +271,7 @@
(Move [2] dst src mem) => (MOVWstore dst (MOVWload src mem) mem)
(Move [4] dst src mem) => (MOVLstore dst (MOVLload src mem) mem)
(Move [8] dst src mem) => (MOVQstore dst (MOVQload src mem) mem)
-(Move [16] dst src mem) && config.useSSE => (MOVOstore dst (MOVOload src mem) mem)
-(Move [16] dst src mem) && !config.useSSE =>
- (MOVQstore [8] dst (MOVQload [8] src mem)
- (MOVQstore dst (MOVQload src mem) mem))
-
-(Move [32] dst src mem) =>
- (Move [16]
- (OffPtr dst [16])
- (OffPtr src [16])
- (Move [16] dst src mem))
-
-(Move [48] dst src mem) && config.useSSE =>
- (Move [32]
- (OffPtr dst [16])
- (OffPtr src [16])
- (Move [16] dst src mem))
-
-(Move [64] dst src mem) && config.useSSE =>
- (Move [32]
- (OffPtr dst [32])
- (OffPtr src [32])
- (Move [32] dst src mem))
+(Move [16] dst src mem) => (MOVOstore dst (MOVOload src mem) mem)
(Move [3] dst src mem) =>
(MOVBstore [2] dst (MOVBload [2] src mem)
@@ -313,35 +301,19 @@
(MOVQstore [int32(s-8)] dst (MOVQload [int32(s-8)] src mem)
(MOVQstore dst (MOVQload src mem) mem))
-// Adjust moves to be a multiple of 16 bytes.
-(Move [s] dst src mem)
- && s > 16 && s%16 != 0 && s%16 <= 8 =>
- (Move [s-s%16]
- (OffPtr dst [s%16])
- (OffPtr src [s%16])
- (MOVQstore dst (MOVQload src mem) mem))
-(Move [s] dst src mem)
- && s > 16 && s%16 != 0 && s%16 > 8 && config.useSSE =>
- (Move [s-s%16]
- (OffPtr dst [s%16])
- (OffPtr src [s%16])
- (MOVOstore dst (MOVOload src mem) mem))
-(Move [s] dst src mem)
- && s > 16 && s%16 != 0 && s%16 > 8 && !config.useSSE =>
- (Move [s-s%16]
- (OffPtr dst [s%16])
- (OffPtr src [s%16])
- (MOVQstore [8] dst (MOVQload [8] src mem)
- (MOVQstore dst (MOVQload src mem) mem)))
+// Copying up to 192 bytes uses straightline code.
+(Move [s] dst src mem) && s > 16 && s < 192 && logLargeCopy(v, s) => (LoweredMove [s] dst src mem)
-// Medium copying uses a duff device.
-(Move [s] dst src mem)
- && s > 64 && s <= 16*64 && s%16 == 0
- && !config.noDuffDevice && logLargeCopy(v, s) =>
- (DUFFCOPY [s] dst src mem)
+// Copying up to ~1KB uses a small loop.
+(Move [s] dst src mem) && s >= 192 && s <= repMoveThreshold && logLargeCopy(v, s) => (LoweredMoveLoop [s] dst src mem)
// Large copying uses REP MOVSQ.
-(Move [s] dst src mem) && (s > 16*64 || config.noDuffDevice) && s%8 == 0 && logLargeCopy(v, s) =>
+(Move [s] dst src mem) && s > repMoveThreshold && s%8 != 0 =>
+ (Move [s-s%8]
+ (OffPtr dst [s%8])
+ (OffPtr src [s%8])
+ (MOVQstore dst (MOVQload src mem) mem))
+(Move [s] dst src mem) && s > repMoveThreshold && s%8 == 0 && logLargeCopy(v, s) =>
(REPMOVSQ dst src (MOVQconst [s/8]) mem)
// Lowering Zero instructions
@@ -364,78 +336,38 @@
(MOVLstoreconst [makeValAndOff(0,3)] destptr
(MOVLstoreconst [makeValAndOff(0,0)] destptr mem))
-// Strip off any fractional word zeroing.
-(Zero [s] destptr mem) && s%8 != 0 && s > 8 && !config.useSSE =>
- (Zero [s-s%8] (OffPtr destptr [s%8])
- (MOVQstoreconst [makeValAndOff(0,0)] destptr mem))
-
// Zero small numbers of words directly.
-(Zero [16] destptr mem) && !config.useSSE =>
- (MOVQstoreconst [makeValAndOff(0,8)] destptr
- (MOVQstoreconst [makeValAndOff(0,0)] destptr mem))
-(Zero [24] destptr mem) && !config.useSSE =>
- (MOVQstoreconst [makeValAndOff(0,16)] destptr
- (MOVQstoreconst [makeValAndOff(0,8)] destptr
- (MOVQstoreconst [makeValAndOff(0,0)] destptr mem)))
-(Zero [32] destptr mem) && !config.useSSE =>
- (MOVQstoreconst [makeValAndOff(0,24)] destptr
- (MOVQstoreconst [makeValAndOff(0,16)] destptr
- (MOVQstoreconst [makeValAndOff(0,8)] destptr
- (MOVQstoreconst [makeValAndOff(0,0)] destptr mem))))
-
-(Zero [9] destptr mem) && config.useSSE =>
+(Zero [9] destptr mem) =>
(MOVBstoreconst [makeValAndOff(0,8)] destptr
(MOVQstoreconst [makeValAndOff(0,0)] destptr mem))
-(Zero [10] destptr mem) && config.useSSE =>
+(Zero [10] destptr mem) =>
(MOVWstoreconst [makeValAndOff(0,8)] destptr
(MOVQstoreconst [makeValAndOff(0,0)] destptr mem))
-(Zero [11] destptr mem) && config.useSSE =>
+(Zero [11] destptr mem) =>
(MOVLstoreconst [makeValAndOff(0,7)] destptr
(MOVQstoreconst [makeValAndOff(0,0)] destptr mem))
-(Zero [12] destptr mem) && config.useSSE =>
+(Zero [12] destptr mem) =>
(MOVLstoreconst [makeValAndOff(0,8)] destptr
(MOVQstoreconst [makeValAndOff(0,0)] destptr mem))
-(Zero [s] destptr mem) && s > 12 && s < 16 && config.useSSE =>
+(Zero [s] destptr mem) && s > 12 && s < 16 =>
(MOVQstoreconst [makeValAndOff(0,int32(s-8))] destptr
(MOVQstoreconst [makeValAndOff(0,0)] destptr mem))
-// Adjust zeros to be a multiple of 16 bytes.
-(Zero [s] destptr mem) && s%16 != 0 && s > 16 && s%16 > 8 && config.useSSE =>
- (Zero [s-s%16] (OffPtr destptr [s%16])
- (MOVOstoreconst [makeValAndOff(0,0)] destptr mem))
+// Zeroing up to 192 bytes uses straightline code.
+(Zero [s] destptr mem) && s >= 16 && s < 192 => (LoweredZero [s] destptr mem)
-(Zero [s] destptr mem) && s%16 != 0 && s > 16 && s%16 <= 8 && config.useSSE =>
- (Zero [s-s%16] (OffPtr destptr [s%16])
- (MOVOstoreconst [makeValAndOff(0,0)] destptr mem))
-
-(Zero [16] destptr mem) && config.useSSE =>
- (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)
-(Zero [32] destptr mem) && config.useSSE =>
- (MOVOstoreconst [makeValAndOff(0,16)] destptr
- (MOVOstoreconst [makeValAndOff(0,0)] destptr mem))
-(Zero [48] destptr mem) && config.useSSE =>
- (MOVOstoreconst [makeValAndOff(0,32)] destptr
- (MOVOstoreconst [makeValAndOff(0,16)] destptr
- (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)))
-(Zero [64] destptr mem) && config.useSSE =>
- (MOVOstoreconst [makeValAndOff(0,48)] destptr
- (MOVOstoreconst [makeValAndOff(0,32)] destptr
- (MOVOstoreconst [makeValAndOff(0,16)] destptr
- (MOVOstoreconst [makeValAndOff(0,0)] destptr mem))))
-
-// Medium zeroing uses a duff device.
-(Zero [s] destptr mem)
- && s > 64 && s <= 1024 && s%16 == 0 && !config.noDuffDevice =>
- (DUFFZERO [s] destptr mem)
+// Zeroing up to ~1KB uses a small loop.
+(Zero [s] destptr mem) && s >= 192 && s <= repZeroThreshold => (LoweredZeroLoop [s] destptr mem)
// Large zeroing uses REP STOSQ.
-(Zero [s] destptr mem)
- && (s > 1024 || (config.noDuffDevice && s > 64 || !config.useSSE && s > 32))
- && s%8 == 0 =>
+(Zero [s] destptr mem) && s > repZeroThreshold && s%8 != 0 =>
+ (Zero [s-s%8] (OffPtr destptr [s%8])
+ (MOVOstoreconst [makeValAndOff(0,0)] destptr mem))
+(Zero [s] destptr mem) && s > repZeroThreshold && s%8 == 0 =>
(REPSTOSQ destptr (MOVQconst [s/8]) (MOVQconst [0]) mem)
// Lowering constants
@@ -591,9 +523,11 @@
// Write barrier.
(WB ...) => (LoweredWB ...)
-(PanicBounds [kind] x y mem) && boundsABI(kind) == 0 => (LoweredPanicBoundsA [kind] x y mem)
-(PanicBounds [kind] x y mem) && boundsABI(kind) == 1 => (LoweredPanicBoundsB [kind] x y mem)
-(PanicBounds [kind] x y mem) && boundsABI(kind) == 2 => (LoweredPanicBoundsC [kind] x y mem)
+(PanicBounds ...) => (LoweredPanicBoundsRR ...)
+(LoweredPanicBoundsRR [kind] x (MOVQconst [c]) mem) => (LoweredPanicBoundsRC [kind] x {PanicBoundsC{C:c}} mem)
+(LoweredPanicBoundsRR [kind] (MOVQconst [c]) y mem) => (LoweredPanicBoundsCR [kind] {PanicBoundsC{C:c}} y mem)
+(LoweredPanicBoundsRC [kind] {p} (MOVQconst [c]) mem) => (LoweredPanicBoundsCC [kind] {PanicBoundsCC{Cx:c, Cy:p.C}} mem)
+(LoweredPanicBoundsCR [kind] {p} (MOVQconst [c]) mem) => (LoweredPanicBoundsCC [kind] {PanicBoundsCC{Cx:p.C, Cy:c}} mem)
// lowering rotates
(RotateLeft8 ...) => (ROLB ...)
@@ -629,6 +563,23 @@
// x & 1 != 0 -> x & 1
(SETNE (TEST(B|W)const [1] x)) => (AND(L|L)const [1] x)
(SETB (BT(L|Q)const [0] x)) => (AND(L|Q)const [1] x)
+// x & 1 == 0 -> (x & 1) ^ 1
+(SETAE (BT(L|Q)const [0] x)) => (XORLconst [1] (ANDLconst [1] x))
+
+// Shorten compare by rewriting x < 128 as x <= 127, which can be encoded in a single-byte immediate on x86.
+(SETL c:(CMP(Q|L)const [128] x)) && c.Uses == 1 => (SETLE (CMP(Q|L)const [127] x))
+(SETB c:(CMP(Q|L)const [128] x)) && c.Uses == 1 => (SETBE (CMP(Q|L)const [127] x))
+
+// x >= 128 -> x > 127
+(SETGE c:(CMP(Q|L)const [128] x)) && c.Uses == 1 => (SETG (CMP(Q|L)const [127] x))
+(SETAE c:(CMP(Q|L)const [128] x)) && c.Uses == 1 => (SETA (CMP(Q|L)const [127] x))
+
+(CMOVQLT x y c:(CMP(Q|L)const [128] z)) && c.Uses == 1 => (CMOVQLE x y (CMP(Q|L)const [127] z))
+(CMOVLLT x y c:(CMP(Q|L)const [128] z)) && c.Uses == 1 => (CMOVLLE x y (CMP(Q|L)const [127] z))
+(LT c:(CMP(Q|L)const [128] z) yes no) && c.Uses == 1 => (LE (CMP(Q|L)const [127] z) yes no)
+(CMOVQGE x y c:(CMP(Q|L)const [128] z)) && c.Uses == 1 => (CMOVQGT x y (CMP(Q|L)const [127] z))
+(CMOVLGE x y c:(CMP(Q|L)const [128] z)) && c.Uses == 1 => (CMOVLGT x y (CMP(Q|L)const [127] z))
+(GE c:(CMP(Q|L)const [128] z) yes no) && c.Uses == 1 => (GT (CMP(Q|L)const [127] z) yes no)
// Recognize bit tests: a&(1< ((ULT|UGE) (BTL x y))
((NE|EQ) (TESTQ (SHLQ (MOVQconst [1]) x) y)) => ((ULT|UGE) (BTQ x y))
-((NE|EQ) (TESTLconst [c] x)) && isUint32PowerOfTwo(int64(c))
- => ((ULT|UGE) (BTLconst [int8(log32(c))] x))
-((NE|EQ) (TESTQconst [c] x)) && isUint64PowerOfTwo(int64(c))
- => ((ULT|UGE) (BTQconst [int8(log32(c))] x))
-((NE|EQ) (TESTQ (MOVQconst [c]) x)) && isUint64PowerOfTwo(c)
- => ((ULT|UGE) (BTQconst [int8(log64(c))] x))
+((NE|EQ) (TESTLconst [c] x)) && isUnsignedPowerOfTwo(uint32(c))
+ => ((ULT|UGE) (BTLconst [int8(log32u(uint32(c)))] x))
+((NE|EQ) (TESTQconst [c] x)) && isUnsignedPowerOfTwo(uint64(c))
+ => ((ULT|UGE) (BTQconst [int8(log32u(uint32(c)))] x))
+((NE|EQ) (TESTQ (MOVQconst [c]) x)) && isUnsignedPowerOfTwo(uint64(c))
+ => ((ULT|UGE) (BTQconst [int8(log64u(uint64(c)))] x))
(SET(NE|EQ) (TESTL (SHLL (MOVLconst [1]) x) y)) => (SET(B|AE) (BTL x y))
(SET(NE|EQ) (TESTQ (SHLQ (MOVQconst [1]) x) y)) => (SET(B|AE) (BTQ x y))
-(SET(NE|EQ) (TESTLconst [c] x)) && isUint32PowerOfTwo(int64(c))
- => (SET(B|AE) (BTLconst [int8(log32(c))] x))
-(SET(NE|EQ) (TESTQconst [c] x)) && isUint64PowerOfTwo(int64(c))
- => (SET(B|AE) (BTQconst [int8(log32(c))] x))
-(SET(NE|EQ) (TESTQ (MOVQconst [c]) x)) && isUint64PowerOfTwo(c)
- => (SET(B|AE) (BTQconst [int8(log64(c))] x))
+(SET(NE|EQ) (TESTLconst [c] x)) && isUnsignedPowerOfTwo(uint32(c))
+ => (SET(B|AE) (BTLconst [int8(log32u(uint32(c)))] x))
+(SET(NE|EQ) (TESTQconst [c] x)) && isUnsignedPowerOfTwo(uint64(c))
+ => (SET(B|AE) (BTQconst [int8(log32u(uint32(c)))] x))
+(SET(NE|EQ) (TESTQ (MOVQconst [c]) x)) && isUnsignedPowerOfTwo(uint64(c))
+ => (SET(B|AE) (BTQconst [int8(log64u(uint64(c)))] x))
// SET..store variant
(SET(NE|EQ)store [off] {sym} ptr (TESTL (SHLL (MOVLconst [1]) x) y) mem)
=> (SET(B|AE)store [off] {sym} ptr (BTL x y) mem)
(SET(NE|EQ)store [off] {sym} ptr (TESTQ (SHLQ (MOVQconst [1]) x) y) mem)
=> (SET(B|AE)store [off] {sym} ptr (BTQ x y) mem)
-(SET(NE|EQ)store [off] {sym} ptr (TESTLconst [c] x) mem) && isUint32PowerOfTwo(int64(c))
- => (SET(B|AE)store [off] {sym} ptr (BTLconst [int8(log32(c))] x) mem)
-(SET(NE|EQ)store [off] {sym} ptr (TESTQconst [c] x) mem) && isUint64PowerOfTwo(int64(c))
- => (SET(B|AE)store [off] {sym} ptr (BTQconst [int8(log32(c))] x) mem)
-(SET(NE|EQ)store [off] {sym} ptr (TESTQ (MOVQconst [c]) x) mem) && isUint64PowerOfTwo(c)
- => (SET(B|AE)store [off] {sym} ptr (BTQconst [int8(log64(c))] x) mem)
+(SET(NE|EQ)store [off] {sym} ptr (TESTLconst [c] x) mem) && isUnsignedPowerOfTwo(uint32(c))
+ => (SET(B|AE)store [off] {sym} ptr (BTLconst [int8(log32u(uint32(c)))] x) mem)
+(SET(NE|EQ)store [off] {sym} ptr (TESTQconst [c] x) mem) && isUnsignedPowerOfTwo(uint64(c))
+ => (SET(B|AE)store [off] {sym} ptr (BTQconst [int8(log32u(uint32(c)))] x) mem)
+(SET(NE|EQ)store [off] {sym} ptr (TESTQ (MOVQconst [c]) x) mem) && isUnsignedPowerOfTwo(uint64(c))
+ => (SET(B|AE)store [off] {sym} ptr (BTQconst [int8(log64u(uint64(c)))] x) mem)
// Handle bit-testing in the form (a>>b)&1 != 0 by building the above rules
// and further combining shifts.
(BT(Q|L)const [c] (SHRQconst [d] x)) && (c+d)<64 => (BTQconst [c+d] x)
+(BT(Q|L)const [c] (ADDQ x x)) && c>1 => (BT(Q|L)const [c-1] x)
(BT(Q|L)const [c] (SHLQconst [d] x)) && c>d => (BT(Q|L)const [c-d] x)
(BT(Q|L)const [0] s:(SHRQ x y)) => (BTQ y x)
(BTLconst [c] (SHRLconst [d] x)) && (c+d)<32 => (BTLconst [c+d] x)
+(BTLconst [c] (ADDL x x)) && c>1 => (BTLconst [c-1] x)
(BTLconst [c] (SHLLconst [d] x)) && c>d => (BTLconst [c-d] x)
(BTLconst [0] s:(SHR(L|XL) x y)) => (BTL y x)
@@ -684,14 +637,14 @@
(XOR(Q|L) (SHL(Q|L) (MOV(Q|L)const [1]) y) x) => (BTC(Q|L) x y)
// Note: only convert OR/XOR to BTS/BTC if the constant wouldn't fit in
// the constant field of the OR/XOR instruction. See issue 61694.
-((OR|XOR)Q (MOVQconst [c]) x) && isUint64PowerOfTwo(c) && uint64(c) >= 1<<31 => (BT(S|C)Qconst [int8(log64(c))] x)
+((OR|XOR)Q (MOVQconst [c]) x) && isUnsignedPowerOfTwo(uint64(c)) && uint64(c) >= 1<<31 => (BT(S|C)Qconst [int8(log64u(uint64(c)))] x)
// Recognize bit clearing: a &^= 1< (BTR(Q|L) x y)
(ANDN(Q|L) x (SHL(Q|L) (MOV(Q|L)const [1]) y)) => (BTR(Q|L) x y)
// Note: only convert AND to BTR if the constant wouldn't fit in
// the constant field of the AND instruction. See issue 61694.
-(ANDQ (MOVQconst [c]) x) && isUint64PowerOfTwo(^c) && uint64(^c) >= 1<<31 => (BTRQconst [int8(log64(^c))] x)
+(ANDQ (MOVQconst [c]) x) && isUnsignedPowerOfTwo(uint64(^c)) && uint64(^c) >= 1<<31 => (BTRQconst [int8(log64u(uint64(^c)))] x)
// Special-case bit patterns on first/last bit.
// generic.rules changes ANDs of high-part/low-part masks into a couple of shifts,
@@ -704,11 +657,11 @@
// We thus special-case them, by detecting the shift patterns.
// Special case resetting first/last bit
-(SHL(L|Q)const [1] (SHR(L|Q)const [1] x))
+(ADD(L|Q) (SHR(L|Q)const [1] x) (SHR(L|Q)const [1] x))
=> (AND(L|Q)const [-2] x)
-(SHRLconst [1] (SHLLconst [1] x))
+(SHRLconst [1] (ADDL x x))
=> (ANDLconst [0x7fffffff] x)
-(SHRQconst [1] (SHLQconst [1] x))
+(SHRQconst [1] (ADDQ x x))
=> (BTRQconst [63] x)
// Special case testing first/last bit (with double-shift generated by generic.rules)
@@ -896,56 +849,24 @@
// (ANDQconst [0xFFFFFFFF] x) => (MOVLQZX x)
// strength reduction
-// Assumes that the following costs from https://gmplib.org/~tege/x86-timing.pdf:
-// 1 - addq, shlq, leaq, negq, subq
-// 3 - imulq
-// This limits the rewrites to two instructions.
-// Note that negq always operates in-place,
-// which can require a register-register move
-// to preserve the original value,
-// so it must be used with care.
-(MUL(Q|L)const [-9] x) => (NEG(Q|L) (LEA(Q|L)8 x x))
-(MUL(Q|L)const [-5] x) => (NEG(Q|L) (LEA(Q|L)4 x x))
-(MUL(Q|L)const [-3] x) => (NEG(Q|L) (LEA(Q|L)2 x x))
-(MUL(Q|L)const [-1] x) => (NEG(Q|L) x)
(MUL(Q|L)const [ 0] _) => (MOV(Q|L)const [0])
(MUL(Q|L)const [ 1] x) => x
-(MUL(Q|L)const [ 3] x) => (LEA(Q|L)2 x x)
-(MUL(Q|L)const [ 5] x) => (LEA(Q|L)4 x x)
-(MUL(Q|L)const [ 7] x) => (LEA(Q|L)2 x (LEA(Q|L)2 x x))
-(MUL(Q|L)const [ 9] x) => (LEA(Q|L)8 x x)
-(MUL(Q|L)const [11] x) => (LEA(Q|L)2 x (LEA(Q|L)4 x x))
-(MUL(Q|L)const [13] x) => (LEA(Q|L)4 x (LEA(Q|L)2 x x))
-(MUL(Q|L)const [19] x) => (LEA(Q|L)2 x (LEA(Q|L)8 x x))
-(MUL(Q|L)const [21] x) => (LEA(Q|L)4 x (LEA(Q|L)4 x x))
-(MUL(Q|L)const [25] x) => (LEA(Q|L)8 x (LEA(Q|L)2 x x))
-(MUL(Q|L)const [27] x) => (LEA(Q|L)8 (LEA(Q|L)2 x x) (LEA(Q|L)2 x x))
-(MUL(Q|L)const [37] x) => (LEA(Q|L)4 x (LEA(Q|L)8 x x))
-(MUL(Q|L)const [41] x) => (LEA(Q|L)8 x (LEA(Q|L)4 x x))
-(MUL(Q|L)const [45] x) => (LEA(Q|L)8 (LEA(Q|L)4 x x) (LEA(Q|L)4 x x))
-(MUL(Q|L)const [73] x) => (LEA(Q|L)8 x (LEA(Q|L)8 x x))
-(MUL(Q|L)const [81] x) => (LEA(Q|L)8 (LEA(Q|L)8 x x) (LEA(Q|L)8 x x))
+(MULQconst [c] x) && canMulStrengthReduce(config, int64(c)) => {mulStrengthReduce(v, x, int64(c))}
+(MULLconst [c] x) && v.Type.Size() <= 4 && canMulStrengthReduce32(config, c) => {mulStrengthReduce32(v, x, c)}
-(MUL(Q|L)const [c] x) && isPowerOfTwo(int64(c)+1) && c >= 15 => (SUB(Q|L) (SHL(Q|L)const [int8(log64(int64(c)+1))] x) x)
-(MUL(Q|L)const [c] x) && isPowerOfTwo(c-1) && c >= 17 => (LEA(Q|L)1 (SHL(Q|L)const [int8(log32(c-1))] x) x)
-(MUL(Q|L)const [c] x) && isPowerOfTwo(c-2) && c >= 34 => (LEA(Q|L)2 (SHL(Q|L)const [int8(log32(c-2))] x) x)
-(MUL(Q|L)const [c] x) && isPowerOfTwo(c-4) && c >= 68 => (LEA(Q|L)4 (SHL(Q|L)const [int8(log32(c-4))] x) x)
-(MUL(Q|L)const [c] x) && isPowerOfTwo(c-8) && c >= 136 => (LEA(Q|L)8 (SHL(Q|L)const [int8(log32(c-8))] x) x)
-(MUL(Q|L)const [c] x) && c%3 == 0 && isPowerOfTwo(c/3) => (SHL(Q|L)const [int8(log32(c/3))] (LEA(Q|L)2 x x))
-(MUL(Q|L)const [c] x) && c%5 == 0 && isPowerOfTwo(c/5) => (SHL(Q|L)const [int8(log32(c/5))] (LEA(Q|L)4 x x))
-(MUL(Q|L)const [c] x) && c%9 == 0 && isPowerOfTwo(c/9) => (SHL(Q|L)const [int8(log32(c/9))] (LEA(Q|L)8 x x))
+// Prefer addition when shifting left by one
+(SHL(Q|L)const [1] x) => (ADD(Q|L) x x)
// combine add/shift into LEAQ/LEAL
(ADD(L|Q) x (SHL(L|Q)const [3] y)) => (LEA(L|Q)8 x y)
(ADD(L|Q) x (SHL(L|Q)const [2] y)) => (LEA(L|Q)4 x y)
-(ADD(L|Q) x (SHL(L|Q)const [1] y)) => (LEA(L|Q)2 x y)
(ADD(L|Q) x (ADD(L|Q) y y)) => (LEA(L|Q)2 x y)
(ADD(L|Q) x (ADD(L|Q) x y)) => (LEA(L|Q)2 y x)
// combine ADDQ/ADDQconst into LEAQ1/LEAL1
(ADD(Q|L)const [c] (ADD(Q|L) x y)) => (LEA(Q|L)1 [c] x y)
(ADD(Q|L) (ADD(Q|L)const [c] x) y) => (LEA(Q|L)1 [c] x y)
-(ADD(Q|L)const [c] (SHL(Q|L)const [1] x)) => (LEA(Q|L)1 [c] x x)
+(ADD(Q|L)const [c] (ADD(Q|L) x x)) => (LEA(Q|L)1 [c] x x)
// fold ADDQ/ADDL into LEAQ/LEAL
(ADD(Q|L)const [c] (LEA(Q|L) [d] {s} x)) && is32Bit(int64(c)+int64(d)) => (LEA(Q|L) [c+d] {s} x)
@@ -967,12 +888,18 @@
(LEA(Q|L)8 [c] {s} x (ADD(Q|L)const [d] y)) && is32Bit(int64(c)+8*int64(d)) && y.Op != OpSB => (LEA(Q|L)8 [c+8*d] {s} x y)
// fold shifts into LEAQx/LEALx
-(LEA(Q|L)1 [c] {s} x (SHL(Q|L)const [1] y)) => (LEA(Q|L)2 [c] {s} x y)
+(LEA(Q|L)1 [c] {s} x z:(ADD(Q|L) y y)) && x != z => (LEA(Q|L)2 [c] {s} x y)
(LEA(Q|L)1 [c] {s} x (SHL(Q|L)const [2] y)) => (LEA(Q|L)4 [c] {s} x y)
(LEA(Q|L)1 [c] {s} x (SHL(Q|L)const [3] y)) => (LEA(Q|L)8 [c] {s} x y)
-(LEA(Q|L)2 [c] {s} x (SHL(Q|L)const [1] y)) => (LEA(Q|L)4 [c] {s} x y)
+(LEA(Q|L)2 [c] {s} x z:(ADD(Q|L) y y)) && x != z => (LEA(Q|L)4 [c] {s} x y)
(LEA(Q|L)2 [c] {s} x (SHL(Q|L)const [2] y)) => (LEA(Q|L)8 [c] {s} x y)
-(LEA(Q|L)4 [c] {s} x (SHL(Q|L)const [1] y)) => (LEA(Q|L)8 [c] {s} x y)
+(LEA(Q|L)4 [c] {s} x z:(ADD(Q|L) y y)) && x != z => (LEA(Q|L)8 [c] {s} x y)
+
+// (x + x) << 1 -> x << 2
+(LEA(Q|L)2 [0] {s} (ADD(Q|L) x x) x) && s == nil => (SHL(Q|L)const [2] x)
+
+// (x + x) << 2 -> x << 3 and similar
+(SHL(Q|L)const [c] (ADD(Q|L) x x)) => (SHL(Q|L)const [c+1] x)
// reverse ordering of compare instruction
(SETL (InvertFlags x)) => (SETG x)
@@ -1463,7 +1390,6 @@
(LEAQ1 [0] x y) && v.Aux == nil => (ADDQ x y)
(MOVQstoreconst [c] {s} p1 x:(MOVQstoreconst [a] {s} p0 mem))
- && config.useSSE
&& x.Uses == 1
&& sequentialAddresses(p0, p1, int64(a.Off()+8-c.Off()))
&& a.Val() == 0
@@ -1472,7 +1398,6 @@
&& clobber(x)
=> (MOVOstoreconst [makeValAndOff(0,a.Off())] {s} p0 mem)
(MOVQstoreconst [a] {s} p0 x:(MOVQstoreconst [c] {s} p1 mem))
- && config.useSSE
&& x.Uses == 1
&& sequentialAddresses(p0, p1, int64(a.Off()+8-c.Off()))
&& a.Val() == 0
@@ -1581,6 +1506,9 @@
(MULSDload x [off] {sym} ptr (MOVQstore [off] {sym} ptr y _)) => (MULSD x (MOVQi2f y))
(MULSSload x [off] {sym} ptr (MOVLstore [off] {sym} ptr y _)) => (MULSS x (MOVLi2f y))
+// Detect FMA
+(ADDS(S|D) (MULS(S|D) x y) z) && buildcfg.GOAMD64 >= 3 && z.Block.Func.useFMA(v) => (VFMADD231S(S|D) z x y)
+
// Redirect stores to use the other register set.
(MOVQstore [off] {sym} ptr (MOVQf2i val) mem) => (MOVSDstore [off] {sym} ptr val mem)
(MOVLstore [off] {sym} ptr (MOVLf2i val) mem) => (MOVSSstore [off] {sym} ptr val mem)
@@ -1640,8 +1568,13 @@
(MOVBload [off] {sym} (SB) _) && symIsRO(sym) => (MOVLconst [int32(read8(sym, int64(off)))])
(MOVWload [off] {sym} (SB) _) && symIsRO(sym) => (MOVLconst [int32(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))])
-(MOVLload [off] {sym} (SB) _) && symIsRO(sym) => (MOVQconst [int64(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))])
+(MOVLload [off] {sym} (SB) _) && symIsRO(sym) => (MOVLconst [int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))])
(MOVQload [off] {sym} (SB) _) && symIsRO(sym) => (MOVQconst [int64(read64(sym, int64(off), config.ctxt.Arch.ByteOrder))])
+(MOVBQSXload [off] {sym} (SB) _) && symIsRO(sym) => (MOVQconst [int64(int8(read8(sym, int64(off))))])
+(MOVWQSXload [off] {sym} (SB) _) && symIsRO(sym) => (MOVQconst [int64(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))])
+(MOVLQSXload [off] {sym} (SB) _) && symIsRO(sym) => (MOVQconst [int64(int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder)))])
+
+
(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))
@@ -1704,3 +1637,13 @@
// Convert atomic logical operations to easier ones if we don't use the result.
(Select1 a:(LoweredAtomic(And64|And32|Or64|Or32) ptr val mem)) && a.Uses == 1 && clobber(a) => ((ANDQ|ANDL|ORQ|ORL)lock ptr val mem)
+
+// If we are checking the results of an add, use the flags directly from the add.
+// Note that this only works for EQ/NE. ADD sets the CF/OF flags differently
+// than TEST sets them.
+// Note also that a.Args[0] here refers to the post-flagify'd value.
+((EQ|NE) t:(TESTQ a:(ADDQconst [c] x) a)) && t.Uses == 1 && flagify(a) => ((EQ|NE) (Select1 a.Args[0]))
+((EQ|NE) t:(TESTL a:(ADDLconst [c] x) a)) && t.Uses == 1 && flagify(a) => ((EQ|NE) (Select1 a.Args[0]))
+
+// If we don't use the flags any more, just use the standard op.
+(Select0 a:(ADD(Q|L)constflags [c] x)) && a.Uses == 1 => (ADD(Q|L)const [c] x)
diff --git a/src/cmd/compile/internal/ssa/_gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/_gen/AMD64Ops.go
index 23fb2361b5..e42b54398d 100644
--- a/src/cmd/compile/internal/ssa/_gen/AMD64Ops.go
+++ b/src/cmd/compile/internal/ssa/_gen/AMD64Ops.go
@@ -95,7 +95,6 @@ func init() {
ax = buildReg("AX")
cx = buildReg("CX")
dx = buildReg("DX")
- bx = buildReg("BX")
gp = buildReg("AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15")
g = buildReg("g")
fp = buildReg("X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14")
@@ -303,6 +302,11 @@ func init() {
// computes -arg0, flags set for 0-arg0.
{name: "NEGLflags", argLength: 1, reg: gp11flags, typ: "(UInt32,Flags)", asm: "NEGL", resultInArg0: true},
+ // compute arg0+auxint. flags set for arg0+auxint.
+ // NOTE: we pretend the CF/OF flags are undefined for these instructions,
+ // so we can use INC/DEC instead of ADDQconst if auxint is +/-1. (INC/DEC don't modify CF.)
+ {name: "ADDQconstflags", argLength: 1, reg: gp11flags, aux: "Int32", asm: "ADDQ", resultInArg0: true},
+ {name: "ADDLconstflags", argLength: 1, reg: gp11flags, aux: "Int32", asm: "ADDL", resultInArg0: true},
// The following 4 add opcodes return the low 64 bits of the sum in the first result and
// the carry (the 65th bit) in the carry flag.
@@ -692,9 +696,15 @@ func init() {
// ROUNDSD instruction is only guaraneteed to be available if GOAMD64>=v2.
// For GOAMD64=v3.
+ // x==S for float32, x==D for float64
+ // arg0 + arg1*arg2, with no intermediate rounding.
+ {name: "VFMADD231SS", argLength: 3, reg: fp31, resultInArg0: true, asm: "VFMADD231SS"},
{name: "VFMADD231SD", argLength: 3, reg: fp31, resultInArg0: true, asm: "VFMADD231SD"},
// Note that these operations don't exactly match the semantics of Go's
@@ -758,7 +768,7 @@ func init() {
{name: "MOVLQSX", argLength: 1, reg: gp11, asm: "MOVLQSX"}, // sign extend arg0 from int32 to int64
{name: "MOVLQZX", argLength: 1, reg: gp11, asm: "MOVL"}, // zero extend arg0 from int32 to int64
- {name: "MOVLconst", reg: gp01, asm: "MOVL", typ: "UInt32", aux: "Int32", rematerializeable: true}, // 32 low bits of auxint
+ {name: "MOVLconst", reg: gp01, asm: "MOVL", typ: "UInt32", aux: "Int32", rematerializeable: true}, // 32 low bits of auxint (upper 32 are zeroed)
{name: "MOVQconst", reg: gp01, asm: "MOVQ", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
{name: "CVTTSD2SL", argLength: 1, reg: fpgp, asm: "CVTTSD2SL"}, // convert float64 to int32
@@ -879,15 +889,30 @@ func init() {
// auxint = # of bytes to zero
// returns mem
{
- name: "DUFFZERO",
+ name: "LoweredZero",
aux: "Int64",
argLength: 2,
reg: regInfo{
- inputs: []regMask{buildReg("DI")},
- clobbers: buildReg("DI"),
+ inputs: []regMask{gp},
},
faultOnNilArg0: true,
- unsafePoint: true, // FP maintenance around DUFFCOPY can be clobbered by interrupts
+ },
+
+ // arg0 = pointer to start of memory to zero
+ // arg1 = mem
+ // auxint = # of bytes to zero
+ // returns mem
+ {
+ name: "LoweredZeroLoop",
+ aux: "Int64",
+ argLength: 2,
+ reg: regInfo{
+ inputs: []regMask{gp},
+ clobbersArg0: true,
+ },
+ clobberFlags: true,
+ faultOnNilArg0: true,
+ needIntTemp: true,
},
// arg0 = address of memory to zero
@@ -914,20 +939,38 @@ func init() {
// arg0 = destination pointer
// arg1 = source pointer
// arg2 = mem
- // auxint = # of bytes to copy, must be multiple of 16
+ // auxint = # of bytes to copy
// returns memory
{
- name: "DUFFCOPY",
+ name: "LoweredMove",
aux: "Int64",
argLength: 3,
reg: regInfo{
- inputs: []regMask{buildReg("DI"), buildReg("SI")},
- clobbers: buildReg("DI SI X0"), // uses X0 as a temporary
+ inputs: []regMask{gp, gp},
+ clobbers: buildReg("X14"), // uses X14 as a temporary
+ },
+ faultOnNilArg0: true,
+ faultOnNilArg1: true,
+ },
+ // arg0 = destination pointer
+ // arg1 = source pointer
+ // arg2 = mem
+ // auxint = # of bytes to copy
+ // returns memory
+ {
+ name: "LoweredMoveLoop",
+ aux: "Int64",
+ argLength: 3,
+ reg: regInfo{
+ inputs: []regMask{gp, gp},
+ clobbers: buildReg("X14"), // uses X14 as a temporary
+ clobbersArg0: true,
+ clobbersArg1: true,
},
clobberFlags: true,
faultOnNilArg0: true,
faultOnNilArg1: true,
- unsafePoint: true, // FP maintenance around DUFFCOPY can be clobbered by interrupts
+ needIntTemp: true,
},
// arg0 = destination pointer
@@ -975,12 +1018,15 @@ func init() {
{name: "LoweredHasCPUFeature", argLength: 0, reg: gp01, rematerializeable: true, typ: "UInt64", aux: "Sym", symEffect: "None"},
- // There are three of these functions so that they can have three different register inputs.
- // When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
- // default registers to match so we don't need to copy registers around unnecessarily.
- {name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{dx, bx}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
- {name: "LoweredPanicBoundsB", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{cx, dx}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
- {name: "LoweredPanicBoundsC", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{ax, cx}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
+ // LoweredPanicBoundsRR takes x and y, two values that caused a bounds check to fail.
+ // the RC and CR versions are used when one of the arguments is a constant. CC is used
+ // when both are constant (normally both 0, as prove derives the fact that a [0] bounds
+ // failure means the length must have also been 0).
+ // AuxInt contains a report code (see PanicBounds in genericOps.go).
+ {name: "LoweredPanicBoundsRR", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{gp, gp}}, typ: "Mem", call: true}, // arg0=x, arg1=y, arg2=mem, returns memory.
+ {name: "LoweredPanicBoundsRC", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{gp}}, typ: "Mem", call: true}, // arg0=x, arg1=mem, returns memory.
+ {name: "LoweredPanicBoundsCR", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{gp}}, typ: "Mem", call: true}, // arg0=y, arg1=mem, returns memory.
+ {name: "LoweredPanicBoundsCC", argLength: 1, aux: "PanicBoundsCC", reg: regInfo{}, typ: "Mem", call: true}, // arg0=mem, returns memory.
// Constant flag values. For any comparison, there are 5 possible
// outcomes: the three from the signed total order (<,==,>) and the
@@ -1156,7 +1202,7 @@ func init() {
//
// output[i] = input.
{name: "PSHUFBbroadcast", argLength: 1, reg: fp11, resultInArg0: true, asm: "PSHUFB"}, // PSHUFB with mask zero, (GOAMD64=v1)
- {name: "VPBROADCASTB", argLength: 1, reg: gpfp, asm: "VPBROADCASTB"}, // Broadcast input byte from gp (GOAMD64=v3)
+ {name: "VPBROADCASTB", argLength: 1, reg: gpfp, asm: "VPBROADCASTB"}, // Broadcast input byte from gp (GOAMD64=v3)
// Byte negate/zero/preserve (GOAMD64=v2).
//
@@ -1180,7 +1226,7 @@ func init() {
// } else {
// output[i] = 0
// }
- {name: "PCMPEQB", argLength: 2, reg: fp21, resultInArg0: true, asm: "PCMPEQB"},
+ {name: "PCMPEQB", argLength: 2, reg: fp21, resultInArg0: true, asm: "PCMPEQB", commutative: true},
// Byte sign mask. Output is a bitmap of sign bits from each input byte.
//
diff --git a/src/cmd/compile/internal/ssa/_gen/AMD64latelower.rules b/src/cmd/compile/internal/ssa/_gen/AMD64latelower.rules
index 1dd804577a..ead4ec45f1 100644
--- a/src/cmd/compile/internal/ssa/_gen/AMD64latelower.rules
+++ b/src/cmd/compile/internal/ssa/_gen/AMD64latelower.rules
@@ -1,13 +1,13 @@
-// Copyright 2022 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.
-
-// Prefer SARX/SHLX/SHRX instruction because it has less register restriction on the shift input.
-(SAR(Q|L) x y) && buildcfg.GOAMD64 >= 3 => (SARX(Q|L) x y)
-(SHL(Q|L) x y) && buildcfg.GOAMD64 >= 3 => (SHLX(Q|L) x y)
-(SHR(Q|L) x y) && buildcfg.GOAMD64 >= 3 => (SHRX(Q|L) x y)
-
-// See comments in ARM64latelower.rules for why these are here.
-(MOVLQZX x) && zeroUpper32Bits(x,3) => x
-(MOVWQZX x) && zeroUpper48Bits(x,3) => x
-(MOVBQZX x) && zeroUpper56Bits(x,3) => x
+// Copyright 2022 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.
+
+// Prefer SARX/SHLX/SHRX instruction because it has less register restriction on the shift input.
+(SAR(Q|L) x y) && buildcfg.GOAMD64 >= 3 => (SARX(Q|L) x y)
+(SHL(Q|L) x y) && buildcfg.GOAMD64 >= 3 => (SHLX(Q|L) x y)
+(SHR(Q|L) x y) && buildcfg.GOAMD64 >= 3 => (SHRX(Q|L) x y)
+
+// See comments in ARM64latelower.rules for why these are here.
+(MOVLQZX x) && zeroUpper32Bits(x,3) => x
+(MOVWQZX x) && zeroUpper48Bits(x,3) => x
+(MOVBQZX x) && zeroUpper56Bits(x,3) => x
diff --git a/src/cmd/compile/internal/ssa/_gen/ARM.rules b/src/cmd/compile/internal/ssa/_gen/ARM.rules
index 9cdb5d8ad5..18b5d6bba6 100644
--- a/src/cmd/compile/internal/ssa/_gen/ARM.rules
+++ b/src/cmd/compile/internal/ssa/_gen/ARM.rules
@@ -80,6 +80,7 @@
// bit length
(BitLen32 x) => (RSBconst [32] (CLZ x))
+(BitLen(16|8) x) => (BitLen32 (ZeroExt(16|8)to32 x))
// byte swap for ARMv5
// let (a, b, c, d) be the bytes of x from high to low
@@ -296,12 +297,12 @@
// 4 and 128 are magic constants, see runtime/mkduff.go
(Zero [s] {t} ptr mem)
&& s%4 == 0 && s > 4 && s <= 512
- && t.Alignment()%4 == 0 && !config.noDuffDevice =>
+ && t.Alignment()%4 == 0 =>
(DUFFZERO [4 * (128 - s/4)] ptr (MOVWconst [0]) mem)
// Large zeroing uses a loop
(Zero [s] {t} ptr mem)
- && (s > 512 || config.noDuffDevice) || t.Alignment()%4 != 0 =>
+ && s > 512 || t.Alignment()%4 != 0 =>
(LoweredZero [t.Alignment()]
ptr
(ADDconst ptr [int32(s-moveSize(t.Alignment(), config))])
@@ -336,12 +337,12 @@
// 8 and 128 are magic constants, see runtime/mkduff.go
(Move [s] {t} dst src mem)
&& s%4 == 0 && s > 4 && s <= 512
- && t.Alignment()%4 == 0 && !config.noDuffDevice && logLargeCopy(v, s) =>
+ && t.Alignment()%4 == 0 && logLargeCopy(v, s) =>
(DUFFCOPY [8 * (128 - s/4)] dst src mem)
// Large move uses a loop
(Move [s] {t} dst src mem)
- && ((s > 512 || config.noDuffDevice) || t.Alignment()%4 != 0) && logLargeCopy(v, s) =>
+ && (s > 512 || t.Alignment()%4 != 0) && logLargeCopy(v, s) =>
(LoweredMove [t.Alignment()]
dst
src
@@ -394,13 +395,16 @@
// Write barrier.
(WB ...) => (LoweredWB ...)
-(PanicBounds [kind] x y mem) && boundsABI(kind) == 0 => (LoweredPanicBoundsA [kind] x y mem)
-(PanicBounds [kind] x y mem) && boundsABI(kind) == 1 => (LoweredPanicBoundsB [kind] x y mem)
-(PanicBounds [kind] x y mem) && boundsABI(kind) == 2 => (LoweredPanicBoundsC [kind] x y mem)
+(PanicBounds ...) => (LoweredPanicBoundsRR ...)
+(PanicExtend ...) => (LoweredPanicExtendRR ...)
-(PanicExtend [kind] hi lo y mem) && boundsABI(kind) == 0 => (LoweredPanicExtendA [kind] hi lo y mem)
-(PanicExtend [kind] hi lo y mem) && boundsABI(kind) == 1 => (LoweredPanicExtendB [kind] hi lo y mem)
-(PanicExtend [kind] hi lo y mem) && boundsABI(kind) == 2 => (LoweredPanicExtendC [kind] hi lo y mem)
+(LoweredPanicBoundsRR [kind] x (MOVWconst [c]) mem) => (LoweredPanicBoundsRC [kind] x {PanicBoundsC{C:int64(c)}} mem)
+(LoweredPanicBoundsRR [kind] (MOVWconst [c]) y mem) => (LoweredPanicBoundsCR [kind] {PanicBoundsC{C:int64(c)}} y mem)
+(LoweredPanicBoundsRC [kind] {p} (MOVWconst [c]) mem) => (LoweredPanicBoundsCC [kind] {PanicBoundsCC{Cx:int64(c), Cy:p.C}} mem)
+
+(LoweredPanicExtendRR [kind] hi lo (MOVWconst [c]) mem) => (LoweredPanicExtendRC [kind] hi lo {PanicBoundsC{C:int64(c)}} mem)
+(LoweredPanicExtendRR [kind] (MOVWconst [hi]) (MOVWconst [lo]) y mem) => (LoweredPanicBoundsCR [kind] {PanicBoundsC{C:int64(hi)<<32 + int64(uint32(lo))}} y mem)
+(LoweredPanicExtendRC [kind] {p} (MOVWconst [hi]) (MOVWconst [lo]) mem) => (LoweredPanicBoundsCC [kind] {PanicBoundsCC{Cx:int64(hi)<<32+int64(uint32(lo)), Cy:p.C}} mem)
// Optimizations
@@ -1473,3 +1477,5 @@
(MOVBUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVWconst [int32(read8(sym, int64(off)))])
(MOVHUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVWconst [int32(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))])
(MOVWload [off] {sym} (SB) _) && symIsRO(sym) => (MOVWconst [int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))])
+(MOVBload [off] {sym} (SB) _) && symIsRO(sym) => (MOVWconst [int32(int8(read8(sym, int64(off))))])
+(MOVHload [off] {sym} (SB) _) && symIsRO(sym) => (MOVWconst [int32(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))])
diff --git a/src/cmd/compile/internal/ssa/_gen/ARM64.rules b/src/cmd/compile/internal/ssa/_gen/ARM64.rules
index 6652d2ec01..f54a692725 100644
--- a/src/cmd/compile/internal/ssa/_gen/ARM64.rules
+++ b/src/cmd/compile/internal/ssa/_gen/ARM64.rules
@@ -103,6 +103,7 @@
(BitLen64 x) => (SUB (MOVDconst [64]) (CLZ x))
(BitLen32 x) => (SUB (MOVDconst [32]) (CLZW x))
+(BitLen(16|8) x) => (BitLen64 (ZeroExt(16|8)to64 x))
(Bswap64 ...) => (REV ...)
(Bswap32 ...) => (REVW ...)
@@ -391,45 +392,8 @@
(Zero [16] ptr mem) =>
(STP [0] ptr (MOVDconst [0]) (MOVDconst [0]) mem)
-(Zero [32] ptr mem) =>
- (STP [16] ptr (MOVDconst [0]) (MOVDconst [0])
- (STP [0] ptr (MOVDconst [0]) (MOVDconst [0]) mem))
-
-(Zero [48] ptr mem) =>
- (STP [32] ptr (MOVDconst [0]) (MOVDconst [0])
- (STP [16] ptr (MOVDconst [0]) (MOVDconst [0])
- (STP [0] ptr (MOVDconst [0]) (MOVDconst [0]) mem)))
-
-(Zero [64] ptr mem) =>
- (STP [48] ptr (MOVDconst [0]) (MOVDconst [0])
- (STP [32] ptr (MOVDconst [0]) (MOVDconst [0])
- (STP [16] ptr (MOVDconst [0]) (MOVDconst [0])
- (STP [0] ptr (MOVDconst [0]) (MOVDconst [0]) mem))))
-
-// strip off fractional word zeroing
-(Zero [s] ptr mem) && s%16 != 0 && s%16 <= 8 && s > 16 =>
- (Zero [8]
- (OffPtr ptr [s-8])
- (Zero [s-s%16] ptr mem))
-(Zero [s] ptr mem) && s%16 != 0 && s%16 > 8 && s > 16 =>
- (Zero [16]
- (OffPtr ptr [s-16])
- (Zero [s-s%16] ptr mem))
-
-// medium zeroing uses a duff device
-// 4, 16, and 64 are magic constants, see runtime/mkduff.go
-(Zero [s] ptr mem)
- && s%16 == 0 && s > 64 && s <= 16*64
- && !config.noDuffDevice =>
- (DUFFZERO [4 * (64 - s/16)] ptr mem)
-
-// large zeroing uses a loop
-(Zero [s] ptr mem)
- && s%16 == 0 && (s > 16*64 || config.noDuffDevice) =>
- (LoweredZero
- ptr
- (ADDconst [s-16] ptr)
- mem)
+(Zero [s] ptr mem) && s > 16 && s < 192 => (LoweredZero [s] ptr mem)
+(Zero [s] ptr mem) && s >= 192 => (LoweredZeroLoop [s] ptr mem)
// moves
(Move [0] _ _ mem) => mem
@@ -472,55 +436,34 @@
(MOVDstore dst (MOVDload src mem) mem))
(Move [16] dst src mem) =>
(STP dst (Select0 (LDP src mem)) (Select1 (LDP src mem)) mem)
-(Move [32] dst src mem) =>
- (STP [16] dst (Select0 (LDP [16] src mem)) (Select1 (LDP [16] src mem))
+
+(Move [s] dst src mem) && s > 16 && s <= 24 =>
+ (MOVDstore [int32(s-8)] dst (MOVDload [int32(s-8)] src mem)
(STP dst (Select0 (LDP src mem)) (Select1 (LDP src mem)) mem))
-(Move [48] dst src mem) =>
- (STP [32] dst (Select0 (LDP [32] src mem)) (Select1 (LDP [32] src mem))
+(Move [s] dst src mem) && s > 24 && s <= 32 =>
+ (STP [int32(s-16)] dst (Select0 (LDP [int32(s-16)] src mem)) (Select1 (LDP [int32(s-16)] src mem))
+ (STP dst (Select0 (LDP src mem)) (Select1 (LDP src mem)) mem))
+(Move [s] dst src mem) && s > 32 && s <= 40 =>
+ (MOVDstore [int32(s-8)] dst (MOVDload [int32(s-8)] src mem)
(STP [16] dst (Select0 (LDP [16] src mem)) (Select1 (LDP [16] src mem))
(STP dst (Select0 (LDP src mem)) (Select1 (LDP src mem)) mem)))
-(Move [64] dst src mem) =>
- (STP [48] dst (Select0 (LDP [48] src mem)) (Select1 (LDP [48] src mem))
+(Move [s] dst src mem) && s > 40 && s <= 48 =>
+ (STP [int32(s-16)] dst (Select0 (LDP [int32(s-16)] src mem)) (Select1 (LDP [int32(s-16)] src mem))
+ (STP [16] dst (Select0 (LDP [16] src mem)) (Select1 (LDP [16] src mem))
+ (STP dst (Select0 (LDP src mem)) (Select1 (LDP src mem)) mem)))
+(Move [s] dst src mem) && s > 48 && s <= 56 =>
+ (MOVDstore [int32(s-8)] dst (MOVDload [int32(s-8)] src mem)
+ (STP [32] dst (Select0 (LDP [32] src mem)) (Select1 (LDP [32] src mem))
+ (STP [16] dst (Select0 (LDP [16] src mem)) (Select1 (LDP [16] src mem))
+ (STP dst (Select0 (LDP src mem)) (Select1 (LDP src mem)) mem))))
+(Move [s] dst src mem) && s > 56 && s <= 64 =>
+ (STP [int32(s-16)] dst (Select0 (LDP [int32(s-16)] src mem)) (Select1 (LDP [int32(s-16)] src mem))
(STP [32] dst (Select0 (LDP [32] src mem)) (Select1 (LDP [32] src mem))
(STP [16] dst (Select0 (LDP [16] src mem)) (Select1 (LDP [16] src mem))
(STP dst (Select0 (LDP src mem)) (Select1 (LDP src mem)) mem))))
-(MOVDstorezero {s} [i] ptr x:(MOVDstorezero {s} [i+8] ptr mem)) && x.Uses == 1 && setPos(v, x.Pos) && clobber(x) => (MOVQstorezero {s} [i] ptr mem)
-(MOVDstorezero {s} [i] ptr x:(MOVDstorezero {s} [i-8] ptr mem)) && x.Uses == 1 && setPos(v, x.Pos) && clobber(x) => (MOVQstorezero {s} [i-8] ptr mem)
-
-// strip off fractional word move
-(Move [s] dst src mem) && s%16 != 0 && s%16 <= 8 && s > 16 =>
- (Move [8]
- (OffPtr dst [s-8])
- (OffPtr src [s-8])
- (Move [s-s%16] dst src mem))
-(Move [s] dst src mem) && s%16 != 0 && s%16 > 8 && s > 16 =>
- (Move [16]
- (OffPtr dst [s-16])
- (OffPtr src [s-16])
- (Move [s-s%16] dst src mem))
-
-// medium move uses a duff device
-(Move [s] dst src mem)
- && s > 64 && s <= 16*64 && s%16 == 0
- && !config.noDuffDevice && logLargeCopy(v, s) =>
- (DUFFCOPY [8 * (64 - s/16)] dst src mem)
-// 8 is the number of bytes to encode:
-//
-// LDP.P 16(R16), (R26, R27)
-// STP.P (R26, R27), 16(R17)
-//
-// 64 is number of these blocks. See runtime/duff_arm64.s:duffcopy
-
-// large move uses a loop
-(Move [s] dst src mem)
- && s%16 == 0 && (s > 16*64 || config.noDuffDevice)
- && logLargeCopy(v, s) =>
- (LoweredMove
- dst
- src
- (ADDconst src [s-16])
- mem)
+(Move [s] dst src mem) && s > 64 && s < 192 && logLargeCopy(v, s) => (LoweredMove [s] dst src mem)
+(Move [s] dst src mem) && s >= 192 && logLargeCopy(v, s) => (LoweredMoveLoop [s] dst src mem)
// calls
(StaticCall ...) => (CALLstatic ...)
@@ -591,9 +534,11 @@
// Publication barrier (0xe is ST option)
(PubBarrier mem) => (DMB [0xe] mem)
-(PanicBounds [kind] x y mem) && boundsABI(kind) == 0 => (LoweredPanicBoundsA [kind] x y mem)
-(PanicBounds [kind] x y mem) && boundsABI(kind) == 1 => (LoweredPanicBoundsB [kind] x y mem)
-(PanicBounds [kind] x y mem) && boundsABI(kind) == 2 => (LoweredPanicBoundsC [kind] x y mem)
+(PanicBounds ...) => (LoweredPanicBoundsRR ...)
+(LoweredPanicBoundsRR [kind] x (MOVDconst [c]) mem) => (LoweredPanicBoundsRC [kind] x {PanicBoundsC{C:c}} mem)
+(LoweredPanicBoundsRR [kind] (MOVDconst [c]) y mem) => (LoweredPanicBoundsCR [kind] {PanicBoundsC{C:c}} y mem)
+(LoweredPanicBoundsRC [kind] {p} (MOVDconst [c]) mem) => (LoweredPanicBoundsCC [kind] {PanicBoundsCC{Cx:c, Cy:p.C}} mem)
+(LoweredPanicBoundsCR [kind] {p} (MOVDconst [c]) mem) => (LoweredPanicBoundsCC [kind] {PanicBoundsCC{Cx:p.C, Cy:c}} mem)
// Optimizations
@@ -673,6 +618,14 @@
((EQ|NE) (CMPconst [0] x) yes no) => ((Z|NZ) x yes no)
((EQ|NE) (CMPWconst [0] x) yes no) => ((ZW|NZW) x yes no)
+((ULE|UGT) (CMPconst [0] x)) => ((EQ|NE) (CMPconst [0] x))
+((ULE|UGT) (CMPWconst [0] x)) => ((EQ|NE) (CMPWconst [0] x))
+
+((Z|NZ) sub:(SUB x y)) && sub.Uses == 1 => ((EQ|NE) (CMP x y))
+((ZW|NZW) sub:(SUB x y)) && sub.Uses == 1 => ((EQ|NE) (CMPW x y))
+((Z|NZ) sub:(SUBconst [c] y)) && sub.Uses == 1 => ((EQ|NE) (CMPconst [c] y))
+((ZW|NZW) sub:(SUBconst [c] y)) && sub.Uses == 1 => ((EQ|NE) (CMPWconst [int32(c)] y))
+
((EQ|NE|LT|LE|GT|GE) (CMPconst [0] z:(MADD a x y)) yes no) && z.Uses==1 => ((EQ|NE|LTnoov|LEnoov|GTnoov|GEnoov) (CMN a (MUL x y)) yes no)
((EQ|NE|LT|LE|GT|GE) (CMPconst [0] z:(MSUB a x y)) yes no) && z.Uses==1 => ((EQ|NE|LTnoov|LEnoov|GTnoov|GEnoov) (CMP a (MUL x y)) yes no)
((EQ|NE|LT|LE|GT|GE) (CMPWconst [0] z:(MADDW a x y)) yes no) && z.Uses==1 => ((EQ|NE|LTnoov|LEnoov|GTnoov|GEnoov) (CMNW a (MULW x y)) yes no)
@@ -817,21 +770,6 @@
(FMOVDstore [off1] {sym} (ADDconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2)
&& (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
(FMOVDstore [off1+int32(off2)] {sym} ptr val mem)
-(MOVBstorezero [off1] {sym} (ADDconst [off2] ptr) mem) && is32Bit(int64(off1)+off2)
- && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
- (MOVBstorezero [off1+int32(off2)] {sym} ptr mem)
-(MOVHstorezero [off1] {sym} (ADDconst [off2] ptr) mem) && is32Bit(int64(off1)+off2)
- && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
- (MOVHstorezero [off1+int32(off2)] {sym} ptr mem)
-(MOVWstorezero [off1] {sym} (ADDconst [off2] ptr) mem) && is32Bit(int64(off1)+off2)
- && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
- (MOVWstorezero [off1+int32(off2)] {sym} ptr mem)
-(MOVDstorezero [off1] {sym} (ADDconst [off2] ptr) mem) && is32Bit(int64(off1)+off2)
- && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
- (MOVDstorezero [off1+int32(off2)] {sym} ptr mem)
-(MOVQstorezero [off1] {sym} (ADDconst [off2] ptr) mem) && is32Bit(int64(off1)+off2)
- && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
- (MOVQstorezero [off1+int32(off2)] {sym} ptr mem)
// register indexed store
(MOVDstore [off] {sym} (ADD ptr idx) val mem) && off == 0 && sym == nil => (MOVDstoreidx ptr idx val mem)
@@ -947,70 +885,6 @@
&& canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
&& (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
(FMOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
-(MOVBstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
- && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
- && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
- (MOVBstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
-(MOVHstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
- && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
- && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
- (MOVHstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
-(MOVWstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
- && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
- && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
- (MOVWstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
-(MOVDstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
- && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
- && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
- (MOVDstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
-(MOVQstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
- && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2))
- && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) =>
- (MOVQstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
-
-// store zero
-(MOVBstore [off] {sym} ptr (MOVDconst [0]) mem) => (MOVBstorezero [off] {sym} ptr mem)
-(MOVHstore [off] {sym} ptr (MOVDconst [0]) mem) => (MOVHstorezero [off] {sym} ptr mem)
-(MOVWstore [off] {sym} ptr (MOVDconst [0]) mem) => (MOVWstorezero [off] {sym} ptr mem)
-(MOVDstore [off] {sym} ptr (MOVDconst [0]) mem) => (MOVDstorezero [off] {sym} ptr mem)
-(STP [off] {sym} ptr (MOVDconst [0]) (MOVDconst [0]) mem) => (MOVQstorezero [off] {sym} ptr mem)
-
-// register indexed store zero
-(MOVDstorezero [off] {sym} (ADD ptr idx) mem) && off == 0 && sym == nil => (MOVDstorezeroidx ptr idx mem)
-(MOVWstorezero [off] {sym} (ADD ptr idx) mem) && off == 0 && sym == nil => (MOVWstorezeroidx ptr idx mem)
-(MOVHstorezero [off] {sym} (ADD ptr idx) mem) && off == 0 && sym == nil => (MOVHstorezeroidx ptr idx mem)
-(MOVBstorezero [off] {sym} (ADD ptr idx) mem) && off == 0 && sym == nil => (MOVBstorezeroidx ptr idx mem)
-(MOVDstoreidx ptr idx (MOVDconst [0]) mem) => (MOVDstorezeroidx ptr idx mem)
-(MOVWstoreidx ptr idx (MOVDconst [0]) mem) => (MOVWstorezeroidx ptr idx mem)
-(MOVHstoreidx ptr idx (MOVDconst [0]) mem) => (MOVHstorezeroidx ptr idx mem)
-(MOVBstoreidx ptr idx (MOVDconst [0]) mem) => (MOVBstorezeroidx ptr idx mem)
-(MOVDstorezeroidx ptr (MOVDconst [c]) mem) && is32Bit(c) => (MOVDstorezero [int32(c)] ptr mem)
-(MOVDstorezeroidx (MOVDconst [c]) idx mem) && is32Bit(c) => (MOVDstorezero [int32(c)] idx mem)
-(MOVWstorezeroidx ptr (MOVDconst [c]) mem) && is32Bit(c) => (MOVWstorezero [int32(c)] ptr mem)
-(MOVWstorezeroidx (MOVDconst [c]) idx mem) && is32Bit(c) => (MOVWstorezero [int32(c)] idx mem)
-(MOVHstorezeroidx ptr (MOVDconst [c]) mem) && is32Bit(c) => (MOVHstorezero [int32(c)] ptr mem)
-(MOVHstorezeroidx (MOVDconst [c]) idx mem) && is32Bit(c) => (MOVHstorezero [int32(c)] idx mem)
-(MOVBstorezeroidx ptr (MOVDconst [c]) mem) && is32Bit(c) => (MOVBstorezero [int32(c)] ptr mem)
-(MOVBstorezeroidx (MOVDconst [c]) idx mem) && is32Bit(c) => (MOVBstorezero [int32(c)] idx mem)
-
-// shifted register indexed store zero
-(MOVDstorezero [off] {sym} (ADDshiftLL [3] ptr idx) mem) && off == 0 && sym == nil => (MOVDstorezeroidx8 ptr idx mem)
-(MOVWstorezero [off] {sym} (ADDshiftLL [2] ptr idx) mem) && off == 0 && sym == nil => (MOVWstorezeroidx4 ptr idx mem)
-(MOVHstorezero [off] {sym} (ADDshiftLL [1] ptr idx) mem) && off == 0 && sym == nil => (MOVHstorezeroidx2 ptr idx mem)
-(MOVDstorezeroidx ptr (SLLconst [3] idx) mem) => (MOVDstorezeroidx8 ptr idx mem)
-(MOVWstorezeroidx ptr (SLLconst [2] idx) mem) => (MOVWstorezeroidx4 ptr idx mem)
-(MOVHstorezeroidx ptr (SLLconst [1] idx) mem) => (MOVHstorezeroidx2 ptr idx mem)
-(MOVHstorezeroidx ptr (ADD idx idx) mem) => (MOVHstorezeroidx2 ptr idx mem)
-(MOVDstorezeroidx (SLLconst [3] idx) ptr mem) => (MOVDstorezeroidx8 ptr idx mem)
-(MOVWstorezeroidx (SLLconst [2] idx) ptr mem) => (MOVWstorezeroidx4 ptr idx mem)
-(MOVHstorezeroidx (SLLconst [1] idx) ptr mem) => (MOVHstorezeroidx2 ptr idx mem)
-(MOVHstorezeroidx (ADD idx idx) ptr mem) => (MOVHstorezeroidx2 ptr idx mem)
-(MOVDstoreidx8 ptr idx (MOVDconst [0]) mem) => (MOVDstorezeroidx8 ptr idx mem)
-(MOVWstoreidx4 ptr idx (MOVDconst [0]) mem) => (MOVWstorezeroidx4 ptr idx mem)
-(MOVHstoreidx2 ptr idx (MOVDconst [0]) mem) => (MOVHstorezeroidx2 ptr idx mem)
-(MOVDstorezeroidx8 ptr (MOVDconst [c]) mem) && is32Bit(c<<3) => (MOVDstorezero [int32(c<<3)] ptr mem)
-(MOVWstorezeroidx4 ptr (MOVDconst [c]) mem) && is32Bit(c<<2) => (MOVWstorezero [int32(c<<2)] ptr mem)
-(MOVHstorezeroidx2 ptr (MOVDconst [c]) mem) && is32Bit(c<<1) => (MOVHstorezero [int32(c<<1)] ptr mem)
// replace load from same location as preceding store with zero/sign extension (or copy in case of full width)
// these seem to have bad interaction with other rules, resulting in slower code
@@ -1025,35 +899,6 @@
//(FMOVDload [off] {sym} ptr (FMOVDstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
//(LDP [off] {sym} ptr (STP [off2] {sym2} ptr2 x y _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x y
-(MOVBload [off] {sym} ptr (MOVBstorezero [off2] {sym2} ptr2 _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) => (MOVDconst [0])
-(MOVBUload [off] {sym} ptr (MOVBstorezero [off2] {sym2} ptr2 _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) => (MOVDconst [0])
-(MOVHload [off] {sym} ptr (MOVHstorezero [off2] {sym2} ptr2 _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) => (MOVDconst [0])
-(MOVHUload [off] {sym} ptr (MOVHstorezero [off2] {sym2} ptr2 _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) => (MOVDconst [0])
-(MOVWload [off] {sym} ptr (MOVWstorezero [off2] {sym2} ptr2 _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) => (MOVDconst [0])
-(MOVWUload [off] {sym} ptr (MOVWstorezero [off2] {sym2} ptr2 _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) => (MOVDconst [0])
-(MOVDload [off] {sym} ptr (MOVDstorezero [off2] {sym2} ptr2 _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) => (MOVDconst [0])
-
-(MOVBloadidx ptr idx (MOVBstorezeroidx ptr2 idx2 _))
- && (isSamePtr(ptr, ptr2) && isSamePtr(idx, idx2) || isSamePtr(ptr, idx2) && isSamePtr(idx, ptr2)) => (MOVDconst [0])
-(MOVBUloadidx ptr idx (MOVBstorezeroidx ptr2 idx2 _))
- && (isSamePtr(ptr, ptr2) && isSamePtr(idx, idx2) || isSamePtr(ptr, idx2) && isSamePtr(idx, ptr2)) => (MOVDconst [0])
-(MOVHloadidx ptr idx (MOVHstorezeroidx ptr2 idx2 _))
- && (isSamePtr(ptr, ptr2) && isSamePtr(idx, idx2) || isSamePtr(ptr, idx2) && isSamePtr(idx, ptr2)) => (MOVDconst [0])
-(MOVHUloadidx ptr idx (MOVHstorezeroidx ptr2 idx2 _))
- && (isSamePtr(ptr, ptr2) && isSamePtr(idx, idx2) || isSamePtr(ptr, idx2) && isSamePtr(idx, ptr2)) => (MOVDconst [0])
-(MOVWloadidx ptr idx (MOVWstorezeroidx ptr2 idx2 _))
- && (isSamePtr(ptr, ptr2) && isSamePtr(idx, idx2) || isSamePtr(ptr, idx2) && isSamePtr(idx, ptr2)) => (MOVDconst [0])
-(MOVWUloadidx ptr idx (MOVWstorezeroidx ptr2 idx2 _))
- && (isSamePtr(ptr, ptr2) && isSamePtr(idx, idx2) || isSamePtr(ptr, idx2) && isSamePtr(idx, ptr2)) => (MOVDconst [0])
-(MOVDloadidx ptr idx (MOVDstorezeroidx ptr2 idx2 _))
- && (isSamePtr(ptr, ptr2) && isSamePtr(idx, idx2) || isSamePtr(ptr, idx2) && isSamePtr(idx, ptr2)) => (MOVDconst [0])
-
-(MOVHloadidx2 ptr idx (MOVHstorezeroidx2 ptr2 idx2 _)) && isSamePtr(ptr, ptr2) && isSamePtr(idx, idx2) => (MOVDconst [0])
-(MOVHUloadidx2 ptr idx (MOVHstorezeroidx2 ptr2 idx2 _)) && isSamePtr(ptr, ptr2) && isSamePtr(idx, idx2) => (MOVDconst [0])
-(MOVWloadidx4 ptr idx (MOVWstorezeroidx4 ptr2 idx2 _)) && isSamePtr(ptr, ptr2) && isSamePtr(idx, idx2) => (MOVDconst [0])
-(MOVWUloadidx4 ptr idx (MOVWstorezeroidx4 ptr2 idx2 _)) && isSamePtr(ptr, ptr2) && isSamePtr(idx, idx2) => (MOVDconst [0])
-(MOVDloadidx8 ptr idx (MOVDstorezeroidx8 ptr2 idx2 _)) && isSamePtr(ptr, ptr2) && isSamePtr(idx, idx2) => (MOVDconst [0])
-
// don't extend before store
(MOVBstore [off] {sym} ptr (MOVBreg x) mem) => (MOVBstore [off] {sym} ptr x mem)
(MOVBstore [off] {sym} ptr (MOVBUreg x) mem) => (MOVBstore [off] {sym} ptr x mem)
@@ -1148,10 +993,12 @@
(SUB a l:(MNEGW x y)) && v.Type.Size() <= 4 && l.Uses==1 && clobber(l) => (MADDW a x y)
// madd/msub can't take constant arguments, so do a bit of reordering if a non-constant is available.
-(ADD a p:(ADDconst [c] m:((MUL|MULW|MNEG|MNEGW) _ _))) && p.Uses==1 && m.Uses==1 => (ADDconst [c] (ADD a m))
-(ADD a p:(SUBconst [c] m:((MUL|MULW|MNEG|MNEGW) _ _))) && p.Uses==1 && m.Uses==1 => (SUBconst [c] (ADD a m))
-(SUB a p:(ADDconst [c] m:((MUL|MULW|MNEG|MNEGW) _ _))) && p.Uses==1 && m.Uses==1 => (SUBconst [c] (SUB a m))
-(SUB a p:(SUBconst [c] m:((MUL|MULW|MNEG|MNEGW) _ _))) && p.Uses==1 && m.Uses==1 => (ADDconst [c] (SUB a m))
+// Note: don't reorder arithmetic concerning pointers, as we must ensure that
+// no intermediate computations are invalid pointers.
+(ADD a p:(ADDconst [c] m:((MUL|MULW|MNEG|MNEGW) _ _))) && p.Uses==1 && m.Uses==1 && !t.IsPtrShaped() => (ADDconst [c] (ADD a m))
+(ADD a p:(SUBconst [c] m:((MUL|MULW|MNEG|MNEGW) _ _))) && p.Uses==1 && m.Uses==1 && !t.IsPtrShaped() => (SUBconst [c] (ADD a m))
+(SUB a p:(ADDconst [c] m:((MUL|MULW|MNEG|MNEGW) _ _))) && p.Uses==1 && m.Uses==1 && !t.IsPtrShaped() => (SUBconst [c] (SUB a m))
+(SUB a p:(SUBconst [c] m:((MUL|MULW|MNEG|MNEGW) _ _))) && p.Uses==1 && m.Uses==1 && !t.IsPtrShaped() => (ADDconst [c] (SUB a m))
// optimize ADCSflags, SBCSflags and friends
(ADCSflags x y (Select1 (ADDSconstflags [-1] (ADCzerocarry c)))) => (ADCSflags x y c)
@@ -1160,27 +1007,14 @@
(SBCSflags x y (Select1 (NEGSflags (MOVDconst [0])))) => (SUBSflags x y)
// mul by constant
-(MUL x (MOVDconst [-1])) => (NEG x)
(MUL _ (MOVDconst [0])) => (MOVDconst [0])
(MUL x (MOVDconst [1])) => x
-(MUL x (MOVDconst [c])) && isPowerOfTwo(c) => (SLLconst [log64(c)] x)
-(MUL x (MOVDconst [c])) && isPowerOfTwo(c-1) && c >= 3 => (ADDshiftLL x x [log64(c-1)])
-(MUL x (MOVDconst [c])) && isPowerOfTwo(c+1) && c >= 7 => (ADDshiftLL (NEG x) x [log64(c+1)])
-(MUL x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) => (SLLconst [log64(c/3)] (ADDshiftLL x x [1]))
-(MUL x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) => (SLLconst [log64(c/5)] (ADDshiftLL x x [2]))
-(MUL x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) => (SLLconst [log64(c/7)] (ADDshiftLL (NEG x) x [3]))
-(MUL x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) => (SLLconst [log64(c/9)] (ADDshiftLL x x [3]))
-(MULW x (MOVDconst [c])) && int32(c)==-1 => (MOVWUreg (NEG x))
(MULW _ (MOVDconst [c])) && int32(c)==0 => (MOVDconst [0])
(MULW x (MOVDconst [c])) && int32(c)==1 => (MOVWUreg x)
-(MULW x (MOVDconst [c])) && isPowerOfTwo(c) => (MOVWUreg (SLLconst [log64(c)] x))
-(MULW x (MOVDconst [c])) && isPowerOfTwo(c-1) && int32(c) >= 3 => (MOVWUreg (ADDshiftLL x x [log64(c-1)]))
-(MULW x (MOVDconst [c])) && isPowerOfTwo(c+1) && int32(c) >= 7 => (MOVWUreg (ADDshiftLL (NEG x) x [log64(c+1)]))
-(MULW x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) => (MOVWUreg (SLLconst [log64(c/3)] (ADDshiftLL x x [1])))
-(MULW x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) => (MOVWUreg (SLLconst [log64(c/5)] (ADDshiftLL x x [2])))
-(MULW x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) => (MOVWUreg (SLLconst [log64(c/7)] (ADDshiftLL (NEG x) x [3])))
-(MULW x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) => (MOVWUreg (SLLconst [log64(c/9)] (ADDshiftLL x x [3])))
+
+(MUL x (MOVDconst [c])) && canMulStrengthReduce(config, c) => {mulStrengthReduce(v, x, c)}
+(MULW x (MOVDconst [c])) && v.Type.Size() <= 4 && canMulStrengthReduce32(config, int32(c)) => {mulStrengthReduce32(v, x, int32(c))}
// mneg by constant
(MNEG x (MOVDconst [-1])) => x
@@ -1307,6 +1141,7 @@
// generic simplifications
(ADD x (NEG y)) => (SUB x y)
+(SUB x (NEG y)) => (ADD x y)
(SUB x x) => (MOVDconst [0])
(AND x x) => x
(OR x x) => x
@@ -1318,6 +1153,7 @@
(XOR x (MVN y)) => (EON x y)
(OR x (MVN y)) => (ORN x y)
(MVN (XOR x y)) => (EON x y)
+(NEG (SUB x y)) => (SUB y x)
(NEG (NEG x)) => x
(CSEL [cc] (MOVDconst [-1]) (MOVDconst [0]) flag) => (CSETM [cc] flag)
@@ -1494,16 +1330,18 @@
(CSNEG [cc] x y (InvertFlags cmp)) => (CSNEG [arm64Invert(cc)] x y cmp)
// absorb flag constants into boolean values
-(Equal (FlagConstant [fc])) => (MOVDconst [b2i(fc.eq())])
-(NotEqual (FlagConstant [fc])) => (MOVDconst [b2i(fc.ne())])
-(LessThan (FlagConstant [fc])) => (MOVDconst [b2i(fc.lt())])
-(LessThanU (FlagConstant [fc])) => (MOVDconst [b2i(fc.ult())])
-(LessEqual (FlagConstant [fc])) => (MOVDconst [b2i(fc.le())])
-(LessEqualU (FlagConstant [fc])) => (MOVDconst [b2i(fc.ule())])
-(GreaterThan (FlagConstant [fc])) => (MOVDconst [b2i(fc.gt())])
-(GreaterThanU (FlagConstant [fc])) => (MOVDconst [b2i(fc.ugt())])
-(GreaterEqual (FlagConstant [fc])) => (MOVDconst [b2i(fc.ge())])
-(GreaterEqualU (FlagConstant [fc])) => (MOVDconst [b2i(fc.uge())])
+(Equal (FlagConstant [fc])) => (MOVDconst [b2i(fc.eq())])
+(NotEqual (FlagConstant [fc])) => (MOVDconst [b2i(fc.ne())])
+(LessThan (FlagConstant [fc])) => (MOVDconst [b2i(fc.lt())])
+(LessThanU (FlagConstant [fc])) => (MOVDconst [b2i(fc.ult())])
+(LessEqual (FlagConstant [fc])) => (MOVDconst [b2i(fc.le())])
+(LessEqualU (FlagConstant [fc])) => (MOVDconst [b2i(fc.ule())])
+(GreaterThan (FlagConstant [fc])) => (MOVDconst [b2i(fc.gt())])
+(GreaterThanU (FlagConstant [fc])) => (MOVDconst [b2i(fc.ugt())])
+(GreaterEqual (FlagConstant [fc])) => (MOVDconst [b2i(fc.ge())])
+(GreaterEqualU (FlagConstant [fc])) => (MOVDconst [b2i(fc.uge())])
+(LessThanNoov (FlagConstant [fc])) => (MOVDconst [b2i(fc.ltNoov())])
+(GreaterEqualNoov (FlagConstant [fc])) => (MOVDconst [b2i(fc.geNoov())])
// absorb InvertFlags into boolean values
(Equal (InvertFlags x)) => (Equal x)
@@ -1763,6 +1601,10 @@
(SRLconst [rc] (MOVHUreg x)) && rc >= 16 => (MOVDconst [0])
(SRLconst [rc] (MOVBUreg x)) && rc >= 8 => (MOVDconst [0])
+// Special cases for slice operations
+(ADD x0 x1:(ANDshiftRA x2:(SLLconst [sl] y) z [63])) && x1.Uses == 1 && x2.Uses == 1 => (ADDshiftLL x0 (ANDshiftRA y z [63]) [sl])
+(ADD x0 x1:(ANDshiftLL x2:(SRAconst [63] z) y [sl])) && x1.Uses == 1 && x2.Uses == 1 => (ADDshiftLL x0 (ANDshiftRA y z [63]) [sl])
+
// bitfield ops
// sbfiz
@@ -1940,6 +1782,9 @@
(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))])
+(MOVBload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(int8(read8(sym, int64(off))))])
+(MOVHload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))])
+(MOVWload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder)))])
// Prefetch instructions (aux is option: 0 - PLDL1KEEP; 1 - PLDL1STRM)
(PrefetchCache addr mem) => (PRFM [0] addr mem)
diff --git a/src/cmd/compile/internal/ssa/_gen/ARM64Ops.go b/src/cmd/compile/internal/ssa/_gen/ARM64Ops.go
index c9cb62cd17..cc3758d109 100644
--- a/src/cmd/compile/internal/ssa/_gen/ARM64Ops.go
+++ b/src/cmd/compile/internal/ssa/_gen/ARM64Ops.go
@@ -47,7 +47,7 @@ var regNamesARM64 = []string{
"R15",
"R16",
"R17",
- "R18", // platform register, not used
+ // R18 = platform register, not used
"R19",
"R20",
"R21",
@@ -57,10 +57,23 @@ var regNamesARM64 = []string{
"R25",
"R26",
// R27 = REGTMP not used in regalloc
- "g", // aka R28
- "R29", // frame pointer, not used
- "R30", // aka REGLINK
- "SP", // aka R31
+ "g", // aka R28
+ "R29", // frame pointer, not used
+ "R30", // aka REGLINK
+ "ZERO", // zero register (aka R31)
+ "SP", // stack pointer (aka R31)
+
+ // Note: both ZERO and SP are register number 31!
+ // What r31 means in a particular instruction depends on
+ // the instruction. Generally, for arguments of instructions
+ // which are addresses to load or store from, r31 means SP.
+ // In other instructions, r31 means ZERO. But there are
+ // exceptions.
+ // See https://stackoverflow.com/questions/61532867
+ // This does not have much of an effect here, as the
+ // cmd/internal/obj/arm64 interface treats them as two
+ // different registers and picks the right instruction
+ // that encodes what r31 means. But see issue 71651.
"F0",
"F1",
@@ -131,10 +144,11 @@ func init() {
gpspsbg = gpspg | buildReg("SB")
fp = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31")
callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g
- r0 = buildReg("R0")
- r1 = buildReg("R1")
- r2 = buildReg("R2")
- r3 = buildReg("R3")
+ r25 = buildReg("R25")
+ r24to25 = buildReg("R24 R25")
+ f16to17 = buildReg("F16 F17")
+ rz = buildReg("ZERO")
+ first16 = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15")
)
// Common regInfo
var (
@@ -143,23 +157,24 @@ func init() {
gp11 = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
gp11sp = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
gp1flags = regInfo{inputs: []regMask{gpg}}
+ gp1flagsflags = regInfo{inputs: []regMask{gpg}}
gp1flags1 = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
gp11flags = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp, 0}}
gp21 = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
gp21nog = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}}
gp21flags = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp, 0}}
gp2flags = regInfo{inputs: []regMask{gpg, gpg}}
+ gp2flagsflags = regInfo{inputs: []regMask{gpg, gpg}}
gp2flags1 = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}}
gp2flags1flags = regInfo{inputs: []regMask{gp, gp, 0}, outputs: []regMask{gp, 0}}
gp2load = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
gp31 = regInfo{inputs: []regMask{gpg, gpg, gpg}, outputs: []regMask{gp}}
gpload = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
gpload2 = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gpg, gpg}}
- gpstore = regInfo{inputs: []regMask{gpspsbg, gpg}}
- gpstore0 = regInfo{inputs: []regMask{gpspsbg}}
- gpstore2 = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}}
- gpxchg = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
- gpcas = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
+ gpstore = regInfo{inputs: []regMask{gpspsbg, gpg | rz}}
+ gpstore2 = regInfo{inputs: []regMask{gpspsbg, gpg | rz, gpg | rz}}
+ gpxchg = regInfo{inputs: []regMask{gpspsbg, gpg | rz}, outputs: []regMask{gp}}
+ gpcas = regInfo{inputs: []regMask{gpspsbg, gpg | rz, gpg | rz}, outputs: []regMask{gp}}
fp01 = regInfo{inputs: nil, outputs: []regMask{fp}}
fp11 = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
fpgp = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
@@ -169,9 +184,11 @@ func init() {
fp2flags = regInfo{inputs: []regMask{fp, fp}}
fp1flags = regInfo{inputs: []regMask{fp}}
fpload = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
+ fpload2 = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp, fp}}
fp2load = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{fp}}
fpstore = regInfo{inputs: []regMask{gpspsbg, fp}}
- fpstore2 = regInfo{inputs: []regMask{gpspsbg, gpg, fp}}
+ fpstoreidx = regInfo{inputs: []regMask{gpspsbg, gpg, fp}}
+ fpstore2 = regInfo{inputs: []regMask{gpspsbg, fp, fp}}
readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
prefreg = regInfo{inputs: []regMask{gpspsbg}}
)
@@ -369,16 +386,27 @@ func init() {
{name: "MOVDaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVD", rematerializeable: true, symEffect: "Addr"}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
- {name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
- {name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
- {name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
- {name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
- {name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
- {name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
- {name: "MOVDload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVD", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
- {name: "LDP", argLength: 2, reg: gpload2, aux: "SymOff", asm: "LDP", typ: "(UInt64,UInt64)", faultOnNilArg0: true, symEffect: "Read"}, // load from ptr = arg0 + auxInt + aux, returns the tuple <*(*uint64)ptr, *(*uint64)(ptr+8)>. arg1=mem.
- {name: "FMOVSload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVS", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
- {name: "FMOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
+ {name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
+ {name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
+ {name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
+ {name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
+ {name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
+ {name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
+ {name: "MOVDload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVD", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
+ {name: "FMOVSload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVS", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
+ {name: "FMOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem.
+
+ // LDP instructions load the contents of two adjacent locations in memory into registers.
+ // Address to start loading is addr = arg0 + auxInt + aux.
+ // x := *(*T)(addr)
+ // y := *(*T)(addr+sizeof(T))
+ // arg1=mem
+ // Returns the tuple .
+ {name: "LDP", argLength: 2, reg: gpload2, aux: "SymOff", asm: "LDP", typ: "(UInt64,UInt64)", faultOnNilArg0: true, symEffect: "Read"}, // T=int64 (gp reg destination)
+ {name: "LDPW", argLength: 2, reg: gpload2, aux: "SymOff", asm: "LDPW", typ: "(UInt32,UInt32)", faultOnNilArg0: true, symEffect: "Read"}, // T=int32 (gp reg destination) unsigned extension
+ {name: "LDPSW", argLength: 2, reg: gpload2, aux: "SymOff", asm: "LDPSW", typ: "(Int32,Int32)", faultOnNilArg0: true, symEffect: "Read"}, // T=int32 (gp reg destination) signed extension
+ {name: "FLDPD", argLength: 2, reg: fpload2, aux: "SymOff", asm: "FLDPD", typ: "(Float64,Float64)", faultOnNilArg0: true, symEffect: "Read"}, // T=float64 (fp reg destination)
+ {name: "FLDPS", argLength: 2, reg: fpload2, aux: "SymOff", asm: "FLDPS", typ: "(Float32,Float32)", faultOnNilArg0: true, symEffect: "Read"}, // T=float32 (fp reg destination)
// register indexed load
{name: "MOVDloadidx", argLength: 3, reg: gp2load, asm: "MOVD", typ: "UInt64"}, // load 64-bit dword from arg0 + arg1, arg2 = mem.
@@ -404,41 +432,33 @@ func init() {
{name: "MOVHstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes of arg1 to arg0 + auxInt + aux. arg2=mem.
{name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of arg1 to arg0 + auxInt + aux. arg2=mem.
{name: "MOVDstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of arg1 to arg0 + auxInt + aux. arg2=mem.
- {name: "STP", argLength: 4, reg: gpstore2, aux: "SymOff", asm: "STP", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 16 bytes of arg1 and arg2 to arg0 + auxInt + aux. arg3=mem.
{name: "FMOVSstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "FMOVS", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of arg1 to arg0 + auxInt + aux. arg2=mem.
{name: "FMOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "FMOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of arg1 to arg0 + auxInt + aux. arg2=mem.
+ // STP instructions store the contents of two registers to adjacent locations in memory.
+ // Address to start storing is addr = arg0 + auxInt + aux.
+ // *(*T)(addr) = arg1
+ // *(*T)(addr+sizeof(T)) = arg2
+ // arg3=mem. Returns mem.
+ {name: "STP", argLength: 4, reg: gpstore2, aux: "SymOff", asm: "STP", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // T=int64 (gp reg source)
+ {name: "STPW", argLength: 4, reg: gpstore2, aux: "SymOff", asm: "STPW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // T=int32 (gp reg source)
+ {name: "FSTPD", argLength: 4, reg: fpstore2, aux: "SymOff", asm: "FSTPD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // T=float64 (fp reg source)
+ {name: "FSTPS", argLength: 4, reg: fpstore2, aux: "SymOff", asm: "FSTPS", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // T=float32 (fp reg source)
+
// register indexed store
- {name: "MOVBstoreidx", argLength: 4, reg: gpstore2, asm: "MOVB", typ: "Mem"}, // store 1 byte of arg2 to arg0 + arg1, arg3 = mem.
- {name: "MOVHstoreidx", argLength: 4, reg: gpstore2, asm: "MOVH", typ: "Mem"}, // store 2 bytes of arg2 to arg0 + arg1, arg3 = mem.
- {name: "MOVWstoreidx", argLength: 4, reg: gpstore2, asm: "MOVW", typ: "Mem"}, // store 4 bytes of arg2 to arg0 + arg1, arg3 = mem.
- {name: "MOVDstoreidx", argLength: 4, reg: gpstore2, asm: "MOVD", typ: "Mem"}, // store 8 bytes of arg2 to arg0 + arg1, arg3 = mem.
- {name: "FMOVSstoreidx", argLength: 4, reg: fpstore2, asm: "FMOVS", typ: "Mem"}, // store 32-bit float of arg2 to arg0 + arg1, arg3=mem.
- {name: "FMOVDstoreidx", argLength: 4, reg: fpstore2, asm: "FMOVD", typ: "Mem"}, // store 64-bit float of arg2 to arg0 + arg1, arg3=mem.
+ {name: "MOVBstoreidx", argLength: 4, reg: gpstore2, asm: "MOVB", typ: "Mem"}, // store 1 byte of arg2 to arg0 + arg1, arg3 = mem.
+ {name: "MOVHstoreidx", argLength: 4, reg: gpstore2, asm: "MOVH", typ: "Mem"}, // store 2 bytes of arg2 to arg0 + arg1, arg3 = mem.
+ {name: "MOVWstoreidx", argLength: 4, reg: gpstore2, asm: "MOVW", typ: "Mem"}, // store 4 bytes of arg2 to arg0 + arg1, arg3 = mem.
+ {name: "MOVDstoreidx", argLength: 4, reg: gpstore2, asm: "MOVD", typ: "Mem"}, // store 8 bytes of arg2 to arg0 + arg1, arg3 = mem.
+ {name: "FMOVSstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVS", typ: "Mem"}, // store 32-bit float of arg2 to arg0 + arg1, arg3=mem.
+ {name: "FMOVDstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVD", typ: "Mem"}, // store 64-bit float of arg2 to arg0 + arg1, arg3=mem.
// shifted register indexed store
- {name: "MOVHstoreidx2", argLength: 4, reg: gpstore2, asm: "MOVH", typ: "Mem"}, // store 2 bytes of arg2 to arg0 + arg1*2, arg3 = mem.
- {name: "MOVWstoreidx4", argLength: 4, reg: gpstore2, asm: "MOVW", typ: "Mem"}, // store 4 bytes of arg2 to arg0 + arg1*4, arg3 = mem.
- {name: "MOVDstoreidx8", argLength: 4, reg: gpstore2, asm: "MOVD", typ: "Mem"}, // store 8 bytes of arg2 to arg0 + arg1*8, arg3 = mem.
- {name: "FMOVSstoreidx4", argLength: 4, reg: fpstore2, asm: "FMOVS", typ: "Mem"}, // store 32-bit float of arg2 to arg0 + arg1*4, arg3=mem.
- {name: "FMOVDstoreidx8", argLength: 4, reg: fpstore2, asm: "FMOVD", typ: "Mem"}, // store 64-bit float of arg2 to arg0 + arg1*8, arg3=mem.
-
- {name: "MOVBstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 1 byte of zero to arg0 + auxInt + aux. arg1=mem.
- {name: "MOVHstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes of zero to arg0 + auxInt + aux. arg1=mem.
- {name: "MOVWstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of zero to arg0 + auxInt + aux. arg1=mem.
- {name: "MOVDstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of zero to arg0 + auxInt + aux. arg1=mem.
- {name: "MOVQstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "STP", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 16 bytes of zero to arg0 + auxInt + aux. arg1=mem.
-
- // register indexed store zero
- {name: "MOVBstorezeroidx", argLength: 3, reg: gpstore, asm: "MOVB", typ: "Mem"}, // store 1 byte of zero to arg0 + arg1, arg2 = mem.
- {name: "MOVHstorezeroidx", argLength: 3, reg: gpstore, asm: "MOVH", typ: "Mem"}, // store 2 bytes of zero to arg0 + arg1, arg2 = mem.
- {name: "MOVWstorezeroidx", argLength: 3, reg: gpstore, asm: "MOVW", typ: "Mem"}, // store 4 bytes of zero to arg0 + arg1, arg2 = mem.
- {name: "MOVDstorezeroidx", argLength: 3, reg: gpstore, asm: "MOVD", typ: "Mem"}, // store 8 bytes of zero to arg0 + arg1, arg2 = mem.
-
- // shifted register indexed store zero
- {name: "MOVHstorezeroidx2", argLength: 3, reg: gpstore, asm: "MOVH", typ: "Mem"}, // store 2 bytes of zero to arg0 + arg1*2, arg2 = mem.
- {name: "MOVWstorezeroidx4", argLength: 3, reg: gpstore, asm: "MOVW", typ: "Mem"}, // store 4 bytes of zero to arg0 + arg1*4, arg2 = mem.
- {name: "MOVDstorezeroidx8", argLength: 3, reg: gpstore, asm: "MOVD", typ: "Mem"}, // store 8 bytes of zero to arg0 + arg1*8, arg2 = mem.
+ {name: "MOVHstoreidx2", argLength: 4, reg: gpstore2, asm: "MOVH", typ: "Mem"}, // store 2 bytes of arg2 to arg0 + arg1*2, arg3 = mem.
+ {name: "MOVWstoreidx4", argLength: 4, reg: gpstore2, asm: "MOVW", typ: "Mem"}, // store 4 bytes of arg2 to arg0 + arg1*4, arg3 = mem.
+ {name: "MOVDstoreidx8", argLength: 4, reg: gpstore2, asm: "MOVD", typ: "Mem"}, // store 8 bytes of arg2 to arg0 + arg1*8, arg3 = mem.
+ {name: "FMOVSstoreidx4", argLength: 4, reg: fpstoreidx, asm: "FMOVS", typ: "Mem"}, // store 32-bit float of arg2 to arg0 + arg1*4, arg3=mem.
+ {name: "FMOVDstoreidx8", argLength: 4, reg: fpstoreidx, asm: "FMOVD", typ: "Mem"}, // store 64-bit float of arg2 to arg0 + arg1*8, arg3=mem.
{name: "FMOVDgpfp", argLength: 1, reg: gpfp, asm: "FMOVD"}, // move int64 to float64 (no conversion)
{name: "FMOVDfpgp", argLength: 1, reg: fpgp, asm: "FMOVD"}, // move float64 to int64 (no conversion)
@@ -491,6 +511,22 @@ func init() {
{name: "CSNEG", argLength: 3, reg: gp2flags1, asm: "CSNEG", aux: "CCop"}, // auxint(flags) ? arg0 : -arg1
{name: "CSETM", argLength: 1, reg: readflags, asm: "CSETM", aux: "CCop"}, // auxint(flags) ? -1 : 0
+ // conditional comparison instructions; auxint is
+ // combination of Cond, Nzcv and optional ConstValue
+ // Behavior:
+ // If the condition 'Cond' evaluates to true against current flags,
+ // flags are set to the result of the comparison operation.
+ // Otherwise, flags are set to the fallback value 'Nzcv'.
+ {name: "CCMP", argLength: 3, reg: gp2flagsflags, asm: "CCMP", aux: "ARM64ConditionalParams", typ: "Flag"}, // If Cond then flags = CMP arg0 arg1 else flags = Nzcv
+ {name: "CCMN", argLength: 3, reg: gp2flagsflags, asm: "CCMN", aux: "ARM64ConditionalParams", typ: "Flag"}, // If Cond then flags = CMN arg0 arg1 else flags = Nzcv
+ {name: "CCMPconst", argLength: 2, reg: gp1flagsflags, asm: "CCMP", aux: "ARM64ConditionalParams", typ: "Flag"}, // If Cond then flags = CMPconst [ConstValue] arg0 else flags = Nzcv
+ {name: "CCMNconst", argLength: 2, reg: gp1flagsflags, asm: "CCMN", aux: "ARM64ConditionalParams", typ: "Flag"}, // If Cond then flags = CMNconst [ConstValue] arg0 else flags = Nzcv
+
+ {name: "CCMPW", argLength: 3, reg: gp2flagsflags, asm: "CCMPW", aux: "ARM64ConditionalParams", typ: "Flag"}, // If Cond then flags = CMPW arg0 arg1 else flags = Nzcv
+ {name: "CCMNW", argLength: 3, reg: gp2flagsflags, asm: "CCMNW", aux: "ARM64ConditionalParams", typ: "Flag"}, // If Cond then flags = CMNW arg0 arg1 else flags = Nzcv
+ {name: "CCMPWconst", argLength: 2, reg: gp1flagsflags, asm: "CCMPW", aux: "ARM64ConditionalParams", typ: "Flag"}, // If Cond then flags = CCMPWconst [ConstValue] arg0 else flags = Nzcv
+ {name: "CCMNWconst", argLength: 2, reg: gp1flagsflags, asm: "CCMNW", aux: "ARM64ConditionalParams", typ: "Flag"}, // If Cond then flags = CCMNWconst [ConstValue] arg0 else flags = Nzcv
+
// function calls
{name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem
{name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem
@@ -521,87 +557,72 @@ func init() {
{name: "LessThanNoov", argLength: 1, reg: readflags}, // bool, true flags encode signed x=y but without honoring overflow, false otherwise.
- // duffzero
+ // medium zeroing
// arg0 = address of memory to zero
// arg1 = mem
- // auxint = offset into duffzero code to start executing
+ // auxint = # of bytes to zero
// returns mem
- // R20 changed as side effect
- // R16 and R17 may be clobbered by linker trampoline.
{
- name: "DUFFZERO",
+ name: "LoweredZero",
aux: "Int64",
argLength: 2,
reg: regInfo{
- inputs: []regMask{buildReg("R20")},
- clobbers: buildReg("R16 R17 R20 R30"),
+ inputs: []regMask{gp},
},
faultOnNilArg0: true,
- unsafePoint: true, // FP maintenance around DUFFZERO can be clobbered by interrupts
},
// large zeroing
- // arg0 = address of memory to zero (in R16 aka arm64.REGRT1, changed as side effect)
- // arg1 = address of the last 16-byte unit to zero
- // arg2 = mem
+ // arg0 = address of memory to zero
+ // arg1 = mem
+ // auxint = # of bytes to zero
// returns mem
- // STP.P (ZR,ZR), 16(R16)
- // CMP Rarg1, R16
- // BLE -2(PC)
- // Note: the-end-of-the-memory may be not a valid pointer. it's a problem if it is spilled.
- // the-end-of-the-memory - 16 is with the area to zero, ok to spill.
{
- name: "LoweredZero",
- argLength: 3,
+ name: "LoweredZeroLoop",
+ aux: "Int64",
+ argLength: 2,
reg: regInfo{
- inputs: []regMask{buildReg("R16"), gp},
- clobbers: buildReg("R16"),
+ inputs: []regMask{gp},
+ clobbersArg0: true,
},
- clobberFlags: true,
faultOnNilArg0: true,
+ needIntTemp: true,
},
- // duffcopy
- // arg0 = address of dst memory (in R21, changed as side effect)
- // arg1 = address of src memory (in R20, changed as side effect)
+ // medium copying
+ // arg0 = address of dst memory
+ // arg1 = address of src memory
// arg2 = mem
- // auxint = offset into duffcopy code to start executing
+ // auxint = # of bytes to copy
// returns mem
- // R20, R21 changed as side effect
- // R16 and R17 may be clobbered by linker trampoline.
{
- name: "DUFFCOPY",
+ name: "LoweredMove",
aux: "Int64",
argLength: 3,
reg: regInfo{
- inputs: []regMask{buildReg("R21"), buildReg("R20")},
- clobbers: buildReg("R16 R17 R20 R21 R26 R30"),
+ inputs: []regMask{gp &^ r25, gp &^ r25},
+ clobbers: r25 | f16to17, // TODO: figure out needIntTemp + x2 for floats
},
faultOnNilArg0: true,
faultOnNilArg1: true,
- unsafePoint: true, // FP maintenance around DUFFCOPY can be clobbered by interrupts
},
- // large move
- // arg0 = address of dst memory (in R17 aka arm64.REGRT2, changed as side effect)
- // arg1 = address of src memory (in R16 aka arm64.REGRT1, changed as side effect)
- // arg2 = address of the last element of src
- // arg3 = mem
+ // large copying
+ // arg0 = address of dst memory
+ // arg1 = address of src memory
+ // arg2 = mem
+ // auxint = # of bytes to copy
// returns mem
- // LDP.P 16(R16), (R25, Rtmp)
- // STP.P (R25, Rtmp), 16(R17)
- // CMP Rarg2, R16
- // BLE -3(PC)
- // Note: the-end-of-src may be not a valid pointer. it's a problem if it is spilled.
- // the-end-of-src - 16 is within the area to copy, ok to spill.
{
- name: "LoweredMove",
- argLength: 4,
+ name: "LoweredMoveLoop",
+ aux: "Int64",
+ argLength: 3,
reg: regInfo{
- inputs: []regMask{buildReg("R17"), buildReg("R16"), gp &^ buildReg("R25")},
- clobbers: buildReg("R16 R17 R25"),
+ inputs: []regMask{gp &^ r24to25, gp &^ r24to25},
+ clobbers: r24to25 | f16to17, // TODO: figure out needIntTemp x2 + x2 for floats
+ clobbersArg0: true,
+ clobbersArg1: true,
},
- clobberFlags: true,
faultOnNilArg0: true,
faultOnNilArg1: true,
},
@@ -742,12 +763,15 @@ func init() {
// Returns a pointer to a write barrier buffer in R25.
{name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: (callerSave &^ gpg) | buildReg("R16 R17 R30"), outputs: []regMask{buildReg("R25")}}, clobberFlags: true, aux: "Int64"},
- // There are three of these functions so that they can have three different register inputs.
- // When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
- // default registers to match so we don't need to copy registers around unnecessarily.
- {name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r2, r3}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
- {name: "LoweredPanicBoundsB", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r1, r2}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
- {name: "LoweredPanicBoundsC", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r0, r1}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
+ // LoweredPanicBoundsRR takes x and y, two values that caused a bounds check to fail.
+ // the RC and CR versions are used when one of the arguments is a constant. CC is used
+ // when both are constant (normally both 0, as prove derives the fact that a [0] bounds
+ // failure means the length must have also been 0).
+ // AuxInt contains a report code (see PanicBounds in genericOps.go).
+ {name: "LoweredPanicBoundsRR", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{first16, first16}}, typ: "Mem", call: true}, // arg0=x, arg1=y, arg2=mem, returns memory.
+ {name: "LoweredPanicBoundsRC", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{first16}}, typ: "Mem", call: true}, // arg0=x, arg1=mem, returns memory.
+ {name: "LoweredPanicBoundsCR", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{first16}}, typ: "Mem", call: true}, // arg0=y, arg1=mem, returns memory.
+ {name: "LoweredPanicBoundsCC", argLength: 1, aux: "PanicBoundsCC", reg: regInfo{}, typ: "Mem", call: true}, // arg0=mem, returns memory.
// Prefetch instruction
// Do prefetch arg0 address with option aux. arg0=addr, arg1=memory, aux=option.
@@ -755,6 +779,7 @@ func init() {
// Publication barrier
{name: "DMB", argLength: 1, aux: "Int64", asm: "DMB", hasSideEffects: true}, // Do data barrier. arg0=memory, aux=option.
+ {name: "ZERO", zeroWidth: true, fixedReg: true}, // reads-as-zero register
}
blocks := []blockData{
diff --git a/src/cmd/compile/internal/ssa/_gen/ARM64latelower.rules b/src/cmd/compile/internal/ssa/_gen/ARM64latelower.rules
index e50d985aa0..8c43b960b9 100644
--- a/src/cmd/compile/internal/ssa/_gen/ARM64latelower.rules
+++ b/src/cmd/compile/internal/ssa/_gen/ARM64latelower.rules
@@ -85,3 +85,19 @@
(MOVWUreg x:(MOVBUreg _)) => (MOVDreg x)
(MOVWUreg x:(MOVHUreg _)) => (MOVDreg x)
(MOVWUreg x:(MOVWUreg _)) => (MOVDreg x)
+
+// if a register move has only 1 use, just use the same register without emitting instruction
+// MOVDnop doesn't emit instruction, only for ensuring the type.
+(MOVDreg x) && x.Uses == 1 => (MOVDnop x)
+
+// TODO: we should be able to get rid of MOVDnop all together.
+// But for now, this is enough to get rid of lots of them.
+(MOVDnop (MOVDconst [c])) => (MOVDconst [c])
+
+// use zero register
+(MOVDconst [0]) => (ZERO)
+
+// Prefer addition when shifting left by one.
+// They have the same latency, but ADD can often be done
+// by more functional units in the processor.
+(SLLconst [1] x) => (ADD x x)
diff --git a/src/cmd/compile/internal/ssa/_gen/ARMOps.go b/src/cmd/compile/internal/ssa/_gen/ARMOps.go
index 3ad96fcac0..01cd48835e 100644
--- a/src/cmd/compile/internal/ssa/_gen/ARMOps.go
+++ b/src/cmd/compile/internal/ssa/_gen/ARMOps.go
@@ -94,11 +94,11 @@ func init() {
gpspsbg = gpspg | buildReg("SB")
fp = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15")
callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g
+ lr = buildReg("R14")
r0 = buildReg("R0")
r1 = buildReg("R1")
r2 = buildReg("R2")
r3 = buildReg("R3")
- r4 = buildReg("R4")
)
// Common regInfo
var (
@@ -540,16 +540,19 @@ func init() {
// See runtime/stubs.go for a more detailed discussion.
{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
- // There are three of these functions so that they can have three different register inputs.
- // When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
- // default registers to match so we don't need to copy registers around unnecessarily.
- {name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r2, r3}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go).
- {name: "LoweredPanicBoundsB", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r1, r2}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go).
- {name: "LoweredPanicBoundsC", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r0, r1}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go).
- // Extend ops are the same as Bounds ops except the indexes are 64-bit.
- {name: "LoweredPanicExtendA", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r4, r2, r3}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
- {name: "LoweredPanicExtendB", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r4, r1, r2}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
- {name: "LoweredPanicExtendC", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r4, r0, r1}}, typ: "Mem", call: true}, // arg0=idxHi, arg1=idxLo, arg2=len, arg3=mem, returns memory. AuxInt contains report code (see PanicExtend in genericOps.go).
+ // LoweredPanicBoundsRR takes x and y, two values that caused a bounds check to fail.
+ // the RC and CR versions are used when one of the arguments is a constant. CC is used
+ // when both are constant (normally both 0, as prove derives the fact that a [0] bounds
+ // failure means the length must have also been 0).
+ // AuxInt contains a report code (see PanicBounds in genericOps.go).
+ {name: "LoweredPanicBoundsRR", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{gp &^ lr, gp &^ lr}}, typ: "Mem", call: true}, // arg0=x, arg1=y, arg2=mem, returns memory.
+ {name: "LoweredPanicBoundsRC", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{gp &^ lr}}, typ: "Mem", call: true}, // arg0=x, arg1=mem, returns memory.
+ {name: "LoweredPanicBoundsCR", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{gp &^ lr}}, typ: "Mem", call: true}, // arg0=y, arg1=mem, returns memory.
+ {name: "LoweredPanicBoundsCC", argLength: 1, aux: "PanicBoundsCC", reg: regInfo{}, typ: "Mem", call: true}, // arg0=mem, returns memory.
+
+ // Same as above, but the x value is 64 bits.
+ {name: "LoweredPanicExtendRR", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r0 | r1 | r2 | r3, r0 | r1 | r2 | r3, gp}}, typ: "Mem", call: true}, // arg0=x_hi, arg1=x_lo, arg2=y, arg3=mem, returns memory.
+ {name: "LoweredPanicExtendRC", argLength: 3, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{r0 | r1 | r2 | r3, r0 | r1 | r2 | r3}}, typ: "Mem", call: true}, // arg0=x_hi, arg1=x_lo, arg2=mem, returns memory.
// Constant flag value.
// Note: there's an "unordered" outcome for floating-point
diff --git a/src/cmd/compile/internal/ssa/_gen/LOONG64.rules b/src/cmd/compile/internal/ssa/_gen/LOONG64.rules
index 00a0a84f33..287eedee37 100644
--- a/src/cmd/compile/internal/ssa/_gen/LOONG64.rules
+++ b/src/cmd/compile/internal/ssa/_gen/LOONG64.rules
@@ -17,8 +17,8 @@
(Hmul64 ...) => (MULHV ...)
(Hmul64u ...) => (MULHVU ...)
-(Hmul32 x y) => (SRAVconst (MULV (SignExt32to64 x) (SignExt32to64 y)) [32])
-(Hmul32u x y) => (SRLVconst (MULV (ZeroExt32to64 x) (ZeroExt32to64 y)) [32])
+(Hmul32 ...) => (MULH ...)
+(Hmul32u ...) => (MULHU ...)
(Div64 x y) => (DIVV x y)
(Div64u ...) => (DIVVU ...)
@@ -57,65 +57,105 @@
// shifts
// hardware instruction uses only the low 6 bits of the shift
// we compare to 64 to ensure Go semantics for large shifts
-(Lsh64x64 x y) => (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y))
-(Lsh64x32 x y) => (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y)))
-(Lsh64x16 x y) => (MASKEQZ (SLLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y)))
-(Lsh64x8 x y) => (MASKEQZ (SLLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y)))
-(Lsh32x64 x y) => (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y))
-(Lsh32x32 x y) => (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y)))
-(Lsh32x16 x y) => (MASKEQZ (SLLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y)))
-(Lsh32x8 x y) => (MASKEQZ (SLLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y)))
+// left shift
+(Lsh64x(64|32|16|8) x y) && shiftIsBounded(v) => (SLLV x y)
+(Lsh32x(64|32|16|8) x y) && shiftIsBounded(v) => (SLL x y)
+(Lsh16x(64|32|16|8) x y) && shiftIsBounded(v) => (SLLV x y)
+(Lsh8x(64|32|16|8) x y) && shiftIsBounded(v) => (SLLV x y)
-(Lsh16x64 x y) => (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y))
-(Lsh16x32 x y) => (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y)))
-(Lsh16x16 x y) => (MASKEQZ (SLLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y)))
-(Lsh16x8 x y) => (MASKEQZ (SLLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y)))
+(Lsh64x64 x y) && !shiftIsBounded(v) => (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y))
+(Lsh64x32 x y) && !shiftIsBounded(v) => (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y)))
+(Lsh64x16 x y) && !shiftIsBounded(v) => (MASKEQZ (SLLV x (ZeroExt16to64 y)) (SGTU (MOVVconst [64]) (ZeroExt16to64 y)))
+(Lsh64x8 x y) && !shiftIsBounded(v) => (MASKEQZ (SLLV x (ZeroExt8to64 y)) (SGTU (MOVVconst [64]) (ZeroExt8to64 y)))
-(Lsh8x64 x y) => (MASKEQZ (SLLV x y) (SGTU (MOVVconst [64]) y))
-(Lsh8x32 x y) => (MASKEQZ (SLLV x (ZeroExt32to64 y)) (SGTU (MOVVconst [64]) (ZeroExt32to64 y)))
-(Lsh8x16