mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.link] all: merge branch 'master' into dev.link
The only conflict is in cmd/internal/obj/link.go and the resolution is trivial. Change-Id: Ic79b760865a972a0ab68291d06386531d012de86
This commit is contained in:
commit
d77b809df9
314 changed files with 6741 additions and 1170 deletions
|
|
@ -115,7 +115,7 @@ Community-related issues should be reported to
|
|||
See the <a href="/conduct">Code of Conduct</a> for more details.
|
||||
</p>
|
||||
|
||||
<h3><a href="/doc/contribute.html">Contributing code</a></h3>
|
||||
<h3><a href="/doc/contribute.html">Contributing code & documentation</a></h3>
|
||||
|
||||
<p>
|
||||
Go is an open source project and we welcome contributions from the community.
|
||||
|
|
|
|||
|
|
@ -112,6 +112,27 @@ TODO
|
|||
graphic characters and spaces.
|
||||
</p>
|
||||
|
||||
<p><!-- golang.org/issue/31481 -->
|
||||
<code>-modcacherw</code> is a new flag that instructs the <code>go</code>
|
||||
command to leave newly-created directories in the module cache at their
|
||||
default permissions rather than making them read-only.
|
||||
The use of this flag makes it more likely that tests or other tools will
|
||||
accidentally add files not included in the module's verified checksum.
|
||||
However, it allows the use of <code>rm</code> <code>-rf</code>
|
||||
(instead of <code>go</code> <code>clean</code> <code>-modcache</code>)
|
||||
to remove the module cache.
|
||||
</p>
|
||||
|
||||
<p><!-- golang.org/issue/34506 -->
|
||||
<code>-modfile=file</code> is a new flag that instructs the <code>go</code>
|
||||
command to read (and possibly write) an alternate go.mod file instead of the
|
||||
one in the module root directory. A file named "go.mod" must still be present
|
||||
in order to determine the module root directory, but it is not
|
||||
accessed. When <code>-modfile</code> is specified, an alternate go.sum file
|
||||
is also used: its path is derived from the <code>-modfile</code> flag by
|
||||
trimming the ".mod" extension and appending ".sum".
|
||||
</p>
|
||||
|
||||
<h2 id="runtime">Runtime</h2>
|
||||
|
||||
<p>
|
||||
|
|
@ -169,6 +190,19 @@ TODO
|
|||
|
||||
</dl><!-- mime -->
|
||||
|
||||
<dl id="math"><dt><a href="/pkg/math/">math</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL 127458 -->
|
||||
The new <a href="/pkg/math/#Fma"><code>Fma</code></a> function
|
||||
computes <code>x*y+z</code> in floating point with no
|
||||
intermediate rounding of the <code>x*y</code>
|
||||
computation. Several architectures implement this computation
|
||||
using dedicated hardware instructions for additional
|
||||
performance.
|
||||
</p>
|
||||
|
||||
</dl><!-- math -->
|
||||
|
||||
<dl id="plugin"><dt><a href="/pkg/plugin/">plugin</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL 191617 -->
|
||||
|
|
|
|||
61
doc/modules.md
Normal file
61
doc/modules.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<!--{
|
||||
"Title": "Go Modules Reference",
|
||||
"Subtitle": "Version of Sep 4, 2019",
|
||||
"Path": "/ref/modules"
|
||||
}-->
|
||||
<!-- TODO(jayconrod): ensure golang.org/x/website can render Markdown or convert
|
||||
this document to HTML before Go 1.14. -->
|
||||
<!-- TODO(jayconrod): ensure Markdown renderer adds anchors or add them
|
||||
manually. -->
|
||||
|
||||
## Introduction
|
||||
|
||||
## Glossary
|
||||
|
||||
## Packages, modules, and versions
|
||||
|
||||
## go.mod files
|
||||
|
||||
### go.mod file format
|
||||
|
||||
### Minimal version selection (MVS)
|
||||
|
||||
### Compatibility with non-module repositories
|
||||
|
||||
## Module-aware build commands
|
||||
|
||||
### Enabling modules
|
||||
|
||||
### Initializing modules
|
||||
|
||||
### Build commands
|
||||
|
||||
### Vendoring
|
||||
|
||||
### `go mod download`
|
||||
|
||||
### `go mod verify`
|
||||
|
||||
### `go mod edit`
|
||||
|
||||
### `go clean -modcache`
|
||||
|
||||
### Module commands outside a module
|
||||
|
||||
## Retrieving modules
|
||||
|
||||
### GOPROXY protocol
|
||||
|
||||
### Module zip requirements
|
||||
|
||||
### Privacy
|
||||
|
||||
### Private modules
|
||||
|
||||
## Authenticating modules
|
||||
|
||||
### go.sum file format
|
||||
|
||||
### Checksum database
|
||||
|
||||
### Privacy
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
// You can edit this code!
|
||||
// Click here and start typing.
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo 'misc/benchcmp has moved:' >&2
|
||||
echo ' go get -u golang.org/x/tools/cmd/benchcmp' >&2
|
||||
exit 2
|
||||
|
|
@ -130,7 +130,9 @@ func MakeSeed(s uint64) Seed {
|
|||
// New returns a new Hash object. Different hash objects allocated by
|
||||
// this function will very likely have different seeds.
|
||||
func New() *Hash {
|
||||
seed := Seed{s: uint64(runtime_fastrand())}
|
||||
s1 := uint64(runtime_fastrand())
|
||||
s2 := uint64(runtime_fastrand())
|
||||
seed := Seed{s: s1<<32 + s2}
|
||||
return &Hash{
|
||||
seed: seed,
|
||||
state: seed,
|
||||
|
|
|
|||
|
|
@ -61,6 +61,20 @@ func TestHashBytesVsString(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHashHighBytes(t *testing.T) {
|
||||
// See issue 34925.
|
||||
const N = 10
|
||||
m := map[uint64]struct{}{}
|
||||
for i := 0; i < N; i++ {
|
||||
h := hash.New()
|
||||
h.AddString("foo")
|
||||
m[h.Hash()>>32] = struct{}{}
|
||||
}
|
||||
if len(m) < N/2 {
|
||||
t.Errorf("from %d seeds, wanted at least %d different hashes; got %d", N, N/2, len(m))
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure a Hash implements the hash.Hash and hash.Hash64 interfaces.
|
||||
var _ basehash.Hash = &hash.Hash{}
|
||||
var _ basehash.Hash64 = &hash.Hash{}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ For example:
|
|||
|
||||
The default pkg-config tool may be changed by setting the PKG_CONFIG environment variable.
|
||||
|
||||
For security reasons, only a limited set of flags are allowed, notably -D, -I, and -l.
|
||||
For security reasons, only a limited set of flags are allowed, notably -D, -U, -I, and -l.
|
||||
To allow additional flags, set CGO_CFLAGS_ALLOW to a regular expression
|
||||
matching the new flags. To disallow flags that would otherwise be allowed,
|
||||
set CGO_CFLAGS_DISALLOW to a regular expression matching arguments
|
||||
|
|
@ -99,7 +99,7 @@ Will be expanded to:
|
|||
|
||||
When the Go tool sees that one or more Go files use the special import
|
||||
"C", it will look for other non-Go files in the directory and compile
|
||||
them as part of the Go package. Any .c, .s, or .S files will be
|
||||
them as part of the Go package. Any .c, .s, .S or .sx files will be
|
||||
compiled with the C compiler. Any .cc, .cpp, or .cxx files will be
|
||||
compiled with the C++ compiler. Any .f, .F, .for or .f90 files will be
|
||||
compiled with the fortran compiler. Any .h, .hh, .hpp, or .hxx files will
|
||||
|
|
|
|||
|
|
@ -164,6 +164,14 @@ func duff(size int64) (int64, int64) {
|
|||
|
||||
func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
|
||||
switch v.Op {
|
||||
case ssa.OpAMD64VFMADD231SD:
|
||||
p := s.Prog(v.Op.Asm())
|
||||
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: v.Args[2].Reg()}
|
||||
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: v.Reg()}
|
||||
p.SetFrom3(obj.Addr{Type: obj.TYPE_REG, Reg: v.Args[1].Reg()})
|
||||
if v.Reg() != v.Args[0].Reg() {
|
||||
v.Fatalf("input[0] and output not in same register %s", v.LongString())
|
||||
}
|
||||
case ssa.OpAMD64ADDQ, ssa.OpAMD64ADDL:
|
||||
r := v.Reg()
|
||||
r1 := v.Args[0].Reg()
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
|
|||
p.To.Reg = r
|
||||
case ssa.OpARMSRR:
|
||||
genregshift(s, arm.AMOVW, 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_RR)
|
||||
case ssa.OpARMMULAF, ssa.OpARMMULAD, ssa.OpARMMULSF, ssa.OpARMMULSD:
|
||||
case ssa.OpARMMULAF, ssa.OpARMMULAD, ssa.OpARMMULSF, ssa.OpARMMULSD, ssa.OpARMFMULAD:
|
||||
r := v.Reg()
|
||||
r0 := v.Args[0].Reg()
|
||||
r1 := v.Args[1].Reg()
|
||||
|
|
|
|||
|
|
@ -178,6 +178,11 @@ func widstruct(errtype *types.Type, t *types.Type, o int64, flag int) int64 {
|
|||
// have not already been calculated, it calls Fatal.
|
||||
// This is used to prevent data races in the back end.
|
||||
func dowidth(t *types.Type) {
|
||||
// Calling dowidth when typecheck tracing enabled is not safe.
|
||||
// See issue #33658.
|
||||
if enableTrace && skipDowidthForTracing {
|
||||
return
|
||||
}
|
||||
if Widthptr == 0 {
|
||||
Fatalf("dowidth without betypeinit")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -185,6 +185,8 @@ var runtimeDecls = [...]struct {
|
|||
{"checkptrArithmetic", funcTag, 122},
|
||||
{"x86HasPOPCNT", varTag, 15},
|
||||
{"x86HasSSE41", varTag, 15},
|
||||
{"x86HasFMA", varTag, 15},
|
||||
{"armHasVFPv4", varTag, 15},
|
||||
{"arm64HasATOMICS", varTag, 15},
|
||||
}
|
||||
|
||||
|
|
@ -310,7 +312,7 @@ func runtimeTypes() []*types.Type {
|
|||
typs[117] = functype(nil, []*Node{anonfield(typs[23]), anonfield(typs[23])}, []*Node{anonfield(typs[23])})
|
||||
typs[118] = functype(nil, []*Node{anonfield(typs[50])}, nil)
|
||||
typs[119] = functype(nil, []*Node{anonfield(typs[50]), anonfield(typs[50])}, nil)
|
||||
typs[120] = functype(nil, []*Node{anonfield(typs[56]), anonfield(typs[1])}, nil)
|
||||
typs[120] = functype(nil, []*Node{anonfield(typs[56]), anonfield(typs[1]), anonfield(typs[50])}, nil)
|
||||
typs[121] = types.NewSlice(typs[56])
|
||||
typs[122] = functype(nil, []*Node{anonfield(typs[56]), anonfield(typs[121])}, nil)
|
||||
return typs[:]
|
||||
|
|
|
|||
|
|
@ -235,10 +235,12 @@ func racewriterange(addr, size uintptr)
|
|||
func msanread(addr, size uintptr)
|
||||
func msanwrite(addr, size uintptr)
|
||||
|
||||
func checkptrAlignment(unsafe.Pointer, *byte)
|
||||
func checkptrAlignment(unsafe.Pointer, *byte, uintptr)
|
||||
func checkptrArithmetic(unsafe.Pointer, []unsafe.Pointer)
|
||||
|
||||
// architecture variants
|
||||
var x86HasPOPCNT bool
|
||||
var x86HasSSE41 bool
|
||||
var x86HasFMA bool
|
||||
var armHasVFPv4 bool
|
||||
var arm64HasATOMICS bool
|
||||
|
|
|
|||
|
|
@ -471,8 +471,8 @@ func (e *Escape) exprSkipInit(k EscHole, n *Node) {
|
|||
e.discard(max)
|
||||
|
||||
case OCONV, OCONVNOP:
|
||||
if checkPtr(e.curfn) && n.Type.Etype == TUNSAFEPTR && n.Left.Type.IsPtr() {
|
||||
// When -d=checkptr is enabled, treat
|
||||
if checkPtr(e.curfn, 2) && n.Type.Etype == TUNSAFEPTR && n.Left.Type.IsPtr() {
|
||||
// When -d=checkptr=2 is enabled, treat
|
||||
// conversions to unsafe.Pointer as an
|
||||
// escaping operation. This allows better
|
||||
// runtime instrumentation, since we can more
|
||||
|
|
@ -880,7 +880,9 @@ func (e *Escape) augmentParamHole(k EscHole, where *Node) EscHole {
|
|||
// non-transient location to avoid arguments from being
|
||||
// transiently allocated.
|
||||
if where.Op == ODEFER && e.loopDepth == 1 {
|
||||
where.Esc = EscNever // force stack allocation of defer record (see ssa.go)
|
||||
// force stack allocation of defer record, unless open-coded
|
||||
// defers are used (see ssa.go)
|
||||
where.Esc = EscNever
|
||||
return e.later(k)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -311,6 +311,8 @@ var (
|
|||
racewriterange,
|
||||
x86HasPOPCNT,
|
||||
x86HasSSE41,
|
||||
x86HasFMA,
|
||||
armHasVFPv4,
|
||||
arm64HasATOMICS,
|
||||
typedmemclr,
|
||||
typedmemmove,
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ func pragmaValue(verb string) syntax.Pragma {
|
|||
case "go:norace":
|
||||
return Norace
|
||||
case "go:nosplit":
|
||||
return Nosplit
|
||||
return Nosplit | NoCheckPtr // implies NoCheckPtr (see #34972)
|
||||
case "go:noinline":
|
||||
return Noinline
|
||||
case "go:nocheckptr":
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ var (
|
|||
Debug_typecheckinl int
|
||||
Debug_gendwarfinl int
|
||||
Debug_softfloat int
|
||||
Debug_defer int
|
||||
)
|
||||
|
||||
// Debug arguments.
|
||||
|
|
@ -83,6 +84,7 @@ var debugtab = []struct {
|
|||
{"typecheckinl", "eager typechecking of inline function bodies", &Debug_typecheckinl},
|
||||
{"dwarfinl", "print information about DWARF inlined function creation", &Debug_gendwarfinl},
|
||||
{"softfloat", "force compiler to emit soft-float code", &Debug_softfloat},
|
||||
{"defer", "print information about defer compilation", &Debug_defer},
|
||||
}
|
||||
|
||||
const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>]
|
||||
|
|
@ -94,6 +96,11 @@ const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>]
|
|||
const debugHelpFooter = `
|
||||
<value> is key-specific.
|
||||
|
||||
Key "checkptr" supports values:
|
||||
"0": instrumentation disabled
|
||||
"1": conversions involving unsafe.Pointer are instrumented
|
||||
"2": conversions to unsafe.Pointer force heap allocation
|
||||
|
||||
Key "pctab" supports values:
|
||||
"pctospadj", "pctofile", "pctoline", "pctoinline", "pctopcdata"
|
||||
`
|
||||
|
|
@ -338,6 +345,11 @@ func Main(archInit func(*Arch)) {
|
|||
if flag_race && flag_msan {
|
||||
log.Fatal("cannot use both -race and -msan")
|
||||
}
|
||||
if (flag_race || flag_msan) && objabi.GOOS != "windows" {
|
||||
// -race and -msan imply -d=checkptr for now (except on windows).
|
||||
// TODO(mdempsky): Re-evaluate before Go 1.14. See #34964.
|
||||
Debug_checkptr = 1
|
||||
}
|
||||
if ispkgin(omit_pkgs) {
|
||||
flag_race = false
|
||||
flag_msan = false
|
||||
|
|
|
|||
|
|
@ -296,6 +296,9 @@ func addGCLocals() {
|
|||
}
|
||||
ggloblsym(x, int32(len(x.P)), attr)
|
||||
}
|
||||
if x := s.Func.OpenCodedDeferInfo; x != nil {
|
||||
ggloblsym(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -863,7 +863,16 @@ func (lv *Liveness) solve() {
|
|||
newliveout.vars.Set(pos)
|
||||
}
|
||||
case ssa.BlockExit:
|
||||
// panic exit - nothing to do
|
||||
if lv.fn.Func.HasDefer() && !lv.fn.Func.OpenCodedDeferDisallowed() {
|
||||
// All stack slots storing args for open-coded
|
||||
// defers are live at panic exit (since they
|
||||
// will be used in running defers)
|
||||
for i, n := range lv.vars {
|
||||
if n.Name.OpenDeferSlot() {
|
||||
newliveout.vars.Set(int32(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
// A variable is live on output from this block
|
||||
// if it is live on input to some successor.
|
||||
|
|
|
|||
|
|
@ -112,27 +112,6 @@ func bmap(t *types.Type) *types.Type {
|
|||
elems := makefield("elems", arr)
|
||||
field = append(field, elems)
|
||||
|
||||
// Make sure the overflow pointer is the last memory in the struct,
|
||||
// because the runtime assumes it can use size-ptrSize as the
|
||||
// offset of the overflow pointer. We double-check that property
|
||||
// below once the offsets and size are computed.
|
||||
//
|
||||
// BUCKETSIZE is 8, so the struct is aligned to 64 bits to this point.
|
||||
// On 32-bit systems, the max alignment is 32-bit, and the
|
||||
// overflow pointer will add another 32-bit field, and the struct
|
||||
// will end with no padding.
|
||||
// On 64-bit systems, the max alignment is 64-bit, and the
|
||||
// overflow pointer will add another 64-bit field, and the struct
|
||||
// will end with no padding.
|
||||
// On nacl/amd64p32, however, the max alignment is 64-bit,
|
||||
// but the overflow pointer will add only a 32-bit field,
|
||||
// so if the struct needs 64-bit padding (because a key or elem does)
|
||||
// then it would end with an extra 32-bit padding field.
|
||||
// Preempt that by emitting the padding here.
|
||||
if int(elemtype.Align) > Widthptr || int(keytype.Align) > Widthptr {
|
||||
field = append(field, makefield("pad", types.Types[TUINTPTR]))
|
||||
}
|
||||
|
||||
// If keys and elems have no pointers, the map implementation
|
||||
// can keep a list of overflow pointers on the side so that
|
||||
// buckets can be marked as having no pointers.
|
||||
|
|
@ -196,7 +175,7 @@ func bmap(t *types.Type) *types.Type {
|
|||
}
|
||||
|
||||
// Double-check that overflow field is final memory in struct,
|
||||
// with no padding at end. See comment above.
|
||||
// with no padding at end.
|
||||
if overflow.Offset != bucket.Width-int64(Widthptr) {
|
||||
Fatalf("bad offset of overflow in bmap for %v", t)
|
||||
}
|
||||
|
|
@ -338,6 +317,7 @@ func deferstruct(stksize int64) *types.Type {
|
|||
makefield("siz", types.Types[TUINT32]),
|
||||
makefield("started", types.Types[TBOOL]),
|
||||
makefield("heap", types.Types[TBOOL]),
|
||||
makefield("openDefer", types.Types[TBOOL]),
|
||||
makefield("sp", types.Types[TUINTPTR]),
|
||||
makefield("pc", types.Types[TUINTPTR]),
|
||||
// Note: the types here don't really matter. Defer structures
|
||||
|
|
@ -346,6 +326,9 @@ func deferstruct(stksize int64) *types.Type {
|
|||
makefield("fn", types.Types[TUINTPTR]),
|
||||
makefield("_panic", types.Types[TUINTPTR]),
|
||||
makefield("link", types.Types[TUINTPTR]),
|
||||
makefield("framepc", types.Types[TUINTPTR]),
|
||||
makefield("varp", types.Types[TUINTPTR]),
|
||||
makefield("fd", types.Types[TUINTPTR]),
|
||||
makefield("args", argtype),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ func TestSizeof(t *testing.T) {
|
|||
_32bit uintptr // size on 32bit platforms
|
||||
_64bit uintptr // size on 64bit platforms
|
||||
}{
|
||||
{Func{}, 116, 208},
|
||||
{Func{}, 124, 224},
|
||||
{Name{}, 32, 56},
|
||||
{Param{}, 24, 48},
|
||||
{Node{}, 76, 128},
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ var ssaDumpStdout bool // whether to dump to stdout
|
|||
var ssaDumpCFG string // generate CFGs for these phases
|
||||
const ssaDumpFile = "ssa.html"
|
||||
|
||||
// The max number of defers in a function using open-coded defers. We enforce this
|
||||
// limit because the deferBits bitmask is currently a single byte (to minimize code size)
|
||||
const maxOpenDefers = 8
|
||||
|
||||
// ssaDumpInlined holds all inlined functions when ssaDump contains a function name.
|
||||
var ssaDumpInlined []*Node
|
||||
|
||||
|
|
@ -91,6 +95,8 @@ func initssaconfig() {
|
|||
racewriterange = sysfunc("racewriterange")
|
||||
x86HasPOPCNT = sysvar("x86HasPOPCNT") // bool
|
||||
x86HasSSE41 = sysvar("x86HasSSE41") // bool
|
||||
x86HasFMA = sysvar("x86HasFMA") // bool
|
||||
armHasVFPv4 = sysvar("armHasVFPv4") // bool
|
||||
arm64HasATOMICS = sysvar("arm64HasATOMICS") // bool
|
||||
typedmemclr = sysfunc("typedmemclr")
|
||||
typedmemmove = sysfunc("typedmemmove")
|
||||
|
|
@ -165,6 +171,111 @@ func initssaconfig() {
|
|||
SigPanic = sysfunc("sigpanic")
|
||||
}
|
||||
|
||||
// getParam returns the Field of ith param of node n (which is a
|
||||
// function/method/interface call), where the receiver of a method call is
|
||||
// considered as the 0th parameter. This does not include the receiver of an
|
||||
// interface call.
|
||||
func getParam(n *Node, i int) *types.Field {
|
||||
t := n.Left.Type
|
||||
if n.Op == OCALLMETH {
|
||||
if i == 0 {
|
||||
return t.Recv()
|
||||
}
|
||||
return t.Params().Field(i - 1)
|
||||
}
|
||||
return t.Params().Field(i)
|
||||
}
|
||||
|
||||
// dvarint writes a varint v to the funcdata in symbol x and returns the new offset
|
||||
func dvarint(x *obj.LSym, off int, v int64) int {
|
||||
if v < 0 || v > 1e9 {
|
||||
panic(fmt.Sprintf("dvarint: bad offset for funcdata - %v", v))
|
||||
}
|
||||
if v < 1<<7 {
|
||||
return duint8(x, off, uint8(v))
|
||||
}
|
||||
off = duint8(x, off, uint8((v&127)|128))
|
||||
if v < 1<<14 {
|
||||
return duint8(x, off, uint8(v>>7))
|
||||
}
|
||||
off = duint8(x, off, uint8(((v>>7)&127)|128))
|
||||
if v < 1<<21 {
|
||||
return duint8(x, off, uint8(v>>14))
|
||||
}
|
||||
off = duint8(x, off, uint8(((v>>14)&127)|128))
|
||||
if v < 1<<28 {
|
||||
return duint8(x, off, uint8(v>>21))
|
||||
}
|
||||
off = duint8(x, off, uint8(((v>>21)&127)|128))
|
||||
return duint8(x, off, uint8(v>>28))
|
||||
}
|
||||
|
||||
// emitOpenDeferInfo emits FUNCDATA information about the defers in a function
|
||||
// that is using open-coded defers. This funcdata is used to determine the active
|
||||
// defers in a function and execute those defers during panic processing.
|
||||
//
|
||||
// The funcdata is all encoded in varints (since values will almost always be less than
|
||||
// 128, but stack offsets could potentially be up to 2Gbyte). All "locations" (offsets)
|
||||
// for stack variables are specified as the number of bytes below varp (pointer to the
|
||||
// top of the local variables) for their starting address. The format is:
|
||||
//
|
||||
// - Max total argument size among all the defers
|
||||
// - Offset of the deferBits variable
|
||||
// - Number of defers in the function
|
||||
// - Information about each defer call, in reverse order of appearance in the function:
|
||||
// - Total argument size of the call
|
||||
// - Offset of the closure value to call
|
||||
// - Number of arguments (including interface receiver or method receiver as first arg)
|
||||
// - Information about each argument
|
||||
// - Offset of the stored defer argument in this function's frame
|
||||
// - Size of the argument
|
||||
// - Offset of where argument should be placed in the args frame when making call
|
||||
func (s *state) emitOpenDeferInfo() {
|
||||
x := Ctxt.Lookup(s.curfn.Func.lsym.Name + ".opendefer")
|
||||
s.curfn.Func.lsym.Func.OpenCodedDeferInfo = x
|
||||
off := 0
|
||||
|
||||
// Compute maxargsize (max size of arguments for all defers)
|
||||
// first, so we can output it first to the funcdata
|
||||
var maxargsize int64
|
||||
for i := len(s.openDefers) - 1; i >= 0; i-- {
|
||||
r := s.openDefers[i]
|
||||
argsize := r.n.Left.Type.ArgWidth()
|
||||
if argsize > maxargsize {
|
||||
maxargsize = argsize
|
||||
}
|
||||
}
|
||||
off = dvarint(x, off, maxargsize)
|
||||
off = dvarint(x, off, -s.deferBitsTemp.Xoffset)
|
||||
off = dvarint(x, off, int64(len(s.openDefers)))
|
||||
|
||||
// Write in reverse-order, for ease of running in that order at runtime
|
||||
for i := len(s.openDefers) - 1; i >= 0; i-- {
|
||||
r := s.openDefers[i]
|
||||
off = dvarint(x, off, r.n.Left.Type.ArgWidth())
|
||||
off = dvarint(x, off, -r.closureNode.Xoffset)
|
||||
numArgs := len(r.argNodes)
|
||||
if r.rcvrNode != nil {
|
||||
// If there's an interface receiver, treat/place it as the first
|
||||
// arg. (If there is a method receiver, it's already included as
|
||||
// first arg in r.argNodes.)
|
||||
numArgs++
|
||||
}
|
||||
off = dvarint(x, off, int64(numArgs))
|
||||
if r.rcvrNode != nil {
|
||||
off = dvarint(x, off, -r.rcvrNode.Xoffset)
|
||||
off = dvarint(x, off, s.config.PtrSize)
|
||||
off = dvarint(x, off, 0)
|
||||
}
|
||||
for j, arg := range r.argNodes {
|
||||
f := getParam(r.n, j)
|
||||
off = dvarint(x, off, -arg.Xoffset)
|
||||
off = dvarint(x, off, f.Type.Size())
|
||||
off = dvarint(x, off, f.Offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// buildssa builds an SSA function for fn.
|
||||
// worker indicates which of the backend workers is doing the processing.
|
||||
func buildssa(fn *Node, worker int) *ssa.Func {
|
||||
|
|
@ -227,11 +338,55 @@ func buildssa(fn *Node, worker int) *ssa.Func {
|
|||
s.labeledNodes = map[*Node]*ssaLabel{}
|
||||
s.fwdVars = map[*Node]*ssa.Value{}
|
||||
s.startmem = s.entryNewValue0(ssa.OpInitMem, types.TypeMem)
|
||||
|
||||
s.hasOpenDefers = Debug['N'] == 0 && s.hasdefer && !s.curfn.Func.OpenCodedDeferDisallowed()
|
||||
if s.hasOpenDefers && (Ctxt.Flag_shared || Ctxt.Flag_dynlink) && thearch.LinkArch.Name == "386" {
|
||||
// Don't support open-coded defers for 386 ONLY when using shared
|
||||
// libraries, because there is extra code (added by rewriteToUseGot())
|
||||
// preceding the deferreturn/ret code that is generated by gencallret()
|
||||
// that we don't track correctly.
|
||||
s.hasOpenDefers = false
|
||||
}
|
||||
if s.hasOpenDefers && s.curfn.Func.Exit.Len() > 0 {
|
||||
// Skip doing open defers if there is any extra exit code (likely
|
||||
// copying heap-allocated return values or race detection), since
|
||||
// we will not generate that code in the case of the extra
|
||||
// deferreturn/ret segment.
|
||||
s.hasOpenDefers = false
|
||||
}
|
||||
if s.hasOpenDefers &&
|
||||
s.curfn.Func.numReturns*s.curfn.Func.numDefers > 15 {
|
||||
// Since we are generating defer calls at every exit for
|
||||
// open-coded defers, skip doing open-coded defers if there are
|
||||
// too many returns (especially if there are multiple defers).
|
||||
// Open-coded defers are most important for improving performance
|
||||
// for smaller functions (which don't have many returns).
|
||||
s.hasOpenDefers = false
|
||||
}
|
||||
|
||||
s.sp = s.entryNewValue0(ssa.OpSP, types.Types[TUINTPTR]) // TODO: use generic pointer type (unsafe.Pointer?) instead
|
||||
s.sb = s.entryNewValue0(ssa.OpSB, types.Types[TUINTPTR])
|
||||
|
||||
s.startBlock(s.f.Entry)
|
||||
s.vars[&memVar] = s.startmem
|
||||
if s.hasOpenDefers {
|
||||
// Create the deferBits variable and stack slot. deferBits is a
|
||||
// bitmask showing which of the open-coded defers in this function
|
||||
// have been activated.
|
||||
deferBitsTemp := tempAt(src.NoXPos, s.curfn, types.Types[TUINT8])
|
||||
s.deferBitsTemp = deferBitsTemp
|
||||
// For this value, AuxInt is initialized to zero by default
|
||||
startDeferBits := s.entryNewValue0(ssa.OpConst8, types.Types[TUINT8])
|
||||
s.vars[&deferBitsVar] = startDeferBits
|
||||
s.deferBitsAddr = s.addr(deferBitsTemp, false)
|
||||
s.store(types.Types[TUINT8], s.deferBitsAddr, startDeferBits)
|
||||
// Make sure that the deferBits stack slot is kept alive (for use
|
||||
// by panics) and stores to deferBits are not eliminated, even if
|
||||
// all checking code on deferBits in the function exit can be
|
||||
// eliminated, because the defer statements were all
|
||||
// unconditional.
|
||||
s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, deferBitsTemp, s.mem(), false)
|
||||
}
|
||||
|
||||
// Generate addresses of local declarations
|
||||
s.decladdrs = map[*Node]*ssa.Value{}
|
||||
|
|
@ -287,6 +442,11 @@ func buildssa(fn *Node, worker int) *ssa.Func {
|
|||
|
||||
// Main call to ssa package to compile function
|
||||
ssa.Compile(s.f)
|
||||
|
||||
if s.hasOpenDefers {
|
||||
s.emitOpenDeferInfo()
|
||||
}
|
||||
|
||||
return s.f
|
||||
}
|
||||
|
||||
|
|
@ -375,6 +535,29 @@ func (s *state) updateUnsetPredPos(b *ssa.Block) {
|
|||
}
|
||||
}
|
||||
|
||||
// Information about each open-coded defer.
|
||||
type openDeferInfo struct {
|
||||
// The ODEFER node representing the function call of the defer
|
||||
n *Node
|
||||
// If defer call is closure call, the address of the argtmp where the
|
||||
// closure is stored.
|
||||
closure *ssa.Value
|
||||
// The node representing the argtmp where the closure is stored - used for
|
||||
// function, method, or interface call, to store a closure that panic
|
||||
// processing can use for this defer.
|
||||
closureNode *Node
|
||||
// If defer call is interface call, the address of the argtmp where the
|
||||
// receiver is stored
|
||||
rcvr *ssa.Value
|
||||
// The node representing the argtmp where the receiver is stored
|
||||
rcvrNode *Node
|
||||
// The addresses of the argtmps where the evaluated arguments of the defer
|
||||
// function call are stored.
|
||||
argVals []*ssa.Value
|
||||
// The nodes representing the argtmps where the args of the defer are stored
|
||||
argNodes []*Node
|
||||
}
|
||||
|
||||
type state struct {
|
||||
// configuration (arch) information
|
||||
config *ssa.Config
|
||||
|
|
@ -416,6 +599,9 @@ type state struct {
|
|||
startmem *ssa.Value
|
||||
sp *ssa.Value
|
||||
sb *ssa.Value
|
||||
// value representing address of where deferBits autotmp is stored
|
||||
deferBitsAddr *ssa.Value
|
||||
deferBitsTemp *Node
|
||||
|
||||
// line number stack. The current line number is top of stack
|
||||
line []src.XPos
|
||||
|
|
@ -432,6 +618,19 @@ type state struct {
|
|||
cgoUnsafeArgs bool
|
||||
hasdefer bool // whether the function contains a defer statement
|
||||
softFloat bool
|
||||
hasOpenDefers bool // whether we are doing open-coded defers
|
||||
|
||||
// If doing open-coded defers, list of info about the defer calls in
|
||||
// scanning order. Hence, at exit we should run these defers in reverse
|
||||
// order of this list
|
||||
openDefers []*openDeferInfo
|
||||
// For open-coded defers, this is the beginning and end blocks of the last
|
||||
// defer exit code that we have generated so far. We use these to share
|
||||
// code between exits if the shareDeferExits option (disabled by default)
|
||||
// is on.
|
||||
lastDeferExit *ssa.Block // Entry block of last defer exit code we generated
|
||||
lastDeferFinalBlock *ssa.Block // Final block of last defer exit code we generated
|
||||
lastDeferCount int // Number of defers encountered at that point
|
||||
}
|
||||
|
||||
type funcLine struct {
|
||||
|
|
@ -475,6 +674,7 @@ var (
|
|||
capVar = Node{Op: ONAME, Sym: &types.Sym{Name: "cap"}}
|
||||
typVar = Node{Op: ONAME, Sym: &types.Sym{Name: "typ"}}
|
||||
okVar = Node{Op: ONAME, Sym: &types.Sym{Name: "ok"}}
|
||||
deferBitsVar = Node{Op: ONAME, Sym: &types.Sym{Name: "deferBits"}}
|
||||
)
|
||||
|
||||
// startBlock sets the current block we're generating code in to b.
|
||||
|
|
@ -865,11 +1065,26 @@ func (s *state) stmt(n *Node) {
|
|||
}
|
||||
}
|
||||
case ODEFER:
|
||||
if Debug_defer > 0 {
|
||||
var defertype string
|
||||
if s.hasOpenDefers {
|
||||
defertype = "open-coded"
|
||||
} else if n.Esc == EscNever {
|
||||
defertype = "stack-allocated"
|
||||
} else {
|
||||
defertype = "heap-allocated"
|
||||
}
|
||||
Warnl(n.Pos, "%s defer", defertype)
|
||||
}
|
||||
if s.hasOpenDefers {
|
||||
s.openDeferRecord(n.Left)
|
||||
} else {
|
||||
d := callDefer
|
||||
if n.Esc == EscNever {
|
||||
d = callDeferStack
|
||||
}
|
||||
s.call(n.Left, d)
|
||||
}
|
||||
case OGO:
|
||||
s.call(n.Left, callGo)
|
||||
|
||||
|
|
@ -1286,13 +1501,29 @@ func (s *state) stmt(n *Node) {
|
|||
}
|
||||
}
|
||||
|
||||
// If true, share as many open-coded defer exits as possible (with the downside of
|
||||
// worse line-number information)
|
||||
const shareDeferExits = false
|
||||
|
||||
// exit processes any code that needs to be generated just before returning.
|
||||
// It returns a BlockRet block that ends the control flow. Its control value
|
||||
// will be set to the final memory state.
|
||||
func (s *state) exit() *ssa.Block {
|
||||
if s.hasdefer {
|
||||
if s.hasOpenDefers {
|
||||
if shareDeferExits && s.lastDeferExit != nil && len(s.openDefers) == s.lastDeferCount {
|
||||
if s.curBlock.Kind != ssa.BlockPlain {
|
||||
panic("Block for an exit should be BlockPlain")
|
||||
}
|
||||
s.curBlock.AddEdgeTo(s.lastDeferExit)
|
||||
s.endBlock()
|
||||
return s.lastDeferFinalBlock
|
||||
}
|
||||
s.openDeferExit()
|
||||
} else {
|
||||
s.rtcall(Deferreturn, true, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// Run exit code. Typically, this code copies heap-allocated PPARAMOUT
|
||||
// variables back to the stack.
|
||||
|
|
@ -1314,6 +1545,9 @@ func (s *state) exit() *ssa.Block {
|
|||
b := s.endBlock()
|
||||
b.Kind = ssa.BlockRet
|
||||
b.SetControl(m)
|
||||
if s.hasdefer && s.hasOpenDefers {
|
||||
s.lastDeferFinalBlock = b
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
|
|
@ -3321,6 +3555,81 @@ func init() {
|
|||
return s.newValue2(ssa.OpCopysign, types.Types[TFLOAT64], args[0], args[1])
|
||||
},
|
||||
sys.PPC64, sys.Wasm)
|
||||
addF("math", "Fma",
|
||||
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
|
||||
return s.newValue3(ssa.OpFma, types.Types[TFLOAT64], args[0], args[1], args[2])
|
||||
},
|
||||
sys.ARM64, sys.PPC64, sys.S390X)
|
||||
addF("math", "Fma",
|
||||
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
|
||||
if !s.config.UseFMA {
|
||||
a := s.call(n, callNormal)
|
||||
s.vars[n] = s.load(types.Types[TFLOAT64], a)
|
||||
return s.variable(n, types.Types[TFLOAT64])
|
||||
}
|
||||
addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), x86HasFMA, s.sb)
|
||||
v := s.load(types.Types[TBOOL], addr)
|
||||
b := s.endBlock()
|
||||
b.Kind = ssa.BlockIf
|
||||
b.SetControl(v)
|
||||
bTrue := s.f.NewBlock(ssa.BlockPlain)
|
||||
bFalse := s.f.NewBlock(ssa.BlockPlain)
|
||||
bEnd := s.f.NewBlock(ssa.BlockPlain)
|
||||
b.AddEdgeTo(bTrue)
|
||||
b.AddEdgeTo(bFalse)
|
||||
b.Likely = ssa.BranchLikely // >= haswell cpus are common
|
||||
|
||||
// We have the intrinsic - use it directly.
|
||||
s.startBlock(bTrue)
|
||||
s.vars[n] = s.newValue3(ssa.OpFma, types.Types[TFLOAT64], args[0], args[1], args[2])
|
||||
s.endBlock().AddEdgeTo(bEnd)
|
||||
|
||||
// Call the pure Go version.
|
||||
s.startBlock(bFalse)
|
||||
a := s.call(n, callNormal)
|
||||
s.vars[n] = s.load(types.Types[TFLOAT64], a)
|
||||
s.endBlock().AddEdgeTo(bEnd)
|
||||
|
||||
// Merge results.
|
||||
s.startBlock(bEnd)
|
||||
return s.variable(n, types.Types[TFLOAT64])
|
||||
},
|
||||
sys.AMD64)
|
||||
addF("math", "Fma",
|
||||
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
|
||||
if !s.config.UseFMA {
|
||||
a := s.call(n, callNormal)
|
||||
s.vars[n] = s.load(types.Types[TFLOAT64], a)
|
||||
return s.variable(n, types.Types[TFLOAT64])
|
||||
}
|
||||
addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), armHasVFPv4, s.sb)
|
||||
v := s.load(types.Types[TBOOL], addr)
|
||||
b := s.endBlock()
|
||||
b.Kind = ssa.BlockIf
|
||||
b.SetControl(v)
|
||||
bTrue := s.f.NewBlock(ssa.BlockPlain)
|
||||
bFalse := s.f.NewBlock(ssa.BlockPlain)
|
||||
bEnd := s.f.NewBlock(ssa.BlockPlain)
|
||||
b.AddEdgeTo(bTrue)
|
||||
b.AddEdgeTo(bFalse)
|
||||
b.Likely = ssa.BranchLikely
|
||||
|
||||
// We have the intrinsic - use it directly.
|
||||
s.startBlock(bTrue)
|
||||
s.vars[n] = s.newValue3(ssa.OpFma, types.Types[TFLOAT64], args[0], args[1], args[2])
|
||||
s.endBlock().AddEdgeTo(bEnd)
|
||||
|
||||
// Call the pure Go version.
|
||||
s.startBlock(bFalse)
|
||||
a := s.call(n, callNormal)
|
||||
s.vars[n] = s.load(types.Types[TFLOAT64], a)
|
||||
s.endBlock().AddEdgeTo(bEnd)
|
||||
|
||||
// Merge results.
|
||||
s.startBlock(bEnd)
|
||||
return s.variable(n, types.Types[TFLOAT64])
|
||||
},
|
||||
sys.ARM)
|
||||
|
||||
makeRoundAMD64 := func(op ssa.Op) func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
|
||||
return func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
|
||||
|
|
@ -3764,6 +4073,230 @@ func (s *state) intrinsicArgs(n *Node) []*ssa.Value {
|
|||
return args
|
||||
}
|
||||
|
||||
// openDeferRecord adds code to evaluate and store the args for an open-code defer
|
||||
// call, and records info about the defer, so we can generate proper code on the
|
||||
// exit paths. n is the sub-node of the defer node that is the actual function
|
||||
// call. We will also record funcdata information on where the args are stored
|
||||
// (as well as the deferBits variable), and this will enable us to run the proper
|
||||
// defer calls during panics.
|
||||
func (s *state) openDeferRecord(n *Node) {
|
||||
// Do any needed expression evaluation for the args (including the
|
||||
// receiver, if any). This may be evaluating something like 'autotmp_3 =
|
||||
// once.mutex'. Such a statement will create a mapping in s.vars[] from
|
||||
// the autotmp name to the evaluated SSA arg value, but won't do any
|
||||
// stores to the stack.
|
||||
s.stmtList(n.List)
|
||||
|
||||
var args []*ssa.Value
|
||||
var argNodes []*Node
|
||||
|
||||
opendefer := &openDeferInfo{
|
||||
n: n,
|
||||
}
|
||||
fn := n.Left
|
||||
if n.Op == OCALLFUNC {
|
||||
// We must always store the function value in a stack slot for the
|
||||
// runtime panic code to use. But in the defer exit code, we will
|
||||
// call the function directly if it is a static function.
|
||||
closureVal := s.expr(fn)
|
||||
closure := s.openDeferSave(fn, fn.Type, closureVal)
|
||||
opendefer.closureNode = closure.Aux.(*Node)
|
||||
if !(fn.Op == ONAME && fn.Class() == PFUNC) {
|
||||
opendefer.closure = closure
|
||||
}
|
||||
} else if n.Op == OCALLMETH {
|
||||
if fn.Op != ODOTMETH {
|
||||
Fatalf("OCALLMETH: n.Left not an ODOTMETH: %v", fn)
|
||||
}
|
||||
closureVal := s.getMethodClosure(fn)
|
||||
// We must always store the function value in a stack slot for the
|
||||
// runtime panic code to use. But in the defer exit code, we will
|
||||
// call the method directly.
|
||||
closure := s.openDeferSave(fn, fn.Type, closureVal)
|
||||
opendefer.closureNode = closure.Aux.(*Node)
|
||||
} else {
|
||||
if fn.Op != ODOTINTER {
|
||||
Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op)
|
||||
}
|
||||
closure, rcvr := s.getClosureAndRcvr(fn)
|
||||
opendefer.closure = s.openDeferSave(fn, closure.Type, closure)
|
||||
// Important to get the receiver type correct, so it is recognized
|
||||
// as a pointer for GC purposes.
|
||||
opendefer.rcvr = s.openDeferSave(nil, fn.Type.Recv().Type, rcvr)
|
||||
opendefer.closureNode = opendefer.closure.Aux.(*Node)
|
||||
opendefer.rcvrNode = opendefer.rcvr.Aux.(*Node)
|
||||
}
|
||||
for _, argn := range n.Rlist.Slice() {
|
||||
v := s.openDeferSave(argn, argn.Type, s.expr(argn))
|
||||
args = append(args, v)
|
||||
argNodes = append(argNodes, v.Aux.(*Node))
|
||||
}
|
||||
opendefer.argVals = args
|
||||
opendefer.argNodes = argNodes
|
||||
index := len(s.openDefers)
|
||||
s.openDefers = append(s.openDefers, opendefer)
|
||||
|
||||
// Update deferBits only after evaluation and storage to stack of
|
||||
// args/receiver/interface is successful.
|
||||
bitvalue := s.constInt8(types.Types[TUINT8], 1<<uint(index))
|
||||
newDeferBits := s.newValue2(ssa.OpOr8, types.Types[TUINT8], s.variable(&deferBitsVar, types.Types[TUINT8]), bitvalue)
|
||||
s.vars[&deferBitsVar] = newDeferBits
|
||||
s.store(types.Types[TUINT8], s.deferBitsAddr, newDeferBits)
|
||||
}
|
||||
|
||||
// openDeferSave generates SSA nodes to store a value val (with type t) for an
|
||||
// open-coded defer on the stack at an explicit autotmp location, so it can be
|
||||
// reloaded and used for the appropriate call on exit. n is the associated node,
|
||||
// which is only needed if the associated type is non-SSAable. It returns an SSA
|
||||
// value representing a pointer to the stack location.
|
||||
func (s *state) openDeferSave(n *Node, t *types.Type, val *ssa.Value) *ssa.Value {
|
||||
argTemp := tempAt(val.Pos.WithNotStmt(), s.curfn, t)
|
||||
argTemp.Name.SetOpenDeferSlot(true)
|
||||
var addrArgTemp *ssa.Value
|
||||
// Use OpVarLive to make sure stack slots for the args, etc. are not
|
||||
// removed by dead-store elimination
|
||||
if s.curBlock.ID != s.f.Entry.ID {
|
||||
// Force the argtmp storing this defer function/receiver/arg to be
|
||||
// declared in the entry block, so that it will be live for the
|
||||
// defer exit code (which will actually access it only if the
|
||||
// associated defer call has been activated).
|
||||
s.defvars[s.f.Entry.ID][&memVar] = s.entryNewValue1A(ssa.OpVarDef, types.TypeMem, argTemp, s.defvars[s.f.Entry.ID][&memVar])
|
||||
s.defvars[s.f.Entry.ID][&memVar] = s.entryNewValue1A(ssa.OpVarLive, types.TypeMem, argTemp, s.defvars[s.f.Entry.ID][&memVar])
|
||||
addrArgTemp = s.entryNewValue2A(ssa.OpLocalAddr, types.NewPtr(argTemp.Type), argTemp, s.sp, s.defvars[s.f.Entry.ID][&memVar])
|
||||
} else {
|
||||
// Special case if we're still in the entry block. We can't use
|
||||
// the above code, since s.defvars[s.f.Entry.ID] isn't defined
|
||||
// until we end the entry block with s.endBlock().
|
||||
s.vars[&memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, argTemp, s.mem(), false)
|
||||
s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, argTemp, s.mem(), false)
|
||||
addrArgTemp = s.newValue2Apos(ssa.OpLocalAddr, types.NewPtr(argTemp.Type), argTemp, s.sp, s.mem(), false)
|
||||
}
|
||||
if types.Haspointers(t) {
|
||||
// Since we may use this argTemp during exit depending on the
|
||||
// deferBits, we must define it unconditionally on entry.
|
||||
// Therefore, we must make sure it is zeroed out in the entry
|
||||
// block if it contains pointers, else GC may wrongly follow an
|
||||
// uninitialized pointer value.
|
||||
argTemp.Name.SetNeedzero(true)
|
||||
}
|
||||
if !canSSAType(t) {
|
||||
if n.Op != ONAME {
|
||||
panic(fmt.Sprintf("Non-SSAable value should be a named location: %v", n))
|
||||
}
|
||||
a := s.addr(n, false)
|
||||
s.move(t, addrArgTemp, a)
|
||||
return addrArgTemp
|
||||
}
|
||||
// We are storing to the stack, hence we can avoid the full checks in
|
||||
// storeType() (no write barrier) and do a simple store().
|
||||
s.store(t, addrArgTemp, val)
|
||||
return addrArgTemp
|
||||
}
|
||||
|
||||
// openDeferExit generates SSA for processing all the open coded defers at exit.
|
||||
// The code involves loading deferBits, and checking each of the bits to see if
|
||||
// the corresponding defer statement was executed. For each bit that is turned
|
||||
// on, the associated defer call is made.
|
||||
func (s *state) openDeferExit() {
|
||||
deferExit := s.f.NewBlock(ssa.BlockPlain)
|
||||
s.endBlock().AddEdgeTo(deferExit)
|
||||
s.startBlock(deferExit)
|
||||
s.lastDeferExit = deferExit
|
||||
s.lastDeferCount = len(s.openDefers)
|
||||
zeroval := s.constInt8(types.Types[TUINT8], 0)
|
||||
// Test for and run defers in reverse order
|
||||
for i := len(s.openDefers) - 1; i >= 0; i-- {
|
||||
r := s.openDefers[i]
|
||||
bCond := s.f.NewBlock(ssa.BlockPlain)
|
||||
bEnd := s.f.NewBlock(ssa.BlockPlain)
|
||||
|
||||
deferBits := s.variable(&deferBitsVar, types.Types[TUINT8])
|
||||
// Generate code to check if the bit associated with the current
|
||||
// defer is set.
|
||||
bitval := s.constInt8(types.Types[TUINT8], 1<<uint(i))
|
||||
andval := s.newValue2(ssa.OpAnd8, types.Types[TUINT8], deferBits, bitval)
|
||||
eqVal := s.newValue2(ssa.OpEq8, types.Types[TBOOL], andval, zeroval)
|
||||
b := s.endBlock()
|
||||
b.Kind = ssa.BlockIf
|
||||
b.SetControl(eqVal)
|
||||
b.AddEdgeTo(bEnd)
|
||||
b.AddEdgeTo(bCond)
|
||||
bCond.AddEdgeTo(bEnd)
|
||||
s.startBlock(bCond)
|
||||
|
||||
// Clear this bit in deferBits and force store back to stack, so
|
||||
// we will not try to re-run this defer call if this defer call panics.
|
||||
nbitval := s.newValue1(ssa.OpCom8, types.Types[TUINT8], bitval)
|
||||
maskedval := s.newValue2(ssa.OpAnd8, types.Types[TUINT8], deferBits, nbitval)
|
||||
s.store(types.Types[TUINT8], s.deferBitsAddr, maskedval)
|
||||
// Use this value for following tests, so we keep previous
|
||||
// bits cleared.
|
||||
s.vars[&deferBitsVar] = maskedval
|
||||
|
||||
// Generate code to call the function call of the defer, using the
|
||||
// closure/receiver/args that were stored in argtmps at the point
|
||||
// of the defer statement.
|
||||
argStart := Ctxt.FixedFrameSize()
|
||||
fn := r.n.Left
|
||||
stksize := fn.Type.ArgWidth()
|
||||
if r.rcvr != nil {
|
||||
// rcvr in case of OCALLINTER
|
||||
v := s.load(r.rcvr.Type.Elem(), r.rcvr)
|
||||
addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart)
|
||||
s.store(types.Types[TUINTPTR], addr, v)
|
||||
}
|
||||
for j, argAddrVal := range r.argVals {
|
||||
f := getParam(r.n, j)
|
||||
pt := types.NewPtr(f.Type)
|
||||
addr := s.constOffPtrSP(pt, argStart+f.Offset)
|
||||
if !canSSAType(f.Type) {
|
||||
s.move(f.Type, addr, argAddrVal)
|
||||
} else {
|
||||
argVal := s.load(f.Type, argAddrVal)
|
||||
s.storeType(f.Type, addr, argVal, 0, false)
|
||||
}
|
||||
}
|
||||
var call *ssa.Value
|
||||
if r.closure != nil {
|
||||
v := s.load(r.closure.Type.Elem(), r.closure)
|
||||
s.maybeNilCheckClosure(v, callDefer)
|
||||
codeptr := s.rawLoad(types.Types[TUINTPTR], v)
|
||||
call = s.newValue3(ssa.OpClosureCall, types.TypeMem, codeptr, v, s.mem())
|
||||
} else {
|
||||
// Do a static call if the original call was a static function or method
|
||||
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, fn.Sym.Linksym(), s.mem())
|
||||
}
|
||||
call.AuxInt = stksize
|
||||
s.vars[&memVar] = call
|
||||
// Make sure that the stack slots with pointers are kept live
|
||||
// through the call (which is a pre-emption point). Also, we will
|
||||
// use the first call of the last defer exit to compute liveness
|
||||
// for the deferreturn, so we want all stack slots to be live.
|
||||
if r.closureNode != nil {
|
||||
s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, r.closureNode, s.mem(), false)
|
||||
}
|
||||
if r.rcvrNode != nil {
|
||||
if types.Haspointers(r.rcvrNode.Type) {
|
||||
s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, r.rcvrNode, s.mem(), false)
|
||||
}
|
||||
}
|
||||
for _, argNode := range r.argNodes {
|
||||
if types.Haspointers(argNode.Type) {
|
||||
s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, argNode, s.mem(), false)
|
||||
}
|
||||
}
|
||||
|
||||
if i == len(s.openDefers)-1 {
|
||||
// Record the call of the first defer. This will be used
|
||||
// to set liveness info for the deferreturn (which is also
|
||||
// used for any location that causes a runtime panic)
|
||||
s.f.LastDeferExit = call
|
||||
}
|
||||
s.endBlock()
|
||||
s.startBlock(bEnd)
|
||||
}
|
||||
}
|
||||
|
||||
// Calls the function n using the specified call type.
|
||||
// Returns the address of the return value (or nil if none).
|
||||
func (s *state) call(n *Node, k callKind) *ssa.Value {
|
||||
|
|
@ -3779,11 +4312,10 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
|
|||
break
|
||||
}
|
||||
closure = s.expr(fn)
|
||||
if k != callDefer && k != callDeferStack && (thearch.LinkArch.Family == sys.Wasm || objabi.GOOS == "aix" && k != callGo) {
|
||||
// Deferred nil function needs to panic when the function is invoked, not the point of defer statement.
|
||||
// On AIX, the closure needs to be verified as fn can be nil, except if it's a call go. This needs to be handled by the runtime to have the "go of nil func value" error.
|
||||
// TODO(neelance): On other architectures this should be eliminated by the optimization steps
|
||||
s.nilCheck(closure)
|
||||
if k != callDefer && k != callDeferStack {
|
||||
// Deferred nil function needs to panic when the function is invoked,
|
||||
// not the point of defer statement.
|
||||
s.maybeNilCheckClosure(closure, k)
|
||||
}
|
||||
case OCALLMETH:
|
||||
if fn.Op != ODOTMETH {
|
||||
|
|
@ -3793,35 +4325,20 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
|
|||
sym = fn.Sym
|
||||
break
|
||||
}
|
||||
// Make a name n2 for the function.
|
||||
// fn.Sym might be sync.(*Mutex).Unlock.
|
||||
// Make a PFUNC node out of that, then evaluate it.
|
||||
// We get back an SSA value representing &sync.(*Mutex).Unlock·f.
|
||||
// We can then pass that to defer or go.
|
||||
n2 := newnamel(fn.Pos, fn.Sym)
|
||||
n2.Name.Curfn = s.curfn
|
||||
n2.SetClass(PFUNC)
|
||||
// n2.Sym already existed, so it's already marked as a function.
|
||||
n2.Pos = fn.Pos
|
||||
n2.Type = types.Types[TUINT8] // dummy type for a static closure. Could use runtime.funcval if we had it.
|
||||
closure = s.expr(n2)
|
||||
closure = s.getMethodClosure(fn)
|
||||
// Note: receiver is already present in n.Rlist, so we don't
|
||||
// want to set it here.
|
||||
case OCALLINTER:
|
||||
if fn.Op != ODOTINTER {
|
||||
s.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op)
|
||||
}
|
||||
i := s.expr(fn.Left)
|
||||
itab := s.newValue1(ssa.OpITab, types.Types[TUINTPTR], i)
|
||||
s.nilCheck(itab)
|
||||
itabidx := fn.Xoffset + 2*int64(Widthptr) + 8 // offset of fun field in runtime.itab
|
||||
itab = s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.UintptrPtr, itabidx, itab)
|
||||
var iclosure *ssa.Value
|
||||
iclosure, rcvr = s.getClosureAndRcvr(fn)
|
||||
if k == callNormal {
|
||||
codeptr = s.load(types.Types[TUINTPTR], itab)
|
||||
codeptr = s.load(types.Types[TUINTPTR], iclosure)
|
||||
} else {
|
||||
closure = itab
|
||||
closure = iclosure
|
||||
}
|
||||
rcvr = s.newValue1(ssa.OpIData, types.Types[TUINTPTR], i)
|
||||
}
|
||||
dowidth(fn.Type)
|
||||
stksize := fn.Type.ArgWidth() // includes receiver, args, and results
|
||||
|
|
@ -3847,18 +4364,22 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
|
|||
s.constInt32(types.Types[TUINT32], int32(stksize)))
|
||||
// 1: started, set in deferprocStack
|
||||
// 2: heap, set in deferprocStack
|
||||
// 3: sp, set in deferprocStack
|
||||
// 4: pc, set in deferprocStack
|
||||
// 5: fn
|
||||
// 3: openDefer
|
||||
// 4: sp, set in deferprocStack
|
||||
// 5: pc, set in deferprocStack
|
||||
// 6: fn
|
||||
s.store(closure.Type,
|
||||
s.newValue1I(ssa.OpOffPtr, closure.Type.PtrTo(), t.FieldOff(5), addr),
|
||||
s.newValue1I(ssa.OpOffPtr, closure.Type.PtrTo(), t.FieldOff(6), addr),
|
||||
closure)
|
||||
// 6: panic, set in deferprocStack
|
||||
// 7: link, set in deferprocStack
|
||||
// 7: panic, set in deferprocStack
|
||||
// 8: link, set in deferprocStack
|
||||
// 9: framepc
|
||||
// 10: varp
|
||||
// 11: fd
|
||||
|
||||
// Then, store all the arguments of the defer call.
|
||||
ft := fn.Type
|
||||
off := t.FieldOff(8)
|
||||
off := t.FieldOff(12)
|
||||
args := n.Rlist.Slice()
|
||||
|
||||
// Set receiver (for interface calls). Always a pointer.
|
||||
|
|
@ -3973,6 +4494,44 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
|
|||
return s.constOffPtrSP(types.NewPtr(fp.Type), fp.Offset+Ctxt.FixedFrameSize())
|
||||
}
|
||||
|
||||
// maybeNilCheckClosure checks if a nil check of a closure is needed in some
|
||||
// architecture-dependent situations and, if so, emits the nil check.
|
||||
func (s *state) maybeNilCheckClosure(closure *ssa.Value, k callKind) {
|
||||
if thearch.LinkArch.Family == sys.Wasm || objabi.GOOS == "aix" && k != callGo {
|
||||
// On AIX, the closure needs to be verified as fn can be nil, except if it's a call go. This needs to be handled by the runtime to have the "go of nil func value" error.
|
||||
// TODO(neelance): On other architectures this should be eliminated by the optimization steps
|
||||
s.nilCheck(closure)
|
||||
}
|
||||
}
|
||||
|
||||
// getMethodClosure returns a value representing the closure for a method call
|
||||
func (s *state) getMethodClosure(fn *Node) *ssa.Value {
|
||||
// Make a name n2 for the function.
|
||||
// fn.Sym might be sync.(*Mutex).Unlock.
|
||||
// Make a PFUNC node out of that, then evaluate it.
|
||||
// We get back an SSA value representing &sync.(*Mutex).Unlock·f.
|
||||
// We can then pass that to defer or go.
|
||||
n2 := newnamel(fn.Pos, fn.Sym)
|
||||
n2.Name.Curfn = s.curfn
|
||||
n2.SetClass(PFUNC)
|
||||
// n2.Sym already existed, so it's already marked as a function.
|
||||
n2.Pos = fn.Pos
|
||||
n2.Type = types.Types[TUINT8] // dummy type for a static closure. Could use runtime.funcval if we had it.
|
||||
return s.expr(n2)
|
||||
}
|
||||
|
||||
// getClosureAndRcvr returns values for the appropriate closure and receiver of an
|
||||
// interface call
|
||||
func (s *state) getClosureAndRcvr(fn *Node) (*ssa.Value, *ssa.Value) {
|
||||
i := s.expr(fn.Left)
|
||||
itab := s.newValue1(ssa.OpITab, types.Types[TUINTPTR], i)
|
||||
s.nilCheck(itab)
|
||||
itabidx := fn.Xoffset + 2*int64(Widthptr) + 8 // offset of fun field in runtime.itab
|
||||
closure := s.newValue1I(ssa.OpOffPtr, s.f.Config.Types.UintptrPtr, itabidx, itab)
|
||||
rcvr := s.newValue1(ssa.OpIData, types.Types[TUINTPTR], i)
|
||||
return closure, rcvr
|
||||
}
|
||||
|
||||
// etypesign returns the signed-ness of e, for integer/pointer etypes.
|
||||
// -1 means signed, +1 means unsigned, 0 means non-integer/non-pointer.
|
||||
func etypesign(e types.EType) int8 {
|
||||
|
|
@ -5146,6 +5705,16 @@ func (s *state) addNamedValue(n *Node, v *ssa.Value) {
|
|||
s.f.NamedValues[loc] = append(values, v)
|
||||
}
|
||||
|
||||
// Generate a disconnected call to a runtime routine and a return.
|
||||
func gencallret(pp *Progs, sym *obj.LSym) *obj.Prog {
|
||||
p := pp.Prog(obj.ACALL)
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_EXTERN
|
||||
p.To.Sym = sym
|
||||
p = pp.Prog(obj.ARET)
|
||||
return p
|
||||
}
|
||||
|
||||
// Branch is an unresolved branch.
|
||||
type Branch struct {
|
||||
P *obj.Prog // branch instruction
|
||||
|
|
@ -5181,6 +5750,11 @@ type SSAGenState struct {
|
|||
|
||||
// wasm: The number of values on the WebAssembly stack. This is only used as a safeguard.
|
||||
OnWasmStackSkipped int
|
||||
|
||||
// Liveness index for the first function call in the final defer exit code
|
||||
// path that we generated. All defer functions and args should be live at
|
||||
// this point. This will be used to set the liveness for the deferreturn.
|
||||
lastDeferLiveness LivenessIndex
|
||||
}
|
||||
|
||||
// Prog appends a new Prog.
|
||||
|
|
@ -5308,6 +5882,17 @@ func genssa(f *ssa.Func, pp *Progs) {
|
|||
s.livenessMap = liveness(e, f, pp)
|
||||
emitStackObjects(e, pp)
|
||||
|
||||
openDeferInfo := e.curfn.Func.lsym.Func.OpenCodedDeferInfo
|
||||
if openDeferInfo != nil {
|
||||
// This function uses open-coded defers -- write out the funcdata
|
||||
// info that we computed at the end of genssa.
|
||||
p := pp.Prog(obj.AFUNCDATA)
|
||||
Addrconst(&p.From, objabi.FUNCDATA_OpenCodedDeferInfo)
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_EXTERN
|
||||
p.To.Sym = openDeferInfo
|
||||
}
|
||||
|
||||
// Remember where each block starts.
|
||||
s.bstart = make([]*obj.Prog, f.NumBlocks())
|
||||
s.pp = pp
|
||||
|
|
@ -5372,6 +5957,12 @@ func genssa(f *ssa.Func, pp *Progs) {
|
|||
// Attach this safe point to the next
|
||||
// instruction.
|
||||
s.pp.nextLive = s.livenessMap.Get(v)
|
||||
|
||||
// Remember the liveness index of the first defer call of
|
||||
// the last defer exit
|
||||
if v.Block.Func.LastDeferExit != nil && v == v.Block.Func.LastDeferExit {
|
||||
s.lastDeferLiveness = s.pp.nextLive
|
||||
}
|
||||
switch v.Op {
|
||||
case ssa.OpInitMem:
|
||||
// memory arg needs no code
|
||||
|
|
@ -5455,6 +6046,13 @@ func genssa(f *ssa.Func, pp *Progs) {
|
|||
// nop (which will never execute) after the call.
|
||||
thearch.Ginsnop(pp)
|
||||
}
|
||||
if openDeferInfo != nil {
|
||||
// When doing open-coded defers, generate a disconnected call to
|
||||
// deferreturn and a return. This will be used to during panic
|
||||
// recovery to unwind the stack and return back to the runtime.
|
||||
s.pp.nextLive = s.lastDeferLiveness
|
||||
gencallret(pp, Deferreturn)
|
||||
}
|
||||
|
||||
if inlMarks != nil {
|
||||
// We have some inline marks. Try to find other instructions we're
|
||||
|
|
|
|||
|
|
@ -295,6 +295,7 @@ const (
|
|||
nameAddrtaken // address taken, even if not moved to heap
|
||||
nameInlFormal // OPAUTO created by inliner, derived from callee formal
|
||||
nameInlLocal // OPAUTO created by inliner, derived from callee local
|
||||
nameOpenDeferSlot // if temporary var storing info for open-coded defers
|
||||
)
|
||||
|
||||
func (n *Name) Captured() bool { return n.flags&nameCaptured != 0 }
|
||||
|
|
@ -310,6 +311,7 @@ func (n *Name) Assigned() bool { return n.flags&nameAssigned != 0 }
|
|||
func (n *Name) Addrtaken() bool { return n.flags&nameAddrtaken != 0 }
|
||||
func (n *Name) InlFormal() bool { return n.flags&nameInlFormal != 0 }
|
||||
func (n *Name) InlLocal() bool { return n.flags&nameInlLocal != 0 }
|
||||
func (n *Name) OpenDeferSlot() bool { return n.flags&nameOpenDeferSlot != 0 }
|
||||
|
||||
func (n *Name) SetCaptured(b bool) { n.flags.set(nameCaptured, b) }
|
||||
func (n *Name) SetReadonly(b bool) { n.flags.set(nameReadonly, b) }
|
||||
|
|
@ -324,6 +326,7 @@ func (n *Name) SetAssigned(b bool) { n.flags.set(nameAssigned, b) }
|
|||
func (n *Name) SetAddrtaken(b bool) { n.flags.set(nameAddrtaken, b) }
|
||||
func (n *Name) SetInlFormal(b bool) { n.flags.set(nameInlFormal, b) }
|
||||
func (n *Name) SetInlLocal(b bool) { n.flags.set(nameInlLocal, b) }
|
||||
func (n *Name) SetOpenDeferSlot(b bool) { n.flags.set(nameOpenDeferSlot, b) }
|
||||
|
||||
type Param struct {
|
||||
Ntype *Node
|
||||
|
|
@ -492,6 +495,8 @@ type Func struct {
|
|||
Pragma syntax.Pragma // go:xxx function annotations
|
||||
|
||||
flags bitset16
|
||||
numDefers int // number of defer calls in the function
|
||||
numReturns int // number of explicit returns in the function
|
||||
|
||||
// nwbrCalls records the LSyms of functions called by this
|
||||
// function for go:nowritebarrierrec analysis. Only filled in
|
||||
|
|
@ -532,6 +537,7 @@ const (
|
|||
funcInlinabilityChecked // inliner has already determined whether the function is inlinable
|
||||
funcExportInline // include inline body in export data
|
||||
funcInstrumentBody // add race/msan instrumentation during SSA construction
|
||||
funcOpenCodedDeferDisallowed // can't do open-coded defers
|
||||
)
|
||||
|
||||
func (f *Func) Dupok() bool { return f.flags&funcDupok != 0 }
|
||||
|
|
@ -544,6 +550,7 @@ func (f *Func) NilCheckDisabled() bool { return f.flags&funcNilCheckDisabled
|
|||
func (f *Func) InlinabilityChecked() bool { return f.flags&funcInlinabilityChecked != 0 }
|
||||
func (f *Func) ExportInline() bool { return f.flags&funcExportInline != 0 }
|
||||
func (f *Func) InstrumentBody() bool { return f.flags&funcInstrumentBody != 0 }
|
||||
func (f *Func) OpenCodedDeferDisallowed() bool { return f.flags&funcOpenCodedDeferDisallowed != 0 }
|
||||
|
||||
func (f *Func) SetDupok(b bool) { f.flags.set(funcDupok, b) }
|
||||
func (f *Func) SetWrapper(b bool) { f.flags.set(funcWrapper, b) }
|
||||
|
|
@ -555,6 +562,7 @@ func (f *Func) SetNilCheckDisabled(b bool) { f.flags.set(funcNilCheckDisabled
|
|||
func (f *Func) SetInlinabilityChecked(b bool) { f.flags.set(funcInlinabilityChecked, b) }
|
||||
func (f *Func) SetExportInline(b bool) { f.flags.set(funcExportInline, b) }
|
||||
func (f *Func) SetInstrumentBody(b bool) { f.flags.set(funcInstrumentBody, b) }
|
||||
func (f *Func) SetOpenCodedDeferDisallowed(b bool) { f.flags.set(funcOpenCodedDeferDisallowed, b) }
|
||||
|
||||
func (f *Func) setWBPos(pos src.XPos) {
|
||||
if Debug_wb != 0 {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ const enableTrace = false
|
|||
|
||||
var trace bool
|
||||
var traceIndent []byte
|
||||
var skipDowidthForTracing bool
|
||||
|
||||
func tracePrint(title string, n *Node) func(np **Node) {
|
||||
indent := traceIndent
|
||||
|
|
@ -29,6 +30,8 @@ func tracePrint(title string, n *Node) func(np **Node) {
|
|||
tc = n.Typecheck()
|
||||
}
|
||||
|
||||
skipDowidthForTracing = true
|
||||
defer func() { skipDowidthForTracing = false }()
|
||||
fmt.Printf("%s: %s%s %p %s %v tc=%d\n", pos, indent, title, n, op, n, tc)
|
||||
traceIndent = append(traceIndent, ". "...)
|
||||
|
||||
|
|
@ -51,6 +54,8 @@ func tracePrint(title string, n *Node) func(np **Node) {
|
|||
typ = n.Type
|
||||
}
|
||||
|
||||
skipDowidthForTracing = true
|
||||
defer func() { skipDowidthForTracing = false }()
|
||||
fmt.Printf("%s: %s=> %p %s %v tc=%d type=%#L\n", pos, indent, n, op, n, tc, typ)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -214,6 +214,18 @@ func walkstmt(n *Node) *Node {
|
|||
|
||||
case ODEFER:
|
||||
Curfn.Func.SetHasDefer(true)
|
||||
Curfn.Func.numDefers++
|
||||
if Curfn.Func.numDefers > maxOpenDefers {
|
||||
// Don't allow open-coded defers if there are more than
|
||||
// 8 defers in the function, since we use a single
|
||||
// byte to record active defers.
|
||||
Curfn.Func.SetOpenCodedDeferDisallowed(true)
|
||||
}
|
||||
if n.Esc != EscNever {
|
||||
// If n.Esc is not EscNever, then this defer occurs in a loop,
|
||||
// so open-coded defers cannot be used in this function.
|
||||
Curfn.Func.SetOpenCodedDeferDisallowed(true)
|
||||
}
|
||||
fallthrough
|
||||
case OGO:
|
||||
switch n.Left.Op {
|
||||
|
|
@ -255,6 +267,7 @@ func walkstmt(n *Node) *Node {
|
|||
walkstmtlist(n.Rlist.Slice())
|
||||
|
||||
case ORETURN:
|
||||
Curfn.Func.numReturns++
|
||||
if n.List.Len() == 0 {
|
||||
break
|
||||
}
|
||||
|
|
@ -951,9 +964,9 @@ opswitch:
|
|||
|
||||
case OCONV, OCONVNOP:
|
||||
n.Left = walkexpr(n.Left, init)
|
||||
if n.Op == OCONVNOP && checkPtr(Curfn) {
|
||||
if n.Op == OCONVNOP && checkPtr(Curfn, 1) {
|
||||
if n.Type.IsPtr() && n.Left.Type.Etype == TUNSAFEPTR { // unsafe.Pointer to *T
|
||||
n = walkCheckPtrAlignment(n, init)
|
||||
n = walkCheckPtrAlignment(n, init, nil)
|
||||
break
|
||||
}
|
||||
if n.Type.Etype == TUNSAFEPTR && n.Left.Type.Etype == TUINTPTR { // uintptr to unsafe.Pointer
|
||||
|
|
@ -1120,7 +1133,12 @@ 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
|
||||
if checkSlice {
|
||||
n.Left.Left = walkexpr(n.Left.Left, init)
|
||||
} else {
|
||||
n.Left = walkexpr(n.Left, init)
|
||||
}
|
||||
low, high, max := n.SliceBounds()
|
||||
low = walkexpr(low, init)
|
||||
if low != nil && isZero(low) {
|
||||
|
|
@ -1130,6 +1148,9 @@ opswitch:
|
|||
high = walkexpr(high, init)
|
||||
max = walkexpr(max, init)
|
||||
n.SetSliceBounds(low, high, max)
|
||||
if checkSlice {
|
||||
n.Left = walkCheckPtrAlignment(n.Left, init, max)
|
||||
}
|
||||
if n.Op.IsSlice3() {
|
||||
if max != nil && max.Op == OCAP && samesafeexpr(n.Left, max.Left) {
|
||||
// Reduce x[i:j:cap(x)] to x[i:j].
|
||||
|
|
@ -3912,13 +3933,29 @@ func isRuneCount(n *Node) bool {
|
|||
return Debug['N'] == 0 && !instrumenting && n.Op == OLEN && n.Left.Op == OSTR2RUNES
|
||||
}
|
||||
|
||||
func walkCheckPtrAlignment(n *Node, init *Nodes) *Node {
|
||||
if n.Type.Elem().Alignment() == 1 && n.Type.Elem().Size() == 1 {
|
||||
func walkCheckPtrAlignment(n *Node, init *Nodes, count *Node) *Node {
|
||||
if !n.Type.IsPtr() {
|
||||
Fatalf("expected pointer type: %v", n.Type)
|
||||
}
|
||||
elem := n.Type.Elem()
|
||||
if count != nil {
|
||||
if !elem.IsArray() {
|
||||
Fatalf("expected array type: %v", elem)
|
||||
}
|
||||
elem = elem.Elem()
|
||||
}
|
||||
|
||||
size := elem.Size()
|
||||
if elem.Alignment() == 1 && (size == 0 || size == 1 && count == nil) {
|
||||
return n
|
||||
}
|
||||
|
||||
if count == nil {
|
||||
count = nodintconst(1)
|
||||
}
|
||||
|
||||
n.Left = cheapexpr(n.Left, init)
|
||||
init.Append(mkcall("checkptrAlignment", nil, init, convnop(n.Left, types.Types[TUNSAFEPTR]), typename(n.Type.Elem())))
|
||||
init.Append(mkcall("checkptrAlignment", nil, init, convnop(n.Left, types.Types[TUNSAFEPTR]), typename(elem), conv(count, types.Types[TUINTPTR])))
|
||||
return n
|
||||
}
|
||||
|
||||
|
|
@ -3941,6 +3978,10 @@ func walkCheckPtrArithmetic(n *Node, init *Nodes) *Node {
|
|||
return n
|
||||
}
|
||||
|
||||
if n.Left.Op == ODOTPTR && isReflectHeaderDataField(n.Left) {
|
||||
return n
|
||||
}
|
||||
|
||||
// Find original unsafe.Pointer operands involved in this
|
||||
// arithmetic expression.
|
||||
//
|
||||
|
|
@ -3976,7 +4017,8 @@ func walkCheckPtrArithmetic(n *Node, init *Nodes) *Node {
|
|||
}
|
||||
|
||||
// checkPtr reports whether pointer checking should be enabled for
|
||||
// function fn.
|
||||
func checkPtr(fn *Node) bool {
|
||||
return Debug_checkptr != 0 && fn.Func.Pragma&NoCheckPtr == 0
|
||||
// function fn at a given level. See debugHelpFooter for defined
|
||||
// levels.
|
||||
func checkPtr(fn *Node, level int) bool {
|
||||
return Debug_checkptr >= level && fn.Func.Pragma&NoCheckPtr == 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ type Config struct {
|
|||
Race bool // race detector enabled
|
||||
NeedsFpScratch bool // No direct move between GP and FP register sets
|
||||
BigEndian bool //
|
||||
UseFMA bool // Use hardware FMA operation
|
||||
}
|
||||
|
||||
type (
|
||||
|
|
@ -326,13 +327,19 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config
|
|||
c.ctxt = ctxt
|
||||
c.optimize = optimize
|
||||
c.useSSE = true
|
||||
c.UseFMA = true
|
||||
|
||||
// Don't use Duff's device nor SSE on Plan 9 AMD64, because
|
||||
// floating point operations are not allowed in note handler.
|
||||
if objabi.GOOS == "plan9" && arch == "amd64" {
|
||||
// On Plan 9, floating point operations are not allowed in note handler.
|
||||
if objabi.GOOS == "plan9" {
|
||||
// Don't use FMA on Plan 9
|
||||
c.UseFMA = false
|
||||
|
||||
// Don't use Duff's device and SSE on Plan 9 AMD64.
|
||||
if arch == "amd64" {
|
||||
c.noDuffDevice = true
|
||||
c.useSSE = false
|
||||
}
|
||||
}
|
||||
|
||||
if ctxt.Flag_shared {
|
||||
// LoweredWB is secretly a CALL and CALLs on 386 in
|
||||
|
|
|
|||
|
|
@ -170,6 +170,11 @@ func elimDeadAutosGeneric(f *Func) {
|
|||
return
|
||||
case OpVarLive:
|
||||
// Don't delete the auto if it needs to be kept alive.
|
||||
|
||||
// We depend on this check to keep the autotmp stack slots
|
||||
// for open-coded defers from being removed (since they
|
||||
// may not be used by the inline code, but will be used by
|
||||
// panic processing).
|
||||
n, ok := v.Aux.(GCNode)
|
||||
if !ok || n.StorageClass() != ClassAuto {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -32,6 +32,14 @@ type Func struct {
|
|||
Type *types.Type // type signature of the function.
|
||||
Blocks []*Block // unordered set of all basic blocks (note: not indexable by ID)
|
||||
Entry *Block // the entry basic block
|
||||
|
||||
// If we are using open-coded defers, this is the first call to a deferred
|
||||
// function in the final defer exit sequence that we generated. This call
|
||||
// should be after all defer statements, and will have all args, etc. of
|
||||
// all defer calls as live. The liveness info of this call will be used
|
||||
// for the deferreturn/ret segment generated for functions with open-coded
|
||||
// defers.
|
||||
LastDeferExit *Value
|
||||
bid idAlloc // block ID allocator
|
||||
vid idAlloc // value ID allocator
|
||||
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@
|
|||
(Floor x) -> (ROUNDSD [1] x)
|
||||
(Ceil x) -> (ROUNDSD [2] x)
|
||||
(Trunc x) -> (ROUNDSD [3] x)
|
||||
(Fma x y z) -> (VFMADD231SD z x y)
|
||||
|
||||
// Lowering extension
|
||||
// Note: we always extend to 64 bits even though some ops don't need that many result bits.
|
||||
|
|
@ -225,9 +226,6 @@
|
|||
(NeqPtr x y) && config.PtrSize == 4 -> (SETNE (CMPL x y))
|
||||
(Neq(32|64)F x y) -> (SETNEF (UCOMIS(S|D) x y))
|
||||
|
||||
(Int64Hi x) -> (SHRQconst [32] x) // needed for amd64p32
|
||||
(Int64Lo x) -> x
|
||||
|
||||
// Lowering loads
|
||||
(Load <t> ptr mem) && (is64BitInt(t) || isPtr(t) && config.PtrSize == 8) -> (MOVQload ptr mem)
|
||||
(Load <t> ptr mem) && (is32BitInt(t) || isPtr(t) && config.PtrSize == 4) -> (MOVLload ptr mem)
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ func init() {
|
|||
|
||||
fp01 = regInfo{inputs: nil, outputs: fponly}
|
||||
fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
|
||||
fp31 = regInfo{inputs: []regMask{fp, fp, fp}, outputs: fponly}
|
||||
fp21load = regInfo{inputs: []regMask{fp, gpspsb, 0}, outputs: fponly}
|
||||
fpgp = regInfo{inputs: fponly, outputs: gponly}
|
||||
gpfp = regInfo{inputs: gponly, outputs: fponly}
|
||||
|
|
@ -478,6 +479,10 @@ func init() {
|
|||
// Any use must be preceded by a successful check of runtime.x86HasSSE41.
|
||||
{name: "ROUNDSD", argLength: 1, reg: fp11, aux: "Int8", asm: "ROUNDSD"}, // rounds arg0 depending on auxint, 1 means math.Floor, 2 Ceil, 3 Trunc
|
||||
|
||||
// VFMADD231SD only exists on platforms with the FMA3 instruction set.
|
||||
// Any use must be preceded by a successful check of runtime.support_fma.
|
||||
{name: "VFMADD231SD", argLength: 3, reg: fp31, resultInArg0: true, asm: "VFMADD231SD"},
|
||||
|
||||
{name: "SBBQcarrymask", argLength: 1, reg: flagsgp, asm: "SBBQ"}, // (int64)(-1) if carry is set, 0 if carry is clear.
|
||||
{name: "SBBLcarrymask", argLength: 1, reg: flagsgp, asm: "SBBL"}, // (int32)(-1) if carry is set, 0 if carry is clear.
|
||||
// Note: SBBW and SBBB are subsumed by SBBL
|
||||
|
|
|
|||
|
|
@ -210,6 +210,9 @@
|
|||
|
||||
(Round(32|64)F x) -> x
|
||||
|
||||
// fused-multiply-add
|
||||
(Fma x y z) -> (FMULAD z x y)
|
||||
|
||||
// comparisons
|
||||
(Eq8 x y) -> (Equal (CMP (ZeroExt8to32 x) (ZeroExt8to32 y)))
|
||||
(Eq16 x y) -> (Equal (CMP (ZeroExt16to32 x) (ZeroExt16to32 y)))
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@
|
|||
(Round x) -> (FRINTAD x)
|
||||
(RoundToEven x) -> (FRINTND x)
|
||||
(Trunc x) -> (FRINTZD x)
|
||||
(Fma x y z) -> (FMADDD z x y)
|
||||
|
||||
// lowering rotates
|
||||
(RotateLeft8 <t> x (MOVDconst [c])) -> (Or8 (Lsh8x64 <t> x (MOVDconst [c&7])) (Rsh8Ux64 <t> x (MOVDconst [-c&7])))
|
||||
|
|
|
|||
|
|
@ -192,6 +192,10 @@ func init() {
|
|||
{name: "MULSF", argLength: 3, reg: fp31, asm: "MULSF", resultInArg0: true}, // arg0 - (arg1 * arg2)
|
||||
{name: "MULSD", argLength: 3, reg: fp31, asm: "MULSD", resultInArg0: true}, // arg0 - (arg1 * arg2)
|
||||
|
||||
// FMULAD only exists on platforms with the VFPv4 instruction set.
|
||||
// Any use must be preceded by a successful check of runtime.arm_support_vfpv4.
|
||||
{name: "FMULAD", argLength: 3, reg: fp31, asm: "FMULAD", resultInArg0: true}, // arg0 + (arg1 * arg2)
|
||||
|
||||
{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0 & arg1
|
||||
{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int32"}, // arg0 & auxInt
|
||||
{name: "OR", argLength: 2, reg: gp21, asm: "ORR", commutative: true}, // arg0 | arg1
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@
|
|||
(Round x) -> (FROUND x)
|
||||
(Copysign x y) -> (FCPSGN y x)
|
||||
(Abs x) -> (FABS x)
|
||||
(Fma x y z) -> (FMADD x y z)
|
||||
|
||||
// Lowering constants
|
||||
(Const(64|32|16|8) [val]) -> (MOVDconst [val])
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@
|
|||
(Trunc x) -> (FIDBR [5] x)
|
||||
(RoundToEven x) -> (FIDBR [4] x)
|
||||
(Round x) -> (FIDBR [1] x)
|
||||
(Fma x y z) -> (FMADD z x y)
|
||||
|
||||
// Atomic loads and stores.
|
||||
// The SYNC instruction (fast-BCR-serialization) prevents store-load
|
||||
|
|
|
|||
|
|
@ -302,6 +302,20 @@ var genericOps = []opData{
|
|||
{name: "Abs", argLength: 1}, // absolute value arg0
|
||||
{name: "Copysign", argLength: 2}, // copy sign from arg0 to arg1
|
||||
|
||||
// 3-input opcode.
|
||||
// Fused-multiply-add, float64 only.
|
||||
// When a*b+c is exactly zero (before rounding), then the result is +0 or -0.
|
||||
// The 0's sign is determined according to the standard rules for the
|
||||
// addition (-0 if both a*b and c are -0, +0 otherwise).
|
||||
//
|
||||
// Otherwise, when a*b+c rounds to zero, then the resulting 0's sign is
|
||||
// determined by the sign of the exact result a*b+c.
|
||||
// See section 6.3 in ieee754.
|
||||
//
|
||||
// When the multiply is an infinity times a zero, the result is NaN.
|
||||
// See section 7.2 in ieee754.
|
||||
{name: "Fma", argLength: 3}, // compute (a*b)+c without intermediate rounding
|
||||
|
||||
// Data movement, max argument length for Phi is indefinite so just pick
|
||||
// a really large number
|
||||
{name: "Phi", argLength: -1, zeroWidth: true}, // select an argument based on which predecessor block we came from
|
||||
|
|
|
|||
|
|
@ -743,6 +743,7 @@ const (
|
|||
OpAMD64POPCNTL
|
||||
OpAMD64SQRTSD
|
||||
OpAMD64ROUNDSD
|
||||
OpAMD64VFMADD231SD
|
||||
OpAMD64SBBQcarrymask
|
||||
OpAMD64SBBLcarrymask
|
||||
OpAMD64SETEQ
|
||||
|
|
@ -924,6 +925,7 @@ const (
|
|||
OpARMMULAD
|
||||
OpARMMULSF
|
||||
OpARMMULSD
|
||||
OpARMFMULAD
|
||||
OpARMAND
|
||||
OpARMANDconst
|
||||
OpARMOR
|
||||
|
|
@ -2419,6 +2421,7 @@ const (
|
|||
OpRoundToEven
|
||||
OpAbs
|
||||
OpCopysign
|
||||
OpFma
|
||||
OpPhi
|
||||
OpCopy
|
||||
OpConvert
|
||||
|
|
@ -9624,6 +9627,22 @@ var opcodeTable = [...]opInfo{
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "VFMADD231SD",
|
||||
argLen: 3,
|
||||
resultInArg0: true,
|
||||
asm: x86.AVFMADD231SD,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
|
||||
{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
|
||||
{2, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
|
||||
},
|
||||
outputs: []outputInfo{
|
||||
{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SBBQcarrymask",
|
||||
argLen: 1,
|
||||
|
|
@ -12101,6 +12120,22 @@ var opcodeTable = [...]opInfo{
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "FMULAD",
|
||||
argLen: 3,
|
||||
resultInArg0: true,
|
||||
asm: arm.AFMULAD,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||
{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||
{2, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||
},
|
||||
outputs: []outputInfo{
|
||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "AND",
|
||||
argLen: 2,
|
||||
|
|
@ -30593,6 +30628,11 @@ var opcodeTable = [...]opInfo{
|
|||
argLen: 2,
|
||||
generic: true,
|
||||
},
|
||||
{
|
||||
name: "Fma",
|
||||
argLen: 3,
|
||||
generic: true,
|
||||
},
|
||||
{
|
||||
name: "Phi",
|
||||
argLen: -1,
|
||||
|
|
|
|||
|
|
@ -164,7 +164,8 @@ type factsTable struct {
|
|||
// order is a couple of partial order sets that record information
|
||||
// about relations between SSA values in the signed and unsigned
|
||||
// domain.
|
||||
order [2]*poset
|
||||
orderS *poset
|
||||
orderU *poset
|
||||
|
||||
// known lower and upper bounds on individual values.
|
||||
limits map[ID]limit
|
||||
|
|
@ -187,10 +188,10 @@ var checkpointBound = limitFact{}
|
|||
|
||||
func newFactsTable(f *Func) *factsTable {
|
||||
ft := &factsTable{}
|
||||
ft.order[0] = f.newPoset() // signed
|
||||
ft.order[1] = f.newPoset() // unsigned
|
||||
ft.order[0].SetUnsigned(false)
|
||||
ft.order[1].SetUnsigned(true)
|
||||
ft.orderS = f.newPoset()
|
||||
ft.orderU = f.newPoset()
|
||||
ft.orderS.SetUnsigned(false)
|
||||
ft.orderU.SetUnsigned(true)
|
||||
ft.facts = make(map[pair]relation)
|
||||
ft.stack = make([]fact, 4)
|
||||
ft.limits = make(map[ID]limit)
|
||||
|
|
@ -221,23 +222,23 @@ func (ft *factsTable) update(parent *Block, v, w *Value, d domain, r relation) {
|
|||
|
||||
if d == signed || d == unsigned {
|
||||
var ok bool
|
||||
idx := 0
|
||||
order := ft.orderS
|
||||
if d == unsigned {
|
||||
idx = 1
|
||||
order = ft.orderU
|
||||
}
|
||||
switch r {
|
||||
case lt:
|
||||
ok = ft.order[idx].SetOrder(v, w)
|
||||
ok = order.SetOrder(v, w)
|
||||
case gt:
|
||||
ok = ft.order[idx].SetOrder(w, v)
|
||||
ok = order.SetOrder(w, v)
|
||||
case lt | eq:
|
||||
ok = ft.order[idx].SetOrderOrEqual(v, w)
|
||||
ok = order.SetOrderOrEqual(v, w)
|
||||
case gt | eq:
|
||||
ok = ft.order[idx].SetOrderOrEqual(w, v)
|
||||
ok = order.SetOrderOrEqual(w, v)
|
||||
case eq:
|
||||
ok = ft.order[idx].SetEqual(v, w)
|
||||
ok = order.SetEqual(v, w)
|
||||
case lt | gt:
|
||||
ok = ft.order[idx].SetNonEqual(v, w)
|
||||
ok = order.SetNonEqual(v, w)
|
||||
default:
|
||||
panic("unknown relation")
|
||||
}
|
||||
|
|
@ -588,6 +589,7 @@ func (ft *factsTable) isNonNegative(v *Value) bool {
|
|||
}
|
||||
|
||||
// Check if the recorded limits can prove that the value is positive
|
||||
|
||||
if l, has := ft.limits[v.ID]; has && (l.min >= 0 || l.umax <= uint64(max)) {
|
||||
return true
|
||||
}
|
||||
|
|
@ -610,7 +612,7 @@ func (ft *factsTable) isNonNegative(v *Value) bool {
|
|||
}
|
||||
|
||||
// Check if the signed poset can prove that the value is >= 0
|
||||
return ft.order[0].OrderedOrEqual(ft.zero, v)
|
||||
return ft.orderS.OrderedOrEqual(ft.zero, v)
|
||||
}
|
||||
|
||||
// checkpoint saves the current state of known relations.
|
||||
|
|
@ -621,8 +623,8 @@ func (ft *factsTable) checkpoint() {
|
|||
}
|
||||
ft.stack = append(ft.stack, checkpointFact)
|
||||
ft.limitStack = append(ft.limitStack, checkpointBound)
|
||||
ft.order[0].Checkpoint()
|
||||
ft.order[1].Checkpoint()
|
||||
ft.orderS.Checkpoint()
|
||||
ft.orderU.Checkpoint()
|
||||
}
|
||||
|
||||
// restore restores known relation to the state just
|
||||
|
|
@ -658,8 +660,8 @@ func (ft *factsTable) restore() {
|
|||
ft.limits[old.vid] = old.limit
|
||||
}
|
||||
}
|
||||
ft.order[0].Undo()
|
||||
ft.order[1].Undo()
|
||||
ft.orderS.Undo()
|
||||
ft.orderU.Undo()
|
||||
}
|
||||
|
||||
func lessByID(v, w *Value) bool {
|
||||
|
|
@ -922,7 +924,7 @@ func prove(f *Func) {
|
|||
ft.restore()
|
||||
|
||||
// Return the posets to the free list
|
||||
for _, po := range ft.order {
|
||||
for _, po := range []*poset{ft.orderS, ft.orderU} {
|
||||
// Make sure it's empty as it should be. A non-empty poset
|
||||
// might cause errors and miscompilations if reused.
|
||||
if checkEnabled {
|
||||
|
|
|
|||
|
|
@ -768,6 +768,8 @@ func rewriteValueAMD64(v *Value) bool {
|
|||
return rewriteValueAMD64_OpEqPtr_0(v)
|
||||
case OpFloor:
|
||||
return rewriteValueAMD64_OpFloor_0(v)
|
||||
case OpFma:
|
||||
return rewriteValueAMD64_OpFma_0(v)
|
||||
case OpGeq16:
|
||||
return rewriteValueAMD64_OpGeq16_0(v)
|
||||
case OpGeq16U:
|
||||
|
|
@ -824,10 +826,6 @@ func rewriteValueAMD64(v *Value) bool {
|
|||
return rewriteValueAMD64_OpHmul64_0(v)
|
||||
case OpHmul64u:
|
||||
return rewriteValueAMD64_OpHmul64u_0(v)
|
||||
case OpInt64Hi:
|
||||
return rewriteValueAMD64_OpInt64Hi_0(v)
|
||||
case OpInt64Lo:
|
||||
return rewriteValueAMD64_OpInt64Lo_0(v)
|
||||
case OpInterCall:
|
||||
return rewriteValueAMD64_OpInterCall_0(v)
|
||||
case OpIsInBounds:
|
||||
|
|
@ -52331,6 +52329,20 @@ func rewriteValueAMD64_OpFloor_0(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueAMD64_OpFma_0(v *Value) bool {
|
||||
// match: (Fma x y z)
|
||||
// result: (VFMADD231SD z x y)
|
||||
for {
|
||||
z := v.Args[2]
|
||||
x := v.Args[0]
|
||||
y := v.Args[1]
|
||||
v.reset(OpAMD64VFMADD231SD)
|
||||
v.AddArg(z)
|
||||
v.AddArg(x)
|
||||
v.AddArg(y)
|
||||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueAMD64_OpGeq16_0(v *Value) bool {
|
||||
b := v.Block
|
||||
// match: (Geq16 x y)
|
||||
|
|
@ -52713,28 +52725,6 @@ func rewriteValueAMD64_OpHmul64u_0(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueAMD64_OpInt64Hi_0(v *Value) bool {
|
||||
// match: (Int64Hi x)
|
||||
// result: (SHRQconst [32] x)
|
||||
for {
|
||||
x := v.Args[0]
|
||||
v.reset(OpAMD64SHRQconst)
|
||||
v.AuxInt = 32
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueAMD64_OpInt64Lo_0(v *Value) bool {
|
||||
// match: (Int64Lo x)
|
||||
// result: x
|
||||
for {
|
||||
x := v.Args[0]
|
||||
v.reset(OpCopy)
|
||||
v.Type = x.Type
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueAMD64_OpInterCall_0(v *Value) bool {
|
||||
// match: (InterCall [argwid] entry mem)
|
||||
// result: (CALLinter [argwid] entry mem)
|
||||
|
|
|
|||
|
|
@ -538,6 +538,8 @@ func rewriteValueARM(v *Value) bool {
|
|||
return rewriteValueARM_OpEqB_0(v)
|
||||
case OpEqPtr:
|
||||
return rewriteValueARM_OpEqPtr_0(v)
|
||||
case OpFma:
|
||||
return rewriteValueARM_OpFma_0(v)
|
||||
case OpGeq16:
|
||||
return rewriteValueARM_OpGeq16_0(v)
|
||||
case OpGeq16U:
|
||||
|
|
@ -17159,6 +17161,20 @@ func rewriteValueARM_OpEqPtr_0(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueARM_OpFma_0(v *Value) bool {
|
||||
// match: (Fma x y z)
|
||||
// result: (FMULAD z x y)
|
||||
for {
|
||||
z := v.Args[2]
|
||||
x := v.Args[0]
|
||||
y := v.Args[1]
|
||||
v.reset(OpARMFMULAD)
|
||||
v.AddArg(z)
|
||||
v.AddArg(x)
|
||||
v.AddArg(y)
|
||||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueARM_OpGeq16_0(v *Value) bool {
|
||||
b := v.Block
|
||||
typ := &b.Func.Config.Types
|
||||
|
|
|
|||
|
|
@ -571,6 +571,8 @@ func rewriteValueARM64(v *Value) bool {
|
|||
return rewriteValueARM64_OpEqPtr_0(v)
|
||||
case OpFloor:
|
||||
return rewriteValueARM64_OpFloor_0(v)
|
||||
case OpFma:
|
||||
return rewriteValueARM64_OpFma_0(v)
|
||||
case OpGeq16:
|
||||
return rewriteValueARM64_OpGeq16_0(v)
|
||||
case OpGeq16U:
|
||||
|
|
@ -28565,6 +28567,20 @@ func rewriteValueARM64_OpFloor_0(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueARM64_OpFma_0(v *Value) bool {
|
||||
// match: (Fma x y z)
|
||||
// result: (FMADDD z x y)
|
||||
for {
|
||||
z := v.Args[2]
|
||||
x := v.Args[0]
|
||||
y := v.Args[1]
|
||||
v.reset(OpARM64FMADDD)
|
||||
v.AddArg(z)
|
||||
v.AddArg(x)
|
||||
v.AddArg(y)
|
||||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueARM64_OpGeq16_0(v *Value) bool {
|
||||
b := v.Block
|
||||
typ := &b.Func.Config.Types
|
||||
|
|
|
|||
|
|
@ -181,6 +181,8 @@ func rewriteValuePPC64(v *Value) bool {
|
|||
return rewriteValuePPC64_OpEqPtr_0(v)
|
||||
case OpFloor:
|
||||
return rewriteValuePPC64_OpFloor_0(v)
|
||||
case OpFma:
|
||||
return rewriteValuePPC64_OpFma_0(v)
|
||||
case OpGeq16:
|
||||
return rewriteValuePPC64_OpGeq16_0(v)
|
||||
case OpGeq16U:
|
||||
|
|
@ -1988,6 +1990,20 @@ func rewriteValuePPC64_OpFloor_0(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValuePPC64_OpFma_0(v *Value) bool {
|
||||
// match: (Fma x y z)
|
||||
// result: (FMADD x y z)
|
||||
for {
|
||||
z := v.Args[2]
|
||||
x := v.Args[0]
|
||||
y := v.Args[1]
|
||||
v.reset(OpPPC64FMADD)
|
||||
v.AddArg(x)
|
||||
v.AddArg(y)
|
||||
v.AddArg(z)
|
||||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValuePPC64_OpGeq16_0(v *Value) bool {
|
||||
b := v.Block
|
||||
typ := &b.Func.Config.Types
|
||||
|
|
|
|||
|
|
@ -166,6 +166,8 @@ func rewriteValueS390X(v *Value) bool {
|
|||
return rewriteValueS390X_OpEqPtr_0(v)
|
||||
case OpFloor:
|
||||
return rewriteValueS390X_OpFloor_0(v)
|
||||
case OpFma:
|
||||
return rewriteValueS390X_OpFma_0(v)
|
||||
case OpGeq16:
|
||||
return rewriteValueS390X_OpGeq16_0(v)
|
||||
case OpGeq16U:
|
||||
|
|
@ -1918,6 +1920,20 @@ func rewriteValueS390X_OpFloor_0(v *Value) bool {
|
|||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueS390X_OpFma_0(v *Value) bool {
|
||||
// match: (Fma x y z)
|
||||
// result: (FMADD z x y)
|
||||
for {
|
||||
z := v.Args[2]
|
||||
x := v.Args[0]
|
||||
y := v.Args[1]
|
||||
v.reset(OpS390XFMADD)
|
||||
v.AddArg(z)
|
||||
v.AddArg(x)
|
||||
v.AddArg(y)
|
||||
return true
|
||||
}
|
||||
}
|
||||
func rewriteValueS390X_OpGeq16_0(v *Value) bool {
|
||||
b := v.Block
|
||||
typ := &b.Func.Config.Types
|
||||
|
|
|
|||
|
|
@ -457,7 +457,7 @@ func TestHtmlUnformatted(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(htmlUDir, "go.mod"), []byte("module htmlunformatted\n"), 0444); err != nil {
|
||||
if err := ioutil.WriteFile(filepath.Join(htmlUDir, "go.mod"), []byte("module htmlunformatted\n"), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
|
@ -540,7 +540,7 @@ func TestFuncWithDuplicateLines(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(lineDupDir, "go.mod"), []byte("module linedup\n"), 0444); err != nil {
|
||||
if err := ioutil.WriteFile(filepath.Join(lineDupDir, "go.mod"), []byte("module linedup\n"), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(lineDupGo, []byte(lineDupContents), 0444); err != nil {
|
||||
|
|
|
|||
2
src/cmd/dist/build.go
vendored
2
src/cmd/dist/build.go
vendored
|
|
@ -814,7 +814,7 @@ func runInstall(dir string, ch chan struct{}) {
|
|||
// Define GOMIPS_value from gomips.
|
||||
asmArgs = append(asmArgs, "-D", "GOMIPS_"+gomips)
|
||||
}
|
||||
if goarch == "mips64" || goarch == "mipsle64" {
|
||||
if goarch == "mips64" || goarch == "mips64le" {
|
||||
// Define GOMIPS64_value from gomips64.
|
||||
asmArgs = append(asmArgs, "-D", "GOMIPS64_"+gomips64)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,5 +8,5 @@ require (
|
|||
golang.org/x/arch v0.0.0-20190815191158-8a70ba74b3a1
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c
|
||||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 // indirect
|
||||
golang.org/x/tools v0.0.0-20190925211824-e4ea94538f5b
|
||||
golang.org/x/tools v0.0.0-20191018203202-04252eccb9d5
|
||||
)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dz
|
|||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190925211824-e4ea94538f5b h1:gyG4T6EqWG9fqSgT0VbHhzp8bHbFux5mvlgz1gUkEaQ=
|
||||
golang.org/x/tools v0.0.0-20190925211824-e4ea94538f5b/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191018203202-04252eccb9d5 h1:TFUhCYbgGMOGnRxJv+j0iAcxCjk8oGjXXWNejQBhUUs=
|
||||
golang.org/x/tools v0.0.0-20191018203202-04252eccb9d5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
|
|
|||
|
|
@ -150,6 +150,16 @@
|
|||
// -mod mode
|
||||
// module download mode to use: readonly or vendor.
|
||||
// See 'go help modules' for more.
|
||||
// -modcacherw
|
||||
// leave newly-created directories in the module cache read-write
|
||||
// instead of making them read-only.
|
||||
// -modfile file
|
||||
// in module aware mode, read (and possibly write) an alternate go.mod
|
||||
// file instead of the one in the module root directory. A file named
|
||||
// "go.mod" must still be present in order to determine the module root
|
||||
// directory, but it is not accessed. When -modfile is specified, an
|
||||
// alternate go.sum file is also used: its path is derived from the
|
||||
// -modfile flag by trimming the ".mod" extension and appending ".sum".
|
||||
// -pkgdir dir
|
||||
// install and load all packages from dir instead of the usual locations.
|
||||
// For example, when building with a non-standard configuration,
|
||||
|
|
@ -1510,8 +1520,8 @@
|
|||
// extension will be passed to SWIG. Any file with a .swigcxx extension
|
||||
// will be passed to SWIG with the -c++ option.
|
||||
//
|
||||
// When either cgo or SWIG is used, go build will pass any .c, .m, .s,
|
||||
// or .S files to the C compiler, and any .cc, .cpp, .cxx files to the C++
|
||||
// When either cgo or SWIG is used, go build will pass any .c, .m, .s, .S
|
||||
// or .sx files to the C compiler, and any .cc, .cpp, .cxx files to the C++
|
||||
// compiler. The CC or CXX environment variables may be set to determine
|
||||
// the C or C++ compiler, respectively, to use.
|
||||
//
|
||||
|
|
@ -1730,7 +1740,7 @@
|
|||
// .m
|
||||
// Objective-C source files. Only useful with cgo, and always
|
||||
// compiled with the OS-native compiler.
|
||||
// .s, .S
|
||||
// .s, .S, .sx
|
||||
// Assembler source files.
|
||||
// If the package uses cgo or SWIG, these will be assembled with the
|
||||
// OS-native assembler (typically gcc (sic)); otherwise they
|
||||
|
|
|
|||
|
|
@ -4938,35 +4938,31 @@ func TestTestRegexps(t *testing.T) {
|
|||
// BenchmarkXX is run but only with N=1, once
|
||||
// BenchmarkX/Y is run in full, twice
|
||||
want := `=== RUN TestX
|
||||
TestX: x_test.go:6: LOG: X running
|
||||
=== RUN TestX/Y
|
||||
x_test.go:6: LOG: X running
|
||||
x_test.go:8: LOG: Y running
|
||||
TestX/Y: x_test.go:8: LOG: Y running
|
||||
=== RUN TestXX
|
||||
z_test.go:10: LOG: XX running
|
||||
TestXX: z_test.go:10: LOG: XX running
|
||||
=== RUN TestX
|
||||
TestX: x_test.go:6: LOG: X running
|
||||
=== RUN TestX/Y
|
||||
x_test.go:6: LOG: X running
|
||||
x_test.go:8: LOG: Y running
|
||||
TestX/Y: x_test.go:8: LOG: Y running
|
||||
=== RUN TestXX
|
||||
z_test.go:10: LOG: XX running
|
||||
--- BENCH: BenchmarkX/Y
|
||||
x_test.go:15: LOG: Y running N=1
|
||||
x_test.go:15: LOG: Y running N=100
|
||||
x_test.go:15: LOG: Y running N=10000
|
||||
x_test.go:15: LOG: Y running N=1000000
|
||||
x_test.go:15: LOG: Y running N=100000000
|
||||
x_test.go:15: LOG: Y running N=1000000000
|
||||
--- BENCH: BenchmarkX/Y
|
||||
x_test.go:15: LOG: Y running N=1
|
||||
x_test.go:15: LOG: Y running N=100
|
||||
x_test.go:15: LOG: Y running N=10000
|
||||
x_test.go:15: LOG: Y running N=1000000
|
||||
x_test.go:15: LOG: Y running N=100000000
|
||||
x_test.go:15: LOG: Y running N=1000000000
|
||||
--- BENCH: BenchmarkX
|
||||
x_test.go:13: LOG: X running N=1
|
||||
--- BENCH: BenchmarkXX
|
||||
z_test.go:18: LOG: XX running N=1
|
||||
TestXX: z_test.go:10: LOG: XX running
|
||||
BenchmarkX: x_test.go:13: LOG: X running N=1
|
||||
BenchmarkX/Y: x_test.go:15: LOG: Y running N=1
|
||||
BenchmarkX/Y: x_test.go:15: LOG: Y running N=100
|
||||
BenchmarkX/Y: x_test.go:15: LOG: Y running N=10000
|
||||
BenchmarkX/Y: x_test.go:15: LOG: Y running N=1000000
|
||||
BenchmarkX/Y: x_test.go:15: LOG: Y running N=100000000
|
||||
BenchmarkX/Y: x_test.go:15: LOG: Y running N=1000000000
|
||||
BenchmarkX/Y: x_test.go:15: LOG: Y running N=1
|
||||
BenchmarkX/Y: x_test.go:15: LOG: Y running N=100
|
||||
BenchmarkX/Y: x_test.go:15: LOG: Y running N=10000
|
||||
BenchmarkX/Y: x_test.go:15: LOG: Y running N=1000000
|
||||
BenchmarkX/Y: x_test.go:15: LOG: Y running N=100000000
|
||||
BenchmarkX/Y: x_test.go:15: LOG: Y running N=1000000000
|
||||
BenchmarkXX: z_test.go:18: LOG: XX running N=1
|
||||
`
|
||||
|
||||
have := strings.Join(lines, "")
|
||||
|
|
|
|||
|
|
@ -44,6 +44,9 @@ var (
|
|||
BuildWork bool // -work flag
|
||||
BuildX bool // -x flag
|
||||
|
||||
ModCacheRW bool // -modcacherw flag
|
||||
ModFile string // -modfile flag
|
||||
|
||||
CmdName string // "build", "install", "list", "mod tidy", etc.
|
||||
|
||||
DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable)
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ var (
|
|||
)
|
||||
|
||||
func init() {
|
||||
work.AddBuildFlags(CmdGet, work.OmitModFlag)
|
||||
work.AddBuildFlags(CmdGet, work.OmitModFlag|work.OmitModCommonFlags)
|
||||
CmdGet.Run = runGet // break init loop
|
||||
CmdGet.Flag.BoolVar(&Insecure, "insecure", Insecure, "")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ http://swig.org/. When running go build, any file with a .swig
|
|||
extension will be passed to SWIG. Any file with a .swigcxx extension
|
||||
will be passed to SWIG with the -c++ option.
|
||||
|
||||
When either cgo or SWIG is used, go build will pass any .c, .m, .s,
|
||||
or .S files to the C compiler, and any .cc, .cpp, .cxx files to the C++
|
||||
When either cgo or SWIG is used, go build will pass any .c, .m, .s, .S
|
||||
or .sx files to the C compiler, and any .cc, .cpp, .cxx files to the C++
|
||||
compiler. The CC or CXX environment variables may be set to determine
|
||||
the C or C++ compiler, respectively, to use.
|
||||
`,
|
||||
|
|
@ -645,7 +645,7 @@ the extension of the file name. These extensions are:
|
|||
.m
|
||||
Objective-C source files. Only useful with cgo, and always
|
||||
compiled with the OS-native compiler.
|
||||
.s, .S
|
||||
.s, .S, .sx
|
||||
Assembler source files.
|
||||
If the package uses cgo or SWIG, these will be assembled with the
|
||||
OS-native assembler (typically gcc (sic)); otherwise they
|
||||
|
|
|
|||
|
|
@ -384,9 +384,22 @@ func runList(cmd *base.Command, args []string) {
|
|||
if modload.Init(); !modload.Enabled() {
|
||||
base.Fatalf("go list -m: not using modules")
|
||||
}
|
||||
|
||||
modload.InitMod() // Parses go.mod and sets cfg.BuildMod.
|
||||
if cfg.BuildMod == "vendor" {
|
||||
base.Fatalf("go list -m: can't list modules with -mod=vendor\n\tuse -mod=mod or -mod=readonly to ignore the vendor directory")
|
||||
for _, arg := range args {
|
||||
// In vendor mode, the module graph is incomplete: it contains only the
|
||||
// explicit module dependencies and the modules that supply packages in
|
||||
// the import graph. Reject queries that imply more information than that.
|
||||
if arg == "all" {
|
||||
base.Fatalf("go list -m: can't compute 'all' using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)")
|
||||
}
|
||||
if strings.Contains(arg, "...") {
|
||||
base.Fatalf("go list -m: can't match module patterns using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modload.LoadBuildList()
|
||||
|
||||
mods := modload.ListModules(args, *listU, *listVersions)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/work"
|
||||
)
|
||||
|
||||
var cmdDownload = &base.Command{
|
||||
|
|
@ -53,6 +54,8 @@ var downloadJSON = cmdDownload.Flag.Bool("json", false, "")
|
|||
|
||||
func init() {
|
||||
cmdDownload.Run = runDownload // break init cycle
|
||||
|
||||
work.AddModCommonFlags(cmdDownload)
|
||||
}
|
||||
|
||||
type moduleJSON struct {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
|
|
@ -20,6 +19,7 @@ import (
|
|||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/work"
|
||||
)
|
||||
|
||||
var cmdEdit = &base.Command{
|
||||
|
|
@ -130,6 +130,7 @@ func init() {
|
|||
cmdEdit.Flag.Var(flagFunc(flagReplace), "replace", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagDropExclude), "dropexclude", "")
|
||||
|
||||
work.AddModCommonFlags(cmdEdit)
|
||||
base.AddBuildFlagsNX(&cmdEdit.Flag)
|
||||
}
|
||||
|
||||
|
|
@ -157,7 +158,7 @@ func runEdit(cmd *base.Command, args []string) {
|
|||
if len(args) == 1 {
|
||||
gomod = args[0]
|
||||
} else {
|
||||
gomod = filepath.Join(modload.ModRoot(), "go.mod")
|
||||
gomod = modload.ModFilePath()
|
||||
}
|
||||
|
||||
if *editModule != "" {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/work"
|
||||
)
|
||||
|
||||
var cmdGraph = &base.Command{
|
||||
|
|
@ -30,6 +31,10 @@ path@version, except for the main module, which has no @version suffix.
|
|||
Run: runGraph,
|
||||
}
|
||||
|
||||
func init() {
|
||||
work.AddModCommonFlags(cmdGraph)
|
||||
}
|
||||
|
||||
func runGraph(cmd *base.Command, args []string) {
|
||||
if len(args) > 0 {
|
||||
base.Fatalf("go mod graph: graph takes no arguments")
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ package modcmd
|
|||
import (
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/work"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
|
@ -27,6 +28,10 @@ To override this guess, supply the module path as an argument.
|
|||
Run: runInit,
|
||||
}
|
||||
|
||||
func init() {
|
||||
work.AddModCommonFlags(cmdInit)
|
||||
}
|
||||
|
||||
func runInit(cmd *base.Command, args []string) {
|
||||
modload.CmdModInit = true
|
||||
if len(args) > 1 {
|
||||
|
|
@ -38,7 +43,8 @@ func runInit(cmd *base.Command, args []string) {
|
|||
if os.Getenv("GO111MODULE") == "off" {
|
||||
base.Fatalf("go mod init: modules disabled by GO111MODULE=off; see 'go help modules'")
|
||||
}
|
||||
if _, err := os.Stat("go.mod"); err == nil {
|
||||
modFilePath := modload.ModFilePath()
|
||||
if _, err := os.Stat(modFilePath); err == nil {
|
||||
base.Fatalf("go mod init: go.mod already exists")
|
||||
}
|
||||
if strings.Contains(modload.CmdModModule, "@") {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@
|
|||
// Package modcmd implements the ``go mod'' command.
|
||||
package modcmd
|
||||
|
||||
import "cmd/go/internal/base"
|
||||
import (
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
)
|
||||
|
||||
var CmdMod = &base.Command{
|
||||
UsageLine: "go mod",
|
||||
|
|
@ -29,3 +32,7 @@ See 'go help modules' for an overview of module functionality.
|
|||
cmdWhy,
|
||||
},
|
||||
}
|
||||
|
||||
func addModFlags(cmd *base.Command) {
|
||||
cmd.Flag.StringVar(&cfg.ModFile, "modfile", "", "")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/work"
|
||||
)
|
||||
|
||||
var cmdTidy = &base.Command{
|
||||
|
|
@ -35,6 +36,7 @@ to standard error.
|
|||
func init() {
|
||||
cmdTidy.Run = runTidy // break init cycle
|
||||
cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "")
|
||||
work.AddModCommonFlags(cmdTidy)
|
||||
}
|
||||
|
||||
func runTidy(cmd *base.Command, args []string) {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/semver"
|
||||
"cmd/go/internal/work"
|
||||
)
|
||||
|
||||
var cmdVendor = &base.Command{
|
||||
|
|
@ -38,6 +39,7 @@ modules and packages to standard error.
|
|||
|
||||
func init() {
|
||||
cmdVendor.Flag.BoolVar(&cfg.BuildV, "v", false, "")
|
||||
work.AddModCommonFlags(cmdVendor)
|
||||
}
|
||||
|
||||
func runVendor(cmd *base.Command, args []string) {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/work"
|
||||
)
|
||||
|
||||
var cmdVerify = &base.Command{
|
||||
|
|
@ -32,13 +33,17 @@ non-zero status.
|
|||
Run: runVerify,
|
||||
}
|
||||
|
||||
func init() {
|
||||
work.AddModCommonFlags(cmdVerify)
|
||||
}
|
||||
|
||||
func runVerify(cmd *base.Command, args []string) {
|
||||
if len(args) != 0 {
|
||||
// NOTE(rsc): Could take a module pattern.
|
||||
base.Fatalf("go mod verify: verify takes no arguments")
|
||||
}
|
||||
// Checks go mod expected behavior
|
||||
if !modload.Enabled() {
|
||||
if !modload.Enabled() || !modload.HasModRoot() {
|
||||
if cfg.Getenv("GO111MODULE") == "off" {
|
||||
base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/work"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
|
@ -54,6 +55,7 @@ var (
|
|||
|
||||
func init() {
|
||||
cmdWhy.Run = runWhy // break init cycle
|
||||
work.AddModCommonFlags(cmdWhy)
|
||||
}
|
||||
|
||||
func runWhy(cmd *base.Command, args []string) {
|
||||
|
|
|
|||
|
|
@ -73,11 +73,10 @@ type Repo interface {
|
|||
// ReadZip downloads a zip file for the subdir subdirectory
|
||||
// of the given revision to a new file in a given temporary directory.
|
||||
// It should refuse to read more than maxSize bytes.
|
||||
// It returns a ReadCloser for a streamed copy of the zip file,
|
||||
// along with the actual subdirectory (possibly shorter than subdir)
|
||||
// contained in the zip file. All files in the zip file are expected to be
|
||||
// It returns a ReadCloser for a streamed copy of the zip file.
|
||||
// All files in the zip file are expected to be
|
||||
// nested in a single top-level directory, whose name is not specified.
|
||||
ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error)
|
||||
ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, err error)
|
||||
|
||||
// RecentTag returns the most recent tag on rev or one of its predecessors
|
||||
// with the given prefix and major version.
|
||||
|
|
|
|||
|
|
@ -795,7 +795,7 @@ func (r *gitRepo) DescendsFrom(rev, tag string) (bool, error) {
|
|||
return false, err
|
||||
}
|
||||
|
||||
func (r *gitRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) {
|
||||
func (r *gitRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, err error) {
|
||||
// TODO: Use maxSize or drop it.
|
||||
args := []string{}
|
||||
if subdir != "" {
|
||||
|
|
@ -803,17 +803,17 @@ func (r *gitRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser,
|
|||
}
|
||||
info, err := r.Stat(rev) // download rev into local git repo
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
unlock, err := r.mu.Lock()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
defer unlock()
|
||||
|
||||
if err := ensureGitAttributes(r.dir); err != nil {
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Incredibly, git produces different archives depending on whether
|
||||
|
|
@ -824,12 +824,12 @@ func (r *gitRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser,
|
|||
archive, err := Run(r.dir, "git", "-c", "core.autocrlf=input", "-c", "core.eol=lf", "archive", "--format=zip", "--prefix=prefix/", info.Name, args)
|
||||
if err != nil {
|
||||
if bytes.Contains(err.(*RunError).Stderr, []byte("did not match any files")) {
|
||||
return nil, "", os.ErrNotExist
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ioutil.NopCloser(bytes.NewReader(archive)), "", nil
|
||||
return ioutil.NopCloser(bytes.NewReader(archive)), nil
|
||||
}
|
||||
|
||||
// ensureGitAttributes makes sure export-subst and export-ignore features are
|
||||
|
|
|
|||
|
|
@ -249,7 +249,6 @@ var readZipTests = []struct {
|
|||
repo string
|
||||
rev string
|
||||
subdir string
|
||||
actualSubdir string
|
||||
err string
|
||||
files map[string]uint64
|
||||
}{
|
||||
|
|
@ -408,7 +407,7 @@ func TestReadZip(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rc, actualSubdir, err := r.ReadZip(tt.rev, tt.subdir, 100000)
|
||||
rc, err := r.ReadZip(tt.rev, tt.subdir, 100000)
|
||||
if err != nil {
|
||||
if tt.err == "" {
|
||||
t.Fatalf("ReadZip: unexpected error %v", err)
|
||||
|
|
@ -425,9 +424,6 @@ func TestReadZip(t *testing.T) {
|
|||
if tt.err != "" {
|
||||
t.Fatalf("ReadZip: no error, wanted %v", tt.err)
|
||||
}
|
||||
if actualSubdir != tt.actualSubdir {
|
||||
t.Fatalf("ReadZip: actualSubdir = %q, want %q", actualSubdir, tt.actualSubdir)
|
||||
}
|
||||
zipdata, err := ioutil.ReadAll(rc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ func main() {
|
|||
if subdir == "-" {
|
||||
subdir = ""
|
||||
}
|
||||
rc, _, err := repo.ReadZip(f[1], subdir, 10<<20)
|
||||
rc, err := repo.ReadZip(f[1], subdir, 10<<20)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "?%s\n", err)
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -417,14 +417,14 @@ func (r *vcsRepo) DescendsFrom(rev, tag string) (bool, error) {
|
|||
return false, vcsErrorf("DescendsFrom not implemented")
|
||||
}
|
||||
|
||||
func (r *vcsRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) {
|
||||
func (r *vcsRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, err error) {
|
||||
if r.cmd.readZip == nil {
|
||||
return nil, "", vcsErrorf("ReadZip not implemented for %s", r.cmd.vcs)
|
||||
return nil, vcsErrorf("ReadZip not implemented for %s", r.cmd.vcs)
|
||||
}
|
||||
|
||||
unlock, err := r.mu.Lock()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
defer unlock()
|
||||
|
||||
|
|
@ -433,7 +433,7 @@ func (r *vcsRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser,
|
|||
}
|
||||
f, err := ioutil.TempFile("", "go-readzip-*.zip")
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
if r.cmd.vcs == "fossil" {
|
||||
// If you run
|
||||
|
|
@ -454,9 +454,9 @@ func (r *vcsRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser,
|
|||
if err != nil {
|
||||
f.Close()
|
||||
os.Remove(f.Name())
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
return &deleteCloser{f}, "", nil
|
||||
return &deleteCloser{f}, nil
|
||||
}
|
||||
|
||||
// deleteCloser is a file that gets deleted on Close.
|
||||
|
|
|
|||
|
|
@ -780,19 +780,16 @@ func (r *codeRepo) Zip(dst io.Writer, version string) error {
|
|||
}
|
||||
}
|
||||
|
||||
rev, dir, _, err := r.findDir(version)
|
||||
rev, subdir, _, err := r.findDir(version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dl, actualDir, err := r.code.ReadZip(rev, dir, codehost.MaxZipFile)
|
||||
dl, err := r.code.ReadZip(rev, subdir, codehost.MaxZipFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dl.Close()
|
||||
if actualDir != "" && !hasPathPrefix(dir, actualDir) {
|
||||
return fmt.Errorf("internal error: downloading %v %v: dir=%q but actualDir=%q", r.modPath, rev, dir, actualDir)
|
||||
}
|
||||
subdir := strings.Trim(strings.TrimPrefix(dir, actualDir), "/")
|
||||
subdir = strings.Trim(subdir, "/")
|
||||
|
||||
// Spool to local file.
|
||||
f, err := ioutil.TempFile("", "go-codehost-")
|
||||
|
|
|
|||
|
|
@ -125,9 +125,11 @@ func download(mod module.Version, dir string) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
if !cfg.ModCacheRW {
|
||||
// Make dir read-only only *after* renaming it.
|
||||
// os.Rename was observed to fail for read-only directories on macOS.
|
||||
makeDirsReadOnly(dir)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,47 +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.
|
||||
|
||||
// TODO: Figure out what gopkg.in should do.
|
||||
|
||||
package modfile
|
||||
|
||||
import "strings"
|
||||
|
||||
// ParseGopkgIn splits gopkg.in import paths into their constituent parts
|
||||
func ParseGopkgIn(path string) (root, repo, major, subdir string, ok bool) {
|
||||
if !strings.HasPrefix(path, "gopkg.in/") {
|
||||
return
|
||||
}
|
||||
f := strings.Split(path, "/")
|
||||
if len(f) >= 2 {
|
||||
if elem, v, ok := dotV(f[1]); ok {
|
||||
root = strings.Join(f[:2], "/")
|
||||
repo = "github.com/go-" + elem + "/" + elem
|
||||
major = v
|
||||
subdir = strings.Join(f[2:], "/")
|
||||
return root, repo, major, subdir, true
|
||||
}
|
||||
}
|
||||
if len(f) >= 3 {
|
||||
if elem, v, ok := dotV(f[2]); ok {
|
||||
root = strings.Join(f[:3], "/")
|
||||
repo = "github.com/" + f[1] + "/" + elem
|
||||
major = v
|
||||
subdir = strings.Join(f[3:], "/")
|
||||
return root, repo, major, subdir, true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func dotV(name string) (elem, v string, ok bool) {
|
||||
i := len(name) - 1
|
||||
for i >= 0 && '0' <= name[i] && name[i] <= '9' {
|
||||
i--
|
||||
}
|
||||
if i <= 2 || i+1 >= len(name) || name[i-1] != '.' || name[i] != 'v' || name[i+1] == '0' && len(name) != i+2 {
|
||||
return "", "", false
|
||||
}
|
||||
return name[:i-1], name[i:], true
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// Format returns a go.mod file as a byte slice, formatted in standard style.
|
||||
func Format(f *FileSyntax) []byte {
|
||||
pr := &printer{}
|
||||
pr.file(f)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ import (
|
|||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// A Position describes the position between two bytes of input.
|
||||
// A Position describes an arbitrary source position in a file, including the
|
||||
// file, line, column, and byte offset.
|
||||
type Position struct {
|
||||
Line int // line in input (starting at 1)
|
||||
LineRune int // rune in line (starting at 1)
|
||||
|
|
|
|||
|
|
@ -119,14 +119,8 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
|
|||
info.GoVersion = loaded.goVersion[m.Path]
|
||||
}
|
||||
|
||||
if cfg.BuildMod == "vendor" {
|
||||
// The vendor directory doesn't contain enough information to reconstruct
|
||||
// anything more about the module.
|
||||
return info
|
||||
}
|
||||
|
||||
// complete fills in the extra fields in m.
|
||||
complete := func(m *modinfo.ModulePublic) {
|
||||
// completeFromModCache fills in the extra fields in m using the module cache.
|
||||
completeFromModCache := func(m *modinfo.ModulePublic) {
|
||||
if m.Version != "" {
|
||||
if q, err := Query(m.Path, m.Version, "", nil); err != nil {
|
||||
m.Error = &modinfo.ModuleError{Err: err.Error()}
|
||||
|
|
@ -152,13 +146,21 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
|
|||
}
|
||||
|
||||
if !fromBuildList {
|
||||
complete(info)
|
||||
completeFromModCache(info) // Will set m.Error in vendor mode.
|
||||
return info
|
||||
}
|
||||
|
||||
r := Replacement(m)
|
||||
if r.Path == "" {
|
||||
complete(info)
|
||||
if cfg.BuildMod == "vendor" {
|
||||
// It's tempting to fill in the "Dir" field to point within the vendor
|
||||
// directory, but that would be misleading: the vendor directory contains
|
||||
// a flattened package tree, not complete modules, and it can even
|
||||
// interleave packages from different modules if one module path is a
|
||||
// prefix of the other.
|
||||
} else {
|
||||
completeFromModCache(info)
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
|
|
@ -177,10 +179,13 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
|
|||
} else {
|
||||
info.Replace.Dir = filepath.Join(ModRoot(), r.Path)
|
||||
}
|
||||
info.Replace.GoMod = filepath.Join(info.Replace.Dir, "go.mod")
|
||||
}
|
||||
complete(info.Replace)
|
||||
if cfg.BuildMod != "vendor" {
|
||||
completeFromModCache(info.Replace)
|
||||
info.Dir = info.Replace.Dir
|
||||
info.GoMod = filepath.Join(info.Dir, "go.mod")
|
||||
info.GoMod = info.Replace.GoMod
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
cwd string // TODO(bcmills): Is this redundant with base.Cwd?
|
||||
mustUseModules = false
|
||||
initialized bool
|
||||
|
||||
|
|
@ -92,6 +91,9 @@ func Init() {
|
|||
}
|
||||
initialized = true
|
||||
|
||||
// Keep in sync with WillBeEnabled. We perform extra validation here, and
|
||||
// there are lots of diagnostics and side effects, so we can't use
|
||||
// WillBeEnabled directly.
|
||||
env := cfg.Getenv("GO111MODULE")
|
||||
switch env {
|
||||
default:
|
||||
|
|
@ -132,18 +134,15 @@ func Init() {
|
|||
os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no")
|
||||
}
|
||||
|
||||
var err error
|
||||
cwd, err = os.Getwd()
|
||||
if err != nil {
|
||||
base.Fatalf("go: %v", err)
|
||||
}
|
||||
|
||||
if CmdModInit {
|
||||
// Running 'go mod init': go.mod will be created in current directory.
|
||||
modRoot = cwd
|
||||
modRoot = base.Cwd
|
||||
} else {
|
||||
modRoot = findModuleRoot(cwd)
|
||||
modRoot = findModuleRoot(base.Cwd)
|
||||
if modRoot == "" {
|
||||
if cfg.ModFile != "" {
|
||||
base.Fatalf("go: cannot find main module, but -modfile was set.\n\t-modfile cannot be used to set the module root directory.")
|
||||
}
|
||||
if !mustUseModules {
|
||||
// GO111MODULE is 'auto', and we can't find a module root.
|
||||
// Stay in GOPATH mode.
|
||||
|
|
@ -159,6 +158,9 @@ func Init() {
|
|||
fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())
|
||||
}
|
||||
}
|
||||
if cfg.ModFile != "" && !strings.HasSuffix(cfg.ModFile, ".mod") {
|
||||
base.Fatalf("go: -modfile=%s: file does not have .mod extension", cfg.ModFile)
|
||||
}
|
||||
|
||||
// We're in module mode. Install the hooks to make it work.
|
||||
|
||||
|
|
@ -217,7 +219,7 @@ func Init() {
|
|||
//
|
||||
// See golang.org/issue/32027.
|
||||
} else {
|
||||
modfetch.GoSumFile = filepath.Join(modRoot, "go.sum")
|
||||
modfetch.GoSumFile = strings.TrimSuffix(ModFilePath(), ".mod") + ".sum"
|
||||
search.SetModRoot(modRoot)
|
||||
}
|
||||
}
|
||||
|
|
@ -233,6 +235,54 @@ func init() {
|
|||
}
|
||||
}
|
||||
|
||||
// WillBeEnabled checks whether modules should be enabled but does not
|
||||
// initialize modules by installing hooks. If Init has already been called,
|
||||
// WillBeEnabled returns the same result as Enabled.
|
||||
//
|
||||
// This function is needed to break a cycle. The main package needs to know
|
||||
// whether modules are enabled in order to install the module or GOPATH version
|
||||
// of 'go get', but Init reads the -modfile flag in 'go get', so it shouldn't
|
||||
// be called until the command is installed and flags are parsed. Instead of
|
||||
// calling Init and Enabled, the main package can call this function.
|
||||
func WillBeEnabled() bool {
|
||||
if modRoot != "" || mustUseModules {
|
||||
return true
|
||||
}
|
||||
if initialized {
|
||||
return false
|
||||
}
|
||||
|
||||
// Keep in sync with Init. Init does extra validation and prints warnings or
|
||||
// exits, so it can't call this function directly.
|
||||
env := cfg.Getenv("GO111MODULE")
|
||||
switch env {
|
||||
case "on":
|
||||
return true
|
||||
case "auto", "":
|
||||
break
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
if CmdModInit {
|
||||
// Running 'go mod init': go.mod will be created in current directory.
|
||||
return true
|
||||
}
|
||||
if modRoot := findModuleRoot(base.Cwd); modRoot == "" {
|
||||
// GO111MODULE is 'auto', and we can't find a module root.
|
||||
// Stay in GOPATH mode.
|
||||
return false
|
||||
} else if search.InDir(modRoot, os.TempDir()) == "." {
|
||||
// If you create /tmp/go.mod for experimenting,
|
||||
// then any tests that create work directories under /tmp
|
||||
// will find it and get modules when they're not expecting them.
|
||||
// It's a bit of a peculiar thing to disallow but quite mysterious
|
||||
// when it happens. See golang.org/issue/26708.
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Enabled reports whether modules are (or must be) enabled.
|
||||
// If modules are enabled but there is no main module, Enabled returns true
|
||||
// and then the first use of module information will call die
|
||||
|
|
@ -259,6 +309,20 @@ func HasModRoot() bool {
|
|||
return modRoot != ""
|
||||
}
|
||||
|
||||
// ModFilePath returns the effective path of the go.mod file. Normally, this
|
||||
// "go.mod" in the directory returned by ModRoot, but the -modfile flag may
|
||||
// change its location. ModFilePath calls base.Fatalf if there is no main
|
||||
// module, even if -modfile is set.
|
||||
func ModFilePath() string {
|
||||
if !HasModRoot() {
|
||||
die()
|
||||
}
|
||||
if cfg.ModFile != "" {
|
||||
return cfg.ModFile
|
||||
}
|
||||
return filepath.Join(modRoot, "go.mod")
|
||||
}
|
||||
|
||||
// printStackInDie causes die to print a stack trace.
|
||||
//
|
||||
// It is enabled by the testgo tag, and helps to diagnose paths that
|
||||
|
|
@ -272,9 +336,8 @@ func die() {
|
|||
if cfg.Getenv("GO111MODULE") == "off" {
|
||||
base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
|
||||
}
|
||||
if cwd != "" {
|
||||
if dir, name := findAltConfig(cwd); dir != "" {
|
||||
rel, err := filepath.Rel(cwd, dir)
|
||||
if dir, name := findAltConfig(base.Cwd); dir != "" {
|
||||
rel, err := filepath.Rel(base.Cwd, dir)
|
||||
if err != nil {
|
||||
rel = dir
|
||||
}
|
||||
|
|
@ -284,12 +347,14 @@ func die() {
|
|||
}
|
||||
base.Fatalf("go: cannot find main module, but found %s in %s\n\tto create a module there, run:\n\t%sgo mod init", name, dir, cdCmd)
|
||||
}
|
||||
}
|
||||
base.Fatalf("go: cannot find main module; see 'go help modules'")
|
||||
}
|
||||
|
||||
// InitMod sets Target and, if there is a main module, parses the initial build
|
||||
// list from its go.mod file, creating and populating that file if needed.
|
||||
//
|
||||
// As a side-effect, InitMod sets a default for cfg.BuildMod if it does not
|
||||
// already have an explicit value.
|
||||
func InitMod() {
|
||||
if len(buildList) > 0 {
|
||||
return
|
||||
|
|
@ -311,7 +376,7 @@ func InitMod() {
|
|||
return
|
||||
}
|
||||
|
||||
gomod := filepath.Join(modRoot, "go.mod")
|
||||
gomod := ModFilePath()
|
||||
data, err := renameio.ReadFile(gomod)
|
||||
if err != nil {
|
||||
base.Fatalf("go: %v", err)
|
||||
|
|
@ -367,7 +432,7 @@ func AllowMissingModuleImports() {
|
|||
func modFileToBuildList() {
|
||||
Target = modFile.Module.Mod
|
||||
targetPrefix = Target.Path
|
||||
if rel := search.InDir(cwd, cfg.GOROOTsrc); rel != "" {
|
||||
if rel := search.InDir(base.Cwd, cfg.GOROOTsrc); rel != "" {
|
||||
targetInGorootSrc = true
|
||||
if Target.Path == "std" {
|
||||
targetPrefix = ""
|
||||
|
|
@ -581,6 +646,9 @@ var altConfigs = []string{
|
|||
}
|
||||
|
||||
func findModuleRoot(dir string) (root string) {
|
||||
if dir == "" {
|
||||
panic("dir not set")
|
||||
}
|
||||
dir = filepath.Clean(dir)
|
||||
|
||||
// Look for enclosing go.mod.
|
||||
|
|
@ -598,6 +666,9 @@ func findModuleRoot(dir string) (root string) {
|
|||
}
|
||||
|
||||
func findAltConfig(dir string) (root, name string) {
|
||||
if dir == "" {
|
||||
panic("dir not set")
|
||||
}
|
||||
dir = filepath.Clean(dir)
|
||||
for {
|
||||
for _, name := range altConfigs {
|
||||
|
|
@ -801,7 +872,7 @@ func WriteGoMod() {
|
|||
unlock := modfetch.SideLock()
|
||||
defer unlock()
|
||||
|
||||
file := filepath.Join(modRoot, "go.mod")
|
||||
file := ModFilePath()
|
||||
old, err := renameio.ReadFile(file)
|
||||
if !bytes.Equal(old, modFileData) {
|
||||
if bytes.Equal(old, new) {
|
||||
|
|
@ -819,7 +890,6 @@ func WriteGoMod() {
|
|||
// want to run concurrent commands, they need to start with a complete,
|
||||
// consistent module definition.
|
||||
base.Fatalf("go: updates to go.mod needed, but contents have changed")
|
||||
|
||||
}
|
||||
|
||||
if err := renameio.WriteFile(file, new, 0666); err != nil {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/modinfo"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/par"
|
||||
|
|
@ -124,10 +125,20 @@ func listModules(args []string, listVersions bool) []*modinfo.ModulePublic {
|
|||
}
|
||||
continue
|
||||
}
|
||||
if cfg.BuildMod == "vendor" {
|
||||
// In vendor mode, we can't determine whether a missing module is “a
|
||||
// known dependency” because the module graph is incomplete.
|
||||
// Give a more explicit error message.
|
||||
mods = append(mods, &modinfo.ModulePublic{
|
||||
Path: arg,
|
||||
Error: modinfoError(arg, "", errors.New("can't resolve module using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)")),
|
||||
})
|
||||
} else {
|
||||
mods = append(mods, &modinfo.ModulePublic{
|
||||
Path: arg,
|
||||
Error: modinfoError(arg, "", errors.New("not a known dependency")),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "warning: pattern %q matched no module dependencies\n", arg)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ func ImportPathsQuiet(patterns []string, tags map[string]bool) []*search.Match {
|
|||
for _, pkg := range pkgs {
|
||||
dir := pkg
|
||||
if !filepath.IsAbs(dir) {
|
||||
dir = filepath.Join(cwd, pkg)
|
||||
dir = filepath.Join(base.Cwd, pkg)
|
||||
} else {
|
||||
dir = filepath.Clean(dir)
|
||||
}
|
||||
|
|
@ -211,11 +211,17 @@ func ImportPathsQuiet(patterns []string, tags map[string]bool) []*search.Match {
|
|||
|
||||
// One last pass to finalize wildcards.
|
||||
updateMatches(matches, false)
|
||||
checkMultiplePaths()
|
||||
WriteGoMod()
|
||||
|
||||
// A given module path may be used as itself or as a replacement for another
|
||||
// module, but not both at the same time. Otherwise, the aliasing behavior is
|
||||
// too subtle (see https://golang.org/issue/26607), and we don't want to
|
||||
// commit to a specific behavior at this point.
|
||||
return matches
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
@ -229,9 +235,6 @@ func ImportPathsQuiet(patterns []string, tags map[string]bool) []*search.Match {
|
|||
}
|
||||
}
|
||||
base.ExitIfErrors()
|
||||
WriteGoMod()
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
// pathInModuleCache returns the import path of the directory dir,
|
||||
|
|
@ -318,7 +321,7 @@ func DirImportPath(dir string) string {
|
|||
}
|
||||
|
||||
if !filepath.IsAbs(dir) {
|
||||
dir = filepath.Join(cwd, dir)
|
||||
dir = filepath.Join(base.Cwd, dir)
|
||||
} else {
|
||||
dir = filepath.Clean(dir)
|
||||
}
|
||||
|
|
@ -383,6 +386,7 @@ func loadAll(testAll bool) []string {
|
|||
}
|
||||
all := TargetPackages("...")
|
||||
loaded.load(func() []string { return all })
|
||||
checkMultiplePaths()
|
||||
WriteGoMod()
|
||||
|
||||
var paths []string
|
||||
|
|
|
|||
|
|
@ -361,10 +361,10 @@ func ImportPathsQuiet(patterns []string) []*Match {
|
|||
return out
|
||||
}
|
||||
|
||||
// CleanPatterns returns the patterns to use for the given
|
||||
// command line. It canonicalizes the patterns but does not
|
||||
// evaluate any matches. It preserves text after '@' for commands
|
||||
// that accept versions.
|
||||
// CleanPatterns returns the patterns to use for the given command line. It
|
||||
// canonicalizes the patterns but does not evaluate any matches. For patterns
|
||||
// that are not local or absolute paths, it preserves text after '@' to avoid
|
||||
// modifying version queries.
|
||||
func CleanPatterns(patterns []string) []string {
|
||||
if len(patterns) == 0 {
|
||||
return []string{"."}
|
||||
|
|
@ -372,7 +372,9 @@ func CleanPatterns(patterns []string) []string {
|
|||
var out []string
|
||||
for _, a := range patterns {
|
||||
var p, v string
|
||||
if i := strings.IndexByte(a, '@'); i < 0 {
|
||||
if build.IsLocalImport(a) || filepath.IsAbs(a) {
|
||||
p = a
|
||||
} else if i := strings.IndexByte(a, '@'); i < 0 {
|
||||
p = a
|
||||
} else {
|
||||
p = a[:i]
|
||||
|
|
|
|||
|
|
@ -102,6 +102,16 @@ and test commands:
|
|||
-mod mode
|
||||
module download mode to use: readonly or vendor.
|
||||
See 'go help modules' for more.
|
||||
-modcacherw
|
||||
leave newly-created directories in the module cache read-write
|
||||
instead of making them read-only.
|
||||
-modfile file
|
||||
in module aware mode, read (and possibly write) an alternate go.mod
|
||||
file instead of the one in the module root directory. A file named
|
||||
"go.mod" must still be present in order to determine the module root
|
||||
directory, but it is not accessed. When -modfile is specified, an
|
||||
alternate go.sum file is also used: its path is derived from the
|
||||
-modfile flag by trimming the ".mod" extension and appending ".sum".
|
||||
-pkgdir dir
|
||||
install and load all packages from dir instead of the usual locations.
|
||||
For example, when building with a non-standard configuration,
|
||||
|
|
@ -221,9 +231,10 @@ type BuildFlagMask int
|
|||
const (
|
||||
DefaultBuildFlags BuildFlagMask = 0
|
||||
OmitModFlag BuildFlagMask = 1 << iota
|
||||
OmitModCommonFlags
|
||||
)
|
||||
|
||||
// addBuildFlags adds the flags common to the build, clean, get,
|
||||
// AddBuildFlags adds the flags common to the build, clean, get,
|
||||
// install, list, run, and test commands.
|
||||
func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
|
||||
cmd.Flag.BoolVar(&cfg.BuildA, "a", false, "")
|
||||
|
|
@ -240,6 +251,9 @@ func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
|
|||
if mask&OmitModFlag == 0 {
|
||||
cmd.Flag.StringVar(&cfg.BuildMod, "mod", "", "")
|
||||
}
|
||||
if mask&OmitModCommonFlags == 0 {
|
||||
AddModCommonFlags(cmd)
|
||||
}
|
||||
cmd.Flag.StringVar(&cfg.BuildContext.InstallSuffix, "installsuffix", "", "")
|
||||
cmd.Flag.Var(&load.BuildLdflags, "ldflags", "")
|
||||
cmd.Flag.BoolVar(&cfg.BuildLinkshared, "linkshared", false, "")
|
||||
|
|
@ -255,6 +269,13 @@ func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
|
|||
cmd.Flag.StringVar(&cfg.DebugActiongraph, "debug-actiongraph", "", "")
|
||||
}
|
||||
|
||||
// AddModCommonFlags adds the module-related flags common to build commands
|
||||
// and 'go mod' subcommands.
|
||||
func AddModCommonFlags(cmd *base.Command) {
|
||||
cmd.Flag.BoolVar(&cfg.ModCacheRW, "modcacherw", false, "")
|
||||
cmd.Flag.StringVar(&cfg.ModFile, "modfile", "", "")
|
||||
}
|
||||
|
||||
// tagsFlag is the implementation of the -tags flag.
|
||||
type tagsFlag []string
|
||||
|
||||
|
|
|
|||
|
|
@ -249,12 +249,20 @@ func buildModeInit() {
|
|||
case "":
|
||||
// ok
|
||||
case "readonly", "vendor", "mod":
|
||||
if load.ModLookup == nil && !inGOFLAGS("-mod") {
|
||||
if !cfg.ModulesEnabled && !inGOFLAGS("-mod") {
|
||||
base.Fatalf("build flag -mod=%s only valid when using modules", cfg.BuildMod)
|
||||
}
|
||||
default:
|
||||
base.Fatalf("-mod=%s not supported (can be '', 'mod', 'readonly', or 'vendor')", cfg.BuildMod)
|
||||
}
|
||||
if !cfg.ModulesEnabled {
|
||||
if cfg.ModCacheRW && !inGOFLAGS("-modcacherw") {
|
||||
base.Fatalf("build flag -modcacherw only valid when using modules")
|
||||
}
|
||||
if cfg.ModFile != "" && !inGOFLAGS("-mod") {
|
||||
base.Fatalf("build flag -modfile only valid when using modules")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func inGOFLAGS(flag string) bool {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ var re = lazyregexp.New
|
|||
|
||||
var validCompilerFlags = []*lazyregexp.Regexp{
|
||||
re(`-D([A-Za-z_].*)`),
|
||||
re(`-U([A-Za-z_]*)`),
|
||||
re(`-F([^@\-].*)`),
|
||||
re(`-I([^@\-].*)`),
|
||||
re(`-O`),
|
||||
|
|
@ -51,6 +52,7 @@ var validCompilerFlags = []*lazyregexp.Regexp{
|
|||
re(`-W([^@,]+)`), // -Wall but not -Wa,-foo.
|
||||
re(`-Wa,-mbig-obj`),
|
||||
re(`-Wp,-D([A-Za-z_].*)`),
|
||||
re(`-Wp, -U([A-Za-z_]*)`),
|
||||
re(`-ansi`),
|
||||
re(`-f(no-)?asynchronous-unwind-tables`),
|
||||
re(`-f(no-)?blocks`),
|
||||
|
|
@ -127,6 +129,7 @@ var validCompilerFlags = []*lazyregexp.Regexp{
|
|||
var validCompilerFlagsWithNextArg = []string{
|
||||
"-arch",
|
||||
"-D",
|
||||
"-U",
|
||||
"-I",
|
||||
"-framework",
|
||||
"-isysroot",
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
var goodCompilerFlags = [][]string{
|
||||
{"-DFOO"},
|
||||
{"-Dfoo=bar"},
|
||||
{"-Ufoo"},
|
||||
{"-F/Qt"},
|
||||
{"-I/"},
|
||||
{"-I/etc/passwd"},
|
||||
|
|
@ -67,6 +68,7 @@ var goodCompilerFlags = [][]string{
|
|||
var badCompilerFlags = [][]string{
|
||||
{"-D@X"},
|
||||
{"-D-X"},
|
||||
{"-Ufoo=bar"},
|
||||
{"-F@dir"},
|
||||
{"-F-dir"},
|
||||
{"-I@dir"},
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ func main() {
|
|||
}
|
||||
|
||||
if args[0] == "get" || args[0] == "help" {
|
||||
if modload.Init(); !modload.Enabled() {
|
||||
if !modload.WillBeEnabled() {
|
||||
// Replace module-aware get with GOPATH get if appropriate.
|
||||
*modget.CmdGet = *get.CmdGet
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ func (ts *testScript) setup() {
|
|||
"GOSUMDB=" + testSumDBVerifierKey,
|
||||
"GONOPROXY=",
|
||||
"GONOSUMDB=",
|
||||
"PWD=" + ts.cd,
|
||||
tempEnvName() + "=" + filepath.Join(ts.workdir, "tmp"),
|
||||
"devnull=" + os.DevNull,
|
||||
"goversion=" + goVersion(ts),
|
||||
|
|
@ -414,6 +415,7 @@ func (ts *testScript) cmdCd(neg bool, args []string) {
|
|||
ts.fatalf("%s is not a directory", dir)
|
||||
}
|
||||
ts.cd = dir
|
||||
ts.envMap["PWD"] = dir
|
||||
fmt.Fprintf(&ts.log, "%s\n", ts.cd)
|
||||
}
|
||||
|
||||
|
|
@ -502,9 +504,6 @@ func (ts *testScript) doCmdCmp(args []string, env, quiet bool) {
|
|||
|
||||
// cp copies files, maybe eventually directories.
|
||||
func (ts *testScript) cmdCp(neg bool, args []string) {
|
||||
if neg {
|
||||
ts.fatalf("unsupported: ! cp")
|
||||
}
|
||||
if len(args) < 2 {
|
||||
ts.fatalf("usage: cp src... dst")
|
||||
}
|
||||
|
|
@ -543,7 +542,14 @@ func (ts *testScript) cmdCp(neg bool, args []string) {
|
|||
if dstDir {
|
||||
targ = filepath.Join(dst, filepath.Base(src))
|
||||
}
|
||||
ts.check(ioutil.WriteFile(targ, data, mode))
|
||||
err := ioutil.WriteFile(targ, data, mode)
|
||||
if neg {
|
||||
if err == nil {
|
||||
ts.fatalf("unexpected command success")
|
||||
}
|
||||
} else {
|
||||
ts.check(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
59
src/cmd/go/testdata/mod/not-rsc.io_quote_v0.1.0-nomod.txt
vendored
Normal file
59
src/cmd/go/testdata/mod/not-rsc.io_quote_v0.1.0-nomod.txt
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
Constructed by hand.
|
||||
(derived from rsc.io/quote@e7a685a342, but without an explicit go.mod file.)
|
||||
|
||||
-- .mod --
|
||||
module "not-rsc.io/quote"
|
||||
-- .info --
|
||||
{"Version":"v0.1.0-nomod","Time":"2018-02-14T00:51:33Z"}
|
||||
-- quote.go --
|
||||
// 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 quote collects pithy sayings.
|
||||
package quote // import "rsc.io/quote"
|
||||
|
||||
// Hello returns a greeting.
|
||||
func Hello() string {
|
||||
return "Hello, world."
|
||||
}
|
||||
|
||||
// Glass returns a useful phrase for world travelers.
|
||||
func Glass() string {
|
||||
// See http://www.oocities.org/nodotus/hbglass.html.
|
||||
return "I can eat glass and it doesn't hurt me."
|
||||
}
|
||||
|
||||
// Go returns a Go proverb.
|
||||
func Go() string {
|
||||
return "Don't communicate by sharing memory, share memory by communicating."
|
||||
}
|
||||
-- quote_test.go --
|
||||
// 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 quote
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestHello(t *testing.T) {
|
||||
hello := "Hello, world."
|
||||
if out := Hello(); out != hello {
|
||||
t.Errorf("Hello() = %q, want %q", out, hello)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlass(t *testing.T) {
|
||||
glass := "I can eat glass and it doesn't hurt me."
|
||||
if out := Glass(); out != glass {
|
||||
t.Errorf("Glass() = %q, want %q", out, glass)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGo(t *testing.T) {
|
||||
go1 := "Don't communicate by sharing memory. Share memory by communicating."
|
||||
if out := Go(); out != go1 {
|
||||
t.Errorf("Go() = %q, want %q", out, go1)
|
||||
}
|
||||
}
|
||||
2
src/cmd/go/testdata/script/README
vendored
2
src/cmd/go/testdata/script/README
vendored
|
|
@ -107,7 +107,7 @@ The commands are:
|
|||
Like cmp, but environment variables are substituted in the file contents
|
||||
before the comparison. For example, $GOOS is replaced by the target GOOS.
|
||||
|
||||
- cp src... dst
|
||||
- [!] cp src... dst
|
||||
Copy the listed files to the target file or existing directory.
|
||||
src can include "stdout" or "stderr" to use the standard output or standard error
|
||||
from the most recent exec or go command.
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ stderr 'package ./foo.go/b.go: cannot find package "."'
|
|||
# Multiple patterns for Go files with a typo. This should
|
||||
# treat the wrong pattern as if it were a non-existint file.
|
||||
! go list ./foo.go/a.go ./foo.go/b.go
|
||||
[plan9] stderr 'stat ./foo.go/b.go: ''./foo.go/b.go'' does not exist'
|
||||
[windows] stderr './foo.go/b.go: The system cannot find the file specified'
|
||||
[!windows] stderr './foo.go/b.go: no such file or directory'
|
||||
[!plan9] [!windows] stderr './foo.go/b.go: no such file or directory'
|
||||
|
||||
-- a.go --
|
||||
package main
|
||||
|
|
|
|||
49
src/cmd/go/testdata/script/mod_cache_rw.txt
vendored
Normal file
49
src/cmd/go/testdata/script/mod_cache_rw.txt
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Regression test for golang.org/issue/31481.
|
||||
|
||||
env GO111MODULE=on
|
||||
|
||||
# golang.org/issue/31481: an explicit flag should make directories in the module
|
||||
# cache writable in order to work around the historical inability of 'rm -rf' to
|
||||
# forcibly remove files in unwritable directories.
|
||||
go get -modcacherw -d rsc.io/quote@v1.5.2
|
||||
cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go
|
||||
|
||||
# After adding an extraneous file, 'go mod verify' should fail.
|
||||
! go mod verify
|
||||
|
||||
# However, files within those directories should still be read-only to avoid
|
||||
# accidental mutations.
|
||||
[!root] ! cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
|
||||
|
||||
# If all 'go' commands ran with the flag, the system's 'rm' binary
|
||||
# should be able to remove the module cache if the '-rf' flags are set.
|
||||
[!windows] [exec:rm] exec rm -rf $GOPATH/pkg/mod
|
||||
[!windows] [!exec:rm] go clean -modcache
|
||||
[windows] [exec:rmdir] exec rmdir /s /q $GOPATH\pkg\mod
|
||||
[windows] [!exec:rmdir] go clean -modcache
|
||||
! exists $GOPATH/pkg/mod
|
||||
|
||||
# The directories in the module cache should by default be unwritable,
|
||||
# so that tests and tools will not accidentally add extraneous files to them.
|
||||
# Windows does not respect FILE_ATTRIBUTE_READONLY on directories, according
|
||||
# to MSDN, so there we disable testing whether the directory itself is
|
||||
# unwritable.
|
||||
go get -d rsc.io/quote@latest
|
||||
[!root] ! cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
|
||||
[!windows] [!root] ! cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go
|
||||
! exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go
|
||||
|
||||
|
||||
# Repeat part of the test with 'go mod download' instead of 'go get' to verify
|
||||
# -modcacherw is supported on 'go mod' subcommands.
|
||||
go clean -modcache
|
||||
go mod download -modcacherw rsc.io/quote
|
||||
cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go
|
||||
! go mod verify
|
||||
[!root] ! cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
|
||||
|
||||
|
||||
-- $WORK/extraneous.txt --
|
||||
module oops
|
||||
-- go.mod --
|
||||
module golang.org/issue/31481
|
||||
|
|
@ -10,7 +10,7 @@ stderr '\s*cd \.\. && go mod init'
|
|||
# The command we suggested should succeed.
|
||||
cd ..
|
||||
go mod init
|
||||
go list -m all
|
||||
go list -m
|
||||
stdout '^m$'
|
||||
|
||||
-- $WORK/test/vendor/vendor.json --
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ stderr '\s*cd \.\. && go mod init'
|
|||
# The command we suggested should succeed.
|
||||
cd ..
|
||||
go mod init
|
||||
go list -m all
|
||||
go list -m
|
||||
stdout '^m$'
|
||||
|
||||
-- $WORK/test/vendor/manifest --
|
||||
|
|
|
|||
35
src/cmd/go/testdata/script/mod_fs_patterns.txt
vendored
35
src/cmd/go/testdata/script/mod_fs_patterns.txt
vendored
|
|
@ -1,7 +1,6 @@
|
|||
# File system pattern searches should skip sub-modules and vendor directories.
|
||||
|
||||
env GO111MODULE=on
|
||||
|
||||
# File system pattern searches should skip sub-modules and vendor directories.
|
||||
cd x
|
||||
|
||||
# all packages
|
||||
|
|
@ -40,6 +39,24 @@ stderr '^can.t load package: package ./nonexist: cannot find package "." in:\n\t
|
|||
! stderr 'import lookup disabled'
|
||||
stderr 'can.t load package: package ./go.mod: cannot find package'
|
||||
|
||||
|
||||
# File system paths and patterns should allow the '@' character.
|
||||
cd ../@at
|
||||
go list $PWD
|
||||
stdout '^at$'
|
||||
go list $PWD/...
|
||||
stdout '^at$'
|
||||
|
||||
# The '@' character is not allowed in directory paths that are part of
|
||||
# a package path.
|
||||
cd ../badat/bad@
|
||||
! go list .
|
||||
stderr 'directory . outside available modules'
|
||||
! go list $PWD
|
||||
stderr 'directory . outside available modules'
|
||||
! go list $PWD/...
|
||||
stderr 'directory . outside available modules'
|
||||
|
||||
-- x/go.mod --
|
||||
module m
|
||||
|
||||
|
|
@ -64,3 +81,17 @@ package z
|
|||
|
||||
-- x/y/z/w/w.go --
|
||||
package w
|
||||
|
||||
-- @at/go.mod --
|
||||
module at
|
||||
|
||||
go 1.14
|
||||
-- @at/at.go --
|
||||
package at
|
||||
|
||||
-- badat/go.mod --
|
||||
module badat
|
||||
|
||||
go 1.14
|
||||
-- badat/bad@/bad.go --
|
||||
package bad
|
||||
|
|
|
|||
|
|
@ -11,10 +11,17 @@ stdout '^rsc.io/quote v1.5.1 .*vendor[\\/]rsc.io[\\/]quote$'
|
|||
stdout '^golang.org/x/text v0.0.0.* .*vendor[\\/]golang.org[\\/]x[\\/]text[\\/]language$'
|
||||
|
||||
! go list -mod=vendor -m rsc.io/quote@latest
|
||||
stderr 'go list -m: can''t list modules with -mod=vendor\n\tuse -mod=mod or -mod=readonly to ignore the vendor directory'
|
||||
stderr 'go list -m: rsc.io/quote@latest: module lookup disabled by -mod=vendor'
|
||||
! go get -mod=vendor -u
|
||||
stderr 'flag provided but not defined: -mod'
|
||||
|
||||
# Since we don't have a complete module graph, 'go list -m' queries
|
||||
# that require the complete graph should fail with a useful error.
|
||||
! go list -mod=vendor -m all
|
||||
stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
|
||||
! go list -mod=vendor -m ...
|
||||
stderr 'go list -m: can''t match module patterns using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
|
||||
|
||||
-- go.mod --
|
||||
module x
|
||||
|
||||
|
|
|
|||
13
src/cmd/go/testdata/script/mod_outside.txt
vendored
13
src/cmd/go/testdata/script/mod_outside.txt
vendored
|
|
@ -82,6 +82,10 @@ stderr 'cannot find main module'
|
|||
stderr 'cannot find main module'
|
||||
|
||||
|
||||
# 'go mod download' without arguments should report an error.
|
||||
! go mod download
|
||||
stderr 'no modules specified'
|
||||
|
||||
# 'go mod download' should download exactly the requested module without dependencies.
|
||||
rm -r $GOPATH/pkg/mod/cache/download/example.com
|
||||
go mod download example.com/printversion@v1.0.0
|
||||
|
|
@ -92,15 +96,16 @@ exists $GOPATH/pkg/mod/cache/download/example.com/printversion/@v/v1.0.0.zip
|
|||
! go mod download all
|
||||
stderr 'go: cannot match "all": working directory is not part of a module'
|
||||
|
||||
|
||||
# 'go mod vendor' should fail: it starts by clearing the existing vendor
|
||||
# directory, and we don't know where that is.
|
||||
! go mod vendor
|
||||
stderr 'cannot find main module'
|
||||
|
||||
# 'go mod verify' should succeed: we have no modules to verify.
|
||||
go mod verify
|
||||
stdout 'all modules verified'
|
||||
! stderr .
|
||||
|
||||
# 'go mod verify' should fail: we have no modules to verify.
|
||||
! go mod verify
|
||||
stderr 'cannot find main module'
|
||||
|
||||
|
||||
# 'go get' without arguments implicitly operates on the main module, and thus
|
||||
|
|
|
|||
7
src/cmd/go/testdata/script/mod_replace.txt
vendored
7
src/cmd/go/testdata/script/mod_replace.txt
vendored
|
|
@ -45,6 +45,13 @@ go mod edit -replace=rsc.io/quote/v3=./local/rsc.io/quote/v3
|
|||
! go get -d rsc.io/quote/v3/missing-package
|
||||
stderr 'module rsc.io/quote/v3@upgrade found \(v3.0.0, replaced by ./local/rsc.io/quote/v3\), but does not contain package'
|
||||
|
||||
# The reported Dir and GoMod for a replaced module should be accurate.
|
||||
cp go.mod.orig go.mod
|
||||
go mod edit -replace=rsc.io/quote/v3=not-rsc.io/quote@v0.1.0-nomod
|
||||
go mod download
|
||||
go list -m -f '{{.Path}} {{.Version}} {{.Dir}} {{.GoMod}}{{with .Replace}} => {{.Path}} {{.Version}} {{.Dir}} {{.GoMod}}{{end}}' rsc.io/quote/v3
|
||||
stdout '^rsc.io/quote/v3 v3.0.0 '$GOPATH'[/\\]pkg[/\\]mod[/\\]not-rsc.io[/\\]quote@v0.1.0-nomod '$GOPATH'[/\\]pkg[/\\]mod[/\\]cache[/\\]download[/\\]not-rsc.io[/\\]quote[/\\]@v[/\\]v0.1.0-nomod.mod => not-rsc.io/quote v0.1.0-nomod '$GOPATH'[/\\]pkg[/\\]mod[/\\]not-rsc.io[/\\]quote@v0.1.0-nomod '$GOPATH'[/\\]pkg[/\\]mod[/\\]cache[/\\]download[/\\]not-rsc.io[/\\]quote[/\\]@v[/\\]v0.1.0-nomod.mod$'
|
||||
|
||||
-- go.mod --
|
||||
module quoter
|
||||
|
||||
|
|
|
|||
26
src/cmd/go/testdata/script/mod_tidy_replace.txt
vendored
26
src/cmd/go/testdata/script/mod_tidy_replace.txt
vendored
|
|
@ -47,6 +47,12 @@ grep 'rsc.io/sampler v1.2.0' go.mod
|
|||
cd outside
|
||||
go list -m all
|
||||
stdout 'rsc.io/sampler v1.3.0'
|
||||
cd ..
|
||||
|
||||
# The same module can't be used as two different paths.
|
||||
cd multiple-paths
|
||||
! go mod tidy
|
||||
stderr 'rsc.io/quote/v3@v3.0.0 used for two different module paths \(not-rsc.io/quote/v3 and rsc.io/quote/v3\)'
|
||||
|
||||
-- go.mod --
|
||||
module example.com/tidy
|
||||
|
|
@ -109,3 +115,23 @@ package b
|
|||
module golang.org/issue/30166/b
|
||||
|
||||
require golang.org/issue/30166/a v0.0.0
|
||||
-- multiple-paths/main.go --
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"rsc.io/quote/v3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(quote.GoV3())
|
||||
}
|
||||
-- multiple-paths/go.mod --
|
||||
module quoter
|
||||
|
||||
require (
|
||||
rsc.io/quote/v3 v3.0.0
|
||||
not-rsc.io/quote/v3 v3.0.0
|
||||
)
|
||||
|
||||
replace not-rsc.io/quote/v3 => rsc.io/quote/v3 v3.0.0
|
||||
|
|
|
|||
30
src/cmd/go/testdata/script/mod_vendor.txt
vendored
30
src/cmd/go/testdata/script/mod_vendor.txt
vendored
|
|
@ -32,19 +32,28 @@ stdout 'src[\\/]x'
|
|||
go list -mod=vendor -f {{.Dir}} x
|
||||
stdout 'src[\\/]vendor[\\/]x'
|
||||
|
||||
# 'go list -mod=vendor -m' does not have enough information to list modules
|
||||
# accurately, and should fail.
|
||||
! go list -mod=vendor -f {{.Dir}} -m x
|
||||
stderr 'can''t list modules with -mod=vendor\n\tuse -mod=mod or -mod=readonly to ignore the vendor directory'
|
||||
# 'go list -mod=vendor -m' should successfully list vendored modules,
|
||||
# but should not provide a module directory because no directory contains
|
||||
# the complete module.
|
||||
go list -mod=vendor -f '{{.Version}} {{.Dir}}' -m x
|
||||
stdout '^v1.0.0 $'
|
||||
|
||||
# 'go list -mod=vendor -m' on a transitive dependency that does not
|
||||
# provide vendored packages should give a helpful error rather than
|
||||
# 'not a known dependency'.
|
||||
! go list -mod=vendor -f '{{.Version}} {{.Dir}}' -m diamondright
|
||||
stderr 'go list -m: module diamondright: can''t resolve module using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
|
||||
|
||||
# 'go list -mod=mod' should report packages outside the import graph,
|
||||
# but 'go list -mod=vendor' should error out for them.
|
||||
go list -mod=mod -f {{.Dir}} w
|
||||
stdout 'src[\\/]w'
|
||||
|
||||
! go list -mod=vendor -f {{.Dir}} w
|
||||
stderr 'src[\\/]vendor[\\/]w'
|
||||
|
||||
go list -mod=mod -f {{.Dir}} diamondright
|
||||
stdout 'src[\\/]diamondright'
|
||||
|
||||
# Test dependencies should not be copied.
|
||||
! exists vendor/x/testdata
|
||||
! exists vendor/a/foo/bar/b/ignored.go
|
||||
|
|
@ -79,6 +88,8 @@ go fmt -mod=vendor ./...
|
|||
-- go.mod --
|
||||
module m
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
a v1.0.0
|
||||
diamondroot v0.0.0
|
||||
|
|
@ -264,10 +275,11 @@ require (
|
|||
-- diamondroot/x.go --
|
||||
package diamondroot
|
||||
|
||||
import (
|
||||
_ "diamondleft"
|
||||
_ "diamondright"
|
||||
)
|
||||
import _ "diamondleft"
|
||||
-- diamondroot/unused/unused.go --
|
||||
package unused
|
||||
|
||||
import _ "diamondright"
|
||||
-- diamondleft/go.mod --
|
||||
module diamondleft
|
||||
|
||||
|
|
|
|||
13
src/cmd/go/testdata/script/mod_vendor_auto.txt
vendored
13
src/cmd/go/testdata/script/mod_vendor_auto.txt
vendored
|
|
@ -17,10 +17,10 @@ stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]printversion$'
|
|||
stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]version$'
|
||||
|
||||
! go list -m all
|
||||
stderr 'can''t list modules with -mod=vendor'
|
||||
stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
|
||||
|
||||
! go list -m -f '{{.Dir}}' all
|
||||
stderr 'can''t list modules with -mod=vendor'
|
||||
stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
|
||||
|
||||
# An explicit -mod=mod should force the vendor directory to be ignored.
|
||||
env GOFLAGS=-mod=mod
|
||||
|
|
@ -103,6 +103,15 @@ stdout '^'$WORK'[/\\]auto$'
|
|||
stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]printversion$'
|
||||
stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]version$'
|
||||
|
||||
# ...but 'go list -m' should continue to fail, this time without
|
||||
# referring to a -mod default that the user didn't set.
|
||||
! go list -m all
|
||||
stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
|
||||
|
||||
! go list -m -f '{{.Dir}}' all
|
||||
stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
|
||||
|
||||
|
||||
# 'go mod init' should work if there is already a GOPATH-mode vendor directory
|
||||
# present. If there are no module dependencies, -mod=vendor should be used by
|
||||
# default and should not fail the consistency check even though no module
|
||||
|
|
|
|||
|
|
@ -21,6 +21,16 @@ stdout '.*[/\\]vendor[/\\]rsc.io[/\\]quote[/\\]v3'
|
|||
! stderr 'finding'
|
||||
! stderr 'lookup disabled'
|
||||
|
||||
# 'go list' should provide the original replacement directory as the module's
|
||||
# replacement path.
|
||||
go list -mod=vendor -f '{{with .Module}}{{with .Replace}}{{.Path}}{{end}}{{end}}' rsc.io/quote/v3
|
||||
stdout '.*[/\\]not-rsc.io[/\\]quote[/\\]v3'
|
||||
|
||||
# The same module can't be used as two different paths.
|
||||
cd multiple-paths
|
||||
! go mod vendor
|
||||
stderr 'rsc.io/quote/v3@v3.0.0 used for two different module paths \(not-rsc.io/quote/v3 and rsc.io/quote/v3\)'
|
||||
|
||||
-- go.mod --
|
||||
module example.com/replace
|
||||
|
||||
|
|
@ -37,3 +47,20 @@ module not-rsc.io/quote/v3
|
|||
|
||||
-- local/not-rsc.io/quote/v3/quote.go --
|
||||
package quote
|
||||
|
||||
-- multiple-paths/main.go --
|
||||
package main
|
||||
import (
|
||||
"fmt"
|
||||
"rsc.io/quote/v3"
|
||||
)
|
||||
func main() {
|
||||
fmt.Println(quote.GoV3())
|
||||
}
|
||||
-- multiple-paths/go.mod --
|
||||
module quoter
|
||||
require (
|
||||
rsc.io/quote/v3 v3.0.0
|
||||
not-rsc.io/quote/v3 v3.0.0
|
||||
)
|
||||
replace not-rsc.io/quote/v3 => rsc.io/quote/v3 v3.0.0
|
||||
|
|
|
|||
67
src/cmd/go/testdata/script/modfile_flag.txt
vendored
Normal file
67
src/cmd/go/testdata/script/modfile_flag.txt
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# Tests the behavior of the -modfile flag in commands that support it.
|
||||
# The go.mod file exists but should not be read or written.
|
||||
# Same with go.sum.
|
||||
|
||||
env GOFLAGS=-modfile=go.alt.mod
|
||||
cp go.mod go.mod.orig
|
||||
cp go.sum go.sum.orig
|
||||
|
||||
|
||||
# go mod init should create a new file, even though go.mod already exists.
|
||||
go mod init example.com/m
|
||||
grep example.com/m go.alt.mod
|
||||
|
||||
# go mod edit should operate on the alternate file
|
||||
go mod edit -require rsc.io/quote@v1.5.2
|
||||
grep rsc.io/quote go.alt.mod
|
||||
|
||||
# other 'go mod' commands should work. 'go mod vendor' is tested later.
|
||||
go mod download rsc.io/quote
|
||||
go mod graph
|
||||
stdout rsc.io/quote
|
||||
go mod tidy
|
||||
grep rsc.io/quote go.alt.sum
|
||||
go mod verify
|
||||
go mod why rsc.io/quote
|
||||
|
||||
|
||||
# 'go list' and other commands with build flags should work.
|
||||
# They should update the alternate go.mod when a dependency is missing.
|
||||
go mod edit -droprequire rsc.io/quote
|
||||
go list .
|
||||
grep rsc.io/quote go.alt.mod
|
||||
go build -n .
|
||||
go test -n .
|
||||
go get -d rsc.io/quote
|
||||
|
||||
|
||||
# 'go mod vendor' should work.
|
||||
go mod vendor
|
||||
exists vendor
|
||||
|
||||
# Automatic vendoring should be broken by editing an explicit requirement
|
||||
# in the alternate go.mod file.
|
||||
go mod edit -require rsc.io/quote@v1.5.1
|
||||
! go list .
|
||||
go list -mod=mod
|
||||
|
||||
|
||||
# The original files should not have been modified.
|
||||
cmp go.mod go.mod.orig
|
||||
cmp go.sum go.sum.orig
|
||||
|
||||
|
||||
# If the altnernate mod file does not have a ".mod" suffix, an error
|
||||
# should be reported.
|
||||
cp go.alt.mod goaltmod
|
||||
! go mod tidy -modfile=goaltmod
|
||||
stderr '-modfile=goaltmod: file does not have .mod extension'
|
||||
|
||||
-- go.mod --
|
||||
ʕ◔ϖ◔ʔ
|
||||
-- go.sum --
|
||||
ʕ◔ϖ◔ʔ
|
||||
-- use.go --
|
||||
package use
|
||||
|
||||
import _ "rsc.io/quote"
|
||||
|
|
@ -413,6 +413,7 @@ type FuncInfo struct {
|
|||
GCLocals *LSym
|
||||
GCRegs *LSym
|
||||
StackObjects *LSym
|
||||
OpenCodedDeferInfo *LSym
|
||||
|
||||
FuncInfoSym *LSym
|
||||
}
|
||||
|
|
|
|||
|
|
@ -419,6 +419,9 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
|
|||
// to a PLT, so make sure the GOT pointer is loaded into BX.
|
||||
// RegTo2 is set on the replacement call insn to stop it being
|
||||
// processed when it is in turn passed to progedit.
|
||||
//
|
||||
// We disable open-coded defers in buildssa() on 386 ONLY with shared
|
||||
// libraries because of this extra code added before deferreturn calls.
|
||||
if ctxt.Arch.Family == sys.AMD64 || (p.To.Sym != nil && p.To.Sym.Local()) || p.RegTo2 != 0 {
|
||||
return
|
||||
}
|
||||
|
|
@ -660,8 +663,6 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
|||
markedPrologue = true
|
||||
}
|
||||
|
||||
deltasp := autoffset
|
||||
|
||||
if bpsize > 0 {
|
||||
// Save caller's BP
|
||||
p = obj.Appendp(p, newprog)
|
||||
|
|
@ -806,7 +807,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
|||
p = end
|
||||
}
|
||||
|
||||
for ; p != nil; p = p.Link {
|
||||
var deltasp int32
|
||||
for p = cursym.Func.Text; p != nil; p = p.Link {
|
||||
pcsize := ctxt.Arch.RegSize
|
||||
switch p.From.Name {
|
||||
case obj.NAME_AUTO:
|
||||
|
|
@ -863,6 +865,11 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
|||
p.Spadj = -2
|
||||
continue
|
||||
|
||||
case AADJSP:
|
||||
p.Spadj = int32(p.From.Offset)
|
||||
deltasp += int32(p.From.Offset)
|
||||
continue
|
||||
|
||||
case obj.ARET:
|
||||
// do nothing
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue