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

Change-Id: Ia6964cb4e09153c15cc9c5b441373d1b3cb8f757
This commit is contained in:
Rob Findley 2020-09-11 14:23:34 -04:00
commit f8b1c17ace
401 changed files with 15472 additions and 12109 deletions

View File

@ -3,3 +3,17 @@ pkg unicode, var Chorasmian *RangeTable
pkg unicode, var Dives_Akuru *RangeTable
pkg unicode, var Khitan_Small_Script *RangeTable
pkg unicode, var Yezidi *RangeTable
pkg text/template/parse, const NodeComment = 20
pkg text/template/parse, const NodeComment NodeType
pkg text/template/parse, const ParseComments = 1
pkg text/template/parse, const ParseComments Mode
pkg text/template/parse, method (*CommentNode) Copy() Node
pkg text/template/parse, method (*CommentNode) String() string
pkg text/template/parse, method (CommentNode) Position() Pos
pkg text/template/parse, method (CommentNode) Type() NodeType
pkg text/template/parse, type CommentNode struct
pkg text/template/parse, type CommentNode struct, Text string
pkg text/template/parse, type CommentNode struct, embedded NodeType
pkg text/template/parse, type CommentNode struct, embedded Pos
pkg text/template/parse, type Mode uint
pkg text/template/parse, type Tree struct, Mode Mode

View File

@ -687,6 +687,13 @@ MOVQ g(CX), AX // Move g into AX.
MOVQ g_m(AX), BX // Move g.m into BX.
</pre>
<p>
Register <code>BP</code> is callee-save.
The assembler automatically inserts <code>BP</code> save/restore when frame size is larger than zero.
Using <code>BP</code> as a general purpose register is allowed,
however it can interfere with sampling-based profiling.
</p>
<h3 id="arm">ARM</h3>
<p>

View File

@ -609,6 +609,12 @@ Do not send CLs removing the interior tags from such phrases.
If a program needs to accept invalid numbers like the empty string,
consider wrapping the type with <a href="/pkg/encoding/json/#Unmarshaler"><code>Unmarshaler</code></a>.
</p>
<p><!-- CL 200237 -->
<a href="/pkg/encoding/json/#Unmarshal"><code>Unmarshal</code></a>
can now support map keys with string underlying type which implement
<a href="/pkg/encoding/#TextUnmarshaler"><code>encoding.TextUnmarshaler</code></a>.
</p>
</dd>
</dl><!-- encoding/json -->

View File

@ -43,6 +43,37 @@ Do not send CLs removing the interior tags from such phrases.
<h3 id="go-command">Go command</h3>
<p><!-- golang.org/issue/24031 -->
<code>retract</code> directives may now be used in a <code>go.mod</code> file
to indicate that certain published versions of the module should not be used
by other modules. A module author may retract a version after a severe problem
is discovered or if the version was published unintentionally.<br>
TODO: write and link to section in golang.org/ref/mod<br>
TODO: write and link to tutorial or blog post
</p>
<p><!-- golang.org/issue/29062 -->
When using <code>go test</code>, a test that
calls <code>os.Exit(0)</code> during execution of a test function
will now be considered to fail.
This will help catch cases in which a test calls code that calls
os.Exit(0) and thereby stops running all future tests.
If a <code>TestMain</code> function calls <code>os.Exit(0)</code>
that is still considered to be a passing test.
</p>
<h4 id="all-pattern">The <code>all</code> pattern</h4>
<p><!-- golang.org/cl/240623 -->
When the main module's <code>go.mod</code> file
declares <code>go</code> <code>1.16</code> or higher, the <code>all</code>
package pattern now matches only those packages that are transitively imported
by a package or test found in the main module. (Packages imported by <em>tests
of</em> packages imported by the main module are no longer included.) This is
the same set of packages retained
by <code>go</code> <code>mod</code> <code>vendor</code> since Go 1.11.
</p>
<p>
TODO
</p>
@ -90,6 +121,28 @@ Do not send CLs removing the interior tags from such phrases.
TODO
</p>
<h3 id="net"><a href="/pkg/net/">net</a></h3>
<p><!-- CL 250357 -->
The case of I/O on a closed network connection, or I/O on a network
connection that is closed before any of the I/O completes, can now
be detected using the new <a href="/pkg/net/#ErrClosed">ErrClosed</a> error.
A typical use would be <code>errors.Is(err, net.ErrClosed)</code>.
In earlier releases the only way to reliably detect this case was to
match the string returned by the <code>Error</code> method
with <code>"use of closed network connection"</code>.
</p>
<h3 id="text/template/parse"><a href="/pkg/text/template/parse/">text/template/parse</a></h3>
<p><!-- CL 229398, golang.org/issue/34652 -->
A new <a href="/pkg/text/template/parse/#CommentNode"><code>CommentNode</code></a>
was added to the parse tree. The <a href="/pkg/text/template/parse/#Mode"><code>Mode</code></a>
field in the <code>parse.Tree</code> enables access to it.
</p>
<!-- text/template/parse -->
<h3 id="unicode"><a href="/pkg/unicode/">unicode</a></h3>
<p><!-- CL 248765 -->
@ -112,3 +165,27 @@ Do not send CLs removing the interior tags from such phrases.
<p>
TODO
</p>
<dl id="net/http"><dt><a href="/pkg/net/http/">net/http</a></dt>
<dd>
<p><!-- CL 233637 -->
In the <a href="/pkg/net/http/"><code>net/http</code></a> package, the
behavior of <a href="/pkg/net/http/#StripPrefix"><code>StripPrefix</code></a>
has been changed to strip the prefix from the request URL's
<code>RawPath</code> field in addition to its <code>Path</code> field.
In past releases, only the <code>Path</code> field was trimmed, and so if the
request URL contained any escaped characters the URL would be modified to
have mismatched <code>Path</code> and <code>RawPath</code> fields.
In Go 1.16, <code>StripPrefix</code> trims both fields.
If there are escaped characters in the prefix part of the request URL the
handler serves a 404 instead of its previous behavior of invoking the
underlying handler with a mismatched <code>Path</code>/<code>RawPath</code> pair.
</p>
<p><!-- CL 252497 -->
The <a href="/pkg/net/http/"><code>net/http</code></a> package now rejects HTTP range requests
of the form <code>"Range": "bytes=--N"</code> where <code>"-N"</code> is a negative suffix length, for
example <code>"Range": "bytes=--2"</code>. It now replies with a <code>416 "Range Not Satisfiable"</code> response.
</p>
</dd>
</dl><!-- net/http -->

View File

@ -600,6 +600,9 @@ The valid combinations of <code>$GOOS</code> and <code>$GOARCH</code> are:
<td></td><td><code>linux</code></td> <td><code>mips64le</code></td>
</tr>
<tr>
<td></td><td><code>linux</code></td> <td><code>riscv64</code></td>
</tr>
<tr>
<td></td><td><code>linux</code></td> <td><code>s390x</code></td>
</tr>
<tr>

View File

@ -319,6 +319,7 @@ typedef enum {
// issue 4339
// We've historically permitted #include <>, so test it here. Issue 29333.
// Also see issue 41059.
#include <issue4339.h>
// issue 4417

View File

@ -11,6 +11,7 @@
// - Node.js
// - Electron
// - Parcel
// - Webpack
if (typeof global !== "undefined") {
// global already exists
@ -28,7 +29,7 @@
if (!global.fs && global.require) {
const fs = require("fs");
if (Object.keys(fs) !== 0) {
if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) {
global.fs = fs;
}
}
@ -556,6 +557,7 @@
}
if (
typeof module !== "undefined" &&
global.require &&
global.require.main === module &&
global.process &&

View File

@ -145,6 +145,37 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
VZIP2 V10.D2, V13.D2, V3.D2 // a379ca4e
VZIP1 V17.S2, V4.S2, V26.S2 // 9a38910e
VZIP2 V25.S2, V14.S2, V25.S2 // d979990e
VUXTL V30.B8, V30.H8 // dea7082f
VUXTL V30.H4, V29.S4 // dda7102f
VUXTL V29.S2, V2.D2 // a2a7202f
VUXTL2 V30.H8, V30.S4 // dea7106f
VUXTL2 V29.S4, V2.D2 // a2a7206f
VUXTL2 V30.B16, V2.H8 // c2a7086f
VBIT V21.B16, V25.B16, V4.B16 // 241fb56e
VBSL V23.B16, V3.B16, V7.B16 // 671c776e
VCMTST V2.B8, V29.B8, V2.B8 // a28f220e
VCMTST V2.D2, V23.D2, V3.D2 // e38ee24e
VSUB V2.B8, V30.B8, V30.B8 // de87222e
VUZP1 V0.B8, V30.B8, V1.B8 // c11b000e
VUZP1 V1.B16, V29.B16, V2.B16 // a21b014e
VUZP1 V2.H4, V28.H4, V3.H4 // 831b420e
VUZP1 V3.H8, V27.H8, V4.H8 // 641b434e
VUZP1 V28.S2, V2.S2, V5.S2 // 45189c0e
VUZP1 V29.S4, V1.S4, V6.S4 // 26189d4e
VUZP1 V30.D2, V0.D2, V7.D2 // 0718de4e
VUZP2 V0.D2, V30.D2, V1.D2 // c15bc04e
VUZP2 V30.D2, V0.D2, V29.D2 // 1d58de4e
VUSHLL $0, V30.B8, V30.H8 // dea7082f
VUSHLL $0, V30.H4, V29.S4 // dda7102f
VUSHLL $0, V29.S2, V2.D2 // a2a7202f
VUSHLL2 $0, V30.B16, V2.H8 // c2a7086f
VUSHLL2 $0, V30.H8, V30.S4 // dea7106f
VUSHLL2 $0, V29.S4, V2.D2 // a2a7206f
VUSHLL $7, V30.B8, V30.H8 // dea70f2f
VUSHLL $15, V30.H4, V29.S4 // dda71f2f
VUSHLL2 $31, V30.S4, V2.D2 // c2a73f6f
VBIF V0.B8, V30.B8, V1.B8 // c11fe02e
VBIF V30.B16, V0.B16, V2.B16 // 021cfe6e
MOVD (R2)(R6.SXTW), R4 // 44c866f8
MOVD (R3)(R6), R5 // MOVD (R3)(R6*1), R5 // 656866f8
MOVD (R2)(R6), R4 // MOVD (R2)(R6*1), R4 // 446866f8
@ -186,6 +217,10 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
FMOVS $(0.96875), F3 // 03f02d1e
FMOVD $(28.0), F4 // 0490671e
// move a large constant to a Vd.
FMOVD $0x8040201008040201, V20 // FMOVD $-9205322385119247871, V20
FMOVQ $0x8040201008040202, V29 // FMOVQ $-9205322385119247870, V29
FMOVS (R2)(R6), F4 // FMOVS (R2)(R6*1), F4 // 446866bc
FMOVS (R2)(R6<<2), F4 // 447866bc
FMOVD (R2)(R6), F4 // FMOVD (R2)(R6*1), F4 // 446866fc
@ -359,18 +394,22 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
VLD4 (R15), [V10.H4, V11.H4, V12.H4, V13.H4] // ea05400c
VLD4.P 32(R24), [V31.B8, V0.B8, V1.B8, V2.B8] // 1f03df0c
VLD4.P (R13)(R9), [V14.S2, V15.S2, V16.S2, V17.S2] // VLD4.P (R13)(R9*1), [V14.S2,V15.S2,V16.S2,V17.S2] // ae09c90c
VLD1R (R0), [V0.B16] // 00c0404d
VLD1R.P 16(R0), [V0.B16] // 00c0df4d
VLD1R.P (R15)(R1), [V15.H4] // VLD1R.P (R15)(R1*1), [V15.H4] // efc5c10d
VLD2R (R15), [V15.H4, V16.H4] // efc5600d
VLD2R.P 32(R0), [V0.D2, V1.D2] // 00ccff4d
VLD2R.P (R0)(R5), [V31.D1, V0.D1] // VLD2R.P (R0)(R5*1), [V31.D1, V0.D1] // 1fcce50d
VLD3R (RSP), [V31.S2, V0.S2, V1.S2] // ffeb400d
VLD3R.P 24(R15), [V15.H4, V16.H4, V17.H4] // efe5df0d
VLD3R.P (R15)(R6), [V15.H8, V16.H8, V17.H8] // VLD3R.P (R15)(R6*1), [V15.H8, V16.H8, V17.H8] // efe5c64d
VLD4R (R0), [V0.B8, V1.B8, V2.B8, V3.B8] // 00e0600d
VLD4R.P 64(RSP), [V31.S4, V0.S4, V1.S4, V2.S4] // ffebff4d
VLD4R.P (R15)(R9), [V15.H4, V16.H4, V17.H4, V18.H4] // VLD4R.P (R15)(R9*1), [V15.H4, V16.H4, V17.H4, V18.H4] // efe5e90d
VLD1R (R1), [V9.B8] // 29c0400d
VLD1R.P (R1), [V9.B8] // 29c0df0d
VLD1R.P 1(R1), [V2.B8] // 22c0df0d
VLD1R.P 2(R1), [V2.H4] // 22c4df0d
VLD1R (R0), [V0.B16] // 00c0404d
VLD1R.P (R0), [V0.B16] // 00c0df4d
VLD1R.P (R15)(R1), [V15.H4] // VLD1R.P (R15)(R1*1), [V15.H4] // efc5c10d
VLD2R (R15), [V15.H4, V16.H4] // efc5600d
VLD2R.P 16(R0), [V0.D2, V1.D2] // 00ccff4d
VLD2R.P (R0)(R5), [V31.D1, V0.D1] // VLD2R.P (R0)(R5*1), [V31.D1, V0.D1] // 1fcce50d
VLD3R (RSP), [V31.S2, V0.S2, V1.S2] // ffeb400d
VLD3R.P 6(R15), [V15.H4, V16.H4, V17.H4] // efe5df0d
VLD3R.P (R15)(R6), [V15.H8, V16.H8, V17.H8] // VLD3R.P (R15)(R6*1), [V15.H8, V16.H8, V17.H8] // efe5c64d
VLD4R (R0), [V0.B8, V1.B8, V2.B8, V3.B8] // 00e0600d
VLD4R.P 16(RSP), [V31.S4, V0.S4, V1.S4, V2.S4] // ffebff4d
VLD4R.P (R15)(R9), [V15.H4, V16.H4, V17.H4, V18.H4] // VLD4R.P (R15)(R9*1), [V15.H4, V16.H4, V17.H4, V18.H4] // efe5e90d
VST1.P [V24.S2], 8(R2) // 58789f0c
VST1 [V29.S2, V30.S2], (R29) // bdab000c
VST1 [V14.H4, V15.H4, V16.H4], (R27) // 6e67000c

View File

@ -591,7 +591,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8
FMOVS R8, F15 // 0f01271e
FMOVD F2, F9 // 4940601e
FMOVS F4, F27 // 9b40201e
//TODO VFMOV $3.125, V8.2D // 28f5006f
//TODO VFMOV $3.125, V8.D2 // 28f5006f
FMSUBS F13, F21, F13, F19 // b3d50d1f
FMSUBD F11, F7, F15, F31 // ff9d4b1f
//TODO VFMUL V9.S[2], F21, F19 // b39a895f
@ -648,7 +648,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8
FSUBS F25, F23, F0 // e03a391e
FSUBD F11, F13, F24 // b8396b1e
//TODO SCVTFSS F30, F20 // d4db215e
//TODO VSCVTF V7.2S, V17.2S // f1d8210e
//TODO VSCVTF V7.S2, V17.S2 // f1d8210e
SCVTFWS R3, F16 // 7000221e
SCVTFWD R20, F4 // 8402621e
SCVTFS R16, F12 // 0c02229e

View File

@ -339,4 +339,18 @@ TEXT errors(SB),$0
MRS ICV_EOIR1_EL1, R3 // ERROR "system register is not readable"
MRS PMSWINC_EL0, R3 // ERROR "system register is not readable"
MRS OSLAR_EL1, R3 // ERROR "system register is not readable"
VLD3R.P 24(R15), [V15.H4,V16.H4,V17.H4] // ERROR "invalid post-increment offset"
VBIT V1.H4, V12.H4, V3.H4 // ERROR "invalid arrangement"
VBSL V1.D2, V12.D2, V3.D2 // ERROR "invalid arrangement"
VUXTL V30.D2, V30.H8 // ERROR "operand mismatch"
VUXTL2 V20.B8, V21.H8 // ERROR "operand mismatch"
VUXTL V3.D2, V4.B8 // ERROR "operand mismatch"
VUZP1 V0.B8, V30.B8, V1.B16 // ERROR "operand mismatch"
VUZP2 V0.Q1, V30.Q1, V1.Q1 // ERROR "invalid arrangement"
VUSHLL $0, V30.D2, V30.H8 // ERROR "operand mismatch"
VUSHLL2 $0, V20.B8, V21.H8 // ERROR "operand mismatch"
VUSHLL $8, V30.B8, V30.H8 // ERROR "shift amount out of range"
VUSHLL2 $32, V30.S4, V2.D2 // ERROR "shift amount out of range"
VBIF V0.B8, V1.B8, V2.B16 // ERROR "operand mismatch"
VBIF V0.D2, V1.D2, V2.D2 // ERROR "invalid arrangement"
RET

View File

@ -112,6 +112,13 @@ The default C and C++ compilers may be changed by the CC and CXX
environment variables, respectively; those environment variables
may include command line options.
The cgo tool will always invoke the C compiler with the source file's
directory in the include path; i.e. -I${SRCDIR} is always implied. This
means that if a header file foo/bar.h exists both in the source
directory and also in the system include directory (or some other place
specified by a -I flag), then "#include <foo/bar.h>" will always find the
local version in preference to any other version.
The cgo tool is enabled by default for native builds on systems where
it is expected to work. It is disabled by default when
cross-compiling. You can control this by setting the CGO_ENABLED

View File

@ -369,7 +369,18 @@ func (p *Package) guessKinds(f *File) []*Name {
fmt.Fprintf(&b, "#line 1 \"completed\"\n"+
"int __cgo__1 = __cgo__2;\n")
stderr := p.gccErrors(b.Bytes())
// We need to parse the output from this gcc command, so ensure that it
// doesn't have any ANSI escape sequences in it. (TERM=dumb is
// insufficient; if the user specifies CGO_CFLAGS=-fdiagnostics-color,
// GCC will ignore TERM, and GCC can also be configured at compile-time
// to ignore TERM.)
stderr := p.gccErrors(b.Bytes(), "-fdiagnostics-color=never")
if strings.Contains(stderr, "unrecognized command line option") {
// We're using an old version of GCC that doesn't understand
// -fdiagnostics-color. Those versions can't print color anyway,
// so just rerun without that option.
stderr = p.gccErrors(b.Bytes())
}
if stderr == "" {
fatalf("%s produced no output\non input:\n%s", p.gccBaseCmd()[0], b.Bytes())
}
@ -1970,22 +1981,25 @@ func (p *Package) gccDefines(stdin []byte) string {
// gccErrors runs gcc over the C program stdin and returns
// the errors that gcc prints. That is, this function expects
// gcc to fail.
func (p *Package) gccErrors(stdin []byte) string {
func (p *Package) gccErrors(stdin []byte, extraArgs ...string) string {
// TODO(rsc): require failure
args := p.gccCmd()
// Optimization options can confuse the error messages; remove them.
nargs := make([]string, 0, len(args))
nargs := make([]string, 0, len(args)+len(extraArgs))
for _, arg := range args {
if !strings.HasPrefix(arg, "-O") {
nargs = append(nargs, arg)
}
}
// Force -O0 optimization but keep the trailing "-" at the end.
nargs = append(nargs, "-O0")
nl := len(nargs)
nargs[nl-2], nargs[nl-1] = nargs[nl-1], nargs[nl-2]
// Force -O0 optimization and append extra arguments, but keep the
// trailing "-" at the end.
li := len(nargs) - 1
last := nargs[li]
nargs[li] = "-O0"
nargs = append(nargs, extraArgs...)
nargs = append(nargs, last)
if *debugGcc {
fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(nargs, " "))

View File

@ -319,8 +319,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// TODO(khr): issue only the -1 fixup code we need.
// For instance, if only the quotient is used, no point in zeroing the remainder.
j1.To.Val = n1
j2.To.Val = s.Pc()
j1.To.SetTarget(n1)
j2.To.SetTarget(s.Pc())
}
case ssa.OpAMD64HMULQ, ssa.OpAMD64HMULL, ssa.OpAMD64HMULQU, ssa.OpAMD64HMULLU:

View File

@ -816,7 +816,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
}
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg
p.From.Reg = condBits[v.Aux.(ssa.Op)]
p.From.Reg = condBits[ssa.Op(v.AuxInt)]
p.Reg = v.Args[0].Reg()
p.SetFrom3(obj.Addr{Type: obj.TYPE_REG, Reg: r1})
p.To.Type = obj.TYPE_REG

View File

@ -429,8 +429,7 @@ func hashfor(t *types.Type) *Node {
}
n := newname(sym)
n.SetClass(PFUNC)
n.Sym.SetFunc(true)
setNodeNameFunc(n)
n.Type = functype(nil, []*Node{
anonfield(types.NewPtr(t)),
anonfield(types.Types[TUINTPTR]),
@ -646,17 +645,11 @@ func geneq(t *types.Type) *obj.LSym {
// Build a list of conditions to satisfy.
// The conditions are a list-of-lists. Conditions are reorderable
// within each inner list. The outer lists must be evaluated in order.
// Even within each inner list, track their order so that we can preserve
// aspects of that order. (TODO: latter part needed?)
type nodeIdx struct {
n *Node
idx int
}
var conds [][]nodeIdx
conds = append(conds, []nodeIdx{})
var conds [][]*Node
conds = append(conds, []*Node{})
and := func(n *Node) {
i := len(conds) - 1
conds[i] = append(conds[i], nodeIdx{n: n, idx: len(conds[i])})
conds[i] = append(conds[i], n)
}
// Walk the struct using memequal for runs of AMEM
@ -674,7 +667,7 @@ func geneq(t *types.Type) *obj.LSym {
if !IsRegularMemory(f.Type) {
if EqCanPanic(f.Type) {
// Enforce ordering by starting a new set of reorderable conditions.
conds = append(conds, []nodeIdx{})
conds = append(conds, []*Node{})
}
p := nodSym(OXDOT, np, f.Sym)
q := nodSym(OXDOT, nq, f.Sym)
@ -688,7 +681,7 @@ func geneq(t *types.Type) *obj.LSym {
}
if EqCanPanic(f.Type) {
// Also enforce ordering after something that can panic.
conds = append(conds, []nodeIdx{})
conds = append(conds, []*Node{})
}
i++
continue
@ -713,14 +706,13 @@ func geneq(t *types.Type) *obj.LSym {
// Sort conditions to put runtime calls last.
// Preserve the rest of the ordering.
var flatConds []nodeIdx
var flatConds []*Node
for _, c := range conds {
isCall := func(n *Node) bool {
return n.Op == OCALL || n.Op == OCALLFUNC
}
sort.SliceStable(c, func(i, j int) bool {
x, y := c[i], c[j]
if (x.n.Op != OCALL) == (y.n.Op != OCALL) {
return x.idx < y.idx
}
return x.n.Op != OCALL
return !isCall(c[i]) && isCall(c[j])
})
flatConds = append(flatConds, c...)
}
@ -729,9 +721,9 @@ func geneq(t *types.Type) *obj.LSym {
if len(flatConds) == 0 {
cond = nodbool(true)
} else {
cond = flatConds[0].n
cond = flatConds[0]
for _, c := range flatConds[1:] {
cond = nod(OANDAND, cond, c.n)
cond = nod(OANDAND, cond, c)
}
}

View File

@ -107,18 +107,7 @@ func typecheckclosure(clo *Node, top int) {
}
xfunc.Func.Nname.Sym = closurename(Curfn)
disableExport(xfunc.Func.Nname.Sym)
if xfunc.Func.Nname.Sym.Def != nil {
// The only case we can reach here is when the outer function was redeclared.
// In that case, don't bother to redeclare the closure. Otherwise, we will get
// a spurious error message, see #17758. While we are here, double check that
// we already reported other error.
if nsavederrors+nerrors == 0 {
Fatalf("unexpected symbol collision %v", xfunc.Func.Nname.Sym)
}
} else {
declare(xfunc.Func.Nname, PFUNC)
}
setNodeNameFunc(xfunc.Func.Nname)
xfunc = typecheck(xfunc, ctxStmt)
// Type check the body now, but only if we're inside a function.
@ -473,7 +462,6 @@ func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node {
tfn.List.Set(structargs(t0.Params(), true))
tfn.Rlist.Set(structargs(t0.Results(), false))
disableExport(sym)
xfunc := dclfunc(sym, tfn)
xfunc.Func.SetDupok(true)
xfunc.Func.SetNeedctxt(true)

View File

@ -90,7 +90,7 @@ func declare(n *Node, ctxt Class) {
lineno = n.Pos
Fatalf("automatic outside function")
}
if Curfn != nil {
if Curfn != nil && ctxt != PFUNC {
Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
}
if n.Op == OTYPE {
@ -297,6 +297,16 @@ func oldname(s *types.Sym) *Node {
return n
}
// importName is like oldname, but it reports an error if sym is from another package and not exported.
func importName(sym *types.Sym) *Node {
n := oldname(sym)
if !types.IsExported(sym.Name) && sym.Pkg != localpkg {
n.SetDiag(true)
yyerror("cannot refer to unexported name %s.%s", sym.Pkg.Name, sym.Name)
}
return n
}
// := declarations
func colasname(n *Node) bool {
switch n.Op {
@ -975,10 +985,14 @@ func makefuncsym(s *types.Sym) {
}
}
// disableExport prevents sym from being included in package export
// data. To be effectual, it must be called before declare.
func disableExport(sym *types.Sym) {
sym.SetOnExportList(true)
// setNodeNameFunc marks a node as a function.
func setNodeNameFunc(n *Node) {
if n.Op != ONAME || n.Class() != Pxxx {
Fatalf("expected ONAME/Pxxx node, got %v", n)
}
n.SetClass(PFUNC)
n.Sym.SetFunc(true)
}
func dclfunc(sym *types.Sym, tfn *Node) *Node {
@ -990,7 +1004,7 @@ func dclfunc(sym *types.Sym, tfn *Node) *Node {
fn.Func.Nname = newfuncnamel(lineno, sym)
fn.Func.Nname.Name.Defn = fn
fn.Func.Nname.Name.Param.Ntype = tfn
declare(fn.Func.Nname, PFUNC)
setNodeNameFunc(fn.Func.Nname)
funchdr(fn)
fn.Func.Nname.Name.Param.Ntype = typecheck(fn.Func.Nname.Name.Param.Ntype, ctxType)
return fn

View File

@ -377,7 +377,7 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
// This really doesn't have much to do with escape analysis per se,
// but we are reusing the ability to annotate an individual function
// argument and pass those annotations along to importing code.
if f.Type.Etype == TUINTPTR {
if f.Type.IsUintptr() {
if Debug['m'] != 0 {
Warnl(f.Pos, "assuming %v is unsafe uintptr", name())
}
@ -407,13 +407,13 @@ func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
}
if fn.Func.Pragma&UintptrEscapes != 0 {
if f.Type.Etype == TUINTPTR {
if f.Type.IsUintptr() {
if Debug['m'] != 0 {
Warnl(f.Pos, "marking %v as escaping uintptr", name())
}
return uintptrEscapesTag
}
if f.IsDDD() && f.Type.Elem().Etype == TUINTPTR {
if f.IsDDD() && f.Type.Elem().IsUintptr() {
// final argument is ...uintptr.
if Debug['m'] != 0 {
Warnl(f.Pos, "marking %v as escaping ...uintptr", name())

View File

@ -485,7 +485,7 @@ func (e *Escape) exprSkipInit(k EscHole, n *Node) {
e.discard(max)
case OCONV, OCONVNOP:
if checkPtr(e.curfn, 2) && n.Type.Etype == TUNSAFEPTR && n.Left.Type.IsPtr() {
if checkPtr(e.curfn, 2) && n.Type.IsUnsafePtr() && n.Left.Type.IsPtr() {
// When -d=checkptr=2 is enabled, treat
// conversions to unsafe.Pointer as an
// escaping operation. This allows better
@ -493,7 +493,7 @@ func (e *Escape) exprSkipInit(k EscHole, n *Node) {
// easily detect object boundaries on the heap
// than the stack.
e.assignHeap(n.Left, "conversion to unsafe.Pointer", n)
} else if n.Type.Etype == TUNSAFEPTR && n.Left.Type.Etype == TUINTPTR {
} else if n.Type.IsUnsafePtr() && n.Left.Type.IsUintptr() {
e.unsafeValue(k, n.Left)
} else {
e.expr(k, n.Left)
@ -625,7 +625,7 @@ func (e *Escape) unsafeValue(k EscHole, n *Node) {
switch n.Op {
case OCONV, OCONVNOP:
if n.Left.Type.Etype == TUNSAFEPTR {
if n.Left.Type.IsUnsafePtr() {
e.expr(k, n.Left)
} else {
e.discard(n.Left)
@ -1029,6 +1029,9 @@ func (e *Escape) newLoc(n *Node, transient bool) *EscLocation {
if e.curfn == nil {
Fatalf("e.curfn isn't set")
}
if n != nil && n.Type != nil && n.Type.NotInHeap() {
yyerrorl(n.Pos, "%v is go:notinheap; stack allocation disallowed", n.Type)
}
n = canonicalNode(n)
loc := &EscLocation{

View File

@ -1616,7 +1616,8 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) {
}
n1.exprfmt(s, nprec, mode)
}
case ODDD:
mode.Fprintf(s, "...")
default:
mode.Fprintf(s, "<node %v>", n.Op)
}

View File

@ -342,6 +342,6 @@ func Patch(p *obj.Prog, to *obj.Prog) {
if p.To.Type != obj.TYPE_BRANCH {
Fatalf("patch: not a branch")
}
p.To.Val = to
p.To.SetTarget(to)
p.To.Offset = to.Pc
}

View File

@ -45,7 +45,6 @@ func fninit(n []*Node) {
if len(nf) > 0 {
lineno = nf[0].Pos // prolog/epilog gets line number of first init stmt
initializers := lookup("init")
disableExport(initializers)
fn := dclfunc(initializers, nod(OTFUNC, nil, nil))
for _, dcl := range dummyInitFn.Func.Dcl {
dcl.Name.Curfn = fn

View File

@ -653,7 +653,7 @@ func (p *noder) expr(expr syntax.Expr) *Node {
obj := p.expr(expr.X)
if obj.Op == OPACK {
obj.Name.SetUsed(true)
return oldname(restrictlookup(expr.Sel.Value, obj.Name.Pkg))
return importName(obj.Name.Pkg.Lookup(expr.Sel.Value))
}
n := nodSym(OXDOT, obj, p.name(expr.Sel))
n.Pos = p.pos(expr) // lineno may have been changed by p.expr(expr.X)
@ -857,7 +857,7 @@ func (p *noder) interfaceType(expr *syntax.InterfaceType) *Node {
p.setlineno(method)
var n *Node
if method.Name == nil {
n = p.nodSym(method, ODCLFIELD, oldname(p.packname(method.Type)), nil)
n = p.nodSym(method, ODCLFIELD, importName(p.packname(method.Type)), nil)
} else {
mname := p.name(method.Name)
sig := p.typeExpr(method.Type)
@ -896,7 +896,7 @@ func (p *noder) packname(expr syntax.Expr) *types.Sym {
def.Name.SetUsed(true)
pkg = def.Name.Pkg
}
return restrictlookup(expr.Sel.Value, pkg)
return pkg.Lookup(expr.Sel.Value)
}
panic(fmt.Sprintf("unexpected packname: %#v", expr))
}
@ -911,7 +911,7 @@ func (p *noder) embedded(typ syntax.Expr) *Node {
}
sym := p.packname(typ)
n := p.nodSym(typ, ODCLFIELD, oldname(sym), lookup(sym.Name))
n := p.nodSym(typ, ODCLFIELD, importName(sym), lookup(sym.Name))
n.SetEmbedded(true)
if isStar {
@ -1641,10 +1641,3 @@ func mkname(sym *types.Sym) *Node {
}
return n
}
func unparen(x *Node) *Node {
for x.Op == OPAREN {
x = x.Left
}
return x
}

View File

@ -502,6 +502,7 @@ func (o *Order) call(n *Node) {
x := o.copyExpr(arg.Left, arg.Left.Type, false)
x.Name.SetKeepalive(true)
arg.Left = x
n.SetNeedsWrapper(true)
}
}

View File

@ -507,7 +507,7 @@ func createSimpleVar(fnsym *obj.LSym, n *Node) *dwarf.Var {
if Ctxt.FixedFrameSize() == 0 {
offs -= int64(Widthptr)
}
if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) || objabi.GOARCH == "arm64" {
if objabi.Framepointer_enabled || objabi.GOARCH == "arm64" {
// There is a word space for FP on ARM64 even if the frame pointer is disabled
offs -= int64(Widthptr)
}
@ -703,7 +703,7 @@ func stackOffset(slot ssa.LocalSlot) int32 {
if Ctxt.FixedFrameSize() == 0 {
base -= int64(Widthptr)
}
if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) || objabi.GOARCH == "arm64" {
if objabi.Framepointer_enabled || objabi.GOARCH == "arm64" {
// There is a word space for FP on ARM64 even if the frame pointer is disabled
base -= int64(Widthptr)
}

View File

@ -20,7 +20,7 @@ func typeWithoutPointers() *types.Type {
func typeWithPointers() *types.Type {
t := types.New(TSTRUCT)
f := &types.Field{Type: types.New(TPTR)}
f := &types.Field{Type: types.NewPtr(types.New(TINT))}
t.SetFields([]*types.Field{f})
return t
}
@ -181,14 +181,6 @@ func TestStackvarSort(t *testing.T) {
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "xyz"}}, PAUTO),
nodeWithClass(Node{Type: typeWithoutPointers(), Sym: &types.Sym{}}, PAUTO),
}
// haspointers updates Type.Haspointers as a side effect, so
// exercise this function on all inputs so that reflect.DeepEqual
// doesn't produce false positives.
for i := range want {
want[i].Type.HasPointers()
inp[i].Type.HasPointers()
}
sort.Sort(byStackVar(inp))
if !reflect.DeepEqual(want, inp) {
t.Error("sort failed")

View File

@ -436,7 +436,7 @@ func (lv *Liveness) regEffects(v *ssa.Value) (uevar, kill liveRegMask) {
case ssa.LocalSlot:
return mask
case *ssa.Register:
if ptrOnly && !v.Type.HasHeapPointer() {
if ptrOnly && !v.Type.HasPointers() {
return mask
}
regs[0] = loc
@ -451,7 +451,7 @@ func (lv *Liveness) regEffects(v *ssa.Value) (uevar, kill liveRegMask) {
if loc1 == nil {
continue
}
if ptrOnly && !v.Type.FieldType(i).HasHeapPointer() {
if ptrOnly && !v.Type.FieldType(i).HasPointers() {
continue
}
regs[nreg] = loc1.(*ssa.Register)
@ -568,13 +568,13 @@ func onebitwalktype1(t *types.Type, off int64, bv bvec) {
if t.Align > 0 && off&int64(t.Align-1) != 0 {
Fatalf("onebitwalktype1: invalid initial alignment: type %v has alignment %d, but offset is %v", t, t.Align, off)
}
if !t.HasPointers() {
// Note: this case ensures that pointers to go:notinheap types
// are not considered pointers by garbage collection and stack copying.
return
}
switch t.Etype {
case TINT8, TUINT8, TINT16, TUINT16,
TINT32, TUINT32, TINT64, TUINT64,
TINT, TUINT, TUINTPTR, TBOOL,
TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128:
case TPTR, TUNSAFEPTR, TFUNC, TCHAN, TMAP:
if off&int64(Widthptr-1) != 0 {
Fatalf("onebitwalktype1: invalid alignment, %v", t)

View File

@ -586,7 +586,7 @@ func arrayClear(n, v1, v2, a *Node) bool {
n.Nbody.Append(nod(OAS, hn, tmp))
var fn *Node
if a.Type.Elem().HasHeapPointer() {
if a.Type.Elem().HasPointers() {
// memclrHasPointers(hp, hn)
Curfn.Func.setWBPos(stmt.Pos)
fn = mkcall("memclrHasPointers", nil, nil, hp, hn)

View File

@ -251,10 +251,8 @@ func walkselectcases(cases *Nodes) []*Node {
r = typecheck(r, ctxStmt)
init = append(init, r)
// No initialization for order; runtime.selectgo is responsible for that.
order := temp(types.NewArray(types.Types[TUINT16], 2*int64(ncas)))
r = nod(OAS, order, nil)
r = typecheck(r, ctxStmt)
init = append(init, r)
var pc0, pcs *Node
if flag_race {

View File

@ -295,7 +295,10 @@ func (s *state) emitOpenDeferInfo() {
// worker indicates which of the backend workers is doing the processing.
func buildssa(fn *Node, worker int) *ssa.Func {
name := fn.funcname()
printssa := name == ssaDump
printssa := false
if ssaDump != "" { // match either a simple name e.g. "(*Reader).Reset", or a package.name e.g. "compress/gzip.(*Reader).Reset"
printssa = name == ssaDump || myimportpath+"."+name == ssaDump
}
var astBuf *bytes.Buffer
if printssa {
astBuf = &bytes.Buffer{}
@ -2110,7 +2113,7 @@ func (s *state) expr(n *Node) *ssa.Value {
}
// unsafe.Pointer <--> *T
if to.Etype == TUNSAFEPTR && from.IsPtrShaped() || from.Etype == TUNSAFEPTR && to.IsPtrShaped() {
if to.IsUnsafePtr() && from.IsPtrShaped() || from.IsUnsafePtr() && to.IsPtrShaped() {
return v
}
@ -6179,7 +6182,7 @@ func genssa(f *ssa.Func, pp *Progs) {
// Resolve branches, and relax DefaultStmt into NotStmt
for _, br := range s.Branches {
br.P.To.Val = s.bstart[br.B.ID]
br.P.To.SetTarget(s.bstart[br.B.ID])
if br.P.Pos.IsStmt() != src.PosIsStmt {
br.P.Pos = br.P.Pos.WithNotStmt()
} else if v0 := br.B.FirstPossibleStmtValue(); v0 != nil && v0.Pos.Line() == br.P.Pos.Line() && v0.Pos.IsStmt() == src.PosIsStmt {

View File

@ -271,13 +271,6 @@ func autolabel(prefix string) *types.Sym {
return lookupN(prefix, int(n))
}
func restrictlookup(name string, pkg *types.Pkg) *types.Sym {
if !types.IsExported(name) && pkg != localpkg {
yyerror("cannot refer to unexported name %s.%s", pkg.Name, name)
}
return pkg.Lookup(name)
}
// find all the exported symbols in package opkg
// and make them available in the current package
func importdot(opkg *types.Pkg, pack *Node) {
@ -788,12 +781,12 @@ func convertop(srcConstant bool, src, dst *types.Type, why *string) Op {
}
// 8. src is a pointer or uintptr and dst is unsafe.Pointer.
if (src.IsPtr() || src.Etype == TUINTPTR) && dst.Etype == TUNSAFEPTR {
if (src.IsPtr() || src.IsUintptr()) && dst.IsUnsafePtr() {
return OCONVNOP
}
// 9. src is unsafe.Pointer and dst is a pointer or uintptr.
if src.Etype == TUNSAFEPTR && (dst.IsPtr() || dst.Etype == TUINTPTR) {
if src.IsUnsafePtr() && (dst.IsPtr() || dst.IsUintptr()) {
return OCONVNOP
}
@ -1550,7 +1543,6 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) {
tfn.List.Set(structargs(method.Type.Params(), true))
tfn.Rlist.Set(structargs(method.Type.Results(), false))
disableExport(newnam)
fn := dclfunc(newnam, tfn)
fn.Func.SetDupok(true)
@ -1638,8 +1630,7 @@ func hashmem(t *types.Type) *Node {
sym := Runtimepkg.Lookup("memhash")
n := newname(sym)
n.SetClass(PFUNC)
n.Sym.SetFunc(true)
setNodeNameFunc(n)
n.Type = functype(nil, []*Node{
anonfield(types.NewPtr(t)),
anonfield(types.Types[TUINTPTR]),

View File

@ -141,19 +141,20 @@ const (
nodeInitorder, _ // tracks state during init1; two bits
_, _ // second nodeInitorder bit
_, nodeHasBreak
_, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only
_, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP; or ANDNOT lowered to OAND
_, nodeIsDDD // is the argument variadic
_, nodeDiag // already printed error about this
_, nodeColas // OAS resulting from :=
_, nodeNonNil // guaranteed to be non-nil
_, nodeTransient // storage can be reused immediately after this statement
_, nodeBounded // bounds check unnecessary
_, nodeHasCall // expression contains a function call
_, nodeLikely // if statement condition likely
_, nodeHasVal // node.E contains a Val
_, nodeHasOpt // node.E contains an Opt
_, nodeEmbedded // ODCLFIELD embedded type
_, nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only
_, nodeImplicit // implicit OADDR or ODEREF; ++/-- statement represented as OASOP; or ANDNOT lowered to OAND
_, nodeIsDDD // is the argument variadic
_, nodeDiag // already printed error about this
_, nodeColas // OAS resulting from :=
_, nodeNonNil // guaranteed to be non-nil
_, nodeTransient // storage can be reused immediately after this statement
_, nodeBounded // bounds check unnecessary
_, nodeHasCall // expression contains a function call
_, nodeLikely // if statement condition likely
_, nodeHasVal // node.E contains a Val
_, nodeHasOpt // node.E contains an Opt
_, nodeEmbedded // ODCLFIELD embedded type
_, nodeNeedsWrapper // OCALLxxx node that needs to be wrapped
)
func (n *Node) Class() Class { return Class(n.flags.get3(nodeClass)) }
@ -286,6 +287,20 @@ func (n *Node) SetIota(x int64) {
n.Xoffset = x
}
func (n *Node) NeedsWrapper() bool {
return n.flags&nodeNeedsWrapper != 0
}
// SetNeedsWrapper indicates that OCALLxxx node needs to be wrapped by a closure.
func (n *Node) SetNeedsWrapper(b bool) {
switch n.Op {
case OCALLFUNC, OCALLMETH, OCALLINTER:
default:
Fatalf("Node.SetNeedsWrapper %v", n.Op)
}
n.flags.set(nodeNeedsWrapper, b)
}
// mayBeShared reports whether n may occur in multiple places in the AST.
// Extra care must be taken when mutating such a node.
func (n *Node) mayBeShared() bool {

View File

@ -232,7 +232,11 @@ func walkstmt(n *Node) *Node {
n.Left = copyany(n.Left, &n.Ninit, true)
default:
n.Left = walkexpr(n.Left, &n.Ninit)
if n.Left.NeedsWrapper() {
n.Left = wrapCall(n.Left, &n.Ninit)
} else {
n.Left = walkexpr(n.Left, &n.Ninit)
}
}
case OFOR, OFORUNTIL:
@ -954,11 +958,11 @@ opswitch:
case OCONV, OCONVNOP:
n.Left = walkexpr(n.Left, init)
if n.Op == OCONVNOP && checkPtr(Curfn, 1) {
if n.Type.IsPtr() && n.Left.Type.Etype == TUNSAFEPTR { // unsafe.Pointer to *T
if n.Type.IsPtr() && n.Left.Type.IsUnsafePtr() { // unsafe.Pointer to *T
n = walkCheckPtrAlignment(n, init, nil)
break
}
if n.Type.Etype == TUNSAFEPTR && n.Left.Type.Etype == TUINTPTR { // uintptr to unsafe.Pointer
if n.Type.IsUnsafePtr() && n.Left.Type.IsUintptr() { // uintptr to unsafe.Pointer
n = walkCheckPtrArithmetic(n, init)
break
}
@ -1123,7 +1127,7 @@ opswitch:
n.List.SetSecond(walkexpr(n.List.Second(), init))
case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
checkSlice := checkPtr(Curfn, 1) && n.Op == OSLICE3ARR && n.Left.Op == OCONVNOP && n.Left.Left.Type.Etype == TUNSAFEPTR
checkSlice := checkPtr(Curfn, 1) && n.Op == OSLICE3ARR && n.Left.Op == OCONVNOP && n.Left.Left.Type.IsUnsafePtr()
if checkSlice {
n.Left.Left = walkexpr(n.Left.Left, init)
} else {
@ -1156,6 +1160,9 @@ opswitch:
}
case ONEW:
if n.Type.Elem().NotInHeap() {
yyerror("%v is go:notinheap; heap allocation disallowed", n.Type.Elem())
}
if n.Esc == EscNone {
if n.Type.Elem().Width >= maxImplicitStackVarSize {
Fatalf("large ONEW with EscNone: %v", n)
@ -1324,6 +1331,9 @@ opswitch:
l = r
}
t := n.Type
if t.Elem().NotInHeap() {
yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem())
}
if n.Esc == EscNone {
if !isSmallMakeSlice(n) {
Fatalf("non-small OMAKESLICE with EscNone: %v", n)
@ -1365,10 +1375,6 @@ opswitch:
// When len and cap can fit into int, use makeslice instead of
// makeslice64, which is faster and shorter on 32 bit platforms.
if t.Elem().NotInHeap() {
yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem())
}
len, cap := l, r
fnname := "makeslice64"
@ -1403,7 +1409,7 @@ opswitch:
t := n.Type
if t.Elem().NotInHeap() {
Fatalf("%v is go:notinheap; heap allocation disallowed", t.Elem())
yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem())
}
length := conv(n.Left, types.Types[TINT])
@ -2012,9 +2018,6 @@ func walkprint(nn *Node, init *Nodes) *Node {
}
func callnew(t *types.Type) *Node {
if t.NotInHeap() {
yyerror("%v is go:notinheap; heap allocation disallowed", t)
}
dowidth(t)
n := nod(ONEWOBJ, typename(t), nil)
n.Type = types.NewPtr(t)
@ -2589,7 +2592,7 @@ func mapfast(t *types.Type) int {
}
switch algtype(t.Key()) {
case AMEM32:
if !t.Key().HasHeapPointer() {
if !t.Key().HasPointers() {
return mapfast32
}
if Widthptr == 4 {
@ -2597,7 +2600,7 @@ func mapfast(t *types.Type) int {
}
Fatalf("small pointer %v", t.Key())
case AMEM64:
if !t.Key().HasHeapPointer() {
if !t.Key().HasPointers() {
return mapfast64
}
if Widthptr == 8 {
@ -2744,7 +2747,7 @@ func appendslice(n *Node, init *Nodes) *Node {
nodes.Append(nod(OAS, s, nt))
var ncopy *Node
if elemtype.HasHeapPointer() {
if elemtype.HasPointers() {
// copy(s[len(l1):], l2)
nptr1 := nod(OSLICE, s, nil)
nptr1.Type = s.Type
@ -3082,7 +3085,7 @@ func walkappend(n *Node, init *Nodes, dst *Node) *Node {
// Also works if b is a string.
//
func copyany(n *Node, init *Nodes, runtimecall bool) *Node {
if n.Left.Type.Elem().HasHeapPointer() {
if n.Left.Type.Elem().HasPointers() {
Curfn.Func.setWBPos(n.Pos)
fn := writebarrierfn("typedslicecopy", n.Left.Type.Elem(), n.Right.Type.Elem())
n.Left = cheapexpr(n.Left, init)
@ -3167,8 +3170,7 @@ func eqfor(t *types.Type) (n *Node, needsize bool) {
case ASPECIAL:
sym := typesymprefix(".eq", t)
n := newname(sym)
n.SetClass(PFUNC)
n.Sym.SetFunc(true)
setNodeNameFunc(n)
n.Type = functype(nil, []*Node{
anonfield(types.NewPtr(t)),
anonfield(types.NewPtr(t)),
@ -3859,6 +3861,14 @@ func candiscard(n *Node) bool {
// builtin(a1, a2, a3)
// }(x, y, z)
// for print, println, and delete.
//
// Rewrite
// go f(x, y, uintptr(unsafe.Pointer(z)))
// into
// go func(a1, a2, a3) {
// builtin(a1, a2, uintptr(a3))
// }(x, y, unsafe.Pointer(z))
// for function contains unsafe-uintptr arguments.
var wrapCall_prgen int
@ -3870,9 +3880,17 @@ func wrapCall(n *Node, init *Nodes) *Node {
init.AppendNodes(&n.Ninit)
}
isBuiltinCall := n.Op != OCALLFUNC && n.Op != OCALLMETH && n.Op != OCALLINTER
// origArgs keeps track of what argument is uintptr-unsafe/unsafe-uintptr conversion.
origArgs := make([]*Node, n.List.Len())
t := nod(OTFUNC, nil, nil)
for i, arg := range n.List.Slice() {
s := lookupN("a", i)
if !isBuiltinCall && arg.Op == OCONVNOP && arg.Type.IsUintptr() && arg.Left.Type.IsUnsafePtr() {
origArgs[i] = arg
arg = arg.Left
n.List.SetIndex(i, arg)
}
t.List.Append(symfield(s, arg.Type))
}
@ -3880,10 +3898,22 @@ func wrapCall(n *Node, init *Nodes) *Node {
sym := lookupN("wrap·", wrapCall_prgen)
fn := dclfunc(sym, t)
a := nod(n.Op, nil, nil)
a.List.Set(paramNnames(t.Type))
a = typecheck(a, ctxStmt)
fn.Nbody.Set1(a)
args := paramNnames(t.Type)
for i, origArg := range origArgs {
if origArg == nil {
continue
}
arg := nod(origArg.Op, args[i], nil)
arg.Type = origArg.Type
args[i] = arg
}
call := nod(n.Op, nil, nil)
if !isBuiltinCall {
call.Op = OCALL
call.Left = n.Left
}
call.List.Set(args)
fn.Nbody.Set1(call)
funcbody()
@ -3891,12 +3921,12 @@ func wrapCall(n *Node, init *Nodes) *Node {
typecheckslice(fn.Nbody.Slice(), ctxStmt)
xtop = append(xtop, fn)
a = nod(OCALL, nil, nil)
a.Left = fn.Func.Nname
a.List.Set(n.List.Slice())
a = typecheck(a, ctxStmt)
a = walkexpr(a, init)
return a
call = nod(OCALL, nil, nil)
call.Left = fn.Func.Nname
call.List.Set(n.List.Slice())
call = typecheck(call, ctxStmt)
call = walkexpr(call, init)
return call
}
// substArgTypes substitutes the given list of types for
@ -4011,7 +4041,7 @@ func walkCheckPtrArithmetic(n *Node, init *Nodes) *Node {
walk(n.Left)
}
case OCONVNOP:
if n.Left.Type.Etype == TUNSAFEPTR {
if n.Left.Type.IsUnsafePtr() {
n.Left = cheapexpr(n.Left, init)
originals = append(originals, convnop(n.Left, types.Types[TUNSAFEPTR]))
}

View File

@ -629,23 +629,6 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = r
case ssa.OpPPC64MaskIfNotCarry:
r := v.Reg()
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = ppc64.REGZERO
p.To.Type = obj.TYPE_REG
p.To.Reg = r
case ssa.OpPPC64ADDconstForCarry:
r1 := v.Args[0].Reg()
p := s.Prog(v.Op.Asm())
p.Reg = r1
p.From.Type = obj.TYPE_CONST
p.From.Offset = v.AuxInt
p.To.Type = obj.TYPE_REG
p.To.Reg = ppc64.REGTMP // Ignored; this is for the carry effect.
case ssa.OpPPC64NEG, ssa.OpPPC64FNEG, ssa.OpPPC64FSQRT, ssa.OpPPC64FSQRTS, ssa.OpPPC64FFLOOR, ssa.OpPPC64FTRUNC, ssa.OpPPC64FCEIL,
ssa.OpPPC64FCTIDZ, ssa.OpPPC64FCTIWZ, ssa.OpPPC64FCFID, ssa.OpPPC64FCFIDS, ssa.OpPPC64FRSP, ssa.OpPPC64CNTLZD, ssa.OpPPC64CNTLZW,
ssa.OpPPC64POPCNTD, ssa.OpPPC64POPCNTW, ssa.OpPPC64POPCNTB, ssa.OpPPC64MFVSRD, ssa.OpPPC64MTVSRD, ssa.OpPPC64FABS, ssa.OpPPC64FNABS,
@ -666,6 +649,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpPPC64SUBFCconst:
p := s.Prog(v.Op.Asm())
p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: v.AuxInt})
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpPPC64ANDCCconst:
p := s.Prog(v.Op.Asm())
p.Reg = v.Args[0].Reg()
@ -1802,7 +1793,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
v.Fatalf("Pseudo-op should not make it to codegen: %s ###\n", v.LongString())
case ssa.OpPPC64InvertFlags:
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
case ssa.OpPPC64FlagEQ, ssa.OpPPC64FlagLT, ssa.OpPPC64FlagGT, ssa.OpPPC64FlagCarrySet, ssa.OpPPC64FlagCarryClear:
case ssa.OpPPC64FlagEQ, ssa.OpPPC64FlagLT, ssa.OpPPC64FlagGT:
v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
case ssa.OpClobber:
// TODO: implement for clobberdead experiment. Nop is ok for now.

View File

@ -338,8 +338,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
n.To.Reg = dividend
}
j.To.Val = n
j2.To.Val = s.Pc()
j.To.SetTarget(n)
j2.To.SetTarget(s.Pc())
}
case ssa.OpS390XADDconst, ssa.OpS390XADDWconst:
opregregimm(s, v.Op.Asm(), v.Reg(), v.Args[0].Reg(), v.AuxInt)

View File

@ -7,12 +7,14 @@ package ssa
// addressingModes combines address calculations into memory operations
// that can perform complicated addressing modes.
func addressingModes(f *Func) {
isInImmediateRange := is32Bit
switch f.Config.arch {
default:
// Most architectures can't do this.
return
case "amd64", "386":
// TODO: s390x?
case "s390x":
isInImmediateRange = is20Bit
}
var tmp []*Value
@ -40,7 +42,7 @@ func addressingModes(f *Func) {
switch [2]auxType{opcodeTable[v.Op].auxType, opcodeTable[p.Op].auxType} {
case [2]auxType{auxSymOff, auxInt32}:
// TODO: introduce auxSymOff32
if !is32Bit(v.AuxInt + p.AuxInt) {
if !isInImmediateRange(v.AuxInt + p.AuxInt) {
continue
}
v.AuxInt += p.AuxInt
@ -48,7 +50,7 @@ func addressingModes(f *Func) {
if v.Aux != nil && p.Aux != nil {
continue
}
if !is32Bit(v.AuxInt + p.AuxInt) {
if !isInImmediateRange(v.AuxInt + p.AuxInt) {
continue
}
if p.Aux != nil {
@ -398,4 +400,61 @@ var combine = map[[2]Op]Op{
[2]Op{Op386ANDLconstmodify, Op386LEAL4}: Op386ANDLconstmodifyidx4,
[2]Op{Op386ORLconstmodify, Op386LEAL4}: Op386ORLconstmodifyidx4,
[2]Op{Op386XORLconstmodify, Op386LEAL4}: Op386XORLconstmodifyidx4,
// s390x
[2]Op{OpS390XMOVDload, OpS390XADD}: OpS390XMOVDloadidx,
[2]Op{OpS390XMOVWload, OpS390XADD}: OpS390XMOVWloadidx,
[2]Op{OpS390XMOVHload, OpS390XADD}: OpS390XMOVHloadidx,
[2]Op{OpS390XMOVBload, OpS390XADD}: OpS390XMOVBloadidx,
[2]Op{OpS390XMOVWZload, OpS390XADD}: OpS390XMOVWZloadidx,
[2]Op{OpS390XMOVHZload, OpS390XADD}: OpS390XMOVHZloadidx,
[2]Op{OpS390XMOVBZload, OpS390XADD}: OpS390XMOVBZloadidx,
[2]Op{OpS390XMOVDBRload, OpS390XADD}: OpS390XMOVDBRloadidx,
[2]Op{OpS390XMOVWBRload, OpS390XADD}: OpS390XMOVWBRloadidx,
[2]Op{OpS390XMOVHBRload, OpS390XADD}: OpS390XMOVHBRloadidx,
[2]Op{OpS390XFMOVDload, OpS390XADD}: OpS390XFMOVDloadidx,
[2]Op{OpS390XFMOVSload, OpS390XADD}: OpS390XFMOVSloadidx,
[2]Op{OpS390XMOVDstore, OpS390XADD}: OpS390XMOVDstoreidx,
[2]Op{OpS390XMOVWstore, OpS390XADD}: OpS390XMOVWstoreidx,
[2]Op{OpS390XMOVHstore, OpS390XADD}: OpS390XMOVHstoreidx,
[2]Op{OpS390XMOVBstore, OpS390XADD}: OpS390XMOVBstoreidx,
[2]Op{OpS390XMOVDBRstore, OpS390XADD}: OpS390XMOVDBRstoreidx,
[2]Op{OpS390XMOVWBRstore, OpS390XADD}: OpS390XMOVWBRstoreidx,
[2]Op{OpS390XMOVHBRstore, OpS390XADD}: OpS390XMOVHBRstoreidx,
[2]Op{OpS390XFMOVDstore, OpS390XADD}: OpS390XFMOVDstoreidx,
[2]Op{OpS390XFMOVSstore, OpS390XADD}: OpS390XFMOVSstoreidx,
[2]Op{OpS390XMOVDload, OpS390XMOVDaddridx}: OpS390XMOVDloadidx,
[2]Op{OpS390XMOVWload, OpS390XMOVDaddridx}: OpS390XMOVWloadidx,
[2]Op{OpS390XMOVHload, OpS390XMOVDaddridx}: OpS390XMOVHloadidx,
[2]Op{OpS390XMOVBload, OpS390XMOVDaddridx}: OpS390XMOVBloadidx,
[2]Op{OpS390XMOVWZload, OpS390XMOVDaddridx}: OpS390XMOVWZloadidx,
[2]Op{OpS390XMOVHZload, OpS390XMOVDaddridx}: OpS390XMOVHZloadidx,
[2]Op{OpS390XMOVBZload, OpS390XMOVDaddridx}: OpS390XMOVBZloadidx,
[2]Op{OpS390XMOVDBRload, OpS390XMOVDaddridx}: OpS390XMOVDBRloadidx,
[2]Op{OpS390XMOVWBRload, OpS390XMOVDaddridx}: OpS390XMOVWBRloadidx,
[2]Op{OpS390XMOVHBRload, OpS390XMOVDaddridx}: OpS390XMOVHBRloadidx,
[2]Op{OpS390XFMOVDload, OpS390XMOVDaddridx}: OpS390XFMOVDloadidx,
[2]Op{OpS390XFMOVSload, OpS390XMOVDaddridx}: OpS390XFMOVSloadidx,
[2]Op{OpS390XMOVDstore, OpS390XMOVDaddridx}: OpS390XMOVDstoreidx,
[2]Op{OpS390XMOVWstore, OpS390XMOVDaddridx}: OpS390XMOVWstoreidx,
[2]Op{OpS390XMOVHstore, OpS390XMOVDaddridx}: OpS390XMOVHstoreidx,
[2]Op{OpS390XMOVBstore, OpS390XMOVDaddridx}: OpS390XMOVBstoreidx,
[2]Op{OpS390XMOVDBRstore, OpS390XMOVDaddridx}: OpS390XMOVDBRstoreidx,
[2]Op{OpS390XMOVWBRstore, OpS390XMOVDaddridx}: OpS390XMOVWBRstoreidx,
[2]Op{OpS390XMOVHBRstore, OpS390XMOVDaddridx}: OpS390XMOVHBRstoreidx,
[2]Op{OpS390XFMOVDstore, OpS390XMOVDaddridx}: OpS390XFMOVDstoreidx,
[2]Op{OpS390XFMOVSstore, OpS390XMOVDaddridx}: OpS390XFMOVSstoreidx,
}

View File

@ -171,10 +171,10 @@ func checkFunc(f *Func) {
canHaveAuxInt = true
canHaveAux = true
case auxCCop:
if _, ok := v.Aux.(Op); !ok {
f.Fatalf("bad type %T for CCop in %v", v.Aux, v)
if opcodeTable[Op(v.AuxInt)].name == "OpInvalid" {
f.Fatalf("value %v has an AuxInt value that is a valid opcode", v)
}
canHaveAux = true
canHaveAuxInt = true
case auxS390XCCMask:
if _, ok := v.Aux.(s390x.CCMask); !ok {
f.Fatalf("bad type %T for S390XCCMask in %v", v.Aux, v)

View File

@ -23,9 +23,11 @@ func decomposeBuiltIn(f *Func) {
}
// Decompose other values
applyRewrite(f, rewriteBlockdec, rewriteValuedec)
// Note: deadcode is false because we need to keep the original
// values around so the name component resolution below can still work.
applyRewrite(f, rewriteBlockdec, rewriteValuedec, leaveDeadValues)
if f.Config.RegSize == 4 {
applyRewrite(f, rewriteBlockdec64, rewriteValuedec64)
applyRewrite(f, rewriteBlockdec64, rewriteValuedec64, leaveDeadValues)
}
// Split up named values into their components.
@ -139,7 +141,7 @@ func decomposeStringPhi(v *Value) {
func decomposeSlicePhi(v *Value) {
types := &v.Block.Func.Config.Types
ptrType := types.BytePtr
ptrType := v.Type.Elem().PtrTo()
lenType := types.Int
ptr := v.Block.NewValue0(v.Pos, OpPhi, ptrType)
@ -215,7 +217,7 @@ func decomposeInterfacePhi(v *Value) {
}
func decomposeArgs(f *Func) {
applyRewrite(f, rewriteBlockdecArgs, rewriteValuedecArgs)
applyRewrite(f, rewriteBlockdecArgs, rewriteValuedecArgs, removeDeadValues)
}
func decomposeUser(f *Func) {

View File

@ -257,6 +257,49 @@ func (f *Func) LogStat(key string, args ...interface{}) {
f.Warnl(f.Entry.Pos, "\t%s\t%s%s\t%s", n, key, value, f.Name)
}
// unCacheLine removes v from f's constant cache "line" for aux,
// resets v.InCache when it is found (and removed),
// and returns whether v was found in that line.
func (f *Func) unCacheLine(v *Value, aux int64) bool {
vv := f.constants[aux]
for i, cv := range vv {
if v == cv {
vv[i] = vv[len(vv)-1]
vv[len(vv)-1] = nil
f.constants[aux] = vv[0 : len(vv)-1]
v.InCache = false
return true
}
}
return false
}
// unCache removes v from f's constant cache.
func (f *Func) unCache(v *Value) {
if v.InCache {
aux := v.AuxInt
if f.unCacheLine(v, aux) {
return
}
if aux == 0 {
switch v.Op {
case OpConstNil:
aux = constNilMagic
case OpConstSlice:
aux = constSliceMagic
case OpConstString:
aux = constEmptyStringMagic
case OpConstInterface:
aux = constInterfaceMagic
}
if aux != 0 && f.unCacheLine(v, aux) {
return
}
}
f.Fatalf("unCached value %s not found in cache, auxInt=0x%x, adjusted aux=0x%x", v.LongString(), v.AuxInt, aux)
}
}
// freeValue frees a value. It must no longer be referenced or have any args.
func (f *Func) freeValue(v *Value) {
if v.Block == nil {
@ -270,19 +313,8 @@ func (f *Func) freeValue(v *Value) {
}
// Clear everything but ID (which we reuse).
id := v.ID
// Values with zero arguments and OpOffPtr values might be cached, so remove them there.
nArgs := opcodeTable[v.Op].argLen
if nArgs == 0 || v.Op == OpOffPtr {
vv := f.constants[v.AuxInt]
for i, cv := range vv {
if v == cv {
vv[i] = vv[len(vv)-1]
vv[len(vv)-1] = nil
f.constants[v.AuxInt] = vv[0 : len(vv)-1]
break
}
}
if v.InCache {
f.unCache(v)
}
*v = Value{}
v.ID = id
@ -548,6 +580,7 @@ func (f *Func) constVal(op Op, t *types.Type, c int64, setAuxInt bool) *Value {
v = f.Entry.NewValue0(src.NoXPos, op, t)
}
f.constants[c] = append(vv, v)
v.InCache = true
return v
}

File diff suppressed because it is too large Load Diff

View File

@ -132,65 +132,65 @@
// we compare to 64 to ensure Go semantics for large shifts
// Rules about rotates with non-const shift are based on the following rules,
// if the following rules change, please also modify the rules based on them.
(Lsh64x64 <t> x y) => (CSEL {OpARM64LessThanU} (SLL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
(Lsh64x32 <t> x y) => (CSEL {OpARM64LessThanU} (SLL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
(Lsh64x16 <t> x y) => (CSEL {OpARM64LessThanU} (SLL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
(Lsh64x8 <t> x y) => (CSEL {OpARM64LessThanU} (SLL <t> x (ZeroExt8to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64 y)))
(Lsh64x64 <t> x y) => (CSEL [OpARM64LessThanU] (SLL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
(Lsh64x32 <t> x y) => (CSEL [OpARM64LessThanU] (SLL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
(Lsh64x16 <t> x y) => (CSEL [OpARM64LessThanU] (SLL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
(Lsh64x8 <t> x y) => (CSEL [OpARM64LessThanU] (SLL <t> x (ZeroExt8to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64 y)))
(Lsh32x64 <t> x y) => (CSEL {OpARM64LessThanU} (SLL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
(Lsh32x32 <t> x y) => (CSEL {OpARM64LessThanU} (SLL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
(Lsh32x16 <t> x y) => (CSEL {OpARM64LessThanU} (SLL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
(Lsh32x8 <t> x y) => (CSEL {OpARM64LessThanU} (SLL <t> x (ZeroExt8to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64 y)))
(Lsh32x64 <t> x y) => (CSEL [OpARM64LessThanU] (SLL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
(Lsh32x32 <t> x y) => (CSEL [OpARM64LessThanU] (SLL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
(Lsh32x16 <t> x y) => (CSEL [OpARM64LessThanU] (SLL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
(Lsh32x8 <t> x y) => (CSEL [OpARM64LessThanU] (SLL <t> x (ZeroExt8to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64 y)))
(Lsh16x64 <t> x y) => (CSEL {OpARM64LessThanU} (SLL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
(Lsh16x32 <t> x y) => (CSEL {OpARM64LessThanU} (SLL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
(Lsh16x16 <t> x y) => (CSEL {OpARM64LessThanU} (SLL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
(Lsh16x8 <t> x y) => (CSEL {OpARM64LessThanU} (SLL <t> x (ZeroExt8to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64 y)))
(Lsh16x64 <t> x y) => (CSEL [OpARM64LessThanU] (SLL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
(Lsh16x32 <t> x y) => (CSEL [OpARM64LessThanU] (SLL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
(Lsh16x16 <t> x y) => (CSEL [OpARM64LessThanU] (SLL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
(Lsh16x8 <t> x y) => (CSEL [OpARM64LessThanU] (SLL <t> x (ZeroExt8to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64 y)))
(Lsh8x64 <t> x y) => (CSEL {OpARM64LessThanU} (SLL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
(Lsh8x32 <t> x y) => (CSEL {OpARM64LessThanU} (SLL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
(Lsh8x16 <t> x y) => (CSEL {OpARM64LessThanU} (SLL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
(Lsh8x8 <t> x y) => (CSEL {OpARM64LessThanU} (SLL <t> x (ZeroExt8to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64 y)))
(Lsh8x64 <t> x y) => (CSEL [OpARM64LessThanU] (SLL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
(Lsh8x32 <t> x y) => (CSEL [OpARM64LessThanU] (SLL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
(Lsh8x16 <t> x y) => (CSEL [OpARM64LessThanU] (SLL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
(Lsh8x8 <t> x y) => (CSEL [OpARM64LessThanU] (SLL <t> x (ZeroExt8to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64 y)))
(Rsh64Ux64 <t> x y) => (CSEL {OpARM64LessThanU} (SRL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
(Rsh64Ux32 <t> x y) => (CSEL {OpARM64LessThanU} (SRL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
(Rsh64Ux16 <t> x y) => (CSEL {OpARM64LessThanU} (SRL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
(Rsh64Ux8 <t> x y) => (CSEL {OpARM64LessThanU} (SRL <t> x (ZeroExt8to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64 y)))
(Rsh64Ux64 <t> x y) => (CSEL [OpARM64LessThanU] (SRL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
(Rsh64Ux32 <t> x y) => (CSEL [OpARM64LessThanU] (SRL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
(Rsh64Ux16 <t> x y) => (CSEL [OpARM64LessThanU] (SRL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
(Rsh64Ux8 <t> x y) => (CSEL [OpARM64LessThanU] (SRL <t> x (ZeroExt8to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64 y)))
(Rsh32Ux64 <t> x y) => (CSEL {OpARM64LessThanU} (SRL <t> (ZeroExt32to64 x) y) (Const64 <t> [0]) (CMPconst [64] y))
(Rsh32Ux32 <t> x y) => (CSEL {OpARM64LessThanU} (SRL <t> (ZeroExt32to64 x) (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
(Rsh32Ux16 <t> x y) => (CSEL {OpARM64LessThanU} (SRL <t> (ZeroExt32to64 x) (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
(Rsh32Ux8 <t> x y) => (CSEL {OpARM64LessThanU} (SRL <t> (ZeroExt32to64 x) (ZeroExt8to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64 y)))
(Rsh32Ux64 <t> x y) => (CSEL [OpARM64LessThanU] (SRL <t> (ZeroExt32to64 x) y) (Const64 <t> [0]) (CMPconst [64] y))
(Rsh32Ux32 <t> x y) => (CSEL [OpARM64LessThanU] (SRL <t> (ZeroExt32to64 x) (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
(Rsh32Ux16 <t> x y) => (CSEL [OpARM64LessThanU] (SRL <t> (ZeroExt32to64 x) (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
(Rsh32Ux8 <t> x y) => (CSEL [OpARM64LessThanU] (SRL <t> (ZeroExt32to64 x) (ZeroExt8to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64 y)))
(Rsh16Ux64 <t> x y) => (CSEL {OpARM64LessThanU} (SRL <t> (ZeroExt16to64 x) y) (Const64 <t> [0]) (CMPconst [64] y))
(Rsh16Ux32 <t> x y) => (CSEL {OpARM64LessThanU} (SRL <t> (ZeroExt16to64 x) (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
(Rsh16Ux16 <t> x y) => (CSEL {OpARM64LessThanU} (SRL <t> (ZeroExt16to64 x) (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
(Rsh16Ux8 <t> x y) => (CSEL {OpARM64LessThanU} (SRL <t> (ZeroExt16to64 x) (ZeroExt8to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64 y)))
(Rsh16Ux64 <t> x y) => (CSEL [OpARM64LessThanU] (SRL <t> (ZeroExt16to64 x) y) (Const64 <t> [0]) (CMPconst [64] y))
(Rsh16Ux32 <t> x y) => (CSEL [OpARM64LessThanU] (SRL <t> (ZeroExt16to64 x) (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
(Rsh16Ux16 <t> x y) => (CSEL [OpARM64LessThanU] (SRL <t> (ZeroExt16to64 x) (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
(Rsh16Ux8 <t> x y) => (CSEL [OpARM64LessThanU] (SRL <t> (ZeroExt16to64 x) (ZeroExt8to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64 y)))
(Rsh8Ux64 <t> x y) => (CSEL {OpARM64LessThanU} (SRL <t> (ZeroExt8to64 x) y) (Const64 <t> [0]) (CMPconst [64] y))
(Rsh8Ux32 <t> x y) => (CSEL {OpARM64LessThanU} (SRL <t> (ZeroExt8to64 x) (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
(Rsh8Ux16 <t> x y) => (CSEL {OpARM64LessThanU} (SRL <t> (ZeroExt8to64 x) (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
(Rsh8Ux8 <t> x y) => (CSEL {OpARM64LessThanU} (SRL <t> (ZeroExt8to64 x) (ZeroExt8to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64 y)))
(Rsh8Ux64 <t> x y) => (CSEL [OpARM64LessThanU] (SRL <t> (ZeroExt8to64 x) y) (Const64 <t> [0]) (CMPconst [64] y))
(Rsh8Ux32 <t> x y) => (CSEL [OpARM64LessThanU] (SRL <t> (ZeroExt8to64 x) (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
(Rsh8Ux16 <t> x y) => (CSEL [OpARM64LessThanU] (SRL <t> (ZeroExt8to64 x) (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
(Rsh8Ux8 <t> x y) => (CSEL [OpARM64LessThanU] (SRL <t> (ZeroExt8to64 x) (ZeroExt8to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64 y)))
(Rsh64x64 x y) => (SRA x (CSEL {OpARM64LessThanU} <y.Type> y (Const64 <y.Type> [63]) (CMPconst [64] y)))
(Rsh64x32 x y) => (SRA x (CSEL {OpARM64LessThanU} <y.Type> (ZeroExt32to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt32to64 y))))
(Rsh64x16 x y) => (SRA x (CSEL {OpARM64LessThanU} <y.Type> (ZeroExt16to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt16to64 y))))
(Rsh64x8 x y) => (SRA x (CSEL {OpARM64LessThanU} <y.Type> (ZeroExt8to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt8to64 y))))
(Rsh64x64 x y) => (SRA x (CSEL [OpARM64LessThanU] <y.Type> y (Const64 <y.Type> [63]) (CMPconst [64] y)))
(Rsh64x32 x y) => (SRA x (CSEL [OpARM64LessThanU] <y.Type> (ZeroExt32to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt32to64 y))))
(Rsh64x16 x y) => (SRA x (CSEL [OpARM64LessThanU] <y.Type> (ZeroExt16to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt16to64 y))))
(Rsh64x8 x y) => (SRA x (CSEL [OpARM64LessThanU] <y.Type> (ZeroExt8to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt8to64 y))))
(Rsh32x64 x y) => (SRA (SignExt32to64 x) (CSEL {OpARM64LessThanU} <y.Type> y (Const64 <y.Type> [63]) (CMPconst [64] y)))
(Rsh32x32 x y) => (SRA (SignExt32to64 x) (CSEL {OpARM64LessThanU} <y.Type> (ZeroExt32to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt32to64 y))))
(Rsh32x16 x y) => (SRA (SignExt32to64 x) (CSEL {OpARM64LessThanU} <y.Type> (ZeroExt16to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt16to64 y))))
(Rsh32x8 x y) => (SRA (SignExt32to64 x) (CSEL {OpARM64LessThanU} <y.Type> (ZeroExt8to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt8to64 y))))
(Rsh32x64 x y) => (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] <y.Type> y (Const64 <y.Type> [63]) (CMPconst [64] y)))
(Rsh32x32 x y) => (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] <y.Type> (ZeroExt32to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt32to64 y))))
(Rsh32x16 x y) => (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] <y.Type> (ZeroExt16to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt16to64 y))))
(Rsh32x8 x y) => (SRA (SignExt32to64 x) (CSEL [OpARM64LessThanU] <y.Type> (ZeroExt8to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt8to64 y))))
(Rsh16x64 x y) => (SRA (SignExt16to64 x) (CSEL {OpARM64LessThanU} <y.Type> y (Const64 <y.Type> [63]) (CMPconst [64] y)))
(Rsh16x32 x y) => (SRA (SignExt16to64 x) (CSEL {OpARM64LessThanU} <y.Type> (ZeroExt32to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt32to64 y))))
(Rsh16x16 x y) => (SRA (SignExt16to64 x) (CSEL {OpARM64LessThanU} <y.Type> (ZeroExt16to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt16to64 y))))
(Rsh16x8 x y) => (SRA (SignExt16to64 x) (CSEL {OpARM64LessThanU} <y.Type> (ZeroExt8to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt8to64 y))))
(Rsh16x64 x y) => (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] <y.Type> y (Const64 <y.Type> [63]) (CMPconst [64] y)))
(Rsh16x32 x y) => (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] <y.Type> (ZeroExt32to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt32to64 y))))
(Rsh16x16 x y) => (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] <y.Type> (ZeroExt16to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt16to64 y))))
(Rsh16x8 x y) => (SRA (SignExt16to64 x) (CSEL [OpARM64LessThanU] <y.Type> (ZeroExt8to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt8to64 y))))
(Rsh8x64 x y) => (SRA (SignExt8to64 x) (CSEL {OpARM64LessThanU} <y.Type> y (Const64 <y.Type> [63]) (CMPconst [64] y)))
(Rsh8x32 x y) => (SRA (SignExt8to64 x) (CSEL {OpARM64LessThanU} <y.Type> (ZeroExt32to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt32to64 y))))
(Rsh8x16 x y) => (SRA (SignExt8to64 x) (CSEL {OpARM64LessThanU} <y.Type> (ZeroExt16to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt16to64 y))))
(Rsh8x8 x y) => (SRA (SignExt8to64 x) (CSEL {OpARM64LessThanU} <y.Type> (ZeroExt8to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt8to64 y))))
(Rsh8x64 x y) => (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] <y.Type> y (Const64 <y.Type> [63]) (CMPconst [64] y)))
(Rsh8x32 x y) => (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] <y.Type> (ZeroExt32to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt32to64 y))))
(Rsh8x16 x y) => (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] <y.Type> (ZeroExt16to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt16to64 y))))
(Rsh8x8 x y) => (SRA (SignExt8to64 x) (CSEL [OpARM64LessThanU] <y.Type> (ZeroExt8to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt8to64 y))))
// constants
(Const(64|32|16|8) [val]) => (MOVDconst [int64(val)])
@ -315,8 +315,8 @@
(FCMPD (FMOVDconst [0]) x) => (InvertFlags (FCMPD0 x))
// CSEL needs a flag-generating argument. Synthesize a CMPW if necessary.
(CondSelect x y boolval) && flagArg(boolval) != nil => (CSEL {boolval.Op} x y flagArg(boolval))
(CondSelect x y boolval) && flagArg(boolval) == nil => (CSEL {OpARM64NotEqual} x y (CMPWconst [0] boolval))
(CondSelect x y boolval) && flagArg(boolval) != nil => (CSEL [boolval.Op] x y flagArg(boolval))
(CondSelect x y boolval) && flagArg(boolval) == nil => (CSEL [OpARM64NotEqual] x y (CMPWconst [0] boolval))
(OffPtr [off] ptr:(SP)) && is32Bit(off) => (MOVDaddr [int32(off)] ptr)
(OffPtr [off] ptr) => (ADDconst [off] ptr)
@ -1324,8 +1324,8 @@
(XOR x (MVN y)) -> (EON x y)
(OR x (MVN y)) -> (ORN x y)
(MVN (XOR x y)) -> (EON x y)
(CSEL {cc} x (MOVDconst [0]) flag) -> (CSEL0 {cc} x flag)
(CSEL {cc} (MOVDconst [0]) y flag) -> (CSEL0 {arm64Negate(cc.(Op))} y flag)
(CSEL [cc] x (MOVDconst [0]) flag) => (CSEL0 [cc] x flag)
(CSEL [cc] (MOVDconst [0]) y flag) => (CSEL0 [arm64Negate(cc)] y flag)
(SUB x (SUB y z)) -> (SUB (ADD <v.Type> x z) y)
(SUB (SUB x y) z) -> (SUB x (ADD <y.Type> y z))
@ -1481,8 +1481,8 @@
(GTnoov (InvertFlags cmp) yes no) => (LTnoov cmp yes no)
// absorb InvertFlags into CSEL(0)
(CSEL {cc} x y (InvertFlags cmp)) => (CSEL {arm64Invert(cc)} x y cmp)
(CSEL0 {cc} x (InvertFlags cmp)) => (CSEL0 {arm64Invert(cc)} x cmp)
(CSEL [cc] x y (InvertFlags cmp)) => (CSEL [arm64Invert(cc)] x y cmp)
(CSEL0 [cc] x (InvertFlags cmp)) => (CSEL0 [arm64Invert(cc)] x cmp)
// absorb flag constants into boolean values
(Equal (FlagConstant [fc])) => (MOVDconst [b2i(fc.eq())])
@ -1517,20 +1517,20 @@
(MOVBUreg x) && x.Type.IsBoolean() => (MOVDreg x)
// absorb flag constants into conditional instructions
(CSEL {cc} x _ flag) && ccARM64Eval(cc, flag) > 0 => x
(CSEL {cc} _ y flag) && ccARM64Eval(cc, flag) < 0 => y
(CSEL0 {cc} x flag) && ccARM64Eval(cc, flag) > 0 => x
(CSEL0 {cc} _ flag) && ccARM64Eval(cc, flag) < 0 => (MOVDconst [0])
(CSEL [cc] x _ flag) && ccARM64Eval(cc, flag) > 0 => x
(CSEL [cc] _ y flag) && ccARM64Eval(cc, flag) < 0 => y
(CSEL0 [cc] x flag) && ccARM64Eval(cc, flag) > 0 => x
(CSEL0 [cc] _ flag) && ccARM64Eval(cc, flag) < 0 => (MOVDconst [0])
// absorb flags back into boolean CSEL
(CSEL {cc} x y (CMPWconst [0] boolval)) && cc == OpARM64NotEqual && flagArg(boolval) != nil =>
(CSEL {boolval.Op} x y flagArg(boolval))
(CSEL {cc} x y (CMPWconst [0] boolval)) && cc == OpARM64Equal && flagArg(boolval) != nil =>
(CSEL {arm64Negate(boolval.Op)} x y flagArg(boolval))
(CSEL0 {cc} x (CMPWconst [0] boolval)) && cc == OpARM64NotEqual && flagArg(boolval) != nil =>
(CSEL0 {boolval.Op} x flagArg(boolval))
(CSEL0 {cc} x (CMPWconst [0] boolval)) && cc == OpARM64Equal && flagArg(boolval) != nil =>
(CSEL0 {arm64Negate(boolval.Op)} x flagArg(boolval))
(CSEL [cc] x y (CMPWconst [0] boolval)) && cc == OpARM64NotEqual && flagArg(boolval) != nil =>
(CSEL [boolval.Op] x y flagArg(boolval))
(CSEL [cc] x y (CMPWconst [0] boolval)) && cc == OpARM64Equal && flagArg(boolval) != nil =>
(CSEL [arm64Negate(boolval.Op)] x y flagArg(boolval))
(CSEL0 [cc] x (CMPWconst [0] boolval)) && cc == OpARM64NotEqual && flagArg(boolval) != nil =>
(CSEL0 [boolval.Op] x flagArg(boolval))
(CSEL0 [cc] x (CMPWconst [0] boolval)) && cc == OpARM64Equal && flagArg(boolval) != nil =>
(CSEL0 [arm64Negate(boolval.Op)] x flagArg(boolval))
// absorb shifts into ops
(NEG x:(SLLconst [c] y)) && clobberIfDead(x) => (NEGshiftLL [c] y)
@ -1691,11 +1691,11 @@
// "|" can also be "^" or "+".
// As arm64 does not have a ROL instruction, so ROL(x, y) is replaced by ROR(x, -y).
((ADD|OR|XOR) (SLL x (ANDconst <t> [63] y))
(CSEL0 <typ.UInt64> {cc} (SRL <typ.UInt64> x (SUB <t> (MOVDconst [64]) (ANDconst <t> [63] y)))
(CSEL0 <typ.UInt64> [cc] (SRL <typ.UInt64> x (SUB <t> (MOVDconst [64]) (ANDconst <t> [63] y)))
(CMPconst [64] (SUB <t> (MOVDconst [64]) (ANDconst <t> [63] y))))) && cc == OpARM64LessThanU
=> (ROR x (NEG <t> y))
((ADD|OR|XOR) (SRL <typ.UInt64> x (ANDconst <t> [63] y))
(CSEL0 <typ.UInt64> {cc} (SLL x (SUB <t> (MOVDconst [64]) (ANDconst <t> [63] y)))
(CSEL0 <typ.UInt64> [cc] (SLL x (SUB <t> (MOVDconst [64]) (ANDconst <t> [63] y)))
(CMPconst [64] (SUB <t> (MOVDconst [64]) (ANDconst <t> [63] y))))) && cc == OpARM64LessThanU
=> (ROR x y)
@ -1705,11 +1705,11 @@
// "|" can also be "^" or "+".
// As arm64 does not have a ROLW instruction, so ROLW(x, y) is replaced by RORW(x, -y).
((ADD|OR|XOR) (SLL x (ANDconst <t> [31] y))
(CSEL0 <typ.UInt32> {cc} (SRL <typ.UInt32> (MOVWUreg x) (SUB <t> (MOVDconst [32]) (ANDconst <t> [31] y)))
(CSEL0 <typ.UInt32> [cc] (SRL <typ.UInt32> (MOVWUreg x) (SUB <t> (MOVDconst [32]) (ANDconst <t> [31] y)))
(CMPconst [64] (SUB <t> (MOVDconst [32]) (ANDconst <t> [31] y))))) && cc == OpARM64LessThanU
=> (RORW x (NEG <t> y))
((ADD|OR|XOR) (SRL <typ.UInt32> (MOVWUreg x) (ANDconst <t> [31] y))
(CSEL0 <typ.UInt32> {cc} (SLL x (SUB <t> (MOVDconst [32]) (ANDconst <t> [31] y)))
(CSEL0 <typ.UInt32> [cc] (SLL x (SUB <t> (MOVDconst [32]) (ANDconst <t> [31] y)))
(CMPconst [64] (SUB <t> (MOVDconst [32]) (ANDconst <t> [31] y))))) && cc == OpARM64LessThanU
=> (RORW x y)

View File

@ -467,8 +467,8 @@ func init() {
// conditional instructions; auxint is
// one of the arm64 comparison pseudo-ops (LessThan, LessThanU, etc.)
{name: "CSEL", argLength: 3, reg: gp2flags1, asm: "CSEL", aux: "CCop"}, // aux(flags) ? arg0 : arg1
{name: "CSEL0", argLength: 2, reg: gp1flags1, asm: "CSEL", aux: "CCop"}, // aux(flags) ? arg0 : 0
{name: "CSEL", argLength: 3, reg: gp2flags1, asm: "CSEL", aux: "CCop"}, // auxint(flags) ? arg0 : arg1
{name: "CSEL0", argLength: 2, reg: gp1flags1, asm: "CSEL", aux: "CCop"}, // auxint(flags) ? arg0 : 0
// function calls
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true, symEffect: "None"}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem

View File

@ -110,13 +110,21 @@
// Rotate generation with non-const shift
// these match patterns from math/bits/RotateLeft[32|64], but there could be others
(ADD (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))) => (ROTL x y)
(ADD (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))) => (ROTL x y)
( OR (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))) => (ROTL x y)
( OR (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))) => (ROTL x y)
(XOR (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))) => (ROTL x y)
(XOR (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))) => (ROTL x y)
(ADD (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))) => (ROTLW x y)
(ADD (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))) => (ROTLW x y)
( OR (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))) => (ROTLW x y)
( OR (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))) => (ROTLW x y)
(XOR (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))) => (ROTLW x y)
(XOR (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))) => (ROTLW x y)
// Lowering rotates
(RotateLeft32 x y) => (ROTLW x y)
(RotateLeft64 x y) => (ROTL x y)
@ -192,11 +200,15 @@
(Rsh64Ux64 x (AND y (MOVDconst [63]))) => (SRD x (ANDconst <typ.Int64> [63] y))
(Rsh64Ux64 x (ANDconst <typ.UInt> [63] y)) => (SRD x (ANDconst <typ.UInt> [63] y))
(Rsh64Ux64 x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y))) => (SRD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))
(Rsh64Ux64 x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y))) => (SRD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))
(Rsh64Ux64 x (SUB <typ.UInt> (MOVDconst [64]) (AND <typ.UInt> y (MOVDconst [63])))) => (SRD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))
(Rsh64Ux64 x (SUBFCconst <typ.UInt> [64] (AND <typ.UInt> y (MOVDconst [63])))) => (SRD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))
(Rsh64x64 x (AND y (MOVDconst [63]))) => (SRAD x (ANDconst <typ.Int64> [63] y))
(Rsh64x64 x (ANDconst <typ.UInt> [63] y)) => (SRAD x (ANDconst <typ.UInt> [63] y))
(Rsh64x64 x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y))) => (SRAD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))
(Rsh64x64 x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y))) => (SRAD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))
(Rsh64x64 x (SUB <typ.UInt> (MOVDconst [64]) (AND <typ.UInt> y (MOVDconst [63])))) => (SRAD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))
(Rsh64x64 x (SUBFCconst <typ.UInt> [64] (AND <typ.UInt> y (MOVDconst [63])))) => (SRAD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))
(Lsh64x64 x y) => (SLD x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [64]))))
(Rsh64x64 x y) => (SRAD x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [64]))))
@ -208,12 +220,16 @@
(Rsh32Ux64 x (AND y (MOVDconst [31]))) => (SRW x (ANDconst <typ.Int32> [31] y))
(Rsh32Ux64 x (ANDconst <typ.UInt> [31] y)) => (SRW x (ANDconst <typ.UInt> [31] y))
(Rsh32Ux64 x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y))) => (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))
(Rsh32Ux64 x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y))) => (SRW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))
(Rsh32Ux64 x (SUB <typ.UInt> (MOVDconst [32]) (AND <typ.UInt> y (MOVDconst [31])))) => (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))
(Rsh32Ux64 x (SUBFCconst <typ.UInt> [32] (AND <typ.UInt> y (MOVDconst [31])))) => (SRW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))
(Rsh32x64 x (AND y (MOVDconst [31]))) => (SRAW x (ANDconst <typ.Int32> [31] y))
(Rsh32x64 x (ANDconst <typ.UInt> [31] y)) => (SRAW x (ANDconst <typ.UInt> [31] y))
(Rsh32x64 x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y))) => (SRAW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))
(Rsh32x64 x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y))) => (SRAW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))
(Rsh32x64 x (SUB <typ.UInt> (MOVDconst [32]) (AND <typ.UInt> y (MOVDconst [31])))) => (SRAW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))
(Rsh32x64 x (SUBFCconst <typ.UInt> [32] (AND <typ.UInt> y (MOVDconst [31])))) => (SRAW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))
(Rsh32x64 x y) => (SRAW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [32]))))
(Rsh32Ux64 x y) => (SRW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [32]))))
@ -276,18 +292,11 @@
(Rsh8Ux8 x y) => (SRW (ZeroExt8to32 x) (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [8]))))
(Lsh8x8 x y) => (SLW x (ISEL [0] y (MOVDconst [-1]) (CMPU (ZeroExt8to64 y) (MOVDconst [8]))))
// Cleaning up shift ops when input is masked
(MaskIfNotCarry (ADDconstForCarry [c] (ANDconst [d] _))) && c < 0 && d > 0 && int64(c) + d < 0 => (MOVDconst [-1])
// Cleaning up shift ops
(ISEL [0] (ANDconst [d] y) (MOVDconst [-1]) (CMPU (ANDconst [d] y) (MOVDconst [c]))) && c >= d => (ANDconst [d] y)
(ISEL [0] (ANDconst [d] y) (MOVDconst [-1]) (CMPUconst [c] (ANDconst [d] y))) && c >= d => (ANDconst [d] y)
(ORN x (MOVDconst [-1])) => x
(ADDconstForCarry [c] (MOVDconst [d])) && c < 0 && (c < 0 || int64(c) + d >= 0) => (FlagCarryClear)
(ADDconstForCarry [c] (MOVDconst [d])) && c < 0 && c >= 0 && int64(c) + d < 0 => (FlagCarrySet)
(MaskIfNotCarry (FlagCarrySet)) => (MOVDconst [0])
(MaskIfNotCarry (FlagCarryClear)) => (MOVDconst [-1])
(S(RAD|RD|LD) x (MOVDconst [c])) => (S(RAD|RD|LD)const [c&63 | (c>>6&1*63)] x)
(S(RAW|RW|LW) x (MOVDconst [c])) => (S(RAW|RW|LW)const [c&31 | (c>>5&1*31)] x)
@ -306,8 +315,8 @@
(Ctz16 x) => (POPCNTW (MOVHZreg (ANDN <typ.Int16> (ADDconst <typ.Int16> [-1] x) x)))
(Ctz8 x) => (POPCNTB (MOVBZreg (ANDN <typ.UInt8> (ADDconst <typ.UInt8> [-1] x) x)))
(BitLen64 x) => (SUB (MOVDconst [64]) (CNTLZD <typ.Int> x))
(BitLen32 x) => (SUB (MOVDconst [32]) (CNTLZW <typ.Int> x))
(BitLen64 x) => (SUBFCconst [64] (CNTLZD <typ.Int> x))
(BitLen32 x) => (SUBFCconst [32] (CNTLZW <typ.Int> x))
(PopCount64 ...) => (POPCNTD ...)
(PopCount32 x) => (POPCNTW (MOVWZreg x))
@ -777,10 +786,19 @@
(ADDconst [c] (ADDconst [d] x)) && is32Bit(c+d) => (ADDconst [c+d] x)
(ADDconst [0] x) => x
(SUB x (MOVDconst [c])) && is32Bit(-c) => (ADDconst [-c] x)
// TODO deal with subtract-from-const
(ADDconst [c] (MOVDaddr [d] {sym} x)) && is32Bit(c+int64(d)) => (MOVDaddr [int32(c+int64(d))] {sym} x)
// Subtract from (with carry, but ignored) constant.
// Note, these clobber the carry bit.
(SUB (MOVDconst [c]) x) && is32Bit(c) => (SUBFCconst [c] x)
(SUBFCconst [c] (NEG x)) => (ADDconst [c] x)
(SUBFCconst [c] (SUBFCconst [d] x)) && is32Bit(c-d) => (ADDconst [c-d] x)
(SUBFCconst [0] x) => (NEG x)
(ADDconst [c] (SUBFCconst [d] x)) && is32Bit(c+d) => (SUBFCconst [c+d] x)
(NEG (ADDconst [c] x)) && is32Bit(-c) => (SUBFCconst [-c] x)
(NEG (SUBFCconst [c] x)) && is32Bit(-c) => (ADDconst [-c] x)
// Use register moves instead of stores and loads to move int<=>float values
// Common with math Float64bits, Float64frombits
(MOVDload [off] {sym} ptr (FMOVDstore [off] {sym} ptr x _)) => (MFVSRD x)

View File

@ -175,6 +175,7 @@ func init() {
{name: "FADD", argLength: 2, reg: fp21, asm: "FADD", commutative: true}, // arg0+arg1
{name: "FADDS", argLength: 2, reg: fp21, asm: "FADDS", commutative: true}, // arg0+arg1
{name: "SUB", argLength: 2, reg: gp21, asm: "SUB"}, // arg0-arg1
{name: "SUBFCconst", argLength: 1, reg: gp11, asm: "SUBC", aux: "Int64"}, // auxInt - arg0 (with carry)
{name: "FSUB", argLength: 2, reg: fp21, asm: "FSUB"}, // arg0-arg1
{name: "FSUBS", argLength: 2, reg: fp21, asm: "FSUBS"}, // arg0-arg1
@ -206,9 +207,7 @@ func init() {
{name: "ROTL", argLength: 2, reg: gp21, asm: "ROTL"}, // arg0 rotate left by arg1 mod 64
{name: "ROTLW", argLength: 2, reg: gp21, asm: "ROTLW"}, // uint32(arg0) rotate left by arg1 mod 32
{name: "LoweredAdd64Carry", argLength: 3, reg: gp32, resultNotInArgs: true}, // arg0 + arg1 + carry, returns (sum, carry)
{name: "ADDconstForCarry", argLength: 1, reg: regInfo{inputs: []regMask{gp | sp | sb}, clobbers: tmp}, aux: "Int16", asm: "ADDC", typ: "Flags"}, // _, carry := arg0 + auxint
{name: "MaskIfNotCarry", argLength: 1, reg: crgp, asm: "ADDME", typ: "Int64"}, // carry - 1 (if carry then 0 else -1)
{name: "LoweredAdd64Carry", argLength: 3, reg: gp32, resultNotInArgs: true}, // arg0 + arg1 + carry, returns (sum, carry)
{name: "SRADconst", argLength: 1, reg: gp11, asm: "SRAD", aux: "Int64"}, // signed arg0 >> auxInt, 0 <= auxInt < 64, 64 bit width
{name: "SRAWconst", argLength: 1, reg: gp11, asm: "SRAW", aux: "Int64"}, // signed arg0 >> auxInt, 0 <= auxInt < 32, 32 bit width
@ -674,11 +673,9 @@ func init() {
// These ops are for temporary use by rewrite rules. They
// cannot appear in the generated assembly.
{name: "FlagEQ"}, // equal
{name: "FlagLT"}, // signed < or unsigned <
{name: "FlagGT"}, // signed > or unsigned >
{name: "FlagCarrySet"}, // carry flag set
{name: "FlagCarryClear"}, // carry flag clear
{name: "FlagEQ"}, // equal
{name: "FlagLT"}, // signed < or unsigned <
{name: "FlagGT"}, // signed > or unsigned >
}
blocks := []blockData{

File diff suppressed because it is too large Load Diff

View File

@ -66,14 +66,14 @@
(Load <typ.Int>
(OffPtr <typ.IntPtr> [2*config.PtrSize] ptr)
mem))
(Store dst (SliceMake ptr len cap) mem) =>
(Store {t} dst (SliceMake ptr len cap) mem) =>
(Store {typ.Int}
(OffPtr <typ.IntPtr> [2*config.PtrSize] dst)
cap
(Store {typ.Int}
(OffPtr <typ.IntPtr> [config.PtrSize] dst)
len
(Store {typ.BytePtr} dst ptr mem)))
(Store {t.Elem().PtrTo()} dst ptr mem)))
// interface ops
(ITab (IMake itab _)) => itab

View File

@ -1423,7 +1423,8 @@ func parseValue(val string, arch arch, loc string) (op opData, oparch, typ, auxi
func opHasAuxInt(op opData) bool {
switch op.aux {
case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", "SymOff", "SymValAndOff", "TypSize", "ARM64BitField", "FlagConstant":
case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64",
"SymOff", "SymValAndOff", "TypSize", "ARM64BitField", "FlagConstant", "CCop":
return true
}
return false
@ -1431,7 +1432,7 @@ func opHasAuxInt(op opData) bool {
func opHasAux(op opData) bool {
switch op.aux {
case "String", "Sym", "SymOff", "SymValAndOff", "Typ", "TypSize", "CCop",
case "String", "Sym", "SymOff", "SymValAndOff", "Typ", "TypSize",
"S390XCCMask", "S390XRotateParams":
return true
}
@ -1784,8 +1785,6 @@ func (op opData) auxType() string {
return "s390x.CCMask"
case "S390XRotateParams":
return "s390x.RotateParams"
case "CCop":
return "CCop"
default:
return "invalid"
}

View File

@ -7,7 +7,7 @@ package ssa
// convert to machine-dependent ops
func lower(f *Func) {
// repeat rewrites until we find no more rewrites
applyRewrite(f, f.Config.lowerBlock, f.Config.lowerValue)
applyRewrite(f, f.Config.lowerBlock, f.Config.lowerValue, removeDeadValues)
}
// checkLower checks for unlowered opcodes and fails if we find one.

View File

@ -235,7 +235,7 @@ func nilcheckelim2(f *Func) {
continue
}
if v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() {
if v.Op == OpVarKill || v.Op == OpVarLive || (v.Op == OpVarDef && !v.Aux.(GCNode).Typ().HasHeapPointer()) {
if v.Op == OpVarKill || v.Op == OpVarLive || (v.Op == OpVarDef && !v.Aux.(GCNode).Typ().HasPointers()) {
// These ops don't really change memory.
continue
// Note: OpVarDef requires that the defined variable not have pointers.

View File

@ -1828,6 +1828,7 @@ const (
OpPPC64FADD
OpPPC64FADDS
OpPPC64SUB
OpPPC64SUBFCconst
OpPPC64FSUB
OpPPC64FSUBS
OpPPC64MULLD
@ -1853,8 +1854,6 @@ const (
OpPPC64ROTL
OpPPC64ROTLW
OpPPC64LoweredAdd64Carry
OpPPC64ADDconstForCarry
OpPPC64MaskIfNotCarry
OpPPC64SRADconst
OpPPC64SRAWconst
OpPPC64SRDconst
@ -2027,8 +2026,6 @@ const (
OpPPC64FlagEQ
OpPPC64FlagLT
OpPPC64FlagGT
OpPPC64FlagCarrySet
OpPPC64FlagCarryClear
OpRISCV64ADD
OpRISCV64ADDI
@ -24317,6 +24314,20 @@ var opcodeTable = [...]opInfo{
},
},
},
{
name: "SUBFCconst",
auxType: auxInt64,
argLen: 1,
asm: ppc64.ASUBC,
reg: regInfo{
inputs: []inputInfo{
{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
},
outputs: []outputInfo{
{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
},
},
},
{
name: "FSUB",
argLen: 2,
@ -24683,28 +24694,6 @@ var opcodeTable = [...]opInfo{
},
},
},
{
name: "ADDconstForCarry",
auxType: auxInt16,
argLen: 1,
asm: ppc64.AADDC,
reg: regInfo{
inputs: []inputInfo{
{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
},
clobbers: 2147483648, // R31
},
},
{
name: "MaskIfNotCarry",
argLen: 1,
asm: ppc64.AADDME,
reg: regInfo{
outputs: []outputInfo{
{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
},
},
},
{
name: "SRADconst",
auxType: auxInt64,
@ -26964,16 +26953,6 @@ var opcodeTable = [...]opInfo{
argLen: 0,
reg: regInfo{},
},
{
name: "FlagCarrySet",
argLen: 0,
reg: regInfo{},
},
{
name: "FlagCarryClear",
argLen: 0,
reg: regInfo{},
},
{
name: "ADD",

View File

@ -6,5 +6,5 @@ package ssa
// machine-independent optimization
func opt(f *Func) {
applyRewrite(f, rewriteBlockgeneric, rewriteValuegeneric)
applyRewrite(f, rewriteBlockgeneric, rewriteValuegeneric, removeDeadValues)
}

View File

@ -588,7 +588,7 @@ func (s *regAllocState) init(f *Func) {
if s.f.Config.hasGReg {
s.allocatable &^= 1 << s.GReg
}
if s.f.Config.ctxt.Framepointer_enabled && s.f.Config.FPReg >= 0 {
if objabi.Framepointer_enabled && s.f.Config.FPReg >= 0 {
s.allocatable &^= 1 << uint(s.f.Config.FPReg)
}
if s.f.Config.LinkReg != -1 {

View File

@ -20,7 +20,15 @@ import (
"path/filepath"
)
func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter) {
type deadValueChoice bool
const (
leaveDeadValues deadValueChoice = false
removeDeadValues = true
)
// deadcode indicates that rewrite should try to remove any values that become dead.
func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter, deadcode deadValueChoice) {
// repeat rewrites until we find no more rewrites
pendingLines := f.cachedLineStarts // Holds statement boundaries that need to be moved to a new value/block
pendingLines.clear()
@ -56,6 +64,18 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter) {
*v0 = *v
v0.Args = append([]*Value{}, v.Args...) // make a new copy, not aliasing
}
if v.Uses == 0 && v.removeable() {
if v.Op != OpInvalid && deadcode == removeDeadValues {
// Reset any values that are now unused, so that we decrement
// the use count of all of its arguments.
// Not quite a deadcode pass, because it does not handle cycles.
// But it should help Uses==1 rules to fire.
v.reset(OpInvalid)
change = true
}
// No point rewriting values which aren't used.
continue
}
vchange := phielimValue(v)
if vchange && debug > 1 {
@ -631,6 +651,10 @@ func auxIntToFlagConstant(x int64) flagConstant {
return flagConstant(x)
}
func auxIntToOp(cc int64) Op {
return Op(cc)
}
func boolToAuxInt(b bool) int64 {
if b {
return 1
@ -674,6 +698,10 @@ func flagConstantToAuxInt(x flagConstant) int64 {
return int64(x)
}
func opToAuxInt(o Op) int64 {
return int64(o)
}
func auxToString(i interface{}) string {
return i.(string)
}
@ -707,13 +735,6 @@ func s390xCCMaskToAux(c s390x.CCMask) interface{} {
func s390xRotateParamsToAux(r s390x.RotateParams) interface{} {
return r
}
func cCopToAux(o Op) interface{} {
return o
}
func auxToCCop(cc interface{}) Op {
return cc.(Op)
}
// uaddOvf reports whether unsigned a+b would overflow.
func uaddOvf(a, b int64) bool {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -428,8 +428,6 @@ func rewriteValuePPC64(v *Value) bool {
return rewriteValuePPC64_OpPPC64ADD(v)
case OpPPC64ADDconst:
return rewriteValuePPC64_OpPPC64ADDconst(v)
case OpPPC64ADDconstForCarry:
return rewriteValuePPC64_OpPPC64ADDconstForCarry(v)
case OpPPC64AND:
return rewriteValuePPC64_OpPPC64AND(v)
case OpPPC64ANDN:
@ -570,8 +568,8 @@ func rewriteValuePPC64(v *Value) bool {
return rewriteValuePPC64_OpPPC64MOVWstorezero(v)
case OpPPC64MTVSRD:
return rewriteValuePPC64_OpPPC64MTVSRD(v)
case OpPPC64MaskIfNotCarry:
return rewriteValuePPC64_OpPPC64MaskIfNotCarry(v)
case OpPPC64NEG:
return rewriteValuePPC64_OpPPC64NEG(v)
case OpPPC64NOR:
return rewriteValuePPC64_OpPPC64NOR(v)
case OpPPC64NotEqual:
@ -600,6 +598,8 @@ func rewriteValuePPC64(v *Value) bool {
return rewriteValuePPC64_OpPPC64SRW(v)
case OpPPC64SUB:
return rewriteValuePPC64_OpPPC64SUB(v)
case OpPPC64SUBFCconst:
return rewriteValuePPC64_OpPPC64SUBFCconst(v)
case OpPPC64XOR:
return rewriteValuePPC64_OpPPC64XOR(v)
case OpPPC64XORconst:
@ -1025,15 +1025,14 @@ func rewriteValuePPC64_OpBitLen32(v *Value) bool {
b := v.Block
typ := &b.Func.Config.Types
// match: (BitLen32 x)
// result: (SUB (MOVDconst [32]) (CNTLZW <typ.Int> x))
// result: (SUBFCconst [32] (CNTLZW <typ.Int> x))
for {
x := v_0
v.reset(OpPPC64SUB)
v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
v0.AuxInt = int64ToAuxInt(32)
v1 := b.NewValue0(v.Pos, OpPPC64CNTLZW, typ.Int)
v1.AddArg(x)
v.AddArg2(v0, v1)
v.reset(OpPPC64SUBFCconst)
v.AuxInt = int64ToAuxInt(32)
v0 := b.NewValue0(v.Pos, OpPPC64CNTLZW, typ.Int)
v0.AddArg(x)
v.AddArg(v0)
return true
}
}
@ -1042,15 +1041,14 @@ func rewriteValuePPC64_OpBitLen64(v *Value) bool {
b := v.Block
typ := &b.Func.Config.Types
// match: (BitLen64 x)
// result: (SUB (MOVDconst [64]) (CNTLZD <typ.Int> x))
// result: (SUBFCconst [64] (CNTLZD <typ.Int> x))
for {
x := v_0
v.reset(OpPPC64SUB)
v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
v0.AuxInt = int64ToAuxInt(64)
v1 := b.NewValue0(v.Pos, OpPPC64CNTLZD, typ.Int)
v1.AddArg(x)
v.AddArg2(v0, v1)
v.reset(OpPPC64SUBFCconst)
v.AuxInt = int64ToAuxInt(64)
v0 := b.NewValue0(v.Pos, OpPPC64CNTLZD, typ.Int)
v0.AddArg(x)
v.AddArg(v0)
return true
}
}
@ -3961,6 +3959,76 @@ func rewriteValuePPC64_OpPPC64ADD(v *Value) bool {
}
break
}
// match: (ADD (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y))))
// result: (ROTL x y)
for {
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
if v_0.Op != OpPPC64SLD {
continue
}
_ = v_0.Args[1]
x := v_0.Args[0]
v_0_1 := v_0.Args[1]
if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int64 || auxIntToInt64(v_0_1.AuxInt) != 63 {
continue
}
y := v_0_1.Args[0]
if v_1.Op != OpPPC64SRD {
continue
}
_ = v_1.Args[1]
if x != v_1.Args[0] {
continue
}
v_1_1 := v_1.Args[1]
if v_1_1.Op != OpPPC64SUBFCconst || v_1_1.Type != typ.UInt || auxIntToInt64(v_1_1.AuxInt) != 64 {
continue
}
v_1_1_0 := v_1_1.Args[0]
if v_1_1_0.Op != OpPPC64ANDconst || v_1_1_0.Type != typ.UInt || auxIntToInt64(v_1_1_0.AuxInt) != 63 || y != v_1_1_0.Args[0] {
continue
}
v.reset(OpPPC64ROTL)
v.AddArg2(x, y)
return true
}
break
}
// match: (ADD (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y))))
// result: (ROTLW x y)
for {
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
if v_0.Op != OpPPC64SLW {
continue
}
_ = v_0.Args[1]
x := v_0.Args[0]
v_0_1 := v_0.Args[1]
if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int32 || auxIntToInt64(v_0_1.AuxInt) != 31 {
continue
}
y := v_0_1.Args[0]
if v_1.Op != OpPPC64SRW {
continue
}
_ = v_1.Args[1]
if x != v_1.Args[0] {
continue
}
v_1_1 := v_1.Args[1]
if v_1_1.Op != OpPPC64SUBFCconst || v_1_1.Type != typ.UInt || auxIntToInt64(v_1_1.AuxInt) != 32 {
continue
}
v_1_1_0 := v_1_1.Args[0]
if v_1_1_0.Op != OpPPC64ANDconst || v_1_1_0.Type != typ.UInt || auxIntToInt64(v_1_1_0.AuxInt) != 31 || y != v_1_1_0.Args[0] {
continue
}
v.reset(OpPPC64ROTLW)
v.AddArg2(x, y)
return true
}
break
}
// match: (ADD (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y))))
// result: (ROTLW x y)
for {
@ -4073,38 +4141,22 @@ func rewriteValuePPC64_OpPPC64ADDconst(v *Value) bool {
v.AddArg(x)
return true
}
return false
}
func rewriteValuePPC64_OpPPC64ADDconstForCarry(v *Value) bool {
v_0 := v.Args[0]
// match: (ADDconstForCarry [c] (MOVDconst [d]))
// cond: c < 0 && (c < 0 || int64(c) + d >= 0)
// result: (FlagCarryClear)
// match: (ADDconst [c] (SUBFCconst [d] x))
// cond: is32Bit(c+d)
// result: (SUBFCconst [c+d] x)
for {
c := auxIntToInt16(v.AuxInt)
if v_0.Op != OpPPC64MOVDconst {
c := auxIntToInt64(v.AuxInt)
if v_0.Op != OpPPC64SUBFCconst {
break
}
d := auxIntToInt64(v_0.AuxInt)
if !(c < 0 && (c < 0 || int64(c)+d >= 0)) {
x := v_0.Args[0]
if !(is32Bit(c + d)) {
break
}
v.reset(OpPPC64FlagCarryClear)
return true
}
// match: (ADDconstForCarry [c] (MOVDconst [d]))
// cond: c < 0 && c >= 0 && int64(c) + d < 0
// result: (FlagCarrySet)
for {
c := auxIntToInt16(v.AuxInt)
if v_0.Op != OpPPC64MOVDconst {
break
}
d := auxIntToInt64(v_0.AuxInt)
if !(c < 0 && c >= 0 && int64(c)+d < 0) {
break
}
v.reset(OpPPC64FlagCarrySet)
v.reset(OpPPC64SUBFCconst)
v.AuxInt = int64ToAuxInt(c + d)
v.AddArg(x)
return true
}
return false
@ -10374,46 +10426,40 @@ func rewriteValuePPC64_OpPPC64MTVSRD(v *Value) bool {
}
return false
}
func rewriteValuePPC64_OpPPC64MaskIfNotCarry(v *Value) bool {
func rewriteValuePPC64_OpPPC64NEG(v *Value) bool {
v_0 := v.Args[0]
// match: (MaskIfNotCarry (ADDconstForCarry [c] (ANDconst [d] _)))
// cond: c < 0 && d > 0 && int64(c) + d < 0
// result: (MOVDconst [-1])
// match: (NEG (ADDconst [c] x))
// cond: is32Bit(-c)
// result: (SUBFCconst [-c] x)
for {
if v_0.Op != OpPPC64ADDconstForCarry {
if v_0.Op != OpPPC64ADDconst {
break
}
c := auxIntToInt16(v_0.AuxInt)
v_0_0 := v_0.Args[0]
if v_0_0.Op != OpPPC64ANDconst {
c := auxIntToInt64(v_0.AuxInt)
x := v_0.Args[0]
if !(is32Bit(-c)) {
break
}
d := auxIntToInt64(v_0_0.AuxInt)
if !(c < 0 && d > 0 && int64(c)+d < 0) {
break
}
v.reset(OpPPC64MOVDconst)
v.AuxInt = int64ToAuxInt(-1)
v.reset(OpPPC64SUBFCconst)
v.AuxInt = int64ToAuxInt(-c)
v.AddArg(x)
return true
}
// match: (MaskIfNotCarry (FlagCarrySet))
// result: (MOVDconst [0])
// match: (NEG (SUBFCconst [c] x))
// cond: is32Bit(-c)
// result: (ADDconst [-c] x)
for {
if v_0.Op != OpPPC64FlagCarrySet {
if v_0.Op != OpPPC64SUBFCconst {
break
}
v.reset(OpPPC64MOVDconst)
v.AuxInt = int64ToAuxInt(0)
return true
}
// match: (MaskIfNotCarry (FlagCarryClear))
// result: (MOVDconst [-1])
for {
if v_0.Op != OpPPC64FlagCarryClear {
c := auxIntToInt64(v_0.AuxInt)
x := v_0.Args[0]
if !(is32Bit(-c)) {
break
}
v.reset(OpPPC64MOVDconst)
v.AuxInt = int64ToAuxInt(-1)
v.reset(OpPPC64ADDconst)
v.AuxInt = int64ToAuxInt(-c)
v.AddArg(x)
return true
}
return false
@ -10592,6 +10638,76 @@ func rewriteValuePPC64_OpPPC64OR(v *Value) bool {
}
break
}
// match: ( OR (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y))))
// result: (ROTL x y)
for {
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
if v_0.Op != OpPPC64SLD {
continue
}
_ = v_0.Args[1]
x := v_0.Args[0]
v_0_1 := v_0.Args[1]
if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int64 || auxIntToInt64(v_0_1.AuxInt) != 63 {
continue
}
y := v_0_1.Args[0]
if v_1.Op != OpPPC64SRD {
continue
}
_ = v_1.Args[1]
if x != v_1.Args[0] {
continue
}
v_1_1 := v_1.Args[1]
if v_1_1.Op != OpPPC64SUBFCconst || v_1_1.Type != typ.UInt || auxIntToInt64(v_1_1.AuxInt) != 64 {
continue
}
v_1_1_0 := v_1_1.Args[0]
if v_1_1_0.Op != OpPPC64ANDconst || v_1_1_0.Type != typ.UInt || auxIntToInt64(v_1_1_0.AuxInt) != 63 || y != v_1_1_0.Args[0] {
continue
}
v.reset(OpPPC64ROTL)
v.AddArg2(x, y)
return true
}
break
}
// match: ( OR (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y))))
// result: (ROTLW x y)
for {
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
if v_0.Op != OpPPC64SLW {
continue
}
_ = v_0.Args[1]
x := v_0.Args[0]
v_0_1 := v_0.Args[1]
if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int32 || auxIntToInt64(v_0_1.AuxInt) != 31 {
continue
}
y := v_0_1.Args[0]
if v_1.Op != OpPPC64SRW {
continue
}
_ = v_1.Args[1]
if x != v_1.Args[0] {
continue
}
v_1_1 := v_1.Args[1]
if v_1_1.Op != OpPPC64SUBFCconst || v_1_1.Type != typ.UInt || auxIntToInt64(v_1_1.AuxInt) != 32 {
continue
}
v_1_1_0 := v_1_1.Args[0]
if v_1_1_0.Op != OpPPC64ANDconst || v_1_1_0.Type != typ.UInt || auxIntToInt64(v_1_1_0.AuxInt) != 31 || y != v_1_1_0.Args[0] {
continue
}
v.reset(OpPPC64ROTLW)
v.AddArg2(x, y)
return true
}
break
}
// match: ( OR (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y))))
// result: (ROTLW x y)
for {
@ -12191,6 +12307,69 @@ func rewriteValuePPC64_OpPPC64SUB(v *Value) bool {
v.AddArg(x)
return true
}
// match: (SUB (MOVDconst [c]) x)
// cond: is32Bit(c)
// result: (SUBFCconst [c] x)
for {
if v_0.Op != OpPPC64MOVDconst {
break
}
c := auxIntToInt64(v_0.AuxInt)
x := v_1
if !(is32Bit(c)) {
break
}
v.reset(OpPPC64SUBFCconst)
v.AuxInt = int64ToAuxInt(c)
v.AddArg(x)
return true
}
return false
}
func rewriteValuePPC64_OpPPC64SUBFCconst(v *Value) bool {
v_0 := v.Args[0]
// match: (SUBFCconst [c] (NEG x))
// result: (ADDconst [c] x)
for {
c := auxIntToInt64(v.AuxInt)
if v_0.Op != OpPPC64NEG {
break
}
x := v_0.Args[0]
v.reset(OpPPC64ADDconst)
v.AuxInt = int64ToAuxInt(c)
v.AddArg(x)
return true
}
// match: (SUBFCconst [c] (SUBFCconst [d] x))
// cond: is32Bit(c-d)
// result: (ADDconst [c-d] x)
for {
c := auxIntToInt64(v.AuxInt)
if v_0.Op != OpPPC64SUBFCconst {
break
}
d := auxIntToInt64(v_0.AuxInt)
x := v_0.Args[0]
if !(is32Bit(c - d)) {
break
}
v.reset(OpPPC64ADDconst)
v.AuxInt = int64ToAuxInt(c - d)
v.AddArg(x)
return true
}
// match: (SUBFCconst [0] x)
// result: (NEG x)
for {
if auxIntToInt64(v.AuxInt) != 0 {
break
}
x := v_0
v.reset(OpPPC64NEG)
v.AddArg(x)
return true
}
return false
}
func rewriteValuePPC64_OpPPC64XOR(v *Value) bool {
@ -12286,6 +12465,76 @@ func rewriteValuePPC64_OpPPC64XOR(v *Value) bool {
}
break
}
// match: (XOR (SLD x (ANDconst <typ.Int64> [63] y)) (SRD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y))))
// result: (ROTL x y)
for {
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
if v_0.Op != OpPPC64SLD {
continue
}
_ = v_0.Args[1]
x := v_0.Args[0]
v_0_1 := v_0.Args[1]
if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int64 || auxIntToInt64(v_0_1.AuxInt) != 63 {
continue
}
y := v_0_1.Args[0]
if v_1.Op != OpPPC64SRD {
continue
}
_ = v_1.Args[1]
if x != v_1.Args[0] {
continue
}
v_1_1 := v_1.Args[1]
if v_1_1.Op != OpPPC64SUBFCconst || v_1_1.Type != typ.UInt || auxIntToInt64(v_1_1.AuxInt) != 64 {
continue
}
v_1_1_0 := v_1_1.Args[0]
if v_1_1_0.Op != OpPPC64ANDconst || v_1_1_0.Type != typ.UInt || auxIntToInt64(v_1_1_0.AuxInt) != 63 || y != v_1_1_0.Args[0] {
continue
}
v.reset(OpPPC64ROTL)
v.AddArg2(x, y)
return true
}
break
}
// match: (XOR (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y))))
// result: (ROTLW x y)
for {
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
if v_0.Op != OpPPC64SLW {
continue
}
_ = v_0.Args[1]
x := v_0.Args[0]
v_0_1 := v_0.Args[1]
if v_0_1.Op != OpPPC64ANDconst || v_0_1.Type != typ.Int32 || auxIntToInt64(v_0_1.AuxInt) != 31 {
continue
}
y := v_0_1.Args[0]
if v_1.Op != OpPPC64SRW {
continue
}
_ = v_1.Args[1]
if x != v_1.Args[0] {
continue
}
v_1_1 := v_1.Args[1]
if v_1_1.Op != OpPPC64SUBFCconst || v_1_1.Type != typ.UInt || auxIntToInt64(v_1_1.AuxInt) != 32 {
continue
}
v_1_1_0 := v_1_1.Args[0]
if v_1_1_0.Op != OpPPC64ANDconst || v_1_1_0.Type != typ.UInt || auxIntToInt64(v_1_1_0.AuxInt) != 31 || y != v_1_1_0.Args[0] {
continue
}
v.reset(OpPPC64ROTLW)
v.AddArg2(x, y)
return true
}
break
}
// match: (XOR (SLW x (ANDconst <typ.Int32> [31] y)) (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y))))
// result: (ROTLW x y)
for {
@ -13257,6 +13506,28 @@ func rewriteValuePPC64_OpRsh32Ux64(v *Value) bool {
v.AddArg2(x, v0)
return true
}
// match: (Rsh32Ux64 x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))
// result: (SRW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))
for {
x := v_0
if v_1.Op != OpPPC64SUBFCconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 32 {
break
}
v_1_0 := v_1.Args[0]
if v_1_0.Op != OpPPC64ANDconst || v_1_0.Type != typ.UInt || auxIntToInt64(v_1_0.AuxInt) != 31 {
break
}
y := v_1_0.Args[0]
v.reset(OpPPC64SRW)
v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt)
v0.AuxInt = int64ToAuxInt(32)
v1 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt)
v1.AuxInt = int64ToAuxInt(31)
v1.AddArg(y)
v0.AddArg(v1)
v.AddArg2(x, v0)
return true
}
// match: (Rsh32Ux64 x (SUB <typ.UInt> (MOVDconst [32]) (AND <typ.UInt> y (MOVDconst [31]))))
// result: (SRW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))
for {
@ -13294,6 +13565,37 @@ func rewriteValuePPC64_OpRsh32Ux64(v *Value) bool {
}
break
}
// match: (Rsh32Ux64 x (SUBFCconst <typ.UInt> [32] (AND <typ.UInt> y (MOVDconst [31]))))
// result: (SRW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))
for {
x := v_0
if v_1.Op != OpPPC64SUBFCconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 32 {
break
}
v_1_0 := v_1.Args[0]
if v_1_0.Op != OpPPC64AND || v_1_0.Type != typ.UInt {
break
}
_ = v_1_0.Args[1]
v_1_0_0 := v_1_0.Args[0]
v_1_0_1 := v_1_0.Args[1]
for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 {
y := v_1_0_0
if v_1_0_1.Op != OpPPC64MOVDconst || auxIntToInt64(v_1_0_1.AuxInt) != 31 {
continue
}
v.reset(OpPPC64SRW)
v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt)
v0.AuxInt = int64ToAuxInt(32)
v1 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt)
v1.AuxInt = int64ToAuxInt(31)
v1.AddArg(y)
v0.AddArg(v1)
v.AddArg2(x, v0)
return true
}
break
}
// match: (Rsh32Ux64 x y)
// result: (SRW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [32]))))
for {
@ -13564,6 +13866,28 @@ func rewriteValuePPC64_OpRsh32x64(v *Value) bool {
v.AddArg2(x, v0)
return true
}
// match: (Rsh32x64 x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))
// result: (SRAW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))
for {
x := v_0
if v_1.Op != OpPPC64SUBFCconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 32 {
break
}
v_1_0 := v_1.Args[0]
if v_1_0.Op != OpPPC64ANDconst || v_1_0.Type != typ.UInt || auxIntToInt64(v_1_0.AuxInt) != 31 {
break
}
y := v_1_0.Args[0]
v.reset(OpPPC64SRAW)
v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt)
v0.AuxInt = int64ToAuxInt(32)
v1 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt)
v1.AuxInt = int64ToAuxInt(31)
v1.AddArg(y)
v0.AddArg(v1)
v.AddArg2(x, v0)
return true
}
// match: (Rsh32x64 x (SUB <typ.UInt> (MOVDconst [32]) (AND <typ.UInt> y (MOVDconst [31]))))
// result: (SRAW x (SUB <typ.UInt> (MOVDconst [32]) (ANDconst <typ.UInt> [31] y)))
for {
@ -13601,6 +13925,37 @@ func rewriteValuePPC64_OpRsh32x64(v *Value) bool {
}
break
}
// match: (Rsh32x64 x (SUBFCconst <typ.UInt> [32] (AND <typ.UInt> y (MOVDconst [31]))))
// result: (SRAW x (SUBFCconst <typ.UInt> [32] (ANDconst <typ.UInt> [31] y)))
for {
x := v_0
if v_1.Op != OpPPC64SUBFCconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 32 {
break
}
v_1_0 := v_1.Args[0]
if v_1_0.Op != OpPPC64AND || v_1_0.Type != typ.UInt {
break
}
_ = v_1_0.Args[1]
v_1_0_0 := v_1_0.Args[0]
v_1_0_1 := v_1_0.Args[1]
for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 {
y := v_1_0_0
if v_1_0_1.Op != OpPPC64MOVDconst || auxIntToInt64(v_1_0_1.AuxInt) != 31 {
continue
}
v.reset(OpPPC64SRAW)
v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt)
v0.AuxInt = int64ToAuxInt(32)
v1 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt)
v1.AuxInt = int64ToAuxInt(31)
v1.AddArg(y)
v0.AddArg(v1)
v.AddArg2(x, v0)
return true
}
break
}
// match: (Rsh32x64 x y)
// result: (SRAW x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [32]))))
for {
@ -13869,6 +14224,28 @@ func rewriteValuePPC64_OpRsh64Ux64(v *Value) bool {
v.AddArg2(x, v0)
return true
}
// match: (Rsh64Ux64 x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))
// result: (SRD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))
for {
x := v_0
if v_1.Op != OpPPC64SUBFCconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 64 {
break
}
v_1_0 := v_1.Args[0]
if v_1_0.Op != OpPPC64ANDconst || v_1_0.Type != typ.UInt || auxIntToInt64(v_1_0.AuxInt) != 63 {
break
}
y := v_1_0.Args[0]
v.reset(OpPPC64SRD)
v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt)
v0.AuxInt = int64ToAuxInt(64)
v1 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt)
v1.AuxInt = int64ToAuxInt(63)
v1.AddArg(y)
v0.AddArg(v1)
v.AddArg2(x, v0)
return true
}
// match: (Rsh64Ux64 x (SUB <typ.UInt> (MOVDconst [64]) (AND <typ.UInt> y (MOVDconst [63]))))
// result: (SRD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))
for {
@ -13906,6 +14283,37 @@ func rewriteValuePPC64_OpRsh64Ux64(v *Value) bool {
}
break
}
// match: (Rsh64Ux64 x (SUBFCconst <typ.UInt> [64] (AND <typ.UInt> y (MOVDconst [63]))))
// result: (SRD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))
for {
x := v_0
if v_1.Op != OpPPC64SUBFCconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 64 {
break
}
v_1_0 := v_1.Args[0]
if v_1_0.Op != OpPPC64AND || v_1_0.Type != typ.UInt {
break
}
_ = v_1_0.Args[1]
v_1_0_0 := v_1_0.Args[0]
v_1_0_1 := v_1_0.Args[1]
for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 {
y := v_1_0_0
if v_1_0_1.Op != OpPPC64MOVDconst || auxIntToInt64(v_1_0_1.AuxInt) != 63 {
continue
}
v.reset(OpPPC64SRD)
v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt)
v0.AuxInt = int64ToAuxInt(64)
v1 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt)
v1.AuxInt = int64ToAuxInt(63)
v1.AddArg(y)
v0.AddArg(v1)
v.AddArg2(x, v0)
return true
}
break
}
// match: (Rsh64Ux64 x y)
// result: (SRD x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [64]))))
for {
@ -14176,6 +14584,28 @@ func rewriteValuePPC64_OpRsh64x64(v *Value) bool {
v.AddArg2(x, v0)
return true
}
// match: (Rsh64x64 x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))
// result: (SRAD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))
for {
x := v_0
if v_1.Op != OpPPC64SUBFCconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 64 {
break
}
v_1_0 := v_1.Args[0]
if v_1_0.Op != OpPPC64ANDconst || v_1_0.Type != typ.UInt || auxIntToInt64(v_1_0.AuxInt) != 63 {
break
}
y := v_1_0.Args[0]
v.reset(OpPPC64SRAD)
v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt)
v0.AuxInt = int64ToAuxInt(64)
v1 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt)
v1.AuxInt = int64ToAuxInt(63)
v1.AddArg(y)
v0.AddArg(v1)
v.AddArg2(x, v0)
return true
}
// match: (Rsh64x64 x (SUB <typ.UInt> (MOVDconst [64]) (AND <typ.UInt> y (MOVDconst [63]))))
// result: (SRAD x (SUB <typ.UInt> (MOVDconst [64]) (ANDconst <typ.UInt> [63] y)))
for {
@ -14213,6 +14643,37 @@ func rewriteValuePPC64_OpRsh64x64(v *Value) bool {
}
break
}
// match: (Rsh64x64 x (SUBFCconst <typ.UInt> [64] (AND <typ.UInt> y (MOVDconst [63]))))
// result: (SRAD x (SUBFCconst <typ.UInt> [64] (ANDconst <typ.UInt> [63] y)))
for {
x := v_0
if v_1.Op != OpPPC64SUBFCconst || v_1.Type != typ.UInt || auxIntToInt64(v_1.AuxInt) != 64 {
break
}
v_1_0 := v_1.Args[0]
if v_1_0.Op != OpPPC64AND || v_1_0.Type != typ.UInt {
break
}
_ = v_1_0.Args[1]
v_1_0_0 := v_1_0.Args[0]
v_1_0_1 := v_1_0.Args[1]
for _i0 := 0; _i0 <= 1; _i0, v_1_0_0, v_1_0_1 = _i0+1, v_1_0_1, v_1_0_0 {
y := v_1_0_0
if v_1_0_1.Op != OpPPC64MOVDconst || auxIntToInt64(v_1_0_1.AuxInt) != 63 {
continue
}
v.reset(OpPPC64SRAD)
v0 := b.NewValue0(v.Pos, OpPPC64SUBFCconst, typ.UInt)
v0.AuxInt = int64ToAuxInt(64)
v1 := b.NewValue0(v.Pos, OpPPC64ANDconst, typ.UInt)
v1.AuxInt = int64ToAuxInt(63)
v1.AddArg(y)
v0.AddArg(v1)
v.AddArg2(x, v0)
return true
}
break
}
// match: (Rsh64x64 x y)
// result: (SRAD x (ISEL [0] y (MOVDconst [-1]) (CMPU y (MOVDconst [64]))))
for {

File diff suppressed because it is too large Load Diff

View File

@ -328,9 +328,10 @@ func rewriteValuedec_OpStore(v *Value) bool {
v.AddArg3(v0, len, v1)
return true
}
// match: (Store dst (SliceMake ptr len cap) mem)
// result: (Store {typ.Int} (OffPtr <typ.IntPtr> [2*config.PtrSize] dst) cap (Store {typ.Int} (OffPtr <typ.IntPtr> [config.PtrSize] dst) len (Store {typ.BytePtr} dst ptr mem)))
// match: (Store {t} dst (SliceMake ptr len cap) mem)
// result: (Store {typ.Int} (OffPtr <typ.IntPtr> [2*config.PtrSize] dst) cap (Store {typ.Int} (OffPtr <typ.IntPtr> [config.PtrSize] dst) len (Store {t.Elem().PtrTo()} dst ptr mem)))
for {
t := auxToType(v.Aux)
dst := v_0
if v_1.Op != OpSliceMake {
break
@ -350,7 +351,7 @@ func rewriteValuedec_OpStore(v *Value) bool {
v2.AuxInt = int64ToAuxInt(config.PtrSize)
v2.AddArg(dst)
v3 := b.NewValue0(v.Pos, OpStore, types.TypeMem)
v3.Aux = typeToAux(typ.BytePtr)
v3.Aux = typeToAux(t.Elem().PtrTo())
v3.AddArg3(dst, ptr, mem)
v1.AddArg3(v2, len, v3)
v.AddArg3(v0, cap, v1)

View File

@ -18,6 +18,7 @@ func softfloat(f *Func) {
for _, b := range f.Blocks {
for _, v := range b.Values {
if v.Type.IsFloat() {
f.unCache(v)
switch v.Op {
case OpPhi, OpLoad, OpArg:
if v.Type.Size() == 4 {
@ -72,7 +73,7 @@ func softfloat(f *Func) {
if newInt64 && f.Config.RegSize == 4 {
// On 32bit arch, decompose Uint64 introduced in the switch above.
decomposeBuiltIn(f)
applyRewrite(f, rewriteBlockdec64, rewriteValuedec64)
applyRewrite(f, rewriteBlockdec64, rewriteValuedec64, removeDeadValues)
}
}

View File

@ -54,6 +54,9 @@ type Value struct {
// nor a slot on Go stack, and the generation of this value is delayed to its use time.
OnWasmStack bool
// Is this value in the per-function constant cache? If so, remove from cache before changing it or recycling it.
InCache bool
// Storage for the first three args
argstorage [3]*Value
}
@ -210,7 +213,7 @@ func (v *Value) auxString() string {
}
return s + fmt.Sprintf(" [%s]", v.AuxValAndOff())
case auxCCop:
return fmt.Sprintf(" {%s}", v.Aux.(Op))
return fmt.Sprintf(" {%s}", Op(v.AuxInt))
case auxS390XCCMask, auxS390XRotateParams:
return fmt.Sprintf(" {%v}", v.Aux)
case auxFlagConstant:
@ -332,6 +335,9 @@ func (v *Value) resetArgs() {
// of cmd/compile by almost 10%, and slows it down.
//go:noinline
func (v *Value) reset(op Op) {
if v.InCache {
v.Block.Func.unCache(v)
}
v.Op = op
v.resetArgs()
v.AuxInt = 0
@ -342,6 +348,9 @@ func (v *Value) reset(op Op) {
// It modifies v to be (Copy a).
//go:noinline
func (v *Value) copyOf(a *Value) {
if v.InCache {
v.Block.Func.unCache(v)
}
v.Op = OpCopy
v.resetArgs()
v.AddArg(a)
@ -460,3 +469,23 @@ func (v *Value) LackingPos() bool {
return v.Op == OpVarDef || v.Op == OpVarKill || v.Op == OpVarLive || v.Op == OpPhi ||
(v.Op == OpFwdRef || v.Op == OpCopy) && v.Type == types.TypeMem
}
// removeable reports whether the value v can be removed from the SSA graph entirely
// if its use count drops to 0.
func (v *Value) removeable() bool {
if v.Type.IsVoid() {
// Void ops, like nil pointer checks, must stay.
return false
}
if v.Type.IsMemory() {
// All memory ops aren't needed here, but we do need
// to keep calls at least (because they might have
// syncronization operations we can't see).
return false
}
if v.Op.HasSideEffects() {
// These are mostly synchronization operations.
return false
}
return true
}

View File

@ -31,7 +31,7 @@ func needwb(v *Value, zeroes map[ID]ZeroRegion) bool {
if !ok {
v.Fatalf("store aux is not a type: %s", v.LongString())
}
if !t.HasHeapPointer() {
if !t.HasPointers() {
return false
}
if IsStackAddr(v.Args[0]) {

View File

@ -1230,6 +1230,11 @@ func (t *Type) IsUnsafePtr() bool {
return t.Etype == TUNSAFEPTR
}
// IsUintptr reports whether t is an uintptr.
func (t *Type) IsUintptr() bool {
return t.Etype == TUINTPTR
}
// IsPtrShaped reports whether t is represented by a single machine pointer.
// In addition to regular Go pointer types, this includes map, channel, and
// function types and unsafe.Pointer. It does not include array or struct types
@ -1398,14 +1403,9 @@ func (t *Type) IsUntyped() bool {
return false
}
// TODO(austin): We probably only need HasHeapPointer. See
// golang.org/cl/73412 for discussion.
// HasPointers reports whether t contains a heap pointer.
// Note that this function ignores pointers to go:notinheap types.
func (t *Type) HasPointers() bool {
return t.hasPointers1(false)
}
func (t *Type) hasPointers1(ignoreNotInHeap bool) bool {
switch t.Etype {
case TINT, TUINT, TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64,
TUINT64, TUINTPTR, TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128, TBOOL, TSSA:
@ -1415,34 +1415,27 @@ func (t *Type) hasPointers1(ignoreNotInHeap bool) bool {
if t.NumElem() == 0 { // empty array has no pointers
return false
}
return t.Elem().hasPointers1(ignoreNotInHeap)
return t.Elem().HasPointers()
case TSTRUCT:
for _, t1 := range t.Fields().Slice() {
if t1.Type.hasPointers1(ignoreNotInHeap) {
if t1.Type.HasPointers() {
return true
}
}
return false
case TPTR, TSLICE:
return !(ignoreNotInHeap && t.Elem().NotInHeap())
return !t.Elem().NotInHeap()
case TTUPLE:
ttup := t.Extra.(*Tuple)
return ttup.first.hasPointers1(ignoreNotInHeap) || ttup.second.hasPointers1(ignoreNotInHeap)
return ttup.first.HasPointers() || ttup.second.HasPointers()
}
return true
}
// HasHeapPointer reports whether t contains a heap pointer.
// This is used for write barrier insertion, so it ignores
// pointers to go:notinheap types.
func (t *Type) HasHeapPointer() bool {
return t.hasPointers1(true)
}
func (t *Type) Symbol() *obj.LSym {
return TypeLinkSym(t)
}

View File

@ -261,8 +261,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
n.To.Reg = x86.REG_DX
}
j.To.Val = n
j2.To.Val = s.Pc()
j.To.SetTarget(n)
j2.To.SetTarget(s.Pc())
}
case ssa.Op386HMULL, ssa.Op386HMULLU:

View File

@ -1106,8 +1106,9 @@ func (t *tester) cgoTest(dt *distTest) error {
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest())
cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=external")
// A -g argument in CGO_CFLAGS should not affect how the test runs.
cmd.Env = append(cmd.Env, "CGO_CFLAGS=-g0")
// cgo should be able to cope with both -g arguments and colored
// diagnostics.
cmd.Env = append(cmd.Env, "CGO_CFLAGS=-g0 -fdiagnostics-color")
t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-ldflags", "-linkmode=auto")
t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-ldflags", "-linkmode=external")

View File

@ -7,13 +7,9 @@ package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"path"
"reflect"
"strconv"
"strings"
)
type fix struct {
@ -323,160 +319,12 @@ func declImports(gen *ast.GenDecl, path string) bool {
return false
}
// isPkgDot reports whether t is the expression "pkg.name"
// where pkg is an imported identifier.
func isPkgDot(t ast.Expr, pkg, name string) bool {
sel, ok := t.(*ast.SelectorExpr)
return ok && isTopName(sel.X, pkg) && sel.Sel.String() == name
}
// isPtrPkgDot reports whether f is the expression "*pkg.name"
// where pkg is an imported identifier.
func isPtrPkgDot(t ast.Expr, pkg, name string) bool {
ptr, ok := t.(*ast.StarExpr)
return ok && isPkgDot(ptr.X, pkg, name)
}
// isTopName reports whether n is a top-level unresolved identifier with the given name.
func isTopName(n ast.Expr, name string) bool {
id, ok := n.(*ast.Ident)
return ok && id.Name == name && id.Obj == nil
}
// isName reports whether n is an identifier with the given name.
func isName(n ast.Expr, name string) bool {
id, ok := n.(*ast.Ident)
return ok && id.String() == name
}
// isCall reports whether t is a call to pkg.name.
func isCall(t ast.Expr, pkg, name string) bool {
call, ok := t.(*ast.CallExpr)
return ok && isPkgDot(call.Fun, pkg, name)
}
// If n is an *ast.Ident, isIdent returns it; otherwise isIdent returns nil.
func isIdent(n interface{}) *ast.Ident {
id, _ := n.(*ast.Ident)
return id
}
// refersTo reports whether n is a reference to the same object as x.
func refersTo(n ast.Node, x *ast.Ident) bool {
id, ok := n.(*ast.Ident)
// The test of id.Name == x.Name handles top-level unresolved
// identifiers, which all have Obj == nil.
return ok && id.Obj == x.Obj && id.Name == x.Name
}
// isBlank reports whether n is the blank identifier.
func isBlank(n ast.Expr) bool {
return isName(n, "_")
}
// isEmptyString reports whether n is an empty string literal.
func isEmptyString(n ast.Expr) bool {
lit, ok := n.(*ast.BasicLit)
return ok && lit.Kind == token.STRING && len(lit.Value) == 2
}
func warn(pos token.Pos, msg string, args ...interface{}) {
if pos.IsValid() {
msg = "%s: " + msg
arg1 := []interface{}{fset.Position(pos).String()}
args = append(arg1, args...)
}
fmt.Fprintf(os.Stderr, msg+"\n", args...)
}
// countUses returns the number of uses of the identifier x in scope.
func countUses(x *ast.Ident, scope []ast.Stmt) int {
count := 0
ff := func(n interface{}) {
if n, ok := n.(ast.Node); ok && refersTo(n, x) {
count++
}
}
for _, n := range scope {
walk(n, ff)
}
return count
}
// rewriteUses replaces all uses of the identifier x and !x in scope
// with f(x.Pos()) and fnot(x.Pos()).
func rewriteUses(x *ast.Ident, f, fnot func(token.Pos) ast.Expr, scope []ast.Stmt) {
var lastF ast.Expr
ff := func(n interface{}) {
ptr, ok := n.(*ast.Expr)
if !ok {
return
}
nn := *ptr
// The child node was just walked and possibly replaced.
// If it was replaced and this is a negation, replace with fnot(p).
not, ok := nn.(*ast.UnaryExpr)
if ok && not.Op == token.NOT && not.X == lastF {
*ptr = fnot(nn.Pos())
return
}
if refersTo(nn, x) {
lastF = f(nn.Pos())
*ptr = lastF
}
}
for _, n := range scope {
walk(n, ff)
}
}
// assignsTo reports whether any of the code in scope assigns to or takes the address of x.
func assignsTo(x *ast.Ident, scope []ast.Stmt) bool {
assigned := false
ff := func(n interface{}) {
if assigned {
return
}
switch n := n.(type) {
case *ast.UnaryExpr:
// use of &x
if n.Op == token.AND && refersTo(n.X, x) {
assigned = true
return
}
case *ast.AssignStmt:
for _, l := range n.Lhs {
if refersTo(l, x) {
assigned = true
return
}
}
}
}
for _, n := range scope {
if assigned {
break
}
walk(n, ff)
}
return assigned
}
// newPkgDot returns an ast.Expr referring to "pkg.name" at position pos.
func newPkgDot(pos token.Pos, pkg, name string) ast.Expr {
return &ast.SelectorExpr{
X: &ast.Ident{
NamePos: pos,
Name: pkg,
},
Sel: &ast.Ident{
NamePos: pos,
Name: name,
},
}
}
// renameTop renames all references to the top-level name old.
// It reports whether it makes any changes.
func renameTop(f *ast.File, old, new string) bool {
@ -707,143 +555,3 @@ func rewriteImport(f *ast.File, oldPath, newPath string) (rewrote bool) {
}
return
}
func usesImport(f *ast.File, path string) (used bool) {
spec := importSpec(f, path)
if spec == nil {
return
}
name := spec.Name.String()
switch name {
case "<nil>":
// If the package name is not explicitly specified,
// make an educated guess. This is not guaranteed to be correct.
lastSlash := strings.LastIndex(path, "/")
if lastSlash == -1 {
name = path
} else {
name = path[lastSlash+1:]
}
case "_", ".":
// Not sure if this import is used - err on the side of caution.
return true
}
walk(f, func(n interface{}) {
sel, ok := n.(*ast.SelectorExpr)
if ok && isTopName(sel.X, name) {
used = true
}
})
return
}
func expr(s string) ast.Expr {
x, err := parser.ParseExpr(s)
if err != nil {
panic("parsing " + s + ": " + err.Error())
}
// Remove position information to avoid spurious newlines.
killPos(reflect.ValueOf(x))
return x
}
var posType = reflect.TypeOf(token.Pos(0))
func killPos(v reflect.Value) {
switch v.Kind() {
case reflect.Ptr, reflect.Interface:
if !v.IsNil() {
killPos(v.Elem())
}
case reflect.Slice:
n := v.Len()
for i := 0; i < n; i++ {
killPos(v.Index(i))
}
case reflect.Struct:
n := v.NumField()
for i := 0; i < n; i++ {
f := v.Field(i)
if f.Type() == posType {
f.SetInt(0)
continue
}
killPos(f)
}
}
}
// A Rename describes a single renaming.
type rename struct {
OldImport string // only apply rename if this import is present
NewImport string // add this import during rewrite
Old string // old name: p.T or *p.T
New string // new name: p.T or *p.T
}
func renameFix(tab []rename) func(*ast.File) bool {
return func(f *ast.File) bool {
return renameFixTab(f, tab)
}
}
func parseName(s string) (ptr bool, pkg, nam string) {
i := strings.Index(s, ".")
if i < 0 {
panic("parseName: invalid name " + s)
}
if strings.HasPrefix(s, "*") {
ptr = true
s = s[1:]
i--
}
pkg = s[:i]
nam = s[i+1:]
return
}
func renameFixTab(f *ast.File, tab []rename) bool {
fixed := false
added := map[string]bool{}
check := map[string]bool{}
for _, t := range tab {
if !imports(f, t.OldImport) {
continue
}
optr, opkg, onam := parseName(t.Old)
walk(f, func(n interface{}) {
np, ok := n.(*ast.Expr)
if !ok {
return
}
x := *np
if optr {
p, ok := x.(*ast.StarExpr)
if !ok {
return
}
x = p.X
}
if !isPkgDot(x, opkg, onam) {
return
}
if t.NewImport != "" && !added[t.NewImport] {
addImport(f, t.NewImport)
added[t.NewImport] = true
}
*np = expr(t.New)
check[t.OldImport] = true
fixed = true
})
}
for ipath := range check {
if !usesImport(f, ipath) {
deleteImport(f, ipath)
}
}
return fixed
}

View File

@ -7,8 +7,8 @@ require (
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340 // indirect
golang.org/x/arch v0.0.0-20200511175325-f7c78586839d
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
golang.org/x/mod v0.3.1-0.20200625141748-0b26df4a2231
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 // indirect
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054
golang.org/x/tools v0.0.0-20200901153117-6e59e24738da
golang.org/x/xerrors v0.0.0-20200806184451-1a77d5e9f316 // indirect
)

View File

@ -6,34 +6,34 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340 h1:S1+yTUaFPXuDZnPDbO+TrDFIjPzQraYH8/CwSlu9Fac=
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/arch v0.0.0-20200511175325-f7c78586839d h1:YvwchuJby5xEAPdBGmdAVSiVME50C+RJfJJwJJsGEV8=
golang.org/x/arch v0.0.0-20200511175325-f7c78586839d/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.1-0.20200625141748-0b26df4a2231 h1:R11LxkoUvECaAHdM5/ZOevSR7n+016EgTw8nbE1l+XM=
golang.org/x/mod v0.3.1-0.20200625141748-0b26df4a2231/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449 h1:xUIPaMhvROX9dhPvRCenIJtU78+lbEenGbgqB5hfHCQ=
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 h1:5B6i6EAiSYyejWfvc5Rc9BbI3rzIsrrXfAQBWnYfn+w=
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 h1:HHeAlu5H9b71C+Fx0K+1dGgVFN1DM1/wz4aoGOA5qS8=
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200901153117-6e59e24738da h1:8nFbt74voFOsM+Hb5XtF+1SNbbf3dzikH5osZO1hyyo=
golang.org/x/tools v0.0.0-20200901153117-6e59e24738da/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200806184451-1a77d5e9f316 h1:Jhw4VC65LaKnpq9FvcK+a8ZzrFm3D+UygvMMrhkOw70=
golang.org/x/xerrors v0.0.0-20200806184451-1a77d5e9f316/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View File

@ -916,6 +916,7 @@
// Dir string // directory holding files for this module, if any
// GoMod string // path to go.mod file used when loading this module, if any
// GoVersion string // go version used in module
// Retracted string // retraction information, if any (with -retracted or -u)
// Error *ModuleError // error loading module
// }
//
@ -947,14 +948,16 @@
// The -u flag adds information about available upgrades.
// When the latest version of a given module is newer than
// the current one, list -u sets the Module's Update field
// to information about the newer module.
// to information about the newer module. list -u will also set
// the module's Retracted field if the current version is retracted.
// The Module's String method indicates an available upgrade by
// formatting the newer version in brackets after the current version.
// If a version is retracted, the string "(retracted)" will follow it.
// For example, 'go list -m -u all' might print:
//
// my/main/module
// golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text
// rsc.io/pdf v0.1.1 [v0.1.2]
// rsc.io/pdf v0.1.1 (retracted) [v0.1.2]
//
// (For tools, 'go list -m -u -json all' may be more convenient to parse.)
//
@ -964,6 +967,14 @@
// the default output format to display the module path followed by the
// space-separated version list.
//
// The -retracted flag causes list to report information about retracted
// module versions. When -retracted is used with -f or -json, the Retracted
// field will be set to a string explaining why the version was retracted.
// The string is taken from comments on the retract directive in the
// module's go.mod file. When -retracted is used with -versions, retracted
// versions are listed together with unretracted versions. The -retracted
// flag may be used with or without -m.
//
// The arguments to list -m are interpreted as a list of modules, not packages.
// The main module is the module containing the current directory.
// The active modules are the main module and its dependencies.
@ -1100,9 +1111,14 @@
// module path and version pair. If the @v is omitted, a replacement without
// a version on the left side is dropped.
//
// The -retract=version and -dropretract=version flags add and drop a
// retraction on the given version. The version may be a single version
// like "v1.2.3" or a closed interval like "[v1.1.0-v1.1.9]". Note that
// -retract=version is a no-op if that retraction already exists.
//
// The -require, -droprequire, -exclude, -dropexclude, -replace,
// and -dropreplace editing flags may be repeated, and the changes
// are applied in the order given.
// -dropreplace, -retract, and -dropretract editing flags may be repeated,
// and the changes are applied in the order given.
//
// The -go=version flag sets the expected Go language version.
//
@ -1136,6 +1152,15 @@
// New Module
// }
//
// type Retract struct {
// Low string
// High string
// Rationale string
// }
//
// Retract entries representing a single version (not an interval) will have
// the "Low" and "High" fields set to the same value.
//
// Note that this only describes the go.mod file itself, not other modules
// referred to indirectly. For the full set of modules available to a build,
// use 'go list -m -json all'.
@ -1894,15 +1919,17 @@
// require new/thing/v2 v2.3.4
// exclude old/thing v1.2.3
// replace bad/thing v1.4.5 => good/thing v1.4.5
// retract v1.5.6
//
// The verbs are
// module, to define the module path;
// go, to set the expected language version;
// require, to require a particular module at a given version or later;
// exclude, to exclude a particular module version from use; and
// replace, to replace a module version with a different module version.
// exclude, to exclude a particular module version from use;
// replace, to replace a module version with a different module version; and
// retract, to indicate a previously released version should not be used.
// Exclude and replace apply only in the main module's go.mod and are ignored
// in dependencies. See https://research.swtch.com/vgo-mvs for details.
// in dependencies. See https://golang.org/ref/mod for details.
//
// The leading verb can be factored out of adjacent lines to create a block,
// like in Go imports:
@ -2145,7 +2172,10 @@
// before resolving dependencies or building the code.
//
// The -insecure flag permits fetching from repositories and resolving
// custom domains using insecure schemes such as HTTP. Use with caution.
// custom domains using insecure schemes such as HTTP. Use with caution. The
// GOINSECURE environment variable is usually a better alternative, since it
// provides control over which modules may be retrieved using an insecure scheme.
// See 'go help environment' for details.
//
// The -t flag instructs get to also download the packages required to build
// the tests for the specified packages.

View File

@ -28,13 +28,42 @@ func (v *StringsFlag) String() string {
return "<StringsFlag>"
}
// explicitStringFlag is like a regular string flag, but it also tracks whether
// the string was set explicitly to a non-empty value.
type explicitStringFlag struct {
value *string
explicit *bool
}
func (f explicitStringFlag) String() string {
if f.value == nil {
return ""
}
return *f.value
}
func (f explicitStringFlag) Set(v string) error {
*f.value = v
if v != "" {
*f.explicit = true
}
return nil
}
// AddBuildFlagsNX adds the -n and -x build flags to the flag set.
func AddBuildFlagsNX(flags *flag.FlagSet) {
flags.BoolVar(&cfg.BuildN, "n", false, "")
flags.BoolVar(&cfg.BuildX, "x", false, "")
}
// AddLoadFlags adds the -mod build flag to the flag set.
func AddLoadFlags(flags *flag.FlagSet) {
flags.StringVar(&cfg.BuildMod, "mod", "", "")
// AddModFlag adds the -mod build flag to the flag set.
func AddModFlag(flags *flag.FlagSet) {
flags.Var(explicitStringFlag{value: &cfg.BuildMod, explicit: &cfg.BuildModExplicit}, "mod", "")
}
// AddModCommonFlags adds the module-related flags common to build commands
// and 'go mod' subcommands.
func AddModCommonFlags(flags *flag.FlagSet) {
flags.BoolVar(&cfg.ModCacheRW, "modcacherw", false, "")
flags.StringVar(&cfg.ModFile, "modfile", "", "")
}

View File

@ -27,7 +27,8 @@ var (
BuildBuildmode string // -buildmode flag
BuildContext = defaultContext()
BuildMod string // -mod flag
BuildModReason string // reason -mod flag is set, if set by default
BuildModExplicit bool // whether -mod was set explicitly
BuildModReason string // reason -mod was set, if set by default
BuildI bool // -i flag
BuildLinkshared bool // -linkshared flag
BuildMSan bool // -msan flag
@ -48,6 +49,8 @@ var (
ModCacheRW bool // -modcacherw flag
ModFile string // -modfile flag
Insecure bool // -insecure flag
CmdName string // "build", "install", "list", "mod tidy", etc.
DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable)

View File

@ -23,7 +23,8 @@ import (
func init() {
base.AddBuildFlagsNX(&CmdFmt.Flag)
base.AddLoadFlags(&CmdFmt.Flag)
base.AddModFlag(&CmdFmt.Flag)
base.AddModCommonFlags(&CmdFmt.Flag)
}
var CmdFmt = &base.Command{

View File

@ -18,8 +18,11 @@ import (
"cmd/go/internal/load"
"cmd/go/internal/search"
"cmd/go/internal/str"
"cmd/go/internal/vcs"
"cmd/go/internal/web"
"cmd/go/internal/work"
"golang.org/x/mod/module"
)
var CmdGet = &base.Command{
@ -41,7 +44,10 @@ The -fix flag instructs get to run the fix tool on the downloaded packages
before resolving dependencies or building the code.
The -insecure flag permits fetching from repositories and resolving
custom domains using insecure schemes such as HTTP. Use with caution.
custom domains using insecure schemes such as HTTP. Use with caution. The
GOINSECURE environment variable is usually a better alternative, since it
provides control over which modules may be retrieved using an insecure scheme.
See 'go help environment' for details.
The -t flag instructs get to also download the packages required to build
the tests for the specified packages.
@ -103,14 +109,12 @@ var (
getT = CmdGet.Flag.Bool("t", false, "")
getU = CmdGet.Flag.Bool("u", false, "")
getFix = CmdGet.Flag.Bool("fix", false, "")
Insecure bool
)
func init() {
work.AddBuildFlags(CmdGet, work.OmitModFlag|work.OmitModCommonFlags)
CmdGet.Run = runGet // break init loop
CmdGet.Flag.BoolVar(&Insecure, "insecure", Insecure, "")
CmdGet.Flag.BoolVar(&cfg.Insecure, "insecure", cfg.Insecure, "")
}
func runGet(ctx context.Context, cmd *base.Command, args []string) {
@ -403,17 +407,12 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
// to make the first copy of or update a copy of the given package.
func downloadPackage(p *load.Package) error {
var (
vcs *vcsCmd
vcsCmd *vcs.Cmd
repo, rootPath string
err error
blindRepo bool // set if the repo has unusual configuration
)
security := web.SecureOnly
if Insecure {
security = web.Insecure
}
// p can be either a real package, or a pseudo-package whose “import path” is
// actually a wildcard pattern.
// Trim the path at the element containing the first wildcard,
@ -427,22 +426,26 @@ func downloadPackage(p *load.Package) error {
}
importPrefix = importPrefix[:slash]
}
if err := CheckImportPath(importPrefix); err != nil {
if err := module.CheckImportPath(importPrefix); err != nil {
return fmt.Errorf("%s: invalid import path: %v", p.ImportPath, err)
}
security := web.SecureOnly
if cfg.Insecure || module.MatchPrefixPatterns(cfg.GOINSECURE, importPrefix) {
security = web.Insecure
}
if p.Internal.Build.SrcRoot != "" {
// Directory exists. Look for checkout along path to src.
vcs, rootPath, err = vcsFromDir(p.Dir, p.Internal.Build.SrcRoot)
vcsCmd, rootPath, err = vcs.FromDir(p.Dir, p.Internal.Build.SrcRoot)
if err != nil {
return err
}
repo = "<local>" // should be unused; make distinctive
// Double-check where it came from.
if *getU && vcs.remoteRepo != nil {
if *getU && vcsCmd.RemoteRepo != nil {
dir := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath))
remote, err := vcs.remoteRepo(vcs, dir)
remote, err := vcsCmd.RemoteRepo(vcsCmd, dir)
if err != nil {
// Proceed anyway. The package is present; we likely just don't understand
// the repo configuration (e.g. unusual remote protocol).
@ -450,10 +453,10 @@ func downloadPackage(p *load.Package) error {
}
repo = remote
if !*getF && err == nil {
if rr, err := RepoRootForImportPath(importPrefix, IgnoreMod, security); err == nil {
if rr, err := vcs.RepoRootForImportPath(importPrefix, vcs.IgnoreMod, security); err == nil {
repo := rr.Repo
if rr.vcs.resolveRepo != nil {
resolved, err := rr.vcs.resolveRepo(rr.vcs, dir, repo)
if rr.VCS.ResolveRepo != nil {
resolved, err := rr.VCS.ResolveRepo(rr.VCS, dir, repo)
if err == nil {
repo = resolved
}
@ -467,13 +470,13 @@ func downloadPackage(p *load.Package) error {
} else {
// Analyze the import path to determine the version control system,
// repository, and the import path for the root of the repository.
rr, err := RepoRootForImportPath(importPrefix, IgnoreMod, security)
rr, err := vcs.RepoRootForImportPath(importPrefix, vcs.IgnoreMod, security)
if err != nil {
return err
}
vcs, repo, rootPath = rr.vcs, rr.Repo, rr.Root
vcsCmd, repo, rootPath = rr.VCS, rr.Repo, rr.Root
}
if !blindRepo && !vcs.isSecure(repo) && !Insecure {
if !blindRepo && !vcsCmd.IsSecure(repo) && security != web.Insecure {
return fmt.Errorf("cannot download, %v uses insecure protocol", repo)
}
@ -496,7 +499,7 @@ func downloadPackage(p *load.Package) error {
}
root := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath))
if err := checkNestedVCS(vcs, root, p.Internal.Build.SrcRoot); err != nil {
if err := vcs.CheckNested(vcsCmd, root, p.Internal.Build.SrcRoot); err != nil {
return err
}
@ -512,7 +515,7 @@ func downloadPackage(p *load.Package) error {
// Check that this is an appropriate place for the repo to be checked out.
// The target directory must either not exist or have a repo checked out already.
meta := filepath.Join(root, "."+vcs.cmd)
meta := filepath.Join(root, "."+vcsCmd.Cmd)
if _, err := os.Stat(meta); err != nil {
// Metadata file or directory does not exist. Prepare to checkout new copy.
// Some version control tools require the target directory not to exist.
@ -533,12 +536,12 @@ func downloadPackage(p *load.Package) error {
fmt.Fprintf(os.Stderr, "created GOPATH=%s; see 'go help gopath'\n", p.Internal.Build.Root)
}
if err = vcs.create(root, repo); err != nil {
if err = vcsCmd.Create(root, repo); err != nil {
return err
}
} else {
// Metadata directory does exist; download incremental updates.
if err = vcs.download(root); err != nil {
if err = vcsCmd.Download(root); err != nil {
return err
}
}
@ -547,12 +550,12 @@ func downloadPackage(p *load.Package) error {
// Do not show tag sync in -n; it's noise more than anything,
// and since we're not running commands, no tag will be found.
// But avoid printing nothing.
fmt.Fprintf(os.Stderr, "# cd %s; %s sync/update\n", root, vcs.cmd)
fmt.Fprintf(os.Stderr, "# cd %s; %s sync/update\n", root, vcsCmd.Cmd)
return nil
}
// Select and sync to appropriate version of the repository.
tags, err := vcs.tags(root)
tags, err := vcsCmd.Tags(root)
if err != nil {
return err
}
@ -560,7 +563,7 @@ func downloadPackage(p *load.Package) error {
if i := strings.Index(vers, " "); i >= 0 {
vers = vers[:i]
}
if err := vcs.tagSync(root, selectTag(vers, tags)); err != nil {
if err := vcsCmd.TagSync(root, selectTag(vers, tags)); err != nil {
return err
}

View File

@ -1,192 +0,0 @@
// Copyright 2018 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 get
import (
"fmt"
"strings"
"unicode"
"unicode/utf8"
)
// The following functions are copied verbatim from golang.org/x/mod/module/module.go,
// with a change to additionally reject Windows short-names,
// and one to accept arbitrary letters (golang.org/issue/29101).
//
// TODO(bcmills): After the call site for this function is backported,
// consolidate this back down to a single copy.
//
// NOTE: DO NOT MERGE THESE UNTIL WE DECIDE ABOUT ARBITRARY LETTERS IN MODULE MODE.
// CheckImportPath checks that an import path is valid.
func CheckImportPath(path string) error {
if err := checkPath(path, false); err != nil {
return fmt.Errorf("malformed import path %q: %v", path, err)
}
return nil
}
// checkPath checks that a general path is valid.
// It returns an error describing why but not mentioning path.
// Because these checks apply to both module paths and import paths,
// the caller is expected to add the "malformed ___ path %q: " prefix.
// fileName indicates whether the final element of the path is a file name
// (as opposed to a directory name).
func checkPath(path string, fileName bool) error {
if !utf8.ValidString(path) {
return fmt.Errorf("invalid UTF-8")
}
if path == "" {
return fmt.Errorf("empty string")
}
if path[0] == '-' {
return fmt.Errorf("leading dash")
}
if strings.Contains(path, "//") {
return fmt.Errorf("double slash")
}
if path[len(path)-1] == '/' {
return fmt.Errorf("trailing slash")
}
elemStart := 0
for i, r := range path {
if r == '/' {
if err := checkElem(path[elemStart:i], fileName); err != nil {
return err
}
elemStart = i + 1
}
}
if err := checkElem(path[elemStart:], fileName); err != nil {
return err
}
return nil
}
// checkElem checks whether an individual path element is valid.
// fileName indicates whether the element is a file name (not a directory name).
func checkElem(elem string, fileName bool) error {
if elem == "" {
return fmt.Errorf("empty path element")
}
if strings.Count(elem, ".") == len(elem) {
return fmt.Errorf("invalid path element %q", elem)
}
if elem[0] == '.' && !fileName {
return fmt.Errorf("leading dot in path element")
}
if elem[len(elem)-1] == '.' {
return fmt.Errorf("trailing dot in path element")
}
charOK := pathOK
if fileName {
charOK = fileNameOK
}
for _, r := range elem {
if !charOK(r) {
return fmt.Errorf("invalid char %q", r)
}
}
// Windows disallows a bunch of path elements, sadly.
// See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
short := elem
if i := strings.Index(short, "."); i >= 0 {
short = short[:i]
}
for _, bad := range badWindowsNames {
if strings.EqualFold(bad, short) {
return fmt.Errorf("disallowed path element %q", elem)
}
}
// Reject path components that look like Windows short-names.
// Those usually end in a tilde followed by one or more ASCII digits.
if tilde := strings.LastIndexByte(short, '~'); tilde >= 0 && tilde < len(short)-1 {
suffix := short[tilde+1:]
suffixIsDigits := true
for _, r := range suffix {
if r < '0' || r > '9' {
suffixIsDigits = false
break
}
}
if suffixIsDigits {
return fmt.Errorf("trailing tilde and digits in path element")
}
}
return nil
}
// pathOK reports whether r can appear in an import path element.
//
// NOTE: This function DIVERGES from module mode pathOK by accepting Unicode letters.
func pathOK(r rune) bool {
if r < utf8.RuneSelf {
return r == '+' || r == '-' || r == '.' || r == '_' || r == '~' ||
'0' <= r && r <= '9' ||
'A' <= r && r <= 'Z' ||
'a' <= r && r <= 'z'
}
return unicode.IsLetter(r)
}
// fileNameOK reports whether r can appear in a file name.
// For now we allow all Unicode letters but otherwise limit to pathOK plus a few more punctuation characters.
// If we expand the set of allowed characters here, we have to
// work harder at detecting potential case-folding and normalization collisions.
// See note about "safe encoding" below.
func fileNameOK(r rune) bool {
if r < utf8.RuneSelf {
// Entire set of ASCII punctuation, from which we remove characters:
// ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
// We disallow some shell special characters: " ' * < > ? ` |
// (Note that some of those are disallowed by the Windows file system as well.)
// We also disallow path separators / : and \ (fileNameOK is only called on path element characters).
// We allow spaces (U+0020) in file names.
const allowed = "!#$%&()+,-.=@[]^_{}~ "
if '0' <= r && r <= '9' || 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' {
return true
}
for i := 0; i < len(allowed); i++ {
if rune(allowed[i]) == r {
return true
}
}
return false
}
// It may be OK to add more ASCII punctuation here, but only carefully.
// For example Windows disallows < > \, and macOS disallows :, so we must not allow those.
return unicode.IsLetter(r)
}
// badWindowsNames are the reserved file path elements on Windows.
// See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
var badWindowsNames = []string{
"CON",
"PRN",
"AUX",
"NUL",
"COM1",
"COM2",
"COM3",
"COM4",
"COM5",
"COM6",
"COM7",
"COM8",
"COM9",
"LPT1",
"LPT2",
"LPT3",
"LPT4",
"LPT5",
"LPT6",
"LPT7",
"LPT8",
"LPT9",
}

View File

@ -10,6 +10,7 @@ import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"os"
"sort"
@ -215,6 +216,7 @@ applied to a Go struct, but now a Module struct:
Dir string // directory holding files for this module, if any
GoMod string // path to go.mod file used when loading this module, if any
GoVersion string // go version used in module
Retracted string // retraction information, if any (with -retracted or -u)
Error *ModuleError // error loading module
}
@ -246,14 +248,16 @@ the replaced source code.)
The -u flag adds information about available upgrades.
When the latest version of a given module is newer than
the current one, list -u sets the Module's Update field
to information about the newer module.
to information about the newer module. list -u will also set
the module's Retracted field if the current version is retracted.
The Module's String method indicates an available upgrade by
formatting the newer version in brackets after the current version.
If a version is retracted, the string "(retracted)" will follow it.
For example, 'go list -m -u all' might print:
my/main/module
golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text
rsc.io/pdf v0.1.1 [v0.1.2]
rsc.io/pdf v0.1.1 (retracted) [v0.1.2]
(For tools, 'go list -m -u -json all' may be more convenient to parse.)
@ -263,6 +267,14 @@ to semantic versioning, earliest to latest. The flag also changes
the default output format to display the module path followed by the
space-separated version list.
The -retracted flag causes list to report information about retracted
module versions. When -retracted is used with -f or -json, the Retracted
field will be set to a string explaining why the version was retracted.
The string is taken from comments on the retract directive in the
module's go.mod file. When -retracted is used with -versions, retracted
versions are listed together with unretracted versions. The -retracted
flag may be used with or without -m.
The arguments to list -m are interpreted as a list of modules, not packages.
The main module is the module containing the current directory.
The active modules are the main module and its dependencies.
@ -296,17 +308,18 @@ func init() {
}
var (
listCompiled = CmdList.Flag.Bool("compiled", false, "")
listDeps = CmdList.Flag.Bool("deps", false, "")
listE = CmdList.Flag.Bool("e", false, "")
listExport = CmdList.Flag.Bool("export", false, "")
listFmt = CmdList.Flag.String("f", "", "")
listFind = CmdList.Flag.Bool("find", false, "")
listJson = CmdList.Flag.Bool("json", false, "")
listM = CmdList.Flag.Bool("m", false, "")
listU = CmdList.Flag.Bool("u", false, "")
listTest = CmdList.Flag.Bool("test", false, "")
listVersions = CmdList.Flag.Bool("versions", false, "")
listCompiled = CmdList.Flag.Bool("compiled", false, "")
listDeps = CmdList.Flag.Bool("deps", false, "")
listE = CmdList.Flag.Bool("e", false, "")
listExport = CmdList.Flag.Bool("export", false, "")
listFmt = CmdList.Flag.String("f", "", "")
listFind = CmdList.Flag.Bool("find", false, "")
listJson = CmdList.Flag.Bool("json", false, "")
listM = CmdList.Flag.Bool("m", false, "")
listRetracted = CmdList.Flag.Bool("retracted", false, "")
listTest = CmdList.Flag.Bool("test", false, "")
listU = CmdList.Flag.Bool("u", false, "")
listVersions = CmdList.Flag.Bool("versions", false, "")
)
var nl = []byte{'\n'}
@ -367,6 +380,16 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
}
}
modload.Init()
if *listRetracted {
if cfg.BuildMod == "vendor" {
base.Fatalf("go list -retracted cannot be used when vendoring is enabled")
}
if !modload.Enabled() {
base.Fatalf("go list -retracted can only be used in module-aware mode")
}
}
if *listM {
// Module mode.
if *listCompiled {
@ -414,9 +437,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
}
}
modload.LoadBuildList(ctx)
mods := modload.ListModules(ctx, args, *listU, *listVersions)
mods := modload.ListModules(ctx, args, *listU, *listVersions, *listRetracted)
if !*listE {
for _, m := range mods {
if m.Error != nil {
@ -522,7 +543,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
// Note that -deps is applied after -test,
// so that you only get descriptions of tests for the things named
// explicitly on the command line, not for all dependencies.
pkgs = load.PackageList(pkgs)
pkgs = loadPackageList(pkgs)
}
// Do we need to run a build to gather information?
@ -557,7 +578,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
if *listTest {
all := pkgs
if !*listDeps {
all = load.PackageList(pkgs)
all = loadPackageList(pkgs)
}
// Update import paths to distinguish the real package p
// from p recompiled for q.test.
@ -607,6 +628,55 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
}
}
// TODO(golang.org/issue/40676): This mechanism could be extended to support
// -u without -m.
if *listRetracted {
// Load retractions for modules that provide packages that will be printed.
// TODO(golang.org/issue/40775): Packages from the same module refer to
// distinct ModulePublic instance. It would be nice if they could all point
// to the same instance. This would require additional global state in
// modload.loaded, so that should be refactored first. For now, we update
// all instances.
modToArg := make(map[*modinfo.ModulePublic]string)
argToMods := make(map[string][]*modinfo.ModulePublic)
var args []string
addModule := func(mod *modinfo.ModulePublic) {
if mod.Version == "" {
return
}
arg := fmt.Sprintf("%s@%s", mod.Path, mod.Version)
if argToMods[arg] == nil {
args = append(args, arg)
}
argToMods[arg] = append(argToMods[arg], mod)
modToArg[mod] = arg
}
for _, p := range pkgs {
if p.Module == nil {
continue
}
addModule(p.Module)
if p.Module.Replace != nil {
addModule(p.Module.Replace)
}
}
if len(args) > 0 {
listU := false
listVersions := false
rmods := modload.ListModules(ctx, args, listU, listVersions, *listRetracted)
for i, arg := range args {
rmod := rmods[i]
for _, mod := range argToMods[arg] {
mod.Retracted = rmod.Retracted
if rmod.Error != nil && mod.Error == nil {
mod.Error = rmod.Error
}
}
}
}
}
// Record non-identity import mappings in p.ImportMap.
for _, p := range pkgs {
for i, srcPath := range p.Internal.RawImports {
@ -625,6 +695,23 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
}
}
// loadPackageList is like load.PackageList, but prints error messages and exits
// with nonzero status if listE is not set and any package in the expanded list
// has errors.
func loadPackageList(roots []*load.Package) []*load.Package {
pkgs := load.PackageList(roots)
if !*listE {
for _, pkg := range pkgs {
if pkg.Error != nil {
base.Errorf("%v", pkg.Error)
}
}
}
return pkgs
}
// TrackingWriter tracks the last byte written on every write so
// we can avoid printing a newline if one was already written or
// if there is no output at all.

View File

@ -191,6 +191,7 @@ func TestPackagesAndErrors(ctx context.Context, p *Package, cover *TestCover) (p
GoFiles: p.XTestGoFiles,
Imports: p.XTestImports,
ForTest: p.ImportPath,
Module: p.Module,
Error: pxtestErr,
},
Internal: PackageInternal{
@ -222,6 +223,7 @@ func TestPackagesAndErrors(ctx context.Context, p *Package, cover *TestCover) (p
ImportPath: p.ImportPath + ".test",
Root: p.Root,
Imports: str.StringList(TestMainDeps),
Module: p.Module,
},
Internal: PackageInternal{
Build: &build.Package{Name: "main"},

View File

@ -12,9 +12,8 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/modload"
"cmd/go/internal/modfetch"
"cmd/go/internal/work"
"cmd/go/internal/modload"
"golang.org/x/mod/module"
)
@ -64,7 +63,7 @@ func init() {
// TODO(jayconrod): https://golang.org/issue/35849 Apply -x to other 'go mod' commands.
cmdDownload.Flag.BoolVar(&cfg.BuildX, "x", false, "")
work.AddModCommonFlags(cmdDownload)
base.AddModCommonFlags(&cmdDownload.Flag)
}
type moduleJSON struct {
@ -136,9 +135,10 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
var mods []*moduleJSON
listU := false
listVersions := false
listRetractions := false
type token struct{}
sem := make(chan token, runtime.GOMAXPROCS(0))
for _, info := range modload.ListModules(ctx, args, listU, listVersions) {
for _, info := range modload.ListModules(ctx, args, listU, listVersions, listRetractions) {
if info.Replace != nil {
info = info.Replace
}
@ -187,4 +187,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
}
base.ExitIfErrors()
}
// Update go.mod and especially go.sum if needed.
modload.WriteGoMod()
}

View File

@ -19,7 +19,6 @@ import (
"cmd/go/internal/lockedfile"
"cmd/go/internal/modfetch"
"cmd/go/internal/modload"
"cmd/go/internal/work"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
@ -68,9 +67,14 @@ The -dropreplace=old[@v] flag drops a replacement of the given
module path and version pair. If the @v is omitted, a replacement without
a version on the left side is dropped.
The -retract=version and -dropretract=version flags add and drop a
retraction on the given version. The version may be a single version
like "v1.2.3" or a closed interval like "[v1.1.0-v1.1.9]". Note that
-retract=version is a no-op if that retraction already exists.
The -require, -droprequire, -exclude, -dropexclude, -replace,
and -dropreplace editing flags may be repeated, and the changes
are applied in the order given.
-dropreplace, -retract, and -dropretract editing flags may be repeated,
and the changes are applied in the order given.
The -go=version flag sets the expected Go language version.
@ -104,6 +108,15 @@ writing it back to go.mod. The JSON output corresponds to these Go types:
New Module
}
type Retract struct {
Low string
High string
Rationale string
}
Retract entries representing a single version (not an interval) will have
the "Low" and "High" fields set to the same value.
Note that this only describes the go.mod file itself, not other modules
referred to indirectly. For the full set of modules available to a build,
use 'go list -m -json all'.
@ -137,8 +150,10 @@ func init() {
cmdEdit.Flag.Var(flagFunc(flagDropReplace), "dropreplace", "")
cmdEdit.Flag.Var(flagFunc(flagReplace), "replace", "")
cmdEdit.Flag.Var(flagFunc(flagDropExclude), "dropexclude", "")
cmdEdit.Flag.Var(flagFunc(flagRetract), "retract", "")
cmdEdit.Flag.Var(flagFunc(flagDropRetract), "dropretract", "")
work.AddModCommonFlags(cmdEdit)
base.AddModCommonFlags(&cmdEdit.Flag)
base.AddBuildFlagsNX(&cmdEdit.Flag)
}
@ -252,12 +267,7 @@ func parsePathVersion(flag, arg string) (path, version string) {
base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err)
}
// We don't call modfile.CheckPathVersion, because that insists
// on versions being in semver form, but here we want to allow
// versions like "master" or "1234abcdef", which the go command will resolve
// the next time it runs (or during -fix).
// Even so, we need to make sure the version is a valid token.
if modfile.MustQuote(version) {
if !allowedVersionArg(version) {
base.Fatalf("go mod: -%s=%s: invalid version %q", flag, arg, version)
}
@ -289,12 +299,48 @@ func parsePathVersionOptional(adj, arg string, allowDirPath bool) (path, version
return path, version, fmt.Errorf("invalid %s path: %v", adj, err)
}
}
if path != arg && modfile.MustQuote(version) {
if path != arg && !allowedVersionArg(version) {
return path, version, fmt.Errorf("invalid %s version: %q", adj, version)
}
return path, version, nil
}
// parseVersionInterval parses a single version like "v1.2.3" or a closed
// interval like "[v1.2.3,v1.4.5]". Note that a single version has the same
// representation as an interval with equal upper and lower bounds: both
// Low and High are set.
func parseVersionInterval(arg string) (modfile.VersionInterval, error) {
if !strings.HasPrefix(arg, "[") {
if !allowedVersionArg(arg) {
return modfile.VersionInterval{}, fmt.Errorf("invalid version: %q", arg)
}
return modfile.VersionInterval{Low: arg, High: arg}, nil
}
if !strings.HasSuffix(arg, "]") {
return modfile.VersionInterval{}, fmt.Errorf("invalid version interval: %q", arg)
}
s := arg[1 : len(arg)-1]
i := strings.Index(s, ",")
if i < 0 {
return modfile.VersionInterval{}, fmt.Errorf("invalid version interval: %q", arg)
}
low := strings.TrimSpace(s[:i])
high := strings.TrimSpace(s[i+1:])
if !allowedVersionArg(low) || !allowedVersionArg(high) {
return modfile.VersionInterval{}, fmt.Errorf("invalid version interval: %q", arg)
}
return modfile.VersionInterval{Low: low, High: high}, nil
}
// allowedVersionArg returns whether a token may be used as a version in go.mod.
// We don't call modfile.CheckPathVersion, because that insists on versions
// being in semver form, but here we want to allow versions like "master" or
// "1234abcdef", which the go command will resolve the next time it runs (or
// during -fix). Even so, we need to make sure the version is a valid token.
func allowedVersionArg(arg string) bool {
return !modfile.MustQuote(arg)
}
// flagRequire implements the -require flag.
func flagRequire(arg string) {
path, version := parsePathVersion("require", arg)
@ -377,6 +423,32 @@ func flagDropReplace(arg string) {
})
}
// flagRetract implements the -retract flag.
func flagRetract(arg string) {
vi, err := parseVersionInterval(arg)
if err != nil {
base.Fatalf("go mod: -retract=%s: %v", arg, err)
}
edits = append(edits, func(f *modfile.File) {
if err := f.AddRetract(vi, ""); err != nil {
base.Fatalf("go mod: -retract=%s: %v", arg, err)
}
})
}
// flagDropRetract implements the -dropretract flag.
func flagDropRetract(arg string) {
vi, err := parseVersionInterval(arg)
if err != nil {
base.Fatalf("go mod: -dropretract=%s: %v", arg, err)
}
edits = append(edits, func(f *modfile.File) {
if err := f.DropRetract(vi); err != nil {
base.Fatalf("go mod: -dropretract=%s: %v", arg, err)
}
})
}
// fileJSON is the -json output data structure.
type fileJSON struct {
Module module.Version
@ -384,6 +456,7 @@ type fileJSON struct {
Require []requireJSON
Exclude []module.Version
Replace []replaceJSON
Retract []retractJSON
}
type requireJSON struct {
@ -397,6 +470,12 @@ type replaceJSON struct {
New module.Version
}
type retractJSON struct {
Low string `json:",omitempty"`
High string `json:",omitempty"`
Rationale string `json:",omitempty"`
}
// editPrintJSON prints the -json output.
func editPrintJSON(modFile *modfile.File) {
var f fileJSON
@ -415,6 +494,9 @@ func editPrintJSON(modFile *modfile.File) {
for _, r := range modFile.Replace {
f.Replace = append(f.Replace, replaceJSON{r.Old, r.New})
}
for _, r := range modFile.Retract {
f.Retract = append(f.Retract, retractJSON{r.Low, r.High, r.Rationale})
}
data, err := json.MarshalIndent(&f, "", "\t")
if err != nil {
base.Fatalf("go: internal error: %v", err)

View File

@ -15,7 +15,6 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/modload"
"cmd/go/internal/work"
"golang.org/x/mod/module"
)
@ -33,7 +32,7 @@ path@version, except for the main module, which has no @version suffix.
}
func init() {
work.AddModCommonFlags(cmdGraph)
base.AddModCommonFlags(&cmdGraph.Flag)
}
func runGraph(ctx context.Context, cmd *base.Command, args []string) {
@ -48,7 +47,7 @@ func runGraph(ctx context.Context, cmd *base.Command, args []string) {
base.Fatalf("go: cannot find main module; see 'go help modules'")
}
}
modload.LoadBuildList(ctx)
modload.LoadAllModules(ctx)
reqs := modload.MinReqs()
format := func(m module.Version) string {

View File

@ -9,7 +9,6 @@ package modcmd
import (
"cmd/go/internal/base"
"cmd/go/internal/modload"
"cmd/go/internal/work"
"context"
"os"
"strings"
@ -30,7 +29,7 @@ To override this guess, supply the module path as an argument.
}
func init() {
work.AddModCommonFlags(cmdInit)
base.AddModCommonFlags(&cmdInit.Flag)
}
func runInit(ctx context.Context, cmd *base.Command, args []string) {

View File

@ -10,7 +10,6 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/modload"
"cmd/go/internal/work"
"context"
)
@ -32,7 +31,7 @@ to standard error.
func init() {
cmdTidy.Run = runTidy // break init cycle
cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "")
work.AddModCommonFlags(cmdTidy)
base.AddModCommonFlags(&cmdTidy.Flag)
}
func runTidy(ctx context.Context, cmd *base.Command, args []string) {
@ -40,6 +39,18 @@ func runTidy(ctx context.Context, cmd *base.Command, args []string) {
base.Fatalf("go mod tidy: no arguments allowed")
}
// Tidy aims to make 'go test' reproducible for any package in 'all', so we
// need to include test dependencies. For modules that specify go 1.15 or
// earlier this is a no-op (because 'all' saturates transitive test
// dependencies).
//
// However, with lazy loading (go 1.16+) 'all' includes only the packages that
// are transitively imported by the main module, not the test dependencies of
// those packages. In order to make 'go test' reproducible for the packages
// that are in 'all' but outside of the main module, we must explicitly
// request that their test dependencies be included.
modload.LoadTests = true
modload.LoadALL(ctx)
modload.TidyBuildList()
modload.TrimGoSum()

View File

@ -19,7 +19,6 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/imports"
"cmd/go/internal/modload"
"cmd/go/internal/work"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
@ -41,7 +40,7 @@ modules and packages to standard error.
func init() {
cmdVendor.Flag.BoolVar(&cfg.BuildV, "v", false, "")
work.AddModCommonFlags(cmdVendor)
base.AddModCommonFlags(&cmdVendor.Flag)
}
func runVendor(ctx context.Context, cmd *base.Command, args []string) {
@ -77,7 +76,7 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) {
}
var buf bytes.Buffer
for _, m := range modload.BuildList()[1:] {
for _, m := range modload.LoadedModules()[1:] {
if pkgs := modpkgs[m]; len(pkgs) > 0 || isExplicit[m] {
line := moduleLine(m, modload.Replacement(m))
buf.WriteString(line)

View File

@ -17,7 +17,6 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/modfetch"
"cmd/go/internal/modload"
"cmd/go/internal/work"
"golang.org/x/mod/module"
"golang.org/x/mod/sumdb/dirhash"
@ -38,7 +37,7 @@ non-zero status.
}
func init() {
work.AddModCommonFlags(cmdVerify)
base.AddModCommonFlags(&cmdVerify.Flag)
}
func runVerify(ctx context.Context, cmd *base.Command, args []string) {
@ -60,7 +59,7 @@ func runVerify(ctx context.Context, cmd *base.Command, args []string) {
sem := make(chan token, runtime.GOMAXPROCS(0))
// Use a slice of result channels, so that the output is deterministic.
mods := modload.LoadBuildList(ctx)[1:]
mods := modload.LoadAllModules(ctx)[1:]
errsChans := make([]<-chan []error, len(mods))
for i, mod := range mods {

View File

@ -11,7 +11,6 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/modload"
"cmd/go/internal/work"
"golang.org/x/mod/module"
)
@ -58,23 +57,26 @@ var (
func init() {
cmdWhy.Run = runWhy // break init cycle
work.AddModCommonFlags(cmdWhy)
base.AddModCommonFlags(&cmdWhy.Flag)
}
func runWhy(ctx context.Context, cmd *base.Command, args []string) {
loadALL := modload.LoadALL
if *whyVendor {
loadALL = modload.LoadVendor
} else {
modload.LoadTests = true
}
if *whyM {
listU := false
listVersions := false
listRetractions := false
for _, arg := range args {
if strings.Contains(arg, "@") {
base.Fatalf("go mod why: module query not allowed")
}
}
mods := modload.ListModules(ctx, args, listU, listVersions)
mods := modload.ListModules(ctx, args, listU, listVersions, listRetractions)
byModule := make(map[module.Version][]string)
for _, path := range loadALL(ctx) {
m := modload.PackageModule(path)

View File

@ -503,6 +503,9 @@ func checkGoMod(path, version string, data []byte) error {
}
// checkModSum checks that the recorded checksum for mod is h.
//
// mod.Version may have the additional suffix "/go.mod" to request the checksum
// for the module's go.mod file only.
func checkModSum(mod module.Version, h string) error {
// We lock goSum when manipulating it,
// but we arrange to release the lock when calling checkSumDB,
@ -579,9 +582,16 @@ func addModSumLocked(mod module.Version, h string) {
// checkSumDB checks the mod, h pair against the Go checksum database.
// It calls base.Fatalf if the hash is to be rejected.
func checkSumDB(mod module.Version, h string) error {
modWithoutSuffix := mod
noun := "module"
if strings.HasSuffix(mod.Version, "/go.mod") {
noun = "go.mod"
modWithoutSuffix.Version = strings.TrimSuffix(mod.Version, "/go.mod")
}
db, lines, err := lookupSumDB(mod)
if err != nil {
return module.VersionError(mod, fmt.Errorf("verifying module: %v", err))
return module.VersionError(modWithoutSuffix, fmt.Errorf("verifying %s: %v", noun, err))
}
have := mod.Path + " " + mod.Version + " " + h
@ -591,7 +601,7 @@ func checkSumDB(mod module.Version, h string) error {
return nil
}
if strings.HasPrefix(line, prefix) {
return module.VersionError(mod, fmt.Errorf("verifying module: checksum mismatch\n\tdownloaded: %v\n\t%s: %v"+sumdbMismatch, h, db, line[len(prefix)-len("h1:"):]))
return module.VersionError(modWithoutSuffix, fmt.Errorf("verifying %s: checksum mismatch\n\tdownloaded: %v\n\t%s: %v"+sumdbMismatch, noun, h, db, line[len(prefix)-len("h1:"):]))
}
}
return nil

View File

@ -6,12 +6,11 @@ package modfetch
import (
"cmd/go/internal/cfg"
"cmd/go/internal/get"
"golang.org/x/mod/module"
)
// allowInsecure reports whether we are allowed to fetch this path in an insecure manner.
func allowInsecure(path string) bool {
return get.Insecure || module.MatchPrefixPatterns(cfg.GOINSECURE, path)
return cfg.Insecure || module.MatchPrefixPatterns(cfg.GOINSECURE, path)
}

View File

@ -13,9 +13,9 @@ import (
"time"
"cmd/go/internal/cfg"
"cmd/go/internal/get"
"cmd/go/internal/modfetch/codehost"
"cmd/go/internal/par"
"cmd/go/internal/vcs"
web "cmd/go/internal/web"
"golang.org/x/mod/module"
@ -261,13 +261,13 @@ func lookupDirect(path string) (Repo, error) {
if allowInsecure(path) {
security = web.Insecure
}
rr, err := get.RepoRootForImportPath(path, get.PreferMod, security)
rr, err := vcs.RepoRootForImportPath(path, vcs.PreferMod, security)
if err != nil {
// We don't know where to find code for a module with this path.
return nil, notExistError{err: err}
}
if rr.VCS == "mod" {
if rr.VCS.Name == "mod" {
// Fetch module from proxy with base URL rr.Repo.
return newProxyRepo(rr.Repo, path)
}
@ -279,8 +279,8 @@ func lookupDirect(path string) (Repo, error) {
return newCodeRepo(code, rr.Root, path)
}
func lookupCodeRepo(rr *get.RepoRoot) (codehost.Repo, error) {
code, err := codehost.NewRepo(rr.VCS, rr.Repo)
func lookupCodeRepo(rr *vcs.RepoRoot) (codehost.Repo, error) {
code, err := codehost.NewRepo(rr.VCS.Cmd, rr.Repo)
if err != nil {
if _, ok := err.(*codehost.VCSError); ok {
return nil, err
@ -306,7 +306,7 @@ func ImportRepoRev(path, rev string) (Repo, *RevInfo, error) {
if allowInsecure(path) {
security = web.Insecure
}
rr, err := get.RepoRootForImportPath(path, get.IgnoreMod, security)
rr, err := vcs.RepoRootForImportPath(path, vcs.IgnoreMod, security)
if err != nil {
return nil, nil, err
}

View File

@ -22,7 +22,6 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/get"
"cmd/go/internal/lockedfile"
"cmd/go/internal/web"
@ -33,7 +32,7 @@ import (
// useSumDB reports whether to use the Go checksum database for the given module.
func useSumDB(mod module.Version) bool {
return cfg.GOSUMDB != "off" && !get.Insecure && !module.MatchPrefixPatterns(cfg.GONOSUMDB, mod.Path)
return cfg.GOSUMDB != "off" && !cfg.Insecure && !module.MatchPrefixPatterns(cfg.GONOSUMDB, mod.Path)
}
// lookupSumDB returns the Go checksum database's go.sum lines for the given module,

View File

@ -17,7 +17,7 @@ import (
"sync"
"cmd/go/internal/base"
"cmd/go/internal/get"
"cmd/go/internal/cfg"
"cmd/go/internal/imports"
"cmd/go/internal/load"
"cmd/go/internal/modload"
@ -181,7 +181,7 @@ var (
getM = CmdGet.Flag.Bool("m", false, "")
getT = CmdGet.Flag.Bool("t", false, "")
getU upgradeFlag
// -insecure is get.Insecure
// -insecure is cfg.Insecure
// -v is cfg.BuildV
)
@ -206,7 +206,7 @@ func (v *upgradeFlag) String() string { return "" }
func init() {
work.AddBuildFlags(CmdGet, work.OmitModFlag)
CmdGet.Run = runGet // break init loop
CmdGet.Flag.BoolVar(&get.Insecure, "insecure", get.Insecure, "")
CmdGet.Flag.BoolVar(&cfg.Insecure, "insecure", cfg.Insecure, "")
CmdGet.Flag.Var(&getU, "u", "")
}
@ -278,7 +278,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
}
modload.LoadTests = *getT
buildList := modload.LoadBuildList(ctx)
buildList := modload.LoadAllModules(ctx)
buildList = buildList[:len(buildList):len(buildList)] // copy on append
versionByPath := make(map[string]string)
for _, m := range buildList {
@ -290,7 +290,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
// what was requested.
modload.DisallowWriteGoMod()
// Allow looking up modules for import paths outside of a module.
// Allow looking up modules for import paths when outside of a module.
// 'go get' is expected to do this, unlike other commands.
modload.AllowMissingModuleImports()
@ -599,7 +599,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
base.ExitIfErrors()
// Stop if no changes have been made to the build list.
buildList = modload.BuildList()
buildList = modload.LoadedModules()
eq := len(buildList) == len(prevBuildList)
for i := 0; eq && i < len(buildList); i++ {
eq = buildList[i] == prevBuildList[i]
@ -617,7 +617,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
// Handle downgrades.
var down []module.Version
for _, m := range modload.BuildList() {
for _, m := range modload.LoadedModules() {
q := byPath[m.Path]
if q != nil && semver.Compare(m.Version, q.m.Version) > 0 {
down = append(down, module.Version{Path: m.Path, Version: q.m.Version})
@ -628,6 +628,10 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
if err != nil {
base.Fatalf("go: %v", err)
}
// TODO(bcmills) What should happen here under lazy loading?
// Downgrading may intentionally violate the lazy-loading invariants.
modload.SetBuildList(buildList)
modload.ReloadBuildList() // note: does not update go.mod
base.ExitIfErrors()
@ -637,7 +641,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
var lostUpgrades []*query
if len(down) > 0 {
versionByPath = make(map[string]string)
for _, m := range modload.BuildList() {
for _, m := range modload.LoadedModules() {
versionByPath[m.Path] = m.Version
}
for _, q := range byPath {
@ -702,6 +706,15 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
// Everything succeeded. Update go.mod.
modload.AllowWriteGoMod()
modload.WriteGoMod()
modload.DisallowWriteGoMod()
// Report warnings if any retracted versions are in the build list.
// This must be done after writing go.mod to avoid spurious '// indirect'
// comments. These functions read and write global state.
// TODO(golang.org/issue/40775): ListModules resets modload.loader, which
// contains information about direct dependencies that WriteGoMod uses.
// Refactor to avoid these kinds of global side effects.
reportRetractions(ctx)
// If -d was specified, we're done after the module work.
// We've already downloaded modules by loading packages above.
@ -804,6 +817,14 @@ func getQuery(ctx context.Context, path, vers string, prevM module.Version, forc
base.Fatalf("go get: internal error: prevM may be set if and only if forceModulePath is set")
}
// If vers is a query like "latest", we should ignore retracted and excluded
// versions. If vers refers to a specific version or commit like "v1.0.0"
// or "master", we should only ignore excluded versions.
allowed := modload.CheckAllowed
if modload.IsRevisionQuery(vers) {
allowed = modload.CheckExclusions
}
// If the query must be a module path, try only that module path.
if forceModulePath {
if path == modload.Target.Path {
@ -812,7 +833,7 @@ func getQuery(ctx context.Context, path, vers string, prevM module.Version, forc
}
}
info, err := modload.Query(ctx, path, vers, prevM.Version, modload.Allowed)
info, err := modload.Query(ctx, path, vers, prevM.Version, allowed)
if err == nil {
if info.Version != vers && info.Version != prevM.Version {
logOncef("go: %s %s => %s", path, vers, info.Version)
@ -838,7 +859,7 @@ func getQuery(ctx context.Context, path, vers string, prevM module.Version, forc
// If it turns out to only exist as a module, we can detect the resulting
// PackageNotInModuleError and avoid a second round-trip through (potentially)
// all of the configured proxies.
results, err := modload.QueryPattern(ctx, path, vers, modload.Allowed)
results, err := modload.QueryPattern(ctx, path, vers, allowed)
if err != nil {
// If the path doesn't contain a wildcard, check whether it was actually a
// module path instead. If so, return that.
@ -864,190 +885,41 @@ func getQuery(ctx context.Context, path, vers string, prevM module.Version, forc
return m, nil
}
// An upgrader adapts an underlying mvs.Reqs to apply an
// upgrade policy to a list of targets and their dependencies.
type upgrader struct {
mvs.Reqs
// cmdline maps a module path to a query made for that module at a
// specific target version. Each query corresponds to a module
// matched by a command line argument.
cmdline map[string]*query
// upgrade is a set of modules providing dependencies of packages
// matched by command line arguments. If -u or -u=patch is set,
// these modules are upgraded accordingly.
upgrade map[string]bool
}
// newUpgrader creates an upgrader. cmdline contains queries made at
// specific versions for modules matched by command line arguments. pkgs
// is the set of packages matched by command line arguments. If -u or -u=patch
// is set, modules providing dependencies of pkgs are upgraded accordingly.
func newUpgrader(cmdline map[string]*query, pkgs map[string]bool) *upgrader {
u := &upgrader{
Reqs: modload.Reqs(),
cmdline: cmdline,
// reportRetractions prints warnings if any modules in the build list are
// retracted.
func reportRetractions(ctx context.Context) {
// Query for retractions of modules in the build list.
// Use modload.ListModules, since that provides information in the same format
// as 'go list -m'. Don't query for "all", since that's not allowed outside a
// module.
buildList := modload.LoadedModules()
args := make([]string, 0, len(buildList))
for _, m := range buildList {
if m.Version == "" {
// main module or dummy target module
continue
}
args = append(args, m.Path+"@"+m.Version)
}
if getU != "" {
u.upgrade = make(map[string]bool)
// Traverse package import graph.
// Initialize work queue with root packages.
seen := make(map[string]bool)
var work []string
add := func(path string) {
if !seen[path] {
seen[path] = true
work = append(work, path)
}
}
for pkg := range pkgs {
add(pkg)
}
for len(work) > 0 {
pkg := work[0]
work = work[1:]
m := modload.PackageModule(pkg)
u.upgrade[m.Path] = true
// testImports is empty unless test imports were actually loaded,
// i.e., -t was set or "all" was one of the arguments.
imports, testImports := modload.PackageImports(pkg)
for _, imp := range imports {
add(imp)
}
for _, imp := range testImports {
add(imp)
listU := false
listVersions := false
listRetractions := true
mods := modload.ListModules(ctx, args, listU, listVersions, listRetractions)
retractPath := ""
for _, mod := range mods {
if len(mod.Retracted) > 0 {
if retractPath == "" {
retractPath = mod.Path
} else {
retractPath = "<module>"
}
rationale := modload.ShortRetractionRationale(mod.Retracted[0])
logOncef("go: warning: %s@%s is retracted: %s", mod.Path, mod.Version, rationale)
}
}
return u
}
// Required returns the requirement list for m.
// For the main module, we override requirements with the modules named
// one the command line, and we include new requirements. Otherwise,
// we defer to u.Reqs.
func (u *upgrader) Required(m module.Version) ([]module.Version, error) {
rs, err := u.Reqs.Required(m)
if err != nil {
return nil, err
if modload.HasModRoot() && retractPath != "" {
logOncef("go: run 'go get %s@latest' to switch to the latest unretracted version", retractPath)
}
if m != modload.Target {
return rs, nil
}
overridden := make(map[string]bool)
for i, m := range rs {
if q := u.cmdline[m.Path]; q != nil && q.m.Version != "none" {
rs[i] = q.m
overridden[q.m.Path] = true
}
}
for _, q := range u.cmdline {
if !overridden[q.m.Path] && q.m.Path != modload.Target.Path && q.m.Version != "none" {
rs = append(rs, q.m)
}
}
return rs, nil
}
// Upgrade returns the desired upgrade for m.
//
// If m was requested at a specific version on the command line, then
// Upgrade returns that version.
//
// If -u is set and m provides a dependency of a package matched by
// command line arguments, then Upgrade may provider a newer tagged version.
// If m is a tagged version, then Upgrade will return the latest tagged
// version (with the same minor version number if -u=patch).
// If m is a pseudo-version, then Upgrade returns the latest tagged version
// only if that version has a time-stamp newer than m. This special case
// prevents accidental downgrades when already using a pseudo-version
// newer than the latest tagged version.
//
// If none of the above cases apply, then Upgrade returns m.
func (u *upgrader) Upgrade(m module.Version) (module.Version, error) {
// Allow pkg@vers on the command line to override the upgrade choice v.
// If q's version is < m.Version, then we're going to downgrade anyway,
// and it's cleaner to avoid moving back and forth and picking up
// extraneous other newer dependencies.
// If q's version is > m.Version, then we're going to upgrade past
// m.Version anyway, and again it's cleaner to avoid moving back and forth
// picking up extraneous other newer dependencies.
if q := u.cmdline[m.Path]; q != nil {
return q.m, nil
}
if !u.upgrade[m.Path] {
// Not involved in upgrade. Leave alone.
return m, nil
}
// Run query required by upgrade semantics.
// Note that Query "latest" is not the same as using repo.Latest,
// which may return a pseudoversion for the latest commit.
// Query "latest" returns the newest tagged version or the newest
// prerelease version if there are no non-prereleases, or repo.Latest
// if there aren't any tagged versions.
// If we're querying "upgrade" or "patch", Query will compare the current
// version against the chosen version and will return the current version
// if it is newer.
info, err := modload.Query(context.TODO(), m.Path, string(getU), m.Version, modload.Allowed)
if err != nil {
// Report error but return m, to let version selection continue.
// (Reporting the error will fail the command at the next base.ExitIfErrors.)
// Special case: if the error is for m.Version itself and m.Version has a
// replacement, then keep it and don't report the error: the fact that the
// version is invalid is likely the reason it was replaced to begin with.
var vErr *module.InvalidVersionError
if errors.As(err, &vErr) && vErr.Version == m.Version && modload.Replacement(m).Path != "" {
return m, nil
}
// Special case: if the error is "no matching versions" then don't
// even report the error. Because Query does not consider pseudo-versions,
// it may happen that we have a pseudo-version but during -u=patch
// the query v0.0 matches no versions (not even the one we're using).
var noMatch *modload.NoMatchingVersionError
if !errors.As(err, &noMatch) {
base.Errorf("go get: upgrading %s@%s: %v", m.Path, m.Version, err)
}
return m, nil
}
if info.Version != m.Version {
logOncef("go: %s %s => %s", m.Path, getU, info.Version)
}
return module.Version{Path: m.Path, Version: info.Version}, nil
}
// buildListForLostUpgrade returns the build list for the module graph
// rooted at lost. Unlike mvs.BuildList, the target module (lost) is not
// treated specially. The returned build list may contain a newer version
// of lost.
//
// buildListForLostUpgrade is used after a downgrade has removed a module
// requested at a specific version. This helps us understand the requirements
// implied by each downgrade.
func buildListForLostUpgrade(lost module.Version, reqs mvs.Reqs) ([]module.Version, error) {
return mvs.BuildList(lostUpgradeRoot, &lostUpgradeReqs{Reqs: reqs, lost: lost})
}
var lostUpgradeRoot = module.Version{Path: "lost-upgrade-root", Version: ""}
type lostUpgradeReqs struct {
mvs.Reqs
lost module.Version
}
func (r *lostUpgradeReqs) Required(mod module.Version) ([]module.Version, error) {
if mod == lostUpgradeRoot {
return []module.Version{r.lost}, nil
}
return r.Reqs.Required(mod)
}
var loggedLines sync.Map

View File

@ -0,0 +1,202 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package modget
import (
"context"
"errors"
"cmd/go/internal/base"
"cmd/go/internal/modload"
"cmd/go/internal/mvs"
"golang.org/x/mod/module"
)
// An upgrader adapts an underlying mvs.Reqs to apply an
// upgrade policy to a list of targets and their dependencies.
type upgrader struct {
mvs.Reqs
// cmdline maps a module path to a query made for that module at a
// specific target version. Each query corresponds to a module
// matched by a command line argument.
cmdline map[string]*query
// upgrade is a set of modules providing dependencies of packages
// matched by command line arguments. If -u or -u=patch is set,
// these modules are upgraded accordingly.
upgrade map[string]bool
}
// newUpgrader creates an upgrader. cmdline contains queries made at
// specific versions for modules matched by command line arguments. pkgs
// is the set of packages matched by command line arguments. If -u or -u=patch
// is set, modules providing dependencies of pkgs are upgraded accordingly.
func newUpgrader(cmdline map[string]*query, pkgs map[string]bool) *upgrader {
u := &upgrader{
Reqs: modload.Reqs(),
cmdline: cmdline,
}
if getU != "" {
u.upgrade = make(map[string]bool)
// Traverse package import graph.
// Initialize work queue with root packages.
seen := make(map[string]bool)
var work []string
add := func(path string) {
if !seen[path] {
seen[path] = true
work = append(work, path)
}
}
for pkg := range pkgs {
add(pkg)
}
for len(work) > 0 {
pkg := work[0]
work = work[1:]
m := modload.PackageModule(pkg)
u.upgrade[m.Path] = true
// testImports is empty unless test imports were actually loaded,
// i.e., -t was set or "all" was one of the arguments.
imports, testImports := modload.PackageImports(pkg)
for _, imp := range imports {
add(imp)
}
for _, imp := range testImports {
add(imp)
}
}
}
return u
}
// Required returns the requirement list for m.
// For the main module, we override requirements with the modules named
// one the command line, and we include new requirements. Otherwise,
// we defer to u.Reqs.
func (u *upgrader) Required(m module.Version) ([]module.Version, error) {
rs, err := u.Reqs.Required(m)
if err != nil {
return nil, err
}
if m != modload.Target {
return rs, nil
}
overridden := make(map[string]bool)
for i, m := range rs {
if q := u.cmdline[m.Path]; q != nil && q.m.Version != "none" {
rs[i] = q.m
overridden[q.m.Path] = true
}
}
for _, q := range u.cmdline {
if !overridden[q.m.Path] && q.m.Path != modload.Target.Path && q.m.Version != "none" {
rs = append(rs, q.m)
}
}
return rs, nil
}
// Upgrade returns the desired upgrade for m.
//
// If m was requested at a specific version on the command line, then
// Upgrade returns that version.
//
// If -u is set and m provides a dependency of a package matched by
// command line arguments, then Upgrade may provider a newer tagged version.
// If m is a tagged version, then Upgrade will return the latest tagged
// version (with the same minor version number if -u=patch).
// If m is a pseudo-version, then Upgrade returns the latest tagged version
// only if that version has a time-stamp newer than m. This special case
// prevents accidental downgrades when already using a pseudo-version
// newer than the latest tagged version.
//
// If none of the above cases apply, then Upgrade returns m.
func (u *upgrader) Upgrade(m module.Version) (module.Version, error) {
// Allow pkg@vers on the command line to override the upgrade choice v.
// If q's version is < m.Version, then we're going to downgrade anyway,
// and it's cleaner to avoid moving back and forth and picking up
// extraneous other newer dependencies.
// If q's version is > m.Version, then we're going to upgrade past
// m.Version anyway, and again it's cleaner to avoid moving back and forth
// picking up extraneous other newer dependencies.
if q := u.cmdline[m.Path]; q != nil {
return q.m, nil
}
if !u.upgrade[m.Path] {
// Not involved in upgrade. Leave alone.
return m, nil
}
// Run query required by upgrade semantics.
// Note that Query "latest" is not the same as using repo.Latest,
// which may return a pseudoversion for the latest commit.
// Query "latest" returns the newest tagged version or the newest
// prerelease version if there are no non-prereleases, or repo.Latest
// if there aren't any tagged versions.
// If we're querying "upgrade" or "patch", Query will compare the current
// version against the chosen version and will return the current version
// if it is newer.
info, err := modload.Query(context.TODO(), m.Path, string(getU), m.Version, modload.CheckAllowed)
if err != nil {
// Report error but return m, to let version selection continue.
// (Reporting the error will fail the command at the next base.ExitIfErrors.)
// Special case: if the error is for m.Version itself and m.Version has a
// replacement, then keep it and don't report the error: the fact that the
// version is invalid is likely the reason it was replaced to begin with.
var vErr *module.InvalidVersionError
if errors.As(err, &vErr) && vErr.Version == m.Version && modload.Replacement(m).Path != "" {
return m, nil
}
// Special case: if the error is "no matching versions" then don't
// even report the error. Because Query does not consider pseudo-versions,
// it may happen that we have a pseudo-version but during -u=patch
// the query v0.0 matches no versions (not even the one we're using).
var noMatch *modload.NoMatchingVersionError
if !errors.As(err, &noMatch) {
base.Errorf("go get: upgrading %s@%s: %v", m.Path, m.Version, err)
}
return m, nil
}
if info.Version != m.Version {
logOncef("go: %s %s => %s", m.Path, getU, info.Version)
}
return module.Version{Path: m.Path, Version: info.Version}, nil
}
// buildListForLostUpgrade returns the build list for the module graph
// rooted at lost. Unlike mvs.BuildList, the target module (lost) is not
// treated specially. The returned build list may contain a newer version
// of lost.
//
// buildListForLostUpgrade is used after a downgrade has removed a module
// requested at a specific version. This helps us understand the requirements
// implied by each downgrade.
func buildListForLostUpgrade(lost module.Version, reqs mvs.Reqs) ([]module.Version, error) {
return mvs.BuildList(lostUpgradeRoot, &lostUpgradeReqs{Reqs: reqs, lost: lost})
}
var lostUpgradeRoot = module.Version{Path: "lost-upgrade-root", Version: ""}
type lostUpgradeReqs struct {
mvs.Reqs
lost module.Version
}
func (r *lostUpgradeReqs) Required(mod module.Version) ([]module.Version, error) {
if mod == lostUpgradeRoot {
return []module.Version{r.lost}, nil
}
return r.Reqs.Required(mod)
}

View File

@ -21,6 +21,7 @@ type ModulePublic struct {
Dir string `json:",omitempty"` // directory holding local copy of files, if any
GoMod string `json:",omitempty"` // path to go.mod file describing module, if any
GoVersion string `json:",omitempty"` // go version used in module
Retracted []string `json:",omitempty"` // retraction information, if any (with -retracted or -u)
Error *ModuleError `json:",omitempty"` // error loading module
}
@ -30,18 +31,26 @@ type ModuleError struct {
func (m *ModulePublic) String() string {
s := m.Path
versionString := func(mm *ModulePublic) string {
v := mm.Version
if len(mm.Retracted) == 0 {
return v
}
return v + " (retracted)"
}
if m.Version != "" {
s += " " + m.Version
s += " " + versionString(m)
if m.Update != nil {
s += " [" + m.Update.Version + "]"
s += " [" + versionString(m.Update) + "]"
}
}
if m.Replace != nil {
s += " => " + m.Replace.Path
if m.Replace.Version != "" {
s += " " + m.Replace.Version
s += " " + versionString(m.Replace)
if m.Replace.Update != nil {
s += " [" + m.Replace.Update.Version + "]"
s += " [" + versionString(m.Replace.Update) + "]"
}
}
}

View File

@ -8,6 +8,7 @@ import (
"bytes"
"context"
"encoding/hex"
"errors"
"fmt"
"internal/goroot"
"os"
@ -58,7 +59,9 @@ func PackageModuleInfo(pkgpath string) *modinfo.ModulePublic {
if !ok {
return nil
}
return moduleInfo(context.TODO(), m, true)
fromBuildList := true
listRetracted := false
return moduleInfo(context.TODO(), m, fromBuildList, listRetracted)
}
func ModuleInfo(ctx context.Context, path string) *modinfo.ModulePublic {
@ -66,13 +69,17 @@ func ModuleInfo(ctx context.Context, path string) *modinfo.ModulePublic {
return nil
}
listRetracted := false
if i := strings.Index(path, "@"); i >= 0 {
return moduleInfo(ctx, module.Version{Path: path[:i], Version: path[i+1:]}, false)
m := module.Version{Path: path[:i], Version: path[i+1:]}
fromBuildList := false
return moduleInfo(ctx, m, fromBuildList, listRetracted)
}
for _, m := range BuildList() {
for _, m := range LoadedModules() {
if m.Path == path {
return moduleInfo(ctx, m, true)
fromBuildList := true
return moduleInfo(ctx, m, fromBuildList, listRetracted)
}
}
@ -90,7 +97,7 @@ func addUpdate(ctx context.Context, m *modinfo.ModulePublic) {
return
}
if info, err := Query(ctx, m.Path, "upgrade", m.Version, Allowed); err == nil && semver.Compare(info.Version, m.Version) > 0 {
if info, err := Query(ctx, m.Path, "upgrade", m.Version, CheckAllowed); err == nil && semver.Compare(info.Version, m.Version) > 0 {
m.Update = &modinfo.ModulePublic{
Path: m.Path,
Version: info.Version,
@ -100,11 +107,37 @@ func addUpdate(ctx context.Context, m *modinfo.ModulePublic) {
}
// addVersions fills in m.Versions with the list of known versions.
func addVersions(m *modinfo.ModulePublic) {
m.Versions, _ = versions(m.Path)
// Excluded versions will be omitted. If listRetracted is false, retracted
// versions will also be omitted.
func addVersions(ctx context.Context, m *modinfo.ModulePublic, listRetracted bool) {
allowed := CheckAllowed
if listRetracted {
allowed = CheckExclusions
}
m.Versions, _ = versions(ctx, m.Path, allowed)
}
func moduleInfo(ctx context.Context, m module.Version, fromBuildList bool) *modinfo.ModulePublic {
// addRetraction fills in m.Retracted if the module was retracted by its author.
// m.Error is set if there's an error loading retraction information.
func addRetraction(ctx context.Context, m *modinfo.ModulePublic) {
if m.Version == "" {
return
}
err := checkRetractions(ctx, module.Version{Path: m.Path, Version: m.Version})
var rerr *retractedError
if errors.As(err, &rerr) {
if len(rerr.rationale) == 0 {
m.Retracted = []string{"retracted by module author"}
} else {
m.Retracted = rerr.rationale
}
} else if err != nil && m.Error == nil {
m.Error = &modinfo.ModuleError{Err: err.Error()}
}
}
func moduleInfo(ctx context.Context, m module.Version, fromBuildList, listRetracted bool) *modinfo.ModulePublic {
if m == Target {
info := &modinfo.ModulePublic{
Path: m.Path,
@ -126,12 +159,14 @@ func moduleInfo(ctx context.Context, m module.Version, fromBuildList bool) *modi
Version: m.Version,
Indirect: fromBuildList && loaded != nil && !loaded.direct[m.Path],
}
if loaded != nil {
info.GoVersion = loaded.goVersion[m.Path]
if v, ok := rawGoVersion.Load(m); ok {
info.GoVersion = v.(string)
}
// completeFromModCache fills in the extra fields in m using the module cache.
completeFromModCache := func(m *modinfo.ModulePublic) {
mod := module.Version{Path: m.Path, Version: m.Version}
if m.Version != "" {
if q, err := Query(ctx, m.Path, m.Version, "", nil); err != nil {
m.Error = &modinfo.ModuleError{Err: err.Error()}
@ -140,7 +175,6 @@ func moduleInfo(ctx context.Context, m module.Version, fromBuildList bool) *modi
m.Time = &q.Time
}
mod := module.Version{Path: m.Path, Version: m.Version}
gomod, err := modfetch.CachePath(mod, "mod")
if err == nil {
if info, err := os.Stat(gomod); err == nil && info.Mode().IsRegular() {
@ -151,10 +185,22 @@ func moduleInfo(ctx context.Context, m module.Version, fromBuildList bool) *modi
if err == nil {
m.Dir = dir
}
if listRetracted {
addRetraction(ctx, m)
}
}
if m.GoVersion == "" {
if summary, err := rawGoModSummary(mod); err == nil && summary.goVersionV != "" {
m.GoVersion = summary.goVersionV[1:]
}
}
}
if !fromBuildList {
// If this was an explicitly-versioned argument to 'go mod download' or
// 'go list -m', report the actual requested version, not its replacement.
completeFromModCache(info) // Will set m.Error in vendor mode.
return info
}
@ -178,9 +224,11 @@ func moduleInfo(ctx context.Context, m module.Version, fromBuildList bool) *modi
// worth the cost, and we're going to overwrite the GoMod and Dir from the
// replacement anyway. See https://golang.org/issue/27859.
info.Replace = &modinfo.ModulePublic{
Path: r.Path,
Version: r.Version,
GoVersion: info.GoVersion,
Path: r.Path,
Version: r.Version,
}
if v, ok := rawGoVersion.Load(m); ok {
info.Replace.GoVersion = v.(string)
}
if r.Version == "" {
if filepath.IsAbs(r.Path) {
@ -194,7 +242,9 @@ func moduleInfo(ctx context.Context, m module.Version, fromBuildList bool) *modi
completeFromModCache(info.Replace)
info.Dir = info.Replace.Dir
info.GoMod = info.Replace.GoMod
info.Retracted = info.Replace.Retracted
}
info.GoVersion = info.Replace.GoVersion
return info
}

View File

@ -0,0 +1,122 @@
// Copyright 2018 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 modload
import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/imports"
"cmd/go/internal/mvs"
"context"
"fmt"
"os"
"golang.org/x/mod/module"
)
// buildList is the list of modules to use for building packages.
// It is initialized by calling ImportPaths, ImportFromFiles,
// LoadALL, or LoadBuildList, each of which uses loaded.load.
//
// Ideally, exactly ONE of those functions would be called,
// and exactly once. Most of the time, that's true.
// During "go get" it may not be. TODO(rsc): Figure out if
// that restriction can be established, or else document why not.
//
var buildList []module.Version
// LoadAllModules loads and returns the list of modules matching the "all"
// module pattern, starting with the Target module and in a deterministic
// (stable) order, without loading any packages.
//
// Modules are loaded automatically (and lazily) in ImportPaths:
// LoadAllModules need only be called if ImportPaths is not,
// typically in commands that care about modules but no particular package.
//
// The caller must not modify the returned list.
func LoadAllModules(ctx context.Context) []module.Version {
InitMod(ctx)
ReloadBuildList()
WriteGoMod()
return buildList
}
// LoadedModules returns the list of module requirements loaded or set by a
// previous call (typically LoadAllModules or ImportPaths), starting with the
// Target module and in a deterministic (stable) order.
//
// The caller must not modify the returned list.
func LoadedModules() []module.Version {
return buildList
}
// SetBuildList sets the module build list.
// The caller is responsible for ensuring that the list is valid.
// SetBuildList does not retain a reference to the original list.
func SetBuildList(list []module.Version) {
buildList = append([]module.Version{}, list...)
}
// ReloadBuildList resets the state of loaded packages, then loads and returns
// the build list set in SetBuildList.
func ReloadBuildList() []module.Version {
loaded = loadFromRoots(loaderParams{
tags: imports.Tags(),
listRoots: func() []string { return nil },
allClosesOverTests: index.allPatternClosesOverTests(), // but doesn't matter because the root list is empty.
})
return buildList
}
// TidyBuildList trims the build list to the minimal requirements needed to
// retain the same versions of all packages from the preceding Load* or
// ImportPaths* call.
func TidyBuildList() {
used := map[module.Version]bool{Target: true}
for _, pkg := range loaded.pkgs {
used[pkg.mod] = true
}
keep := []module.Version{Target}
var direct []string
for _, m := range buildList[1:] {
if used[m] {
keep = append(keep, m)
if loaded.direct[m.Path] {
direct = append(direct, m.Path)
}
} else if cfg.BuildV {
if _, ok := index.require[m]; ok {
fmt.Fprintf(os.Stderr, "unused %s\n", m.Path)
}
}
}
min, err := mvs.Req(Target, direct, &mvsReqs{buildList: keep})
if err != nil {
base.Fatalf("go: %v", err)
}
buildList = append([]module.Version{Target}, min...)
}
// checkMultiplePaths verifies that a given module path is used as itself
// or as a replacement for another module, but not both at the same time.
//
// (See https://golang.org/issue/26607 and https://golang.org/issue/34650.)
func checkMultiplePaths() {
firstPath := make(map[module.Version]string, len(buildList))
for _, mod := range buildList {
src := mod
if rep := Replacement(mod); rep.Path != "" {
src = rep
}
if prev, ok := firstPath[src]; !ok {
firstPath[src] = mod.Path
} else if prev != mod.Path {
base.Errorf("go: %s@%s used for two different module paths (%s and %s)", src.Path, src.Version, prev, mod.Path)
}
}
base.ExitIfErrors()
}

View File

@ -432,15 +432,17 @@ verb followed by arguments. For example:
require new/thing/v2 v2.3.4
exclude old/thing v1.2.3
replace bad/thing v1.4.5 => good/thing v1.4.5
retract v1.5.6
The verbs are
module, to define the module path;
go, to set the expected language version;
require, to require a particular module at a given version or later;
exclude, to exclude a particular module version from use; and
replace, to replace a module version with a different module version.
exclude, to exclude a particular module version from use;
replace, to replace a module version with a different module version; and
retract, to indicate a previously released version should not be used.
Exclude and replace apply only in the main module's go.mod and are ignored
in dependencies. See https://research.swtch.com/vgo-mvs for details.
in dependencies. See https://golang.org/ref/mod for details.
The leading verb can be factored out of adjacent lines to create a block,
like in Go imports:

View File

@ -26,6 +26,8 @@ import (
"golang.org/x/mod/semver"
)
var errImportMissing = errors.New("import missing")
type ImportMissingError struct {
Path string
Module module.Version
@ -48,6 +50,11 @@ func (e *ImportMissingError) Error() string {
}
return "cannot find module providing package " + e.Path
}
if e.newMissingVersion != "" {
return fmt.Sprintf("package %s provided by %s at latest version %s but not at required version %s", e.Path, e.Module.Path, e.Module.Version, e.newMissingVersion)
}
return fmt.Sprintf("missing module for import: %s@%s provides %s", e.Module.Path, e.Module.Version, e.Path)
}
@ -100,18 +107,39 @@ func (e *AmbiguousImportError) Error() string {
var _ load.ImportPathError = &AmbiguousImportError{}
// Import finds the module and directory in the build list
// containing the package with the given import path.
// The answer must be unique: Import returns an error
// if multiple modules attempt to provide the same package.
// Import can return a module with an empty m.Path, for packages in the standard library.
// Import can return an empty directory string, for fake packages like "C" and "unsafe".
type invalidImportError struct {
importPath string
err error
}
func (e *invalidImportError) ImportPath() string {
return e.importPath
}
func (e *invalidImportError) Error() string {
return e.err.Error()
}
func (e *invalidImportError) Unwrap() error {
return e.err
}
var _ load.ImportPathError = &invalidImportError{}
// importFromBuildList finds the module and directory in the build list
// containing the package with the given import path. The answer must be unique:
// importFromBuildList returns an error if multiple modules attempt to provide
// the same package.
//
// importFromBuildList can return a module with an empty m.Path, for packages in
// the standard library.
//
// importFromBuildList can return an empty directory string, for fake packages
// like "C" and "unsafe".
//
// If the package cannot be found in the current build list,
// Import returns an ImportMissingError as the error.
// If Import can identify a module that could be added to supply the package,
// the ImportMissingError records that module.
func Import(ctx context.Context, path string) (m module.Version, dir string, err error) {
// importFromBuildList returns errImportMissing as the error.
func importFromBuildList(ctx context.Context, path string) (m module.Version, dir string, err error) {
if strings.Contains(path, "@") {
return module.Version{}, "", fmt.Errorf("import path should not have @version")
}
@ -190,29 +218,25 @@ func Import(ctx context.Context, path string) (m module.Version, dir string, err
return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods}
}
// Look up module containing the package, for addition to the build list.
// Goal is to determine the module, download it to dir, and return m, dir, ErrMissing.
if cfg.BuildMod == "readonly" {
var queryErr error
if !pathIsStd {
if cfg.BuildModReason == "" {
queryErr = fmt.Errorf("import lookup disabled by -mod=%s", cfg.BuildMod)
} else {
queryErr = fmt.Errorf("import lookup disabled by -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason)
}
}
return module.Version{}, "", &ImportMissingError{Path: path, QueryErr: queryErr}
}
return module.Version{}, "", errImportMissing
}
// queryImport attempts to locate a module that can be added to the current
// build list to provide the package with the given import path.
func queryImport(ctx context.Context, path string) (module.Version, error) {
pathIsStd := search.IsStandardImportPath(path)
if modRoot == "" && !allowMissingModuleImports {
return module.Version{}, "", &ImportMissingError{
return module.Version{}, &ImportMissingError{
Path: path,
QueryErr: errors.New("working directory is not part of a module"),
}
}
// Not on build list.
// To avoid spurious remote fetches, next try the latest replacement for each module.
// (golang.org/issue/26241)
// To avoid spurious remote fetches, next try the latest replacement for each
// module (golang.org/issue/26241). This should give a useful message
// in -mod=readonly, and it will allow us to add a requirement with -mod=mod.
if modFile != nil {
latest := map[string]string{} // path -> version
for _, r := range modFile.Replace {
@ -226,7 +250,7 @@ func Import(ctx context.Context, path string) (m module.Version, dir string, err
}
}
mods = make([]module.Version, 0, len(latest))
mods := make([]module.Version, 0, len(latest))
for p, v := range latest {
// If the replacement didn't specify a version, synthesize a
// pseudo-version with an appropriate major version and a timestamp below
@ -252,19 +276,19 @@ func Import(ctx context.Context, path string) (m module.Version, dir string, err
root, isLocal, err := fetch(ctx, m)
if err != nil {
// Report fetch error as above.
return module.Version{}, "", err
return module.Version{}, err
}
if _, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil {
return m, "", err
return m, err
} else if ok {
return m, "", &ImportMissingError{Path: path, Module: m}
return m, nil
}
}
if len(mods) > 0 && module.CheckPath(path) != nil {
// The package path is not valid to fetch remotely,
// so it can only exist if in a replaced module,
// and we know from the above loop that it is not.
return module.Version{}, "", &PackageNotInModuleError{
return module.Version{}, &PackageNotInModuleError{
Mod: mods[0],
Query: "latest",
Pattern: path,
@ -273,6 +297,11 @@ func Import(ctx context.Context, path string) (m module.Version, dir string, err
}
}
// Before any further lookup, check that the path is valid.
if err := module.CheckImportPath(path); err != nil {
return module.Version{}, &invalidImportError{importPath: path, err: err}
}
if pathIsStd {
// This package isn't in the standard library, isn't in any module already
// in the build list, and isn't in any other module that the user has
@ -281,25 +310,39 @@ func Import(ctx context.Context, path string) (m module.Version, dir string, err
// QueryPackage cannot possibly find a module containing this package.
//
// Instead of trying QueryPackage, report an ImportMissingError immediately.
return module.Version{}, "", &ImportMissingError{Path: path}
return module.Version{}, &ImportMissingError{Path: path}
}
if cfg.BuildMod == "readonly" {
var queryErr error
if cfg.BuildModExplicit {
queryErr = fmt.Errorf("import lookup disabled by -mod=%s", cfg.BuildMod)
} else if cfg.BuildModReason != "" {
queryErr = fmt.Errorf("import lookup disabled by -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason)
}
return module.Version{}, &ImportMissingError{Path: path, QueryErr: queryErr}
}
// Look up module containing the package, for addition to the build list.
// Goal is to determine the module, download it to dir,
// and return m, dir, ImpportMissingError.
fmt.Fprintf(os.Stderr, "go: finding module for package %s\n", path)
candidates, err := QueryPackage(ctx, path, "latest", Allowed)
candidates, err := QueryPackage(ctx, path, "latest", CheckAllowed)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
// Return "cannot find module providing package […]" instead of whatever
// low-level error QueryPackage produced.
return module.Version{}, "", &ImportMissingError{Path: path, QueryErr: err}
return module.Version{}, &ImportMissingError{Path: path, QueryErr: err}
} else {
return module.Version{}, "", err
return module.Version{}, err
}
}
m = candidates[0].Mod
newMissingVersion := ""
for _, c := range candidates {
candidate0MissingVersion := ""
for i, c := range candidates {
cm := c.Mod
canAdd := true
for _, bm := range buildList {
if bm.Path == cm.Path && semver.Compare(bm.Version, cm.Version) > 0 {
// QueryPackage proposed that we add module cm to provide the package,
@ -310,13 +353,22 @@ func Import(ctx context.Context, path string) (m module.Version, dir string, err
// version (e.g., v1.0.0) of a module, but we have a newer version
// of the same module in the build list (e.g., v1.0.1-beta), and
// the package is not present there.
m = cm
newMissingVersion = bm.Version
canAdd = false
if i == 0 {
candidate0MissingVersion = bm.Version
}
break
}
}
if canAdd {
return cm, nil
}
}
return module.Version{}, &ImportMissingError{
Path: path,
Module: candidates[0].Mod,
newMissingVersion: candidate0MissingVersion,
}
return m, "", &ImportMissingError{Path: path, Module: m, newMissingVersion: newMissingVersion}
}
// maybeInModule reports whether, syntactically,

View File

@ -10,15 +10,20 @@ import (
"regexp"
"strings"
"testing"
"golang.org/x/mod/module"
)
var importTests = []struct {
path string
m module.Version
err string
}{
{
path: "golang.org/x/net/context",
err: "missing module for import: golang.org/x/net@.* provides golang.org/x/net/context",
m: module.Version{
Path: "golang.org/x/net",
},
},
{
path: "golang.org/x/net",
@ -26,15 +31,23 @@ var importTests = []struct {
},
{
path: "golang.org/x/text",
err: "missing module for import: golang.org/x/text@.* provides golang.org/x/text",
m: module.Version{
Path: "golang.org/x/text",
},
},
{
path: "github.com/rsc/quote/buggy",
err: "missing module for import: github.com/rsc/quote@v1.5.2 provides github.com/rsc/quote/buggy",
m: module.Version{
Path: "github.com/rsc/quote",
Version: "v1.5.2",
},
},
{
path: "github.com/rsc/quote",
err: "missing module for import: github.com/rsc/quote@v1.5.2 provides github.com/rsc/quote",
m: module.Version{
Path: "github.com/rsc/quote",
Version: "v1.5.2",
},
},
{
path: "golang.org/x/foo/bar",
@ -42,7 +55,7 @@ var importTests = []struct {
},
}
func TestImport(t *testing.T) {
func TestQueryImport(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
defer func(old bool) {
@ -55,12 +68,23 @@ func TestImport(t *testing.T) {
for _, tt := range importTests {
t.Run(strings.ReplaceAll(tt.path, "/", "_"), func(t *testing.T) {
// Note that there is no build list, so Import should always fail.
m, dir, err := Import(ctx, tt.path)
if err == nil {
t.Fatalf("Import(%q) = %v, %v, nil; expected error", tt.path, m, dir)
m, err := queryImport(ctx, tt.path)
if tt.err == "" {
if err != nil {
t.Fatalf("queryImport(_, %q): %v", tt.path, err)
}
} else {
if err == nil {
t.Fatalf("queryImport(_, %q) = %v, nil; expected error", tt.path, m)
}
if !regexp.MustCompile(tt.err).MatchString(err.Error()) {
t.Fatalf("queryImport(_, %q): error %q, want error matching %#q", tt.path, err, tt.err)
}
}
if !regexp.MustCompile(tt.err).MatchString(err.Error()) {
t.Fatalf("Import(%q): error %q, want error matching %#q", tt.path, err, tt.err)
if m.Path != tt.m.Path || (tt.m.Version != "" && m.Version != tt.m.Version) {
t.Errorf("queryImport(_, %q) = %v, _; want %v", tt.path, m, tt.m)
}
})
}

View File

@ -368,13 +368,9 @@ func InitMod(ctx context.Context) {
modFile = f
index = indexModFile(data, f, fixed)
if len(f.Syntax.Stmt) == 0 || f.Module == nil {
// Empty mod file. Must add module path.
path, err := findModulePath(modRoot)
if err != nil {
base.Fatalf("go: %v", err)
}
f.AddModuleStmt(path)
if f.Module == nil {
// No module declaration. Must add module path.
base.Fatalf("go: no module declaration in go.mod.\n\tRun 'go mod edit -module=example.com/mod' to specify the module path.")
}
if len(f.Syntax.Stmt) == 1 && f.Module != nil {
@ -383,14 +379,61 @@ func InitMod(ctx context.Context) {
legacyModInit()
}
modFileToBuildList()
if err := checkModulePathLax(f.Module.Mod.Path); err != nil {
base.Fatalf("go: %v", err)
}
setDefaultBuildMod()
modFileToBuildList()
if cfg.BuildMod == "vendor" {
readVendorList()
checkVendorConsistency()
}
}
// checkModulePathLax checks that the path meets some minimum requirements
// to avoid confusing users or the module cache. The requirements are weaker
// than those of module.CheckPath to allow room for weakening module path
// requirements in the future, but strong enough to help users avoid significant
// problems.
func checkModulePathLax(p string) error {
// TODO(matloob): Replace calls of this function in this CL with calls
// to module.CheckImportPath once it's been laxened, if it becomes laxened.
// See golang.org/issue/29101 for a discussion about whether to make CheckImportPath
// more lax or more strict.
errorf := func(format string, args ...interface{}) error {
return fmt.Errorf("invalid module path %q: %s", p, fmt.Sprintf(format, args...))
}
// Disallow shell characters " ' * < > ? ` | to avoid triggering bugs
// with file systems and subcommands. Disallow file path separators : and \
// because path separators other than / will confuse the module cache.
// See fileNameOK in golang.org/x/mod/module/module.go.
shellChars := "`" + `\"'*<>?|`
fsChars := `\:`
if i := strings.IndexAny(p, shellChars); i >= 0 {
return errorf("contains disallowed shell character %q", p[i])
}
if i := strings.IndexAny(p, fsChars); i >= 0 {
return errorf("contains disallowed path separator character %q", p[i])
}
// Ensure path.IsAbs and build.IsLocalImport are false, and that the path is
// invariant under path.Clean, also to avoid confusing the module cache.
if path.IsAbs(p) {
return errorf("is an absolute path")
}
if build.IsLocalImport(p) {
return errorf("is a local import path")
}
if path.Clean(p) != p {
return errorf("is not clean")
}
return nil
}
// fixVersion returns a modfile.VersionFixer implemented using the Query function.
//
// It resolves commit hashes and branch names to versions,
@ -459,7 +502,15 @@ func modFileToBuildList() {
list := []module.Version{Target}
for _, r := range modFile.Require {
list = append(list, r.Mod)
if index != nil && index.exclude[r.Mod] {
if cfg.BuildMod == "mod" {
fmt.Fprintf(os.Stderr, "go: dropping requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
} else {
fmt.Fprintf(os.Stderr, "go: ignoring requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
}
} else {
list = append(list, r.Mod)
}
}
buildList = list
}
@ -467,46 +518,49 @@ func modFileToBuildList() {
// setDefaultBuildMod sets a default value for cfg.BuildMod
// if it is currently empty.
func setDefaultBuildMod() {
if cfg.BuildMod != "" {
if cfg.BuildModExplicit {
// Don't override an explicit '-mod=' argument.
return
}
cfg.BuildMod = "mod"
if cfg.CmdName == "get" || strings.HasPrefix(cfg.CmdName, "mod ") {
// Don't set -mod implicitly for commands whose purpose is to
// manipulate the build list.
// 'get' and 'go mod' commands may update go.mod automatically.
// TODO(jayconrod): should this narrower? Should 'go mod download' or
// 'go mod graph' update go.mod by default?
cfg.BuildMod = "mod"
return
}
if modRoot == "" {
cfg.BuildMod = "mod"
return
}
if fi, err := os.Stat(filepath.Join(modRoot, "vendor")); err == nil && fi.IsDir() {
modGo := "unspecified"
if index.goVersion != "" {
if semver.Compare("v"+index.goVersion, "v1.14") >= 0 {
if index.goVersionV != "" {
if semver.Compare(index.goVersionV, "v1.14") >= 0 {
// The Go version is at least 1.14, and a vendor directory exists.
// Set -mod=vendor by default.
cfg.BuildMod = "vendor"
cfg.BuildModReason = "Go version in go.mod is at least 1.14 and vendor directory exists."
return
} else {
modGo = index.goVersion
modGo = index.goVersionV[1:]
}
}
// Since a vendor directory exists, we have a non-trivial reason for
// choosing -mod=mod, although it probably won't be used for anything.
// Record the reason anyway for consistency.
// It may be overridden if we switch to mod=readonly below.
cfg.BuildModReason = fmt.Sprintf("Go version in go.mod is %s.", modGo)
// Since a vendor directory exists, we should record why we didn't use it.
// This message won't normally be shown, but it may appear with import errors.
cfg.BuildModReason = fmt.Sprintf("Go version in go.mod is %s, so vendor directory was not used.", modGo)
}
p := ModFilePath()
if fi, err := os.Stat(p); err == nil && !hasWritePerm(p, fi) {
cfg.BuildMod = "readonly"
cfg.BuildModReason = "go.mod file is read-only."
return
}
cfg.BuildMod = "mod"
}
func legacyModInit() {
@ -674,16 +728,35 @@ func findModulePath(dir string) (string, error) {
}
// Look for path in GOPATH.
var badPathErr error
for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
if gpdir == "" {
continue
}
if rel := search.InDir(dir, filepath.Join(gpdir, "src")); rel != "" && rel != "." {
return filepath.ToSlash(rel), nil
path := filepath.ToSlash(rel)
// TODO(matloob): replace this with module.CheckImportPath
// once it's been laxened.
// Only checkModulePathLax here. There are some unpublishable
// module names that are compatible with checkModulePathLax
// but they already work in GOPATH so don't break users
// trying to do a build with modules. gorelease will alert users
// publishing their modules to fix their paths.
if err := checkModulePathLax(path); err != nil {
badPathErr = err
break
}
return path, nil
}
}
msg := `cannot determine module path for source directory %s (outside GOPATH, module path must be specified)
reason := "outside GOPATH, module path must be specified"
if badPathErr != nil {
// return a different error message if the module was in GOPATH, but
// the module path determined above would be an invalid path.
reason = fmt.Sprintf("bad module path inferred from directory in GOPATH: %v", badPathErr)
}
msg := `cannot determine module path for source directory %s (%s)
Example usage:
'go mod init example.com/m' to initialize a v0 or v1 module
@ -691,7 +764,7 @@ Example usage:
Run 'go help mod init' for more information.
`
return "", fmt.Errorf(msg, dir)
return "", fmt.Errorf(msg, dir, reason)
}
var (
@ -787,19 +860,16 @@ func WriteGoMod() {
// prefer to report a dirty go.mod over a dirty go.sum
if cfg.BuildModReason != "" {
base.Fatalf("go: updates to go.mod needed, disabled by -mod=readonly\n\t(%s)", cfg.BuildModReason)
} else {
} else if cfg.BuildModExplicit {
base.Fatalf("go: updates to go.mod needed, disabled by -mod=readonly")
}
}
// Always update go.sum, even if we didn't change go.mod: we may have
// downloaded modules that we didn't have before.
modfetch.WriteGoSum(keepSums())
if !dirty && cfg.CmdName != "mod tidy" {
// The go.mod file has the same semantic content that it had before
// (but not necessarily the same exact bytes).
// Ignore any intervening edits.
// Don't write go.mod, but write go.sum in case we added or trimmed sums.
modfetch.WriteGoSum(keepSums(true))
return
}
@ -810,6 +880,9 @@ func WriteGoMod() {
defer func() {
// At this point we have determined to make the go.mod file on disk equal to new.
index = indexModFile(new, modFile, false)
// Update go.sum after releasing the side lock and refreshing the index.
modfetch.WriteGoSum(keepSums(true))
}()
// Make a best-effort attempt to acquire the side lock, only to exclude
@ -850,7 +923,10 @@ func WriteGoMod() {
// the last load function like ImportPaths, LoadALL, etc.). It also contains
// entries for go.mod files needed for MVS (the version of these entries
// ends with "/go.mod").
func keepSums() map[module.Version]bool {
//
// If addDirect is true, the set also includes sums for modules directly
// required by go.mod, as represented by the index, with replacements applied.
func keepSums(addDirect bool) map[module.Version]bool {
// Walk the module graph and keep sums needed by MVS.
modkey := func(m module.Version) module.Version {
return module.Version{Path: m.Path, Version: m.Version + "/go.mod"}
@ -862,9 +938,6 @@ func keepSums() map[module.Version]bool {
walk = func(m module.Version) {
// If we build using a replacement module, keep the sum for the replacement,
// since that's the code we'll actually use during a build.
//
// TODO(golang.org/issue/29182): Perhaps we should keep both sums, and the
// sums for both sets of transitive requirements.
r := Replacement(m)
if r.Path == "" {
keep[modkey(m)] = true
@ -894,9 +967,27 @@ func keepSums() map[module.Version]bool {
}
}
// Add entries for modules directly required by go.mod.
if addDirect {
for m := range index.require {
var kept module.Version
if r := Replacement(m); r.Path != "" {
kept = r
} else {
kept = m
}
keep[kept] = true
keep[module.Version{Path: kept.Path, Version: kept.Version + "/go.mod"}] = true
}
}
return keep
}
func TrimGoSum() {
modfetch.TrimGoSum(keepSums())
// Don't retain sums for direct requirements in go.mod. When TrimGoSum is
// called, go.mod has not been updated, and it may contain requirements on
// modules deleted from the build list.
addDirect := false
modfetch.TrimGoSum(keepSums(addDirect))
}

View File

@ -20,12 +20,12 @@ import (
"golang.org/x/mod/module"
)
func ListModules(ctx context.Context, args []string, listU, listVersions bool) []*modinfo.ModulePublic {
mods := listModules(ctx, args, listVersions)
func ListModules(ctx context.Context, args []string, listU, listVersions, listRetracted bool) []*modinfo.ModulePublic {
mods := listModules(ctx, args, listVersions, listRetracted)
type token struct{}
sem := make(chan token, runtime.GOMAXPROCS(0))
if listU || listVersions {
if listU || listVersions || listRetracted {
for _, m := range mods {
add := func(m *modinfo.ModulePublic) {
sem <- token{}
@ -34,7 +34,10 @@ func ListModules(ctx context.Context, args []string, listU, listVersions bool) [
addUpdate(ctx, m)
}
if listVersions {
addVersions(m)
addVersions(ctx, m, listRetracted)
}
if listRetracted || listU {
addRetraction(ctx, m)
}
<-sem
}()
@ -54,10 +57,10 @@ func ListModules(ctx context.Context, args []string, listU, listVersions bool) [
return mods
}
func listModules(ctx context.Context, args []string, listVersions bool) []*modinfo.ModulePublic {
LoadBuildList(ctx)
func listModules(ctx context.Context, args []string, listVersions, listRetracted bool) []*modinfo.ModulePublic {
LoadAllModules(ctx)
if len(args) == 0 {
return []*modinfo.ModulePublic{moduleInfo(ctx, buildList[0], true)}
return []*modinfo.ModulePublic{moduleInfo(ctx, buildList[0], true, listRetracted)}
}
var mods []*modinfo.ModulePublic
@ -83,7 +86,13 @@ func listModules(ctx context.Context, args []string, listVersions bool) []*modin
}
}
info, err := Query(ctx, path, vers, current, nil)
allowed := CheckAllowed
if IsRevisionQuery(vers) || listRetracted {
// Allow excluded and retracted versions if the user asked for a
// specific revision or used 'go list -retracted'.
allowed = nil
}
info, err := Query(ctx, path, vers, current, allowed)
if err != nil {
mods = append(mods, &modinfo.ModulePublic{
Path: path,
@ -92,7 +101,8 @@ func listModules(ctx context.Context, args []string, listVersions bool) []*modin
})
continue
}
mods = append(mods, moduleInfo(ctx, module.Version{Path: path, Version: info.Version}, false))
mod := moduleInfo(ctx, module.Version{Path: path, Version: info.Version}, false, listRetracted)
mods = append(mods, mod)
continue
}
@ -117,7 +127,7 @@ func listModules(ctx context.Context, args []string, listVersions bool) []*modin
matched = true
if !matchedBuildList[i] {
matchedBuildList[i] = true
mods = append(mods, moduleInfo(ctx, m, true))
mods = append(mods, moduleInfo(ctx, m, true, listRetracted))
}
}
}
@ -129,7 +139,8 @@ func listModules(ctx context.Context, args []string, listVersions bool) []*modin
// Instead, resolve the module, even if it isn't an existing dependency.
info, err := Query(ctx, arg, "latest", "", nil)
if err == nil {
mods = append(mods, moduleInfo(ctx, module.Version{Path: arg, Version: info.Version}, false))
mod := moduleInfo(ctx, module.Version{Path: arg, Version: info.Version}, false, listRetracted)
mods = append(mods, mod)
} else {
mods = append(mods, &modinfo.ModulePublic{
Path: arg,

File diff suppressed because it is too large Load Diff

View File

@ -5,13 +5,31 @@
package modload
import (
"context"
"errors"
"fmt"
"path/filepath"
"strings"
"sync"
"unicode"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/lockedfile"
"cmd/go/internal/modfetch"
"cmd/go/internal/par"
"cmd/go/internal/trace"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
)
// lazyLoadingVersion is the Go version (plus leading "v") at which lazy module
// loading takes effect.
const lazyLoadingVersionV = "v1.16"
const go116EnableLazyLoading = true
var modFile *modfile.File
// A modFileIndex is an index of data corresponding to a modFile
@ -20,7 +38,7 @@ type modFileIndex struct {
data []byte
dataNeedsFix bool // true if fixVersion applied a change while parsing data
module module.Version
goVersion string
goVersionV string // GoVersion with "v" prefix
require map[module.Version]requireMeta
replace map[module.Version]module.Version
exclude map[module.Version]bool
@ -33,9 +51,151 @@ type requireMeta struct {
indirect bool
}
// Allowed reports whether module m is allowed (not excluded) by the main module's go.mod.
func Allowed(m module.Version) bool {
return index == nil || !index.exclude[m]
// CheckAllowed returns an error equivalent to ErrDisallowed if m is excluded by
// the main module's go.mod or retracted by its author. Most version queries use
// this to filter out versions that should not be used.
func CheckAllowed(ctx context.Context, m module.Version) error {
if err := CheckExclusions(ctx, m); err != nil {
return err
}
if err := checkRetractions(ctx, m); err != nil {
return err
}
return nil
}
// ErrDisallowed is returned by version predicates passed to Query and similar
// functions to indicate that a version should not be considered.
var ErrDisallowed = errors.New("disallowed module version")
// CheckExclusions returns an error equivalent to ErrDisallowed if module m is
// excluded by the main module's go.mod file.
func CheckExclusions(ctx context.Context, m module.Version) error {
if index != nil && index.exclude[m] {
return module.VersionError(m, errExcluded)
}
return nil
}
var errExcluded = &excludedError{}
type excludedError struct{}
func (e *excludedError) Error() string { return "excluded by go.mod" }
func (e *excludedError) Is(err error) bool { return err == ErrDisallowed }
// checkRetractions returns an error if module m has been retracted by
// its author.
func checkRetractions(ctx context.Context, m module.Version) error {
if m.Version == "" {
// Main module, standard library, or file replacement module.
// Cannot be retracted.
return nil
}
// Look up retraction information from the latest available version of
// the module. Cache retraction information so we don't parse the go.mod
// file repeatedly.
type entry struct {
retract []retraction
err error
}
path := m.Path
e := retractCache.Do(path, func() (v interface{}) {
ctx, span := trace.StartSpan(ctx, "checkRetractions "+path)
defer span.Done()
if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
// All versions of the module were replaced with a local directory.
// Don't load retractions.
return &entry{nil, nil}
}
// Find the latest version of the module.
// Ignore exclusions from the main module's go.mod.
// We may need to account for the current version: for example,
// v2.0.0+incompatible is not "latest" if v1.0.0 is current.
rev, err := Query(ctx, path, "latest", findCurrentVersion(path), nil)
if err != nil {
return &entry{err: err}
}
// Load go.mod for that version.
// If the version is replaced, we'll load retractions from the replacement.
// If there's an error loading the go.mod, we'll return it here.
// These errors should generally be ignored by callers of checkRetractions,
// since they happen frequently when we're offline. These errors are not
// equivalent to ErrDisallowed, so they may be distinguished from
// retraction errors.
summary, err := goModSummary(module.Version{Path: path, Version: rev.Version})
if err != nil {
return &entry{err: err}
}
return &entry{retract: summary.retract}
}).(*entry)
if e.err != nil {
return fmt.Errorf("loading module retractions: %v", e.err)
}
var rationale []string
isRetracted := false
for _, r := range e.retract {
if semver.Compare(r.Low, m.Version) <= 0 && semver.Compare(m.Version, r.High) <= 0 {
isRetracted = true
if r.Rationale != "" {
rationale = append(rationale, r.Rationale)
}
}
}
if isRetracted {
return &retractedError{rationale: rationale}
}
return nil
}
var retractCache par.Cache
type retractedError struct {
rationale []string
}
func (e *retractedError) Error() string {
msg := "retracted by module author"
if len(e.rationale) > 0 {
// This is meant to be a short error printed on a terminal, so just
// print the first rationale.
msg += ": " + ShortRetractionRationale(e.rationale[0])
}
return msg
}
func (e *retractedError) Is(err error) bool {
return err == ErrDisallowed
}
// ShortRetractionRationale returns a retraction rationale string that is safe
// to print in a terminal. It returns hard-coded strings if the rationale
// is empty, too long, or contains non-printable characters.
func ShortRetractionRationale(rationale string) string {
const maxRationaleBytes = 500
if i := strings.Index(rationale, "\n"); i >= 0 {
rationale = rationale[:i]
}
rationale = strings.TrimSpace(rationale)
if rationale == "" {
return "retracted by module author"
}
if len(rationale) > maxRationaleBytes {
return "(rationale omitted: too long)"
}
for _, r := range rationale {
if !unicode.IsGraphic(r) && !unicode.IsSpace(r) {
return "(rationale omitted: contains non-printable characters)"
}
}
// NOTE: the go.mod parser rejects invalid UTF-8, so we don't check that here.
return rationale
}
// Replacement returns the replacement for mod, if any, from go.mod.
@ -66,9 +226,11 @@ func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileInd
i.module = modFile.Module.Mod
}
i.goVersion = ""
i.goVersionV = ""
if modFile.Go != nil {
i.goVersion = modFile.Go.Version
// We're going to use the semver package to compare Go versions, so go ahead
// and add the "v" prefix it expects once instead of every time.
i.goVersionV = "v" + modFile.Go.Version
}
i.require = make(map[module.Version]requireMeta, len(modFile.Require))
@ -92,6 +254,23 @@ func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileInd
return i
}
// allPatternClosesOverTests reports whether the "all" pattern includes
// dependencies of tests outside the main module (as in Go 1.111.15).
// (Otherwise — as in Go 1.16+ — the "all" pattern includes only the packages
// transitively *imported by* the packages and tests in the main module.)
func (i *modFileIndex) allPatternClosesOverTests() bool {
if !go116EnableLazyLoading {
return true
}
if i != nil && semver.Compare(i.goVersionV, lazyLoadingVersionV) < 0 {
// The module explicitly predates the change in "all" for lazy loading, so
// continue to use the older interpretation. (If i == nil, we not in any
// module at all and should use the latest semantics.)
return true
}
return false
}
// modFileIsDirty reports whether the go.mod file differs meaningfully
// from what was indexed.
// If modFile has been changed (even cosmetically) since it was first read,
@ -114,11 +293,11 @@ func (i *modFileIndex) modFileIsDirty(modFile *modfile.File) bool {
}
if modFile.Go == nil {
if i.goVersion != "" {
if i.goVersionV != "" {
return true
}
} else if modFile.Go.Version != i.goVersion {
if i.goVersion == "" && cfg.BuildMod == "readonly" {
} else if "v"+modFile.Go.Version != i.goVersionV {
if i.goVersionV == "" && cfg.BuildMod == "readonly" {
// go.mod files did not always require a 'go' version, so do not error out
// if one is missing — we may be inside an older module in the module
// cache, and should bias toward providing useful behavior.
@ -162,3 +341,190 @@ func (i *modFileIndex) modFileIsDirty(modFile *modfile.File) bool {
return false
}
// rawGoVersion records the Go version parsed from each module's go.mod file.
//
// If a module is replaced, the version of the replacement is keyed by the
// replacement module.Version, not the version being replaced.
var rawGoVersion sync.Map // map[module.Version]string
// A modFileSummary is a summary of a go.mod file for which we do not need to
// retain complete information — for example, the go.mod file of a dependency
// module.
type modFileSummary struct {
module module.Version
goVersionV string // GoVersion with "v" prefix
require []module.Version
retract []retraction
}
// A retraction consists of a retracted version interval and rationale.
// retraction is like modfile.Retract, but it doesn't point to the syntax tree.
type retraction struct {
modfile.VersionInterval
Rationale string
}
// goModSummary returns a summary of the go.mod file for module m,
// taking into account any replacements for m, exclusions of its dependencies,
// and/or vendoring.
//
// goModSummary cannot be used on the Target module, as its requirements
// may change.
//
// The caller must not modify the returned summary.
func goModSummary(m module.Version) (*modFileSummary, error) {
if m == Target {
panic("internal error: goModSummary called on the Target module")
}
type cached struct {
summary *modFileSummary
err error
}
c := goModSummaryCache.Do(m, func() interface{} {
if cfg.BuildMod == "vendor" {
summary := &modFileSummary{
module: module.Version{Path: m.Path},
}
if vendorVersion[m.Path] != m.Version {
// This module is not vendored, so packages cannot be loaded from it and
// it cannot be relevant to the build.
return cached{summary, nil}
}
// For every module other than the target,
// return the full list of modules from modules.txt.
readVendorList()
// TODO(#36876): Load the "go" version from vendor/modules.txt and store it
// in rawGoVersion with the appropriate key.
// We don't know what versions the vendored module actually relies on,
// so assume that it requires everything.
summary.require = vendorList
return cached{summary, nil}
}
actual := Replacement(m)
if actual.Path == "" {
actual = m
}
summary, err := rawGoModSummary(actual)
if err != nil {
return cached{nil, err}
}
if actual.Version == "" {
// The actual module is a filesystem-local replacement, for which we have
// unfortunately not enforced any sort of invariants about module lines or
// matching module paths. Anything goes.
//
// TODO(bcmills): Remove this special-case, update tests, and add a
// release note.
} else {
if summary.module.Path == "" {
return cached{nil, module.VersionError(actual, errors.New("parsing go.mod: missing module line"))}
}
// In theory we should only allow mpath to be unequal to m.Path here if the
// version that we fetched lacks an explicit go.mod file: if the go.mod file
// is explicit, then it should match exactly (to ensure that imports of other
// packages within the module are interpreted correctly). Unfortunately, we
// can't determine that information from the module proxy protocol: we'll have
// to leave that validation for when we load actual packages from within the
// module.
if mpath := summary.module.Path; mpath != m.Path && mpath != actual.Path {
return cached{nil, module.VersionError(actual, fmt.Errorf(`parsing go.mod:
module declares its path as: %s
but was required as: %s`, mpath, m.Path))}
}
}
if index != nil && len(index.exclude) > 0 {
// Drop any requirements on excluded versions.
nonExcluded := summary.require[:0]
for _, r := range summary.require {
if !index.exclude[r] {
nonExcluded = append(nonExcluded, r)
}
}
summary.require = nonExcluded
}
return cached{summary, nil}
}).(cached)
return c.summary, c.err
}
var goModSummaryCache par.Cache // module.Version → goModSummary result
// rawGoModSummary returns a new summary of the go.mod file for module m,
// ignoring all replacements that may apply to m and excludes that may apply to
// its dependencies.
//
// rawGoModSummary cannot be used on the Target module.
func rawGoModSummary(m module.Version) (*modFileSummary, error) {
if m == Target {
panic("internal error: rawGoModSummary called on the Target module")
}
summary := new(modFileSummary)
var f *modfile.File
if m.Version == "" {
// m is a replacement module with only a file path.
dir := m.Path
if !filepath.IsAbs(dir) {
dir = filepath.Join(ModRoot(), dir)
}
gomod := filepath.Join(dir, "go.mod")
data, err := lockedfile.Read(gomod)
if err != nil {
return nil, module.VersionError(m, fmt.Errorf("reading %s: %v", base.ShortPath(gomod), err))
}
f, err = modfile.ParseLax(gomod, data, nil)
if err != nil {
return nil, module.VersionError(m, fmt.Errorf("parsing %s: %v", base.ShortPath(gomod), err))
}
} else {
if !semver.IsValid(m.Version) {
// Disallow the broader queries supported by fetch.Lookup.
base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", m.Path, m.Version)
}
data, err := modfetch.GoMod(m.Path, m.Version)
if err != nil {
return nil, err
}
f, err = modfile.ParseLax("go.mod", data, nil)
if err != nil {
return nil, module.VersionError(m, fmt.Errorf("parsing go.mod: %v", err))
}
}
if f.Module != nil {
summary.module = f.Module.Mod
}
if f.Go != nil && f.Go.Version != "" {
rawGoVersion.LoadOrStore(m, f.Go.Version)
summary.goVersionV = "v" + f.Go.Version
}
if len(f.Require) > 0 {
summary.require = make([]module.Version, 0, len(f.Require))
for _, req := range f.Require {
summary.require = append(summary.require, req.Mod)
}
}
if len(f.Retract) > 0 {
summary.retract = make([]retraction, 0, len(f.Retract))
for _, ret := range f.Retract {
summary.retract = append(summary.retract, retraction{
VersionInterval: ret.VersionInterval,
Rationale: ret.Rationale,
})
}
}
return summary, nil
}

View File

@ -11,16 +11,10 @@ import (
"os"
"path/filepath"
"sort"
"sync"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/lockedfile"
"cmd/go/internal/modfetch"
"cmd/go/internal/mvs"
"cmd/go/internal/par"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
)
@ -29,8 +23,6 @@ import (
// with any exclusions or replacements applied internally.
type mvsReqs struct {
buildList []module.Version
cache par.Cache
versions sync.Map
}
// Reqs returns the current module requirement graph.
@ -44,118 +36,21 @@ func Reqs() mvs.Reqs {
}
func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) {
type cached struct {
list []module.Version
err error
}
c := r.cache.Do(mod, func() interface{} {
list, err := r.required(mod)
if err != nil {
return cached{nil, err}
}
for i, mv := range list {
if index != nil {
for index.exclude[mv] {
mv1, err := r.next(mv)
if err != nil {
return cached{nil, err}
}
if mv1.Version == "none" {
return cached{nil, fmt.Errorf("%s(%s) depends on excluded %s(%s) with no newer version available", mod.Path, mod.Version, mv.Path, mv.Version)}
}
mv = mv1
}
}
list[i] = mv
}
return cached{list, nil}
}).(cached)
return c.list, c.err
}
func (r *mvsReqs) modFileToList(f *modfile.File) []module.Version {
list := make([]module.Version, 0, len(f.Require))
for _, r := range f.Require {
list = append(list, r.Mod)
}
return list
}
// required returns a unique copy of the requirements of mod.
func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
if mod == Target {
if modFile != nil && modFile.Go != nil {
r.versions.LoadOrStore(mod, modFile.Go.Version)
}
return append([]module.Version(nil), r.buildList[1:]...), nil
}
if cfg.BuildMod == "vendor" {
// For every module other than the target,
// return the full list of modules from modules.txt.
readVendorList()
return append([]module.Version(nil), vendorList...), nil
}
origPath := mod.Path
if repl := Replacement(mod); repl.Path != "" {
if repl.Version == "" {
// TODO: need to slip the new version into the tags list etc.
dir := repl.Path
if !filepath.IsAbs(dir) {
dir = filepath.Join(ModRoot(), dir)
}
gomod := filepath.Join(dir, "go.mod")
data, err := lockedfile.Read(gomod)
if err != nil {
return nil, fmt.Errorf("parsing %s: %v", base.ShortPath(gomod), err)
}
f, err := modfile.ParseLax(gomod, data, nil)
if err != nil {
return nil, fmt.Errorf("parsing %s: %v", base.ShortPath(gomod), err)
}
if f.Go != nil {
r.versions.LoadOrStore(mod, f.Go.Version)
}
return r.modFileToList(f), nil
}
mod = repl
// Use the build list as it existed when r was constructed, not the current
// global build list.
return r.buildList[1:], nil
}
if mod.Version == "none" {
return nil, nil
}
if !semver.IsValid(mod.Version) {
// Disallow the broader queries supported by fetch.Lookup.
base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", mod.Path, mod.Version)
}
data, err := modfetch.GoMod(mod.Path, mod.Version)
summary, err := goModSummary(mod)
if err != nil {
return nil, err
}
f, err := modfile.ParseLax("go.mod", data, nil)
if err != nil {
return nil, module.VersionError(mod, fmt.Errorf("parsing go.mod: %v", err))
}
if f.Module == nil {
return nil, module.VersionError(mod, errors.New("parsing go.mod: missing module line"))
}
if mpath := f.Module.Mod.Path; mpath != origPath && mpath != mod.Path {
return nil, module.VersionError(mod, fmt.Errorf(`parsing go.mod:
module declares its path as: %s
but was required as: %s`, mpath, origPath))
}
if f.Go != nil {
r.versions.LoadOrStore(mod, f.Go.Version)
}
return r.modFileToList(f), nil
return summary.require, nil
}
// Max returns the maximum of v1 and v2 according to semver.Compare.
@ -177,16 +72,29 @@ func (*mvsReqs) Upgrade(m module.Version) (module.Version, error) {
return m, nil
}
func versions(path string) ([]string, error) {
func versions(ctx context.Context, path string, allowed AllowedFunc) ([]string, error) {
// Note: modfetch.Lookup and repo.Versions are cached,
// so there's no need for us to add extra caching here.
var versions []string
err := modfetch.TryProxies(func(proxy string) error {
repo, err := modfetch.Lookup(proxy, path)
if err == nil {
versions, err = repo.Versions("")
if err != nil {
return err
}
return err
allVersions, err := repo.Versions("")
if err != nil {
return err
}
allowedVersions := make([]string, 0, len(allVersions))
for _, v := range allVersions {
if err := allowed(ctx, module.Version{Path: path, Version: v}); err == nil {
allowedVersions = append(allowedVersions, v)
} else if !errors.Is(err, ErrDisallowed) {
return err
}
}
versions = allowedVersions
return nil
})
return versions, err
}
@ -194,7 +102,8 @@ func versions(path string) ([]string, error) {
// Previous returns the tagged version of m.Path immediately prior to
// m.Version, or version "none" if no prior version is tagged.
func (*mvsReqs) Previous(m module.Version) (module.Version, error) {
list, err := versions(m.Path)
// TODO(golang.org/issue/38714): thread tracing context through MVS.
list, err := versions(context.TODO(), m.Path, CheckAllowed)
if err != nil {
return module.Version{}, err
}
@ -209,7 +118,8 @@ func (*mvsReqs) Previous(m module.Version) (module.Version, error) {
// It is only used by the exclusion processing in the Required method,
// not called directly by MVS.
func (*mvsReqs) next(m module.Version) (module.Version, error) {
list, err := versions(m.Path)
// TODO(golang.org/issue/38714): thread tracing context through MVS.
list, err := versions(context.TODO(), m.Path, CheckAllowed)
if err != nil {
return module.Version{}, err
}

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