[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:
Cherry Zhang 2019-10-25 13:41:36 -04:00
commit d77b809df9
314 changed files with 6741 additions and 1170 deletions

View file

@ -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 &amp; documentation</a></h3>
<p>
Go is an open source project and we welcome contributions from the community.

View file

@ -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
View 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

View file

@ -1,3 +1,5 @@
// You can edit this code!
// Click here and start typing.
package main
import "fmt"

View file

@ -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

View file

@ -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,

View file

@ -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{}

View file

@ -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

View file

@ -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()

View file

@ -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()

View file

@ -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")
}

View file

@ -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[:]

View file

@ -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

View file

@ -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)
}

View file

@ -311,6 +311,8 @@ var (
racewriterange,
x86HasPOPCNT,
x86HasSSE41,
x86HasFMA,
armHasVFPv4,
arm64HasATOMICS,
typedmemclr,
typedmemmove,

View file

@ -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":

View file

@ -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

View file

@ -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)
}
}
}

View file

@ -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.

View file

@ -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),
}

View file

@ -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},

View file

@ -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

View file

@ -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 {

View file

@ -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)
}
}

View file

@ -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
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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)))

View file

@ -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])))

View file

@ -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

View file

@ -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])

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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 {

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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)
}

View file

@ -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
)

View file

@ -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=

View file

@ -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

View file

@ -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, "")

View file

@ -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)

View file

@ -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, "")
}

View file

@ -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

View file

@ -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)

View file

@ -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 {

View file

@ -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 != "" {

View file

@ -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")

View file

@ -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, "@") {

View file

@ -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", "", "")
}

View file

@ -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) {

View file

@ -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) {

View file

@ -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 {

View file

@ -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) {

View file

@ -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.

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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.

View file

@ -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-")

View file

@ -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
}

View file

@ -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
}

View file

@ -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)

View file

@ -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)

View file

@ -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
}

View file

@ -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 {

View file

@ -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)
}

View file

@ -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

View file

@ -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]

View file

@ -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

View file

@ -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 {

View file

@ -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",

View file

@ -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"},

View file

@ -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
}

View file

@ -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)
}
}
}

View 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)
}
}

View file

@ -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.

View file

@ -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

View 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

View file

@ -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 --

View file

@ -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 --

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View 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"

View file

@ -413,6 +413,7 @@ type FuncInfo struct {
GCLocals *LSym
GCRegs *LSym
StackObjects *LSym
OpenCodedDeferInfo *LSym
FuncInfoSym *LSym
}

View file

@ -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