Compare commits

...

490 Commits

Author SHA1 Message Date
Cherry Zhang
d3cd4830ad [dev.regabi] test: run abi/regabipragma test with -c=1
Currently, we call Warnl in SSA backend when we see a function
(defined or called) with regparams pragma. Calling Warnl in
concurrent environment is racy. As the debugging output is
temporary, for testing purposes we just pass -c=1. We'll remove
the pragma and the debugging print some time soon.

Change-Id: I6f925a665b953259453fc458490c5ff91f67c91a
Reviewed-on: https://go-review.googlesource.com/c/go/+/291710
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
2021-02-16 21:51:58 +00:00
Cherry Zhang
03cea563d1 [dev.regabi] all: merge master (5faf941) into dev.regabi
Merge List:

+ 2021-02-16 5faf941df0 internal/goversion: update Version to 1.17
+ 2021-02-16 6f3da9d2f6 README: pull gopher image from website
+ 2021-02-16 098504c73f cmd/link: generate trampoline for inter-dependent packages
+ 2021-02-16 1004a7cb31 runtime/metrics: update documentation to current interface
+ 2021-02-16 6530f2617f doc/go1.16: remove draft notice
+ 2021-02-16 353e111455 doc/go1.16: fix mismatched id attribute
+ 2021-02-16 f0d23c9dbb internal/poll: netpollcheckerr before sendfile
+ 2021-02-16 0cb3415154 doc: remove all docs not tied to distribution
+ 2021-02-16 626ef08127 doc: remove install.html and install-source.html
+ 2021-02-16 30641e36aa internal/poll: if copy_file_range returns 0, assume it failed
+ 2021-02-15 33d72fd412 doc/faq: update generics entry to reflect accepted proposal
+ 2021-02-15 852ce7c212 cmd/go: provide a more helpful suggestion for "go vet -?"
+ 2021-02-13 66c27093d0 cmd/link: fix typo in link_test.go

Change-Id: I98f047b79b93c5ceb344dd43408bcb919b23aeb3
2021-02-16 16:18:19 -05:00
Cherry Zhang
b8fb049c7a [dev.regabi] cmd/go: copy internal/abi in TestNewReleaseRebuildsStalePackagesInGOPATH
The internal/abi package is used by runtime and needs to be
copied.

Fix longtest builders.

Change-Id: I7a962df3db2c6bf68cc6a7da74b579f381920009
Reviewed-on: https://go-review.googlesource.com/c/go/+/292592
Reviewed-by: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
2021-02-16 21:17:18 +00:00
Bryan C. Mills
5faf941df0 internal/goversion: update Version to 1.17
(The corresponding update for the last release cycle was CL 248038.)

For #40705.

Change-Id: I13becdc4c3718a1c6986876ec56879cce3bcb34f
Reviewed-on: https://go-review.googlesource.com/c/go/+/275297
Trust: Bryan C. Mills <bcmills@google.com>
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
2021-02-16 21:07:42 +00:00
Rob Findley
ed55da46ab [dev.regabi] go/types: overlapping embedded interfaces requires go1.14
This is an exact port of CL 290911 to go/types.

For #31793

Change-Id: I28c42727735f467a5984594b455ca58ab3375591
Reviewed-on: https://go-review.googlesource.com/c/go/+/291319
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-16 21:01:52 +00:00
Rob Findley
7696c94334 [dev.regabi] go/types: type alias decl requires go1.9
This is a port of CL 289570 to go/types. It has some notable differences
with that CL:
 + A new _BadDecl error code is added, to indicate declarations with bad
   syntax.
 + declInfo is updated hold not an 'alias' bool, but an aliasPos
   token.Pos to identify the location of the type aliasing '=' token.
   This allows for error messages to be accurately placed on the '='

For #31793

Change-Id: Ib15969f9cd5be30228b7a4c6406f978d6fc58018
Reviewed-on: https://go-review.googlesource.com/c/go/+/291318
Trust: Robert Findley <rfindley@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-16 21:01:14 +00:00
Michael Anthony Knyszek
c2358a1ae7 [dev.regabi] runtime: stub out spillArgs and unspillArgs
Currently these two functions assume that constants in internal/abi are
set correctly, but we actually just made them zero if
GOEXPERIMENT_REGABI is set. This means reflectcall is broken. Fix it by
stubbing out these routines even if GOEXPERIMENT_REGABI is set.

Change-Id: I4c8df6d6af28562c5bb7b85f48c03d37daa9ee0d
Reviewed-on: https://go-review.googlesource.com/c/go/+/292650
Reviewed-by: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Michael Knyszek <mknyszek@google.com>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
2021-02-16 20:42:26 +00:00
Michael Anthony Knyszek
8cfbf34dd9 internal/abi: set register count constants to zero for regabi experiment
This change sets the register count constants to zero for the
GOEXPERIMENT regabi because currently the users of it (i.e. reflect)
will be broken, since they expect Go functions that implement the new
ABI.

Change-Id: Id3e874c61821a36605eb4e1cccdee36a2759f303
Reviewed-on: https://go-review.googlesource.com/c/go/+/292649
Reviewed-by: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Michael Knyszek <mknyszek@google.com>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
2021-02-16 19:40:58 +00:00
Ian Lance Taylor
6f3da9d2f6 README: pull gopher image from website
Fixes breakage accidentally introduced by https://golang.org/cl/291711.

Fixes #44295

Change-Id: I76f3e5577d1d24027d4ed2a725b5b749ab2d059c
Reviewed-on: https://go-review.googlesource.com/c/go/+/292629
Trust: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2021-02-16 18:25:10 +00:00
Cherry Zhang
d28aae26b0 [dev.regabi] cmd/link: recognize internal/abi as runtime package
The runtime imports the internal/abi package. Recognize
internal/abi as a runtime dependent, to make trampoline generation
algorithm work.

Fix ARM build.

Change-Id: I26b6778aa41dcb959bc226ff04abe08a5a82c4f6
Reviewed-on: https://go-review.googlesource.com/c/go/+/292610
Reviewed-by: Than McIntosh <thanm@google.com>
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-02-16 18:21:51 +00:00
Cherry Zhang
098504c73f cmd/link: generate trampoline for inter-dependent packages
Currently, in the trampoline generation pass we expect packages
are laid out in dependency order, so a cross-package jump always
has a known target address so we can check if a trampoline is
needed. With linknames, there can be cycles in the package
dependency graph, making this algorithm no longer work. For them,
as the target address is unkown we conservatively generate a
trampoline. This may generate unnecessary trampolines (if the
packages turn out laid together), but package cycles are extremely
rare so this is fine.

Updates #44073.

Change-Id: I2dc2998edacbda27d726fc79452313a21d07787a
Reviewed-on: https://go-review.googlesource.com/c/go/+/292490
Trust: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
2021-02-16 17:42:55 +00:00
Michael Anthony Knyszek
e0215315f5 [dev.regabi] reflect: support for register ABI on amd64 for reflect.(Value).Call
This change adds support for the new register ABI on amd64 to
reflect.(Value).Call. If internal/abi's register counts are non-zero,
reflect will try to set up arguments in registers on the Call path.

Note that because the register ABI becomes ABI0 with zero registers
available, this should keep working as it did before.

This change does not add any tests for the register ABI case because
there's no way to do so at the moment.

For #40724.

Change-Id: I8aa089a5aa5a31b72e56b3d9388dd3f82203985b
Reviewed-on: https://go-review.googlesource.com/c/go/+/272568
Trust: Michael Knyszek <mknyszek@google.com>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
2021-02-16 15:51:50 +00:00
Rob Findley
b81efb7ec4 [dev.regabi] go/types: add support for language version checking
This is a port of CL 289509 to go/types. It differs from that CL in
codes added to errors, to fit the new factoring of check_test.go, and to
allow go/types to import regexp in deps_test.go

For #31793

Change-Id: Ia9e4c7f5aac1493001189184227c2ebc79a76e77
Reviewed-on: https://go-review.googlesource.com/c/go/+/291317
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-13 00:40:18 +00:00
Rob Findley
a7e9b4b948 [dev.regabi] go/types: untyped shift counts must fit into uint
This is a port of CL 283872 to go/types. It differs from that CL only in
added error codes.

For #43697

Change-Id: I62277834cef1c0359bcf2c6ee4388731babbc855
Reviewed-on: https://go-review.googlesource.com/c/go/+/291316
Trust: Robert Findley <rfindley@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-13 00:39:59 +00:00
Rob Findley
060fa49bd2 [dev.regabi] go/types: refuse excessively long constants
This is a port of CL 289049 to go/types. In that CL, tests were written
using the ability of tests/run.go to generate test packages dynamically.
For this CL, similar functionality is added to the go/types errmap
tests: tests are refactored to decouple the loading of source code from
the filesystem, so that tests for long constants may be generated
dynamically rather than checked-in as a large testdata file.

Change-Id: I92c7cb61a8d42c6593570ef7ae0af86b501fa34e
Reviewed-on: https://go-review.googlesource.com/c/go/+/290949
Trust: Robert Findley <rfindley@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-13 00:39:48 +00:00
Michael Anthony Knyszek
baa6c75dce [dev.regabi] internal/abi: add new internal/abi package for ABI constants
This change creates a new internal std package internal/abi which is
intended to hold constants with platform-specific values related to
our ABI that is useful to different std packages, such as runtime and
reflect.

For #40724.

Change-Id: Ie7ae7f687629cd3d613ba603e9371f0887601fe6
Reviewed-on: https://go-review.googlesource.com/c/go/+/272567
Trust: Michael Knyszek <mknyszek@google.com>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Austin Clements <austin@google.com>
2021-02-12 22:11:52 +00:00
Cherry Zhang
d1fd9a8863 [dev.regabi] all: merge master (ff0e93e) into dev.regabi
Conflicts:

- src/cmd/compile/internal/gc/walk.go

  gc/walk.go is changed in CL 290950 on the master branch but
  deleted in the dev.regabi branch and moved over to the walk
  package. This CL includes porting CL 290950 over to the new
  walk.

Merge List:

+ 2021-02-12 ff0e93ea31 doc/go1.16: note that package path elements beginning with '.' are disallowed
+ 2021-02-11 249da7ec02 CONTRIBUTORS: update for the Go 1.16 release
+ 2021-02-11 864d4f1c6b cmd/go: multiple small 'go help' fixes
+ 2021-02-11 26ceae85a8 spec: More precise wording in section on function calls.
+ 2021-02-11 930c2c9a68 cmd/go: reject embedded files that can't be packed into modules
+ 2021-02-11 e5b08e6d5c io/fs: allow backslash in ValidPath, reject in os.DirFS.Open
+ 2021-02-10 ed8079096f cmd/compile: mark concrete call of reflect.(*rtype).Method as REFLECTMETHOD
+ 2021-02-09 e9c9683597 cmd/go: suppress errors from 'go get -d' for packages that only conditionally exist
+ 2021-02-09 e0ac989cf3 archive/tar: detect out of bounds accesses in PAX records resulting from padded lengths
+ 2021-02-09 c9d6f45fec runtime/metrics: fix a couple of documentation typpos
+ 2021-02-09 cea4e21b52 io/fs: backslash is always a glob meta character
+ 2021-02-08 dc725bfb3c doc/go1.16: mention new vet check for asn1.Unmarshal

Change-Id: Ib28fffa7dfbff7f6cdbfaf4a304757fead7bbf19
2021-02-12 16:35:21 -05:00
Cherry Zhang
59703d53e2 [dev.regabi] cmd/link: stop using ABI aliases if wrapper is enabled
If ABI wrappers are enabled, we should not see ABI aliases at
link time. Stop resolving them. One exception is shared linkage,
where we still use ABI aliases as we don't always know the ABI
for symbols from shared libraries.

Change-Id: Ia89a788094382adeb4c4ef9b0312aa6e8c2f79ef
Reviewed-on: https://go-review.googlesource.com/c/go/+/290032
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
2021-02-10 15:40:37 +00:00
Rob Findley
168d6a49a5 [dev.regabi] go/types: use 512 bits as max. integer precision
This is a port of CL 288633 to go/types. It differs from that CL
in the implementation of opName, which now uses ast Exprs.

Additionally, a couple tests had to be updated:
 + TestEvalArith is updated to not overflow.
 + stmt0.src is updated to have an error positioned on the '<<'
   operator.

Change-Id: I628357c33a1e7b0bb5bb7de5736f1fb10ce404e4
Reviewed-on: https://go-review.googlesource.com/c/go/+/290630
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-09 14:17:01 +00:00
Rob Findley
0a62067708 [dev.regabi] go/types: adjust importer to match compiler importer
This is an exact port of CL 288632 to go/types.

Change-Id: Ie46e13355bdd0713b392e042844bab8491a16504
Reviewed-on: https://go-review.googlesource.com/c/go/+/290629
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-09 14:15:43 +00:00
Rob Findley
1c58fcf7ed [dev.regabi] go/types: handle untyped constant arithmetic overflow
This is a port of CL 287832 for go/types. It differs from that CL in its
handling of position data. Unlike the syntax package, which has a
unified Operation node, go/types checks operations for ast.UnaryExpr,
IncDecStmt, and BinaryExpr. It was simpler to keep the existing position
logic. Notably, this correctly puts the errors on the operator.

Change-Id: Id1e3aefe863da225eb0a9b3628cfc8a5684c0c4f
Reviewed-on: https://go-review.googlesource.com/c/go/+/290569
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Robert Findley <rfindley@google.com>
2021-02-09 14:15:25 +00:00
Rob Findley
493363ccff [dev.regabi] go/types: must not import a package called "init"
This is a port of CL 287494 to go/types. The additional checks in
test/fixedbugs are included, though they won't be executed by go/types.
Support for errorcheckdir checks will be added to go/types in a later
CL.

Change-Id: I37e202ea5daf7d7b8fc6ae93a4c4dbd11762480f
Reviewed-on: https://go-review.googlesource.com/c/go/+/290570
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-02-09 14:00:14 +00:00
Rob Findley
c48d1503ba [dev.regabi] go/types: report unused packages in source order
This is a port of CL 287072 to go/types.

Change-Id: I08f56995f0323c1f238d1b44703a481d393471d5
Reviewed-on: https://go-review.googlesource.com/c/go/+/289720
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Robert Findley <rfindley@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-09 03:59:20 +00:00
Rob Findley
813958f13c [dev.regabi] go/types: factor out sorting of methods
This is a port of CL 285993 to go/types.

Change-Id: I7560cf1176fea5de2c54786a086e547c73294a60
Reviewed-on: https://go-review.googlesource.com/c/go/+/289717
Trust: Robert Findley <rfindley@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-09 03:57:38 +00:00
Rob Findley
11d15c171b [dev.regabi] go/types: convert untyped arguments to delete
This is a port of CL 285059 to go/types. The error assertion is updated
to match go/types error for assignment, which has been improved.

Change-Id: Icdd2751edea0abef7c84feadcbf9265d71239ade
Reviewed-on: https://go-review.googlesource.com/c/go/+/289716
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-09 03:28:12 +00:00
Rob Findley
618e3c15bd [dev.regabi] go/types: consistently report nil type as "untyped nil"
This is a port of CL 284052 to go/types. The port is not entirely
faithful, as untyped conversion has been refactored in go/types.
Additionally, a comment was added to reference issue #13061 in the
implicitType method.

For #13061

Change-Id: Iec17611f6432c988624023d1d74121ff34eb0c83
Reviewed-on: https://go-review.googlesource.com/c/go/+/289715
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Robert Findley <rfindley@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-08 23:25:48 +00:00
Cherry Zhang
50449de66a [dev.regabi] all: merge master (1901853) into dev.regabi
Merge List:

+ 2021-02-08 1901853098 runtime/metrics: fix panic in readingAllMetric example
+ 2021-02-08 ed3e4afa12 syscall/plan9: remove spooky fd action at a distance
+ 2021-02-05 724d0720b3 doc/go1.16: add missed heading tag in vet section
+ 2021-02-05 b54cd94d47 embed, io/fs: clarify that leading and trailing slashes are disallowed
+ 2021-02-05 4516afebed testing/fstest: avoid symlink-induced failures in tester
+ 2021-02-04 8869086d8f runtime: fix typo in histogram.go
+ 2021-02-03 e491c6eea9 math/big: fix comment in divRecursiveStep

Change-Id: I5e2bd18f42251eeb4ad0bf5c93a08c6a9b873d7b
2021-02-08 14:01:08 -05:00
Cherry Zhang
7b0dfb177f [dev.regabi] runtime: use g register in some assembly functions on AMD64
Now that we have a g register, just use it.

Note: functions that can be called from ABI0 context (e.g.
morestack) is unchanged. Functions that switch g is also
unchanged, because we need to set the new g in both the register
and TLS.

TODO: other OSes.

Change-Id: I692a82a7caa8417ff620a59676a6275f56747b94
Reviewed-on: https://go-review.googlesource.com/c/go/+/289718
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
2021-02-08 18:48:12 +00:00
Cherry Zhang
2e60c00f56 [dev.regabi] cmd/internal/obj/x86: use g register in stack bounds check
In ABIInternal context, we can directly use the g register for
stack bounds check.

Change-Id: I8b1073a3343984a6cd76cf5734ddc4a8cd5dc73f
Reviewed-on: https://go-review.googlesource.com/c/go/+/289711
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
2021-02-08 17:49:38 +00:00
Cherry Zhang
22f9e1ccbc [dev.regabi] runtime: initialize special registers before sigpanic
In case that we are panicking in ABI0 context or external code,
special registers are not initialized. Initialized them in
injected code before calling sigpanic.

TODO: Windows, Plan 9.

Change-Id: I0919b80e7cc55463f3dd94f1f63cba305717270a
Reviewed-on: https://go-review.googlesource.com/c/go/+/289710
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
2021-02-08 17:48:48 +00:00
Cherry Zhang
5d7dc53888 [dev.regabi] cmd/compile, runtime: reserve R14 as g registers on AMD64
This is a proof-of-concept change for using the g register on
AMD64. getg is now lowered to R14 in the new ABI. The g register
is not yet used in all places where it can be used (e.g. stack
bounds check, runtime assembly code).

Change-Id: I10123ddf38e31782cf58bafcdff170aee0ff0d1b
Reviewed-on: https://go-review.googlesource.com/c/go/+/289196
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: David Chase <drchase@google.com>
2021-02-08 16:30:07 +00:00
Cherry Zhang
a21de9ec73 [dev.regabi] cmd/link: resolve symbol ABI in shared linkage
In shared build mode and linkage, currently we assume all
function symbols are ABI0 (except for generated type algorithm
functions), and alias them to ABIInternal. When the two ABIs
actually differ (as it is now), this is not actually correct.
This CL resolves symbol ABI based on their mangled names.
If the symbol's name has a ".abi0" or ".abiinternal" suffix, it
is of the corresponding ABI. The symbol without the suffix is
the other ABI. For functions without ABI wrapper generated,
only one ABI exists but we don't know what it is, so we still
use alias (for now).

Change-Id: I2165f149bc83d513e81eb1eb4ee95464335b0e75
Reviewed-on: https://go-review.googlesource.com/c/go/+/289289
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
2021-02-08 15:50:35 +00:00
Cherry Zhang
8fa84772ba [dev.regabi] runtime: delete gosave function
The runtime.gosave function is not used anywhere. Delete.

Note: there is also a gosave<> function, which is actually used
and not deleted.

Change-Id: I64149a7afdd217de26d1e6396233f2becfad7153
Reviewed-on: https://go-review.googlesource.com/c/go/+/289719
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
2021-02-05 20:05:30 +00:00
Cherry Zhang
946351d5a2 [dev.regabi] runtime: zero X15 in racecall
racecall can be called in ABIInternal context (e.g. raceread
calling racecalladdr calling racecall) without wrapper. racecall
calls C code, which doesn't preserve our special registers. Set
them explicitly in racecall upon returning from C.

Change-Id: Ic990479c1fca6bb8a3b151325c7a89be8331a530
Reviewed-on: https://go-review.googlesource.com/c/go/+/289709
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
2021-02-05 17:34:54 +00:00
Cherry Zhang
397a46a10a [dev.regabi] cmd/asm: define g register on AMD64
Define g register as R14 on AMD64. It is not used now, but will
be in later CLs.

The name "R14" is still recognized.

Change-Id: I9a066b15bf1051113db8c6640605e350cea397b9
Reviewed-on: https://go-review.googlesource.com/c/go/+/289195
Trust: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
2021-02-05 17:34:26 +00:00
Cherry Zhang
e79c2fd428 [dev.regabi] runtime: mark racecallbackthunk as ABIInternal
racecallbackthunk is called from C, and it needs to follow C ABI.
The assembly code preserves C callee-save registers. It must not
be called via wrappers, which may not preserve those registers.

Change-Id: Icd72c399f4424d73c4882860d85057fe2671f6aa
Reviewed-on: https://go-review.googlesource.com/c/go/+/289194
Trust: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
2021-02-05 16:52:30 +00:00
Cherry Zhang
7cc6de59f2 [dev.regabi] runtime: don't mark rt0_go ABIInternal
rt0_go is not actually ABIInternal, and it actually has callers
(e.g. _rt0_amd64).

Change-Id: Id730176e620ad9f443e6bfca6ded81a1367531ba
Reviewed-on: https://go-review.googlesource.com/c/go/+/289193
Trust: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
2021-02-05 16:52:18 +00:00
Cherry Zhang
63de211014 [dev.regabi] runtime/cgo: call setg_gcc in crosscall_amd64
Currently, when using cgo, the g pointer is set via a separate
call to setg_gcc or with inline assembly in threadentry. This CL
changes it to call setg_gcc in crosscall_amd64, like other g-
register platforms. When we have an actual g register on AMD64,
we'll need to set the register immediately before calling into
Go.

Change-Id: Ib1171e05cd0dabba3b7d12e072084d141051cf3d
Reviewed-on: https://go-review.googlesource.com/c/go/+/289192
Trust: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
2021-02-05 16:09:50 +00:00
Rob Findley
120b819f45 [dev.regabi] go/types: report error for invalid main function signature
This is a port of CL 279424, which didn't make it into master in time
for go1.16. Move it to dev.regabi so that it may be merged.

Notably, this port no longer removes the _InvalidInitSig error code,
instead opting to deprecate it. Now that error codes are 'locked in' for
go1.16, even if their API may not yet be exposed, we should follow the
practice of not changing their values. In the future, code generation
can make it easier to keep error code values constant.

For #43308

Change-Id: I5260b93fd063393d38d6458e45a67e7f9b7426ed
Reviewed-on: https://go-review.googlesource.com/c/go/+/289714
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-04 20:43:49 +00:00
Cherry Zhang
52d5cb2822 [dev.regabi] cmd/internal/obj: access Attribute atomically
Symbol's Attributes and ABI are in the same word. In the
concurrent backend, we may read one symbol's ABI (the callee)
while setting its attributes in another goroutine.

Fix racecompile build.

Change-Id: I500e869bafdd72080119ab243db94eee3afcf926
Reviewed-on: https://go-review.googlesource.com/c/go/+/289290
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
2021-02-04 20:25:59 +00:00
Rob Findley
bc451b5770 [dev.regabi] go/types: port check_test.go ergonomics from dev.typeparams
On the dev.typeparams and dev.go2go branches, check_test.go has been
updated to automatically discover test data. This is convenient, so port
it to dev.regabi.

Change-Id: I5da9a9a5139c35a2693e64364eb9928ece1cd7c6
Reviewed-on: https://go-review.googlesource.com/c/go/+/289713
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-04 17:13:55 +00:00
Rob Findley
afd67f3334 [dev.regabi] go/types: no "declared but not used" errors for invalid var decls
This is a port of CL 274615, adapted to go/types. The only change was in
the positioning of expected errors in vardecl.src: in go/types they are
positioned on the identifier.

Change-Id: Iab03265a7c4287749373e4380c6db6a95f262f30
Reviewed-on: https://go-review.googlesource.com/c/go/+/289712
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-04 17:13:14 +00:00
Cherry Zhang
401d7e5a24 [dev.regabi] cmd/compile: reserve X15 as zero register on AMD64
In ABIInternal, reserve X15 as constant zero, and use it to zero
memory. (Maybe there can be more use of it?)

The register is zeroed when transition to ABIInternal from ABI0.

Caveat: using X15 generates longer instructions than using X0.
Maybe we want to use X0?

Change-Id: I12d5ee92a01fc0b59dad4e5ab023ac71bc2a8b7d
Reviewed-on: https://go-review.googlesource.com/c/go/+/288093
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
2021-02-03 22:44:53 +00:00
Cherry Zhang
bfc7418e6d [dev.regabi] runtime, syscall, etc.: mark Darwin syscall wrappers as ABIInternal
Mark the syscall wrappers as ABIInternal, as they have addresses
taken from Go code, and it is important to call to them without
wrappers.

Previously, the wrapper is just a single JMP instruction, which
makes it not matter. In the next CL we'll make the wrapper
actually have a frame. The real wrappers will mess up things
such as stack alignment for C ABI.

This doesn't look really nice, but I don't know how we can do
better...

TODO: other OSes.

Change-Id: Ifb3920494990a7775e3e6902fbcaf137df3cc653
Reviewed-on: https://go-review.googlesource.com/c/go/+/288092
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
2021-02-03 22:43:42 +00:00
Robert Griesemer
23b0c1f76e [dev.regabi] all: merge master (fca94ab) into dev.regabi
Conflicts:

- src/syscall/mksyscall.pl

Merge List:

+ 2021-02-02 fca94ab3ab spec: improve the example in Type assertions section
+ 2021-02-02 98f8454a73 cmd/link: don't decode type symbol in shared library in deadcode
+ 2021-02-02 1426a571b7 cmd/link: fix off-by-1 error in findShlibSection
+ 2021-02-01 32e789f4fb test: fix incorrectly laid out instructions in issue11656.go
+ 2021-02-01 0b6cfea634 doc/go1.16: document that on OpenBSD syscalls are now made through libc
+ 2021-02-01 26e29aa15a cmd/link: disable TestPIESize if CGO isn't enabled
+ 2021-02-01 6ac91e460c doc/go1.16: minor markup fixes
+ 2021-01-29 44361140c0 embed: update docs for proposal tweaks
+ 2021-01-29 68058edc39 runtime: document pointer write atomicity for memclrNoHeapPointers
+ 2021-01-28 c8bd8010ff syscall: generate readlen/writelen for openbsd libc
+ 2021-01-28 41bb49b878 cmd/go: revert TestScript/build_trimpath to use ioutil.ReadFile
+ 2021-01-28 725a642c2d runtime: correct syscall10/syscall10X on openbsd/amd64
+ 2021-01-28 4b068cafb5 doc/go1.16: document go/build/constraint package
+ 2021-01-28 376518d77f runtime,syscall: convert syscall on openbsd/arm64 to libc
+ 2021-01-27 00f2ff5c94 api/go1.16: add go/build/constraint APIs
+ 2021-01-27 35334caf18 crypto/x509: remove leftover CertificateRequest field
+ 2021-01-27 a5a5e2c968 runtime: make sure to remove open-coded defer entries in all cases after a recover
+ 2021-01-27 8cfa01943a runtime: block console ctrlhandler when the signal is handled
+ 2021-01-27 ff9e8364c6 cmd/go: skip issue33139 when the 'cc' script command is unavailable
+ 2021-01-27 cd176b3615 runtime: switch runtime to libc for openbsd/arm64
+ 2021-01-27 6c8fbfbdcf runtime: convert openbsd/arm64 locking to libc
+ 2021-01-27 5cdf0da1bf syscall: clean up mkasm related changes
+ 2021-01-27 210f70e298 doc/go1.16: fix closing brace in .Export format
+ 2021-01-27 0f797f168d math: fix typo in sqrt.go code comment
+ 2021-01-26 8634a234df runtime,syscall: convert syscall on openbsd/amd64 to libc
+ 2021-01-26 1d5e14632e os: further document limitations around naked file descriptors
+ 2021-01-26 cf263e9f77 os: correct names in CreateTemp and MkdirTemp doc comments
+ 2021-01-26 ce8b318624 net/http/fcgi: remove locking added to prevent a test-only race

Change-Id: I9e89df040dfbbeb50f4ce653a8da437cb72b3ef9
2021-02-02 10:27:02 -08:00
Dan Scales
ca6999e27c [dev.regabi] test: add a test for inlining closures
Add a test case for issue 43818. We don't want to mark as inlinable a
function with a closure that has an operation (such as OSELRECV2) that
we don't currently support for exporting. This test case fails to
compile without the fix for #43818.

Updates #43818

Change-Id: Ief322a14aefaefc6913c40a6b8505214bd622fda
Reviewed-on: https://go-review.googlesource.com/c/go/+/288392
Run-TryBot: Dan Scales <danscales@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
2021-02-01 18:28:05 +00:00
David Chase
aca22bddf2 [dev.regabi] cmd/compile: remove nested functions from expands_calls.go
Replace nested function spaghetti with state object and methods.
Still somewhat complex, but a bit more explicit.

Change-Id: I21987c8e4be75821faa5a248af05d2095cdfb0d9
Reviewed-on: https://go-review.googlesource.com/c/go/+/287132
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2021-01-27 21:57:05 +00:00
David Chase
667e08ba8c [dev.regabi] cmd/go: Use GOMAXPROCS to limit default build, compile parallelism
When people want deterministic/single-process builds, they probably
assume that GOMAXPROCS=1 will do that.  It currently does not,
neither for build parallelism nor for compiler internal parallelism.
(Current incantation for that is "go build -p=1 -gcflags=all=-c=1 ... ")

This CL makes
  "GOMAXPROCS=1 go build ..."
behave like
  "go build -p=1 -gcflags=all=-c=1 ... "

RELNOTE=yes

Change-Id: I9cfe50b7deee7334d2f1057b58385f6c98547b9f
Reviewed-on: https://go-review.googlesource.com/c/go/+/284695
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
2021-01-27 21:55:30 +00:00
David Chase
9b636feafe [dev.regabi] cmd/compile: missing last patch set for cl286013
Forgot to mail last patch set before committing, repair that.

Change-Id: I1ef72d0d7df56e89369e6fb4d6e5690f254e6aa8
Reviewed-on: https://go-review.googlesource.com/c/go/+/286912
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-26 19:56:01 +00:00
David Chase
f7dad5eae4 [dev.regabi] cmd/compile: remove leftover code form late call lowering work
It's no longer conditional.

Change-Id: I697bb0e9ffe9644ec4d2766f7e8be8b82d3b0638
Reviewed-on: https://go-review.googlesource.com/c/go/+/286013
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-26 18:35:19 +00:00
Matthew Dempsky
5e4a0cdde3 [dev.regabi] all: merge master (bf0f7c9) into dev.regabi
This merge involved two merge conflicts:

1. walk's ascompatee code has been substantially refactored on
dev.regabi, so CL 285633 is ported to the new style.

2. The os.TestDirFS workaround added in CL 286213 can be removed now
that #42637 has been fixed by CL 285720.

Conflicts:

- src/cmd/compile/internal/gc/walk.go
- src/os/os_test.go

Merge List:

+ 2021-01-25 bf0f7c9d78 doc/go1.16: mention os.DirFS in os section
+ 2021-01-25 deaf29a8a8 cmd/compile: fix order-of-assignment issue w/ defers
+ 2021-01-25 ad2ca26a52 doc/go1.16: mention os.DirEntry and types moved from os to io/fs
+ 2021-01-25 a51921fa5b doc/go1.16: mention new testing/iotest functions
+ 2021-01-25 e6b6d107f7 doc/go1.16: mention deprecation of io/ioutil
+ 2021-01-25 96a276363b doc/go1.16: mention go/build changes
+ 2021-01-25 3d85c69a0b html/template: revert "avoid race when escaping updates template"
+ 2021-01-25 54514c6b28 cmd/go: fix TestScript/cgo_path, cgo_path_space when CC set
+ 2021-01-25 6de8443f3b doc/asm: add a section on go_asm.h, clean up go_tls.h section
+ 2021-01-25 54b251f542 lib/time, time/tzdata: update tzdata to 2021a
+ 2021-01-25 ff82cc971a os: force consistent mtime before running fstest on directory on Windows
+ 2021-01-25 044f937a73 doc/go1.16: fix WalkDir and Walk links
+ 2021-01-23 b634f5d97a doc/go1.16: add crypto/x509 memory optimization
+ 2021-01-23 9897655c61 doc/go1.16: reword ambiguously parsable sentence
+ 2021-01-23 cd99385ff4 cmd/internal/obj/arm64: fix VMOVQ instruction encoding error
+ 2021-01-23 66ee8b158f runtime: restore cgo_import_dynamic for libc.so on openbsd
+ 2021-01-22 25c39e4fb5 io/ioutil: fix example test for WriteFile to allow it to run in the playground
+ 2021-01-22 eb21b31e48 runtime: define dummy msanmove
+ 2021-01-22 3a778ff50f runtime: check for g0 stack last in signal handler
+ 2021-01-22 a2cef9b544 cmd/go: don't lookup the path for CC when invoking cgo

Change-Id: I651949f9eb18b57e3c996c4f3b2b3bf458bc5d97
2021-01-25 16:14:35 -08:00
Matthew Dempsky
7eaaf28cae [dev.regabi] cmd/compile: disallow taking address of SSA'd values
Adds some extra validation that the frontend is setting flags like
Addrtaken correctly.

Change-Id: Iffde83e32ba1c4c917ab8cb3fe410a4f623cf635
Reviewed-on: https://go-review.googlesource.com/c/go/+/286434
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
2021-01-25 22:46:58 +00:00
Austin Clements
6f5e79f470 [dev.regabi] cmd/compile/internal: specify memory layout
This CL expands internal-abi.md to cover Go's memory layout rules and
then uses this to specify the calling convention more precisely.

Change-Id: Ifeef9e49d9ccc8c7333dec81bdd47b511b028469
Reviewed-on: https://go-review.googlesource.com/c/go/+/286073
Trust: Austin Clements <austin@google.com>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2021-01-25 19:34:45 +00:00
Austin Clements
cabffc199d [dev.regabi] cmd/compile/internal: add internal ABI specification
This adds a document specifying the internal ABI (specifically the
calling convention). This document lives in the Go tree (rather than
the proposal repository) because the intent is for it to track the
reality in the compiler.

Updates #40724.

Change-Id: I583190080cd7d8cb1084f616fd1384d0f1f25725
Reviewed-on: https://go-review.googlesource.com/c/go/+/285292
Trust: Austin Clements <austin@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2021-01-25 19:34:43 +00:00
Matthew Dempsky
6a4739ccc5 [dev.regabi] cmd/compile: enable rational constant arithmetic
This allows more precision and matches types2's behavior.

For backwards compatibility with gcimporter, for now we still need to
write out declared constants as limited-precision floating-point
values. To ensure consistent behavior of constant arithmetic whether
it spans package boundaries or not, we include the full-precision
rational representation in the compiler's extension section of the
export data.

Also, this CL simply uses the math/big.Rat.String text representation
as the encoding. This is inefficient, but because it's only in the
compiler's extension section, we can easily revisit this in the
future.

Declaring exported untyped float and complex constants isn't very
common anyway. Within the standard library, only package math declares
any at all, containing just 15. And those 15 are only imported a total
of 12 times elsewhere in the standard library.

Change-Id: I85ea23ab712e93fd3b68e52d60cbedce9be696a0
Reviewed-on: https://go-review.googlesource.com/c/go/+/286215
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-01-25 18:53:24 +00:00
Matthew Dempsky
be9612a832 [dev.regabi] os: disable TestDirFS until #42637 is fixed
This test is causing nearly every trybot run on dev.regabi and
dev.typeparams to fail. It's already a release blocker for Go 1.16, so
the failures on the development branches is entirely noise; and
because it causes the trybots to short-circuit, it risks masking
actual Windows-specific failures.

This CL disables the test until a proper solution is decided upon and
implemented for Go 1.16.

Updates #42637.

Change-Id: Ibc85edaed591f1c125cf0b210867aa89d2b0a4b6
Reviewed-on: https://go-review.googlesource.com/c/go/+/286213
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Robert Griesemer <gri@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-01-25 18:06:58 +00:00
Matthew Dempsky
8ee3d39838 [dev.regabi] cmd/go: workaround -race issue on ppc64le
The race detector on ppc64le corrupts command-line arguments lists if
they contain an empty string, and cmd/go often generates compiler
argument lists containing `-D ""`. Since this is equivalent to not
specifying the `-D` flag at all, just do that. This allows using a
race-detector-enabled cmd/compile on ppc64le.

Updates #43883.

Change-Id: Ifac5cd9a44932129438b9b0b3ecc6101ad3716b2
Reviewed-on: https://go-review.googlesource.com/c/go/+/286173
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
Reviewed-by: Jay Conrod <jayconrod@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-01-25 17:07:19 +00:00
David Chase
5a76c3d548 [dev.regabi] cmd/compile: modify abiutils for recently updated ABI
Discovered difficluties posed by earlier design, these modifications
should work better.  Updated tests, also added some helper functions
for use in call lowering.

Change-Id: I459f0f71ad8a6730c571244925c3f395e1df28de
Reviewed-on: https://go-review.googlesource.com/c/go/+/285392
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
2021-01-25 15:56:52 +00:00
Matthew Dempsky
063c72f06d [dev.regabi] cmd/compile: backport changes from dev.typeparams (9456804)
This CL backports a bunch of changes that landed on dev.typeparams,
but are not dependent on types2 or generics. By backporting, we reduce
the divergence between development branches, hopefully improving test
coverage and reducing risk of merge conflicts.

Updates #43866.

Change-Id: I382510855c9b5fac52b17066e44a00bd07fe86f5
Reviewed-on: https://go-review.googlesource.com/c/go/+/286172
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-01-25 01:31:37 +00:00
Baokun Lee
d05d6fab32 [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSet for SSA 2
Same as CL 284897, the last one.

Passes toolstash -cmp.

Updates #43819

Change-Id: I0bd8958b3717fb58a5a6576f1819a85f33b76e2d
Reviewed-on: https://go-review.googlesource.com/c/go/+/285913
Run-TryBot: Baokun Lee <bk@golangcn.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Trust: Baokun Lee <bk@golangcn.org>
2021-01-23 10:38:11 +00:00
Dan Scales
48badc5fa8 [dev.regabi] cmd/compile: fix escape analysis problem with closures
In reflect.methodWrapper, we call escape analysis without including the
full batch of dependent functions, including the closure functions.
Because of this, we haven't created locations for the params/local
variables of a closure when we are processing a function that
inlines that closure. (Whereas in the normal compilation of the
function, we do call with the full batch.) To deal with this, I am
creating locations for the params/local variables of a closure when
needed.

Without this fix, the new test closure6.go would fail.

Updates #43818

Change-Id: I5f91cfb6f35efe2937ef88cbcc468e403e0da9ad
Reviewed-on: https://go-review.googlesource.com/c/go/+/285677
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-23 02:15:24 +00:00
Dan Scales
51e1819a8d [dev.regabi] cmd/compile: scan body of closure in tooHairy to check for disallowed nodes
Several of the bugs in #43818 are because we were not scanning the body
of an possibly inlined closure in tooHairy(). I think this scanning got
lost in the rebase past some of the ir changes. This fixes the issue
related to the SELRECV2 and the bug reported from cuonglm. There is at
least one other bug related to escape analysis which I'll fix in another
change.

Change-Id: I8f38cd12a287881155403bbabbc540ed5fc2248e
Reviewed-on: https://go-review.googlesource.com/c/go/+/285676
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-23 00:30:58 +00:00
Matthew Dempsky
7e0a81d280 [dev.regabi] all: merge master (dab3e5a) into dev.regabi
This merge had two conflicts to resolve:

1. The embed code on master had somewhat substantially diverged, so
this CL tediously backported the changes to dev.regabi. In particular,
I went through all of the embed changes to gc/{embed,noder,syntax}.go
and made sure the analogous code on dev.regabi in noder/noder.go and
staticdata/embed.go mirrors it.

2. The init-cycle reporting code on master was extended slightly to
track already visited declarations to avoid exponential behavior. The
same fix is applied on dev.regabi, just using ir.NameSet instead of
map[ir.Node]bool.

Conflicts:

- src/cmd/compile/internal/gc/embed.go
- src/cmd/compile/internal/gc/noder.go
- src/cmd/compile/internal/gc/syntax.go
- src/cmd/compile/internal/pkginit/initorder.go
- src/embed/internal/embedtest/embed_test.go
- src/go/types/stdlib_test.go

Merge List:

+ 2021-01-22 dab3e5affe runtime: switch runtime to libc for openbsd/amd64
+ 2021-01-22 a1b53d85da cmd/go: add documentation for test and xtest fields output by go list
+ 2021-01-22 b268b60774 runtime: remove pthread_kill/pthread_self for openbsd
+ 2021-01-22 ec4051763d runtime: fix typo in mgcscavenge.go
+ 2021-01-22 7ece3a7b17 net/http: fix flaky TestDisableKeepAliveUpgrade
+ 2021-01-22 50cba0506f time: clarify Timer.Reset behavior on AfterFunc Timers
+ 2021-01-22 cf10e69f17 doc/go1.16: mention net/http.Transport.GetProxyConnectHeader
+ 2021-01-22 ec1b945265 doc/go1.16: mention path/filepath.WalkDir
+ 2021-01-22 11def3d40b doc/go1.16: mention syscall.AllThreadsSyscall
+ 2021-01-21 07b0235609 doc/go1.16: add notes about package-specific fs.FS changes
+ 2021-01-21 e2b4f1fea5 doc/go1.16: minor formatting fix
+ 2021-01-21 9f43a9e07b doc/go1.16: mention new debug/elf constants
+ 2021-01-21 3c2f11ba5b cmd/go: overwrite program name with full path
+ 2021-01-21 953d1feca9 all: introduce and use internal/execabs
+ 2021-01-21 b186e4d70d cmd/go: add test case for cgo CC setting
+ 2021-01-21 5a8a2265fb cmd/cgo: report exec errors a bit more clearly
+ 2021-01-21 46e2e2e9d9 cmd/go: pass resolved CC, GCCGO to cgo
+ 2021-01-21 3d40895e36 runtime: switch openbsd/arm64 to pthreads
+ 2021-01-21 d95ca91380 crypto/elliptic: fix P-224 field reduction
+ 2021-01-20 ecf4ebf100 cmd/internal/moddeps: check content of all modules in GOROOT
+ 2021-01-20 d2d155d1ae runtime: don't adjust timer pp field in timerWaiting status
+ 2021-01-20 803d18fc6c cmd/go: set Incomplete field on go list output if no files match embed
+ 2021-01-20 6e243ce71d cmd/go: have go mod vendor copy embedded files in subdirs
+ 2021-01-20 be28e5abc5 cmd/go: fix mod_get_fallback test
+ 2021-01-20 928bda4f4a runtime: convert openbsd/amd64 locking to libc
+ 2021-01-19 824f2d635c cmd/go: allow go fmt to complete when embedded file is missing
+ 2021-01-19 0575e35e50 cmd/compile: require 'go 1.16' go.mod line for //go:embed
+ 2021-01-19 ccb2e90688 cmd/link: exit before Asmb2 if error
+ 2021-01-19 ca5774a5a5 embed: treat uninitialized FS as empty
+ 2021-01-19 d047c91a6c cmd/link,runtime: switch openbsd/amd64 to pthreads
+ 2021-01-19 61debffd97 runtime: factor out usesLibcall
+ 2021-01-19 9fed39d281 runtime: factor out mStackIsSystemAllocated
+ 2021-01-18 dbab079835 runtime: free Windows event handles after last lock is dropped
+ 2021-01-18 5a8fbb0d2d os: do not close syscall.Stdin in TestReadStdin
+ 2021-01-15 682a1d2176 runtime: detect errors in DuplicateHandle
+ 2021-01-15 9f83418b83 cmd/link: remove GOROOT write in TestBuildForTvOS
+ 2021-01-15 ec9470162f cmd/compile: allow embed into any string or byte slice type
+ 2021-01-15 54198b04db cmd/compile: disallow embed of var inside func
+ 2021-01-15 b386c735e7 cmd/go: fix go generate docs
+ 2021-01-15 bb5075a525 syscall: remove RtlGenRandom and move it into internal/syscall
+ 2021-01-15 1deae0b597 os: invoke processKiller synchronously in testKillProcess
+ 2021-01-15 ff196c3e84 crypto/x509: update iOS bundled roots to version 55188.40.9
+ 2021-01-14 e125ccd10e cmd/go: in 'go mod edit', validate versions given to -retract and -exclude
+ 2021-01-14 eb330020dc cmd/dist, cmd/go: pass -arch for C compilation on Darwin
+ 2021-01-14 84e8a06f62 cmd/cgo: remove unnecessary space in cgo export header
+ 2021-01-14 0c86b999c3 cmd/test2json: document passing -test.paniconexit0
+ 2021-01-14 9135795891 cmd/go/internal/load: report positions for embed errors
+ 2021-01-14 d9b79e53bb cmd/compile: fix wrong complement for arm64 floating-point comparisons
+ 2021-01-14 c73232d08f cmd/go/internal/load: refactor setErrorPos to PackageError.setPos
+ 2021-01-14 6aa28d3e06 go/build: report positions for go:embed directives
+ 2021-01-13 7eb31d999c cmd/go: add hints to more missing sum error messages
+ 2021-01-12 ba76567bc2 cmd/go/internal/modload: delete unused *mvsReqs.next method
+ 2021-01-12 665def2c11 encoding/asn1: document unmarshaling behavior for IMPLICIT string fields
+ 2021-01-11 81ea89adf3 cmd/go: fix non-script staleness checks interacting badly with GOFLAGS
+ 2021-01-11 759309029f doc: update editors.html for Go 1.16
+ 2021-01-11 c3b4c7093a cmd/internal/objfile: don't require runtime.symtab symbol for XCOFF
+ 2021-01-08 59bfc18e34 cmd/go: add hint to read 'go help vcs' to GOVCS errors
+ 2021-01-08 cd6f3a54e4 cmd/go: revise 'go help' documentation for modules
+ 2021-01-08 6192b98751 cmd/go: make hints in error messages more consistent
+ 2021-01-08 25886cf4bd cmd/go: preserve sums for indirect deps fetched by 'go mod download'
+ 2021-01-08 6250833911 runtime/metrics: mark histogram metrics as cumulative
+ 2021-01-08 8f6a9acbb3 runtime/metrics: remove unused StopTheWorld Description field
+ 2021-01-08 6598c65646 cmd/compile: fix exponential-time init-cycle reporting
+ 2021-01-08 fefad1dc85 test: fix timeout code for invoking compiler
+ 2021-01-08 6728118e0a cmd/go: pass signals forward during "go tool"
+ 2021-01-08 e65c543f3c go/build/constraint: add parser for build tag constraint expressions
+ 2021-01-08 0c5afc4fb7 testing/fstest,os: clarify racy behavior of TestFS
+ 2021-01-08 32afcc9436 runtime/metrics: change unit on *-by-size metrics to match bucket unit
+ 2021-01-08 c6513bca5a io/fs: minor corrections to Glob doc
+ 2021-01-08 304f769ffc cmd/compile: don't short-circuit copies whose source is volatile
+ 2021-01-08 ae97717133 runtime,runtime/metrics: use explicit histogram boundaries
+ 2021-01-08 a9ccd2d795 go/build: skip string literal while findEmbed
+ 2021-01-08 d92f8add32 archive/tar: fix typo in comment
+ 2021-01-08 cab1202183 cmd/link: accept extra blocks in TestFallocate
+ 2021-01-08 ee4d32249b io/fs: minor corrections to Glob release date
+ 2021-01-08 54bd1ccce2 cmd: update to latest golang.org/x/tools
+ 2021-01-07 9ec21a8f34 Revert "reflect: support multiple keys in struct tags"
+ 2021-01-07 091414b5b7 io/fs: correct WalkDirFunc documentation
+ 2021-01-07 9b55088d6b doc/go1.16: add release note for disallowing non-ASCII import paths
+ 2021-01-07 fa90aaca7d cmd/compile: fix late expand_calls leaf type for OpStructSelect/OpArraySelect
+ 2021-01-07 7cee66d4cb cmd/go: add documentation for Embed fields in go list output
+ 2021-01-07 e60cffa4ca html/template: attach functions to namespace
+ 2021-01-07 6da2d3b7d7 cmd/link: fix typo in asm.go
+ 2021-01-07 df81a15819 runtime: check mips64 VDSO clock_gettime return code
+ 2021-01-06 4787e906cf crypto/x509: rollback new CertificateRequest fields
+ 2021-01-06 c9658bee93 cmd/go: make module suggestion more friendly
+ 2021-01-06 4c668b25c6 runtime/metrics: fix panic message for Float64Histogram
+ 2021-01-06 d2131704a6 net/http/httputil: fix deadlock in DumpRequestOut
+ 2021-01-05 3e1e13ce6d cmd/go: set cfg.BuildMod to "readonly" by default with no module root
+ 2021-01-05 0b0d004983 cmd/go: pass embedcfg to gccgo if supported
+ 2021-01-05 1b85e7c057 cmd/go: don't scan gccgo standard library packages for imports
+ 2021-01-05 6b37b15d95 runtime: don't take allglock in tracebackothers
+ 2021-01-04 9eef49cfa6 math/rand: fix typo in comment
+ 2021-01-04 b01fb2af9e testing/fstest: fix typo in error message
+ 2021-01-01 3dd5867605 doc: 2021 is the Year of the Gopher
+ 2020-12-31 95ce805d14 io/fs: remove darwin/arm64 special condition
+ 2020-12-30 20d0991b86 lib/time, time/tzdata: update tzdata to 2020f
+ 2020-12-30 ed301733bb misc/cgo/testcarchive: remove special flags for Darwin/ARM
+ 2020-12-30 0ae2e032f2 misc/cgo/test: enable TestCrossPackageTests on darwin/arm64
+ 2020-12-29 780b4de16b misc/ios: fix wording for command line instructions
+ 2020-12-29 b4a71c95d2 doc/go1.16: reference misc/ios/README for how to build iOS programs
+ 2020-12-29 f83e0f6616 misc/ios: add to README how to build ios executables
+ 2020-12-28 4fd9455882 io/fs: fix typo in comment

Change-Id: I2f257bbc5fbb05f15c2d959f8cfe0ce13b083538
2021-01-22 12:01:13 -08:00
Baokun Lee
d7e71c01ad [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSet for dwarf
Same as CL 284897, but for dwarf.

Passes toolstash -cmp.

Fixes #43819

Change-Id: Icbe43aa2e3cb96e6a6c318523c643247da8e4c74
Reviewed-on: https://go-review.googlesource.com/c/go/+/284899
Run-TryBot: Baokun Lee <bk@golangcn.org>
Trust: Baokun Lee <bk@golangcn.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-21 09:56:27 +00:00
Baokun Lee
5248f59a22 [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSet for SSA
Same as CL 284897, but for SSA.

Passes toolstash -cmp.

Updates #43819

Change-Id: I3c500ad635a3192d95d16fdc36f154ba3ea5df69
Reviewed-on: https://go-review.googlesource.com/c/go/+/284898
Run-TryBot: Baokun Lee <bk@golangcn.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Baokun Lee <bk@golangcn.org>
2021-01-21 09:56:12 +00:00
Baokun Lee
970d8b6cb2 [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSet in inlining
As CL 282212 mentioned, we should clean all map[*ir.Name]bool with
ir.NameSet.

Passes toolstash -cmp.

Updates #43819

Change-Id: I1ce5d2055f88539f807dc021cd8e3941b425bc4e
Reviewed-on: https://go-review.googlesource.com/c/go/+/284897
Run-TryBot: Baokun Lee <bk@golangcn.org>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Baokun Lee <bk@golangcn.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-21 09:55:49 +00:00
Cuong Manh Le
68a4664475 [dev.regabi] cmd/compile: remove tempAssigns in walkCall1
Passes toolstash -cmp.

Change-Id: I588c663324443e02b901cda461b999ff192e150c
Reviewed-on: https://go-review.googlesource.com/c/go/+/284896
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-21 06:40:06 +00:00
Cuong Manh Le
fd9a391cdd [dev.regabi] cmd/compile: remove CallExpr.Rargs
Instead, push the temps assignments to init. This does not pass
toolstash, since when before this, the temps were evaluated after
function callee, now we evaluate them before.

Change-Id: Icb9cb10e036925b56c1ef3eec468416a11f4932f
Reviewed-on: https://go-review.googlesource.com/c/go/+/284894
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-21 06:38:25 +00:00
Cuong Manh Le
19a6db6b63 [dev.regabi] cmd/compile: make sure mkcall* passed non-nil init
So next CL can pass temporaries assignments for function arguments in to
init instead of CallExpr.Rargs.

Passes toolstash -cmp.

Change-Id: I2c3cb6a63e8bf9d0418052b39c1db58050f71305
Reviewed-on: https://go-review.googlesource.com/c/go/+/284893
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-21 06:38:18 +00:00
Baokun Lee
9f036844db [dev.regabi] cmd/compile: use ir.DoChildren directly in inlining
Passes toolstash -cmp.

Change-Id: Ie35e8163fa0e61ed9e1b259929c8cbe82ee5301e
Reviewed-on: https://go-review.googlesource.com/c/go/+/282212
Run-TryBot: Baokun Lee <bk@golangcn.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Trust: Baokun Lee <bk@golangcn.org>
2021-01-21 05:44:57 +00:00
Cuong Manh Le
213c3905e9 [dev.regabi] cmd/compile: use node walked flag to prevent double walk for walkSelect
Same as CL 283733, but for walkSelect.

Passes toolstash -cmp.

Change-Id: I3ecb8d6eafd395379191c15fc58c95f75809fec9
Reviewed-on: https://go-review.googlesource.com/c/go/+/284895
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-21 05:22:22 +00:00
Dan Scales
1760d736f6 [dev.regabi] cmd/compile: exporting, importing, and inlining functions with OCLOSURE
I have exporting, importing, and inlining of functions with closures
working in all cases (issue #28727). all.bash runs successfully without
errors.

Approach:
  - Write out the Func type, Dcls, ClosureVars, and Body when exporting
    an OCLOSURE.

  - When importing an OCLOSURE, read in the type, dcls, closure vars,
    and body, and then do roughly equivalent code to (*noder).funcLit

  - During inlining of a closure within inlined function, create new
    nodes for all params and local variables (including closure
    variables), so they can have a new Curfn and some other field
    values. Must substitute not only on the Nbody of the closure, but
    also the Type, Cvars, and Dcl fields.

Fixes #28727

Change-Id: I4da1e2567c3fa31a5121afbe82dc4e5ee32b3170
Reviewed-on: https://go-review.googlesource.com/c/go/+/283112
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Trust: Dan Scales <danscales@google.com>
2021-01-20 22:53:32 +00:00
David Chase
92cb157cf3 [dev.regabi] cmd/compile: late expansion of return values
By-hand rebase of earlier CL, because that was easier than
letting git try to figure things out.

This will naively insert self-moves; in the case that these
involve memory, the expander detects these and removes them
and their vardefs.

Change-Id: Icf72575eb7ae4a186b0de462bc8cf0bedc84d3e9
Reviewed-on: https://go-review.googlesource.com/c/go/+/279519
Trust: David Chase <drchase@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
2021-01-20 19:57:48 +00:00
Cuong Manh Le
9423d50d53 [dev.regabi] cmd/compile: use '%q' for printing rune values less than 128
Fixes #43762

Change-Id: I51734c9b4ee2366a5dae53b2d27b363f4d5fe6c1
Reviewed-on: https://go-review.googlesource.com/c/go/+/284592
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-19 18:27:50 +00:00
Matthew Dempsky
a2f825c542 [dev.regabi] cmd/compile: directly create go.map and go.track symbols
These symbols are implementation details and don't correspond to Go
source symbols, so directly create them as linker symbols and get rid
of their pseudo packages.

Passes toolstash -cmp w/ -gcflags=all=-abiwrap.

Change-Id: I2e97374c21f3e909f6d350f15e7a5ed3574cadf4
Reviewed-on: https://go-review.googlesource.com/c/go/+/284372
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Trust: Matthew Dempsky <mdempsky@google.com>
2021-01-19 03:02:52 +00:00
Matthew Dempsky
4a4212c0e5 [dev.regabi] cmd/compile: refactor Linksym creation
Currently there's a lot of logic within package types for creating
Linksyms. This CL pulls it out into base, where it can be more easily
reused by other compiler code that shouldn't need to depend on package
types.

Package base probably isn't the best place for this, but it's
convenient because it's a package that types already depends on. It's
also where the Ctxt object lives, which these functions depend upon.

Passes toolstash -cmp w/ -gcflags=all=-abiwrap.

Change-Id: I50d8b7e4596955205036969eab24d7dab053b363
Reviewed-on: https://go-review.googlesource.com/c/go/+/284231
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Trust: Matthew Dempsky <mdempsky@google.com>
2021-01-19 02:41:42 +00:00
Matthew Dempsky
4f5c603c0f [dev.regabi] cmd/compile: cleanup callTargetLSym
Now that TailCallStmt carries an *ir.Name instead of a *types.Sym,
callTargetLSym can be similarly updated to take the target function as
an *ir.Name.

This inches us closer towards being able to move Linksym and other
properties from *types.Sym to *ir.Name, where they belong.

Passes toolstash -cmp w/ -gcflags=all=-abiwrap.

Change-Id: I091da290751970eba8ed0438f66d6cca88b665a8
Reviewed-on: https://go-review.googlesource.com/c/go/+/284228
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
2021-01-19 02:41:30 +00:00
Matthew Dempsky
422f38fb6c [dev.regabi] cmd/compile: move stack objects to liveness
Calculating and emitting stack objects are essentially part of
liveness analysis, so move the code from ssagen to liveness. Allows
unexporting liveness.ShouldTrack.

Passes toolstash -cmp.

Change-Id: I88b5b2e75b8dfb46b8b03a2fa09a9236865cbf3e
Reviewed-on: https://go-review.googlesource.com/c/go/+/284413
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-01-18 06:33:56 +00:00
Matthew Dempsky
6113db0bb4 [dev.regabi] cmd/compile: convert OPANIC argument to interface{} during typecheck
Currently, typecheck leaves arguments to OPANIC as their original
type. This CL changes it to insert implicit OCONVIFACE operations to
convert arguments to `interface{}` like how any other function call
would be handled.

No immediate benefits, other than getting to remove a tiny bit of
special-case logic in order.go's handling of OPANICs. Instead, the
generic code path for handling OCONVIFACE is used, if necessary.
Longer term, this should be marginally helpful for #43753, as it
reduces the number of cases where we need values to be addressable for
runtime calls.

However, this does require adding some hacks to appease existing
tests:

1. We need yet another kludge in inline budgeting, to ensure that
reflect.flag.mustBe stays inlinable for cmd/compile/internal/test's
TestIntendedInlining.

2. Since the OCONVIFACE expressions are now being introduced during
typecheck, they're now visible to escape analysis. So expressions like
"panic(1)" are now seen as "panic(interface{}(1))", and escape
analysis warns that the "interface{}(1)" escapes to the heap. These
have always escaped to heap, just now we're accurately reporting about
it.

(Also, unfortunately fmt.go hides implicit conversions by default in
diagnostics messages, so instead of reporting "interface{}(1) escapes
to heap", it actually reports "1 escapes to heap", which is
confusing. However, this confusing messaging also isn't new.)

Change-Id: Icedf60e1d2e464e219441b8d1233a313770272af
Reviewed-on: https://go-review.googlesource.com/c/go/+/284412
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Trust: Matthew Dempsky <mdempsky@google.com>
2021-01-18 05:55:08 +00:00
Cuong Manh Le
4c835f9169 [dev.regabi] cmd/compile: use LinksymOffsetExpr in TypePtr/ItabAddr
Passes toolstash -cmp.

Fixes #43737

Change-Id: I2d5228c0213b5f8742e3cea6fac9bc985b19d78c
Reviewed-on: https://go-review.googlesource.com/c/go/+/284122
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-18 04:52:51 +00:00
Cuong Manh Le
0ffa1ead6e [dev.regabi] cmd/compile: use *obj.LSym instead of *ir.Name for staticdata functions
Those functions only use (*ir.Name).Linksym(), so just change them to
get an *obj.LSym directly. This helps get rid of un-necessary
validations that their callers have already done.

Passes toolstash -cmp.

For #43737.

Change-Id: Ifd6c2525e472f8e790940bc167665f9d74dd1bc5
Reviewed-on: https://go-review.googlesource.com/c/go/+/284121
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-18 04:33:50 +00:00
Matthew Dempsky
7e0fa38aad [dev.regabi] cmd/compile: remove unneeded packages from ir.Pkgs
ir.Pkgs.Itablink isn't used anymore. (I don't recall what it was ever
used for.)

ir.Pkgs.Race and ir.Pkgs.Msan are only needed in exactly only place,
so just create them on demand there, the same way that we create
"main" on demand.

Change-Id: I3474bb949f71cd40c7a462b9f4a369adeacde0d6
Reviewed-on: https://go-review.googlesource.com/c/go/+/284230
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-17 22:16:41 +00:00
Matthew Dempsky
99a5db11ac [dev.regabi] cmd/compile: use LinksymOffsetExpr in walkConvInterface
This CL updates walkConvInterface to use LinksymOffsetExpr for
referencing runtime.staticuint64s and runtime.zerobase.

Passes toolstash -cmp (surprisingly).

Change-Id: Iad7e30371f89c8a5e176b5ddbc53faf57012ba0d
Reviewed-on: https://go-review.googlesource.com/c/go/+/284229
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-17 11:17:54 +00:00
Matthew Dempsky
87845d14f9 [dev.regabi] cmd/compile: add ir.TailCallStmt
This CL splits out ORETJMP as a new TailCallStmt node, separate from
the other BranchStmt nodes. In doing so, this allows us to change it
from identifying a function by *types.Sym to identifying one by
directly pointing to the *ir.Func.

While here, also rename the operation to OTAILCALL.

Passes toolstash -cmp.

Change-Id: I273e6ea5d92bf3005ae02fb59b3240a190a6cf1b
Reviewed-on: https://go-review.googlesource.com/c/go/+/284227
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-17 11:14:51 +00:00
Matthew Dempsky
e3027c6828 [dev.regabi] cmd/compile: fix linux-amd64-noopt builder
CL 284223 tightened down the allowed expressions in mayCall, but
evidently a little too tight. The linux-amd64-noopt builder does in
fact see expressions with non-empty Init lists in arguments list.

Since I believe these can only appear on the RHS of LogicalExpr
expressions, this CL relaxes that one case.

Change-Id: I1e6bbd0449778c40ed2610b3e1ef6a825a84ada7
Reviewed-on: https://go-review.googlesource.com/c/go/+/284226
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-17 07:23:00 +00:00
Cuong Manh Le
59ff93fe64 [dev.regabi] cmd/compile: rename NameOffsetExpr to LinksymOffsetExpr
Updates #43737

[git-generate]

cd src/cmd/compile/internal/ir

rf '
  mv NameOffsetExpr LinksymOffsetExpr
  mv ONAMEOFFSET OLINKSYMOFFSET
'

go generate

Change-Id: I8c6b8aa576e88278c0320d16bb2e8e424a15b907
Reviewed-on: https://go-review.googlesource.com/c/go/+/284120
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-17 06:38:07 +00:00
Cuong Manh Le
82b9cae700 [dev.regabi] cmd/compile: change ir.NameOffsetExpr to use *obj.LSym instead of *Name
Because NameOffsetExpr is always used with global variables, and SSA
backend only needs (*Name).Linksym() to generate value for them.

Passes toolstash -cmp.

Updates #43737

Change-Id: I17209e21383edb766070c0accd1fa4660659caef
Reviewed-on: https://go-review.googlesource.com/c/go/+/284119
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-17 06:37:42 +00:00
Cuong Manh Le
88956fc4b1 [dev.regabi] cmd/compile: stop analyze NameOffsetExpr.Name_ in escape analysis
It is always used with global variables, so we can skip analyze it, the
same as what we are doing for ONAME/PEXTERN nodes.

While at it, add a Fatalf check to ensure NewNameOffsetExpr is only
called for global variables.

For #43737

Change-Id: Iac444ed8d583baba5042bea096531301843b1e8f
Reviewed-on: https://go-review.googlesource.com/c/go/+/284118
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-17 06:37:18 +00:00
Matthew Dempsky
7ce2a8383d [dev.regabi] cmd/compile: simplify stack temp initialization
This CL simplifies the previous one a little bit further, by combining
reordering stack-temporary initialization and getting rid of an
unneeded temporary variable. (Does not pass toolstash -cmp.)

Change-Id: I17799dfe368484f33a8ddd0ab4f68647d6262147
Reviewed-on: https://go-review.googlesource.com/c/go/+/284225
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-17 05:08:11 +00:00
Matthew Dempsky
ba0e8a92fa [dev.regabi] cmd/compile: refactor temp construction in walk
This CL adds a few new helper functions for constructing and
initializing temporary variables during walk.

Passes toolstash -cmp.

Change-Id: I54965d992cd8dfef7cb7dc92a17c88372e52a0d6
Reviewed-on: https://go-review.googlesource.com/c/go/+/284224
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-17 05:08:05 +00:00
Matthew Dempsky
78e5aabcdb [dev.regabi] cmd/compile: replace Node.HasCall with walk.mayCall
After CL 284220, we now only need to detect expressions that contain
function calls in the arguments list of further function calls. So we
can simplify Node.HasCall/fncall/etc a lot.

Instead of incrementally tracking whether an expression contains
function calls all throughout walk, simply check once at the point of
using an expression as a function call argument. Since any expression
checked here will itself become a function call argument, it won't be
checked again because we'll short circuit at the enclosing function
call.

Also, restructure the recursive walk code to use mayCall, and trim
down the list of acceptable expressions. It should be okay to be
stricter, since we'll now only see function call arguments and after
they've already been walked.

It's possible I was overly aggressive removing Ops here. But if so,
we'll get an ICE, and it'll be easy to re-add them. I think this is
better than the alternative of accidentally allowing expressions
through that risk silently clobbering the stack.

Passes toolstash -cmp.

Change-Id: I585ef35dcccd9f4018e4bf2c3f9ccb1514a826f3
Reviewed-on: https://go-review.googlesource.com/c/go/+/284223
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-17 05:07:59 +00:00
Matthew Dempsky
6de9423445 [dev.regabi] cmd/compile: cleanup OAS2FUNC ordering
Currently, to ensure OAS2FUNC results are assigned in the correct
order, they're always assigned to temporary variables. However, these
temporary variables are typed based on the destination type, which may
require an interface conversion. This means walk may have to then
introduce a second set of temporaries to ensure result parameters are
all copied out of the results area, before it emits calls to runtime
conversion functions.

That's just silly. Instead, this CL changes order to allocate the
result temporaries with the same type as the function returns in the
first place, and then assign them one at a time to their destinations,
with conversions as needed.

While here, also fix an order-of-evaluation issue with has-ok
assignments that I almost added to multi-value function call
assignments, and add tests for each.

Change-Id: I9f4e962425fe3c5e3305adbbfeae2c7f253ec365
Reviewed-on: https://go-review.googlesource.com/c/go/+/284220
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-16 23:19:26 +00:00
Dan Scales
a956a0e909 [dev.regabi] cmd/compile, runtime: fix up comments/error messages from recent renames
Went in a semi-automated way through the clearest renames of functions,
and updated comments and error messages where it made sense.

Change-Id: Ied8e152b562b705da7f52f715991a77dab60da35
Reviewed-on: https://go-review.googlesource.com/c/go/+/284216
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-16 02:31:08 +00:00
Cuong Manh Le
ab3b67abfd [dev.regabi] cmd/compile: remove ONEWOBJ
After CL 283233, SSA can now handle new(typ) without the frontend to
generate the type address, so we can remove ONEWOBJ in favor of ONEW
only.

This is also not save for toolstash, the same reason with CL 284115.

Change-Id: Ie03ea36b3b6f95fc7ce080376c6f7afc402d51a3
Reviewed-on: https://go-review.googlesource.com/c/go/+/284117
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-16 01:36:46 +00:00
Cuong Manh Le
c9b1445ac8 [dev.regabi] cmd/compile: remove TypeAssertExpr {Src,Dst}Type fields
CL 283233 added reflectType method to ssagen.state, which we can use to
setup type address in the SSA backend in favor of the frontend. However,
this will change the order of symbols generation, so not safe for toolstash.

Change-Id: Ib6932ec42a9d28c3fd7a1c055596e75494c29843
Reviewed-on: https://go-review.googlesource.com/c/go/+/284115
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-16 01:36:11 +00:00
Matthew Dempsky
03a875137f [dev.regabi] cmd/compile: unexport reflectdata.WriteType
WriteType isn't safe for direct concurrent use, and users should
instead use TypeLinksym or another higher-level API provided by
reflectdata. After the previous CL, there are no remaining uses of
WriteType elsewhere in the compiler, so unexport it to keep it that
way.

For #43701.

[git-generate]
cd src/cmd/compile/internal/reflectdata
rf '
	mv WriteType writeType
'

Change-Id: I294a78be570a47feb38a1ad4eaae7723653d5991
Reviewed-on: https://go-review.googlesource.com/c/go/+/284077
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-15 16:13:25 +00:00
Matthew Dempsky
14537e6e54 [dev.regabi] cmd/compile: move stkobj symbol generation to SSA
The code for allocating linksyms and recording that we need runtime
type descriptors is now concurrent-safe, so move it to where those
symbols are actually needed to reduce complexity and risk of failing
to generate all needed symbols in advance.

For #43701.

Change-Id: I759d2508213ac9a4e0b504b51a75fa10dfa37a8d
Reviewed-on: https://go-review.googlesource.com/c/go/+/284076
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Trust: Matthew Dempsky <mdempsky@google.com>
2021-01-15 16:13:14 +00:00
Matthew Dempsky
ab523fc510 [dev.regabi] cmd/compile: don't promote Byval CaptureVars if Addrtaken
We decide during escape analysis whether to pass closure variables by
value or reference. One of the factors that's considered is whether a
variable has had its address taken.

However, this analysis is based only on the user-written source code,
whereas order+walk may introduce rewrites that take the address of a
variable (e.g., passing a uint16 key by reference to the size-generic
map runtime builtins).

Typically this would be harmless, albeit suboptimal. But in #43701 it
manifested as needing a stack object for a function where we didn't
realize we needed one up front when we generate symbols.

Probably we should just generate symbols on demand, now that those
routines are all concurrent-safe, but this is a first fix.

Thanks to Alberto Donizetti for reporting the issue, and Cuong Manh Le
for initial investigation.

Fixes #43701.

Change-Id: I16d87e9150723dcb16de7b43f2a8f3cd807a9437
Reviewed-on: https://go-review.googlesource.com/c/go/+/284075
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-15 16:13:04 +00:00
David Chase
b7a698c73f [dev.regabi] test: disable test on windows because expected contains path separators.
The feature being tested is insensitive to the OS anyway.

Change-Id: Ieac9bfaafc6a54c00017afcc0b87bd8bbe80af7b
Reviewed-on: https://go-review.googlesource.com/c/go/+/284032
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
2021-01-15 15:16:05 +00:00
Matthew Dempsky
4be7af23f9 [dev.regabi] cmd/compile: fix ICE during ir.Dump
fmt.go:dumpNodeHeader uses reflection to call all "func() bool"-typed
methods on Nodes during printing, but the OnStack method that I added
in CL 283233 isn't meant to be called on non-variables.

dumpNodeHeader does already guard against panics, as happen in some
other accessors, but not against Fatalf, as I was using in OnStack. So
simply change OnStack to use panic too.

Thanks to drchase@ for the report.

Change-Id: I0cfac84a96292193401a32fc5e7fd3c48773e008
Reviewed-on: https://go-review.googlesource.com/c/go/+/284074
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-01-15 04:08:47 +00:00
Than McIntosh
35b9c66601 [dev.regabi] cmd/compile,cmd/link: additional code review suggestions for CL 270863
This patch pulls in a few additional changes requested by code
reviewers for CL 270863 that were accidentally left out. Specifically,
guarding use of ORETJMP to insure it is not used when building dynlink
on ppc64le, and a tweaking the command line flags used to control
wrapper generation.

Change-Id: I4f96462e570180887eb8693e11badd83d142710a
Reviewed-on: https://go-review.googlesource.com/c/go/+/279527
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Trust: Than McIntosh <thanm@google.com>
2021-01-14 17:35:39 +00:00
Cuong Manh Le
9734fd482d [dev.regabi] cmd/compile: use node walked flag to prevent double walk for walkSwitch
CL 283672 added a flag to prevent double walking, use that flag instead
of checking SwitchStmt.Compiled field.

Passes toolstash -cmp.

Change-Id: Idb8f9078412fb789f51ed4fc4206638011e38a93
Reviewed-on: https://go-review.googlesource.com/c/go/+/283733
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-14 08:31:22 +00:00
Matthew Dempsky
f97983249a [dev.regabi] cmd/compile: move more PAUTOHEAP to SSA construction
This CL moves almost all PAUTOHEAP handling code to SSA construction.
Instead of changing Names to PAUTOHEAP, escape analysis now only sets
n.Esc() to ir.EscHeap, and SSA handles creating the "&x"
pseudo-variables and associating them via Heapaddr.

This CL also gets rid of n.Stackcopy, which was used to distinguish
the heap copy of a parameter used within a function from the stack
copy used in the function calling convention. In practice, this is
always obvious from context: liveness and function prologue/epilogue
want to know about the stack copies, and everywhere else wants the
heap copy.

Hopefully moving all parameter/result handling into SSA helps with
making the register ABI stuff easier.

Also, the only remaining uses of PAUTOHEAP are now for closure
variables, so I intend to rename it to PCLOSUREVAR or get rid of those
altogether too. But this CL is already big and scary enough.

Change-Id: Ief5ef6205041b9d0ee445314310c0c5a98187e77
Reviewed-on: https://go-review.googlesource.com/c/go/+/283233
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: David Chase <drchase@google.com>
2021-01-14 06:10:09 +00:00
Cuong Manh Le
4476300425 [dev.regabi] cmd/compile: use byte for CallExpr.Use
Reduce 16 byte for CallExpr, from 184 to 168 on 64-bit archs.

Passes toolstash -cmp.

Change-Id: I59c7609ccd03e8b4a7df8d2c30de8022ae312cee
Reviewed-on: https://go-review.googlesource.com/c/go/+/283732
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-01-14 05:48:36 +00:00
Cuong Manh Le
5a5ab24689 [dev.regabi] cmd/compile: do not rely on CallExpr.Rargs for detect already walked calls
Currently, there's an awkward issue with walk pass. When walking the AST
tree, the compiler generate code for runtime functions (using mkcall* variants),
add/modify the AST tree and walk new generated tree again. This causes the
double walking on some CallExpr, which is relying on checking Rargs to prevent
that. But checking Rargs has its own issue as well.

For functions that does not have arguments, this check is failed, and we
still double walk the CallExpr node.

This CL change the way that compiler detects double walking, by using
separated field instead of relying on Rargs. In perfect world, we should make
the compiler walks the AST tree just once, but it's not safe to do that at
this moment.

Passes toolstash -cmp.

Change-Id: Ifdd1e0f98940ddb1f574af2da2ac7f005b5fcadd
Reviewed-on: https://go-review.googlesource.com/c/go/+/283672
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2021-01-14 05:48:28 +00:00
Matthew Dempsky
983ac4b086 [dev.regabi] cmd/compile: fix ICE when initializing blank vars
CL 278914 introduced NameOffsetExpr to avoid copying ONAME nodes and
hacking up their offsets, but evidently staticinit subtly depended on
the prior behavior to allow dynamic initialization of blank variables.

This CL refactors the code somewhat to avoid using NameOffsetExpr with
blank variables, and to instead create dynamic assignments directly to
the global blank node. It also adds a check to NewNameOffsetExpr to
guard against misuse like this, since I suspect there could be other
cases still lurking within staticinit. (This code is overdue for an
makeover anyway.)

Thanks to thanm@ for bisect and test case minimization.

Fixes #43677.

Change-Id: Ic71cb5d6698382feb9548dc3bb9fd606b207a172
Reviewed-on: https://go-review.googlesource.com/c/go/+/283537
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
2021-01-14 00:14:28 +00:00
Matthew Dempsky
d6d4673728 [dev.regabi] cmd/compile: fix GOEXPERIMENT=regabi builder
I misread the FIXME comment in InitLSym the first time. It's referring
to how InitLSym is supposed to be called exactly once per
function (see function documentation), but this is evidently not
actually the case currently in GOEXPERIMENT=regabi mode.

So just move the NeedFuncSym call below the GOEXPERIMENT=regabi
workaround.

Also, to fix the linux-arm64-{aws,packet} builders, move the call to
reflectdata.WriteFuncSyms() to after the second batch of functions are
compiled. This is necessary to make sure we catch all the funcsyms
that can be added by late function compilation.

Change-Id: I6d6396d48e2ee29c1fb007fa2b99e065b36375db
Reviewed-on: https://go-review.googlesource.com/c/go/+/283552
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Trust: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
2021-01-13 23:07:18 +00:00
David Chase
c41b999ad4 [dev.regabi] cmd/compile: refactor abiutils from "gc" into new "abi"
Needs to be visible to ssagen, and might as well start clean to avoid
creating a lot of accidental dependencies.

Added some methods for export.

Decided to use a pointer instead of value for ABIConfig uses.

Tests ended up separate from abiutil itself; otherwise there are import cycles.

Change-Id: I5570e1e6a463e303c5e2dc84e8dd4125e7c1adcc
Reviewed-on: https://go-review.googlesource.com/c/go/+/282614
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
2021-01-13 15:54:19 +00:00
David Chase
861707a8c8 [dev.regabi] cmd/compile: added limited //go:registerparams pragma for new ABI dev
This only works for functions; if you try it with a method, it will
fail.  It does work for both local package and imports.  For now,
it tells you when it thinks it sees either a declaration or a call of
such a function (this will normally be silent since no existing
code uses this pragma).

Note: it appears to be really darn hard to figure out if this
pragma was set for a method, and the method's call site.  Better
ir.Node wranglers than I might be able to make headway, but it
seemed unnecessary for this experiment.

Change-Id: I601c2ddd124457bf6d62f714d7ac871705743c0a
Reviewed-on: https://go-review.googlesource.com/c/go/+/279521
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
2021-01-13 15:50:04 +00:00
David Chase
c1370e918f [dev.regabi] cmd/compile: add code to support register ABI spills around morestack calls
This is a selected copy from the register ABI experiment CL, focused
on the files and data structures that handle spilling around morestack.
Unnecessary code from the experiment was removed, other code was adapted.

Would it make sense to leave comments in the experiment as pieces are
brought over?

Experiment CL (for comparison purposes)
https://go-review.googlesource.com/c/go/+/28832

Change-Id: I92136f070351d4fcca1407b52ecf9b80898fed95
Reviewed-on: https://go-review.googlesource.com/c/go/+/279520
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
2021-01-13 15:47:13 +00:00
David Chase
2abd24f3b7 [dev.regabi] test: make run.go error messages slightly more informative
This is intended to make it easier to write/change a test
without referring to the source code to figure out what the
error messages actually mean, or how to correct them.

Change-Id: Ie79ff7cd9f2d1fa605257fe97eace68adc8a6716
Reviewed-on: https://go-review.googlesource.com/c/go/+/281452
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
2021-01-13 02:41:11 +00:00
David Chase
9a19481acb [dev.regabi] cmd/compile: make ordering for InvertFlags more stable
Current many architectures use a rule along the lines of

// Canonicalize the order of arguments to comparisons - helps with CSE.
((CMP|CMPW) x y) && x.ID > y.ID => (InvertFlags ((CMP|CMPW) y x))

to normalize comparisons as much as possible for CSE.  Replace the
ID comparison with something less variable across compiler changes.
This helps avoid spurious failures in some of the codegen-comparison
tests (though the current choice of comparison is sensitive to Op
ordering).

Two tests changed to accommodate modified instruction choice.

Change-Id: Ib35f450bd2bae9d4f9f7838ceaf7ec682bcf1e1a
Reviewed-on: https://go-review.googlesource.com/c/go/+/280155
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2021-01-13 02:40:43 +00:00
Matthew Dempsky
d9acf6f3a3 [dev.regabi] cmd/compile: remove Func.ClosureType
The closure's type always matches the corresponding function's type,
so just use one instance rather than carrying around two. Simplifies
construction of closures, rewriting them during walk, and shrinks
memory usage.

Passes toolstash -cmp.

Change-Id: I83b8b8f435b02ab25a30fb7aa15d5ec7ad97189d
Reviewed-on: https://go-review.googlesource.com/c/go/+/283152
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
2021-01-12 23:23:14 +00:00
Matthew Dempsky
41352fd401 [dev.regabi] cmd/compile: transform closures during walk
We used to transform directly called closures in a separate pass
before walk, because we couldn't guarantee whether we'd see the
closure call or the closure itself first. As of the last CL, this
ordering is always guaranteed, so we can rewrite calls and the closure
at the same time.

Change-Id: Ia6f4d504c24795e41500108589b53395d301123b
Reviewed-on: https://go-review.googlesource.com/c/go/+/283315
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
2021-01-12 23:23:00 +00:00
Matthew Dempsky
d6ad88b4db [dev.regabi] cmd/compile: compile functions before closures
This CL reorders function compilation to ensure that functions are
always compiled before any enclosed function literals. The primary
goal of this is to reduce the risk of race conditions that arise due
to compilation of function literals needing to inspect data from their
closure variables. However, a pleasant side effect is that it allows
skipping the redundant, separate compilation of function literals that
were inlined into their enclosing function.

Change-Id: I03ee96212988cb578c2452162b7e99cc5e92918f
Reviewed-on: https://go-review.googlesource.com/c/go/+/282892
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2021-01-12 23:22:41 +00:00
Matthew Dempsky
432f9ffb11 [dev.regabi] cmd/compile: unindent compileFunctions
No real code changes. Just splitting into a separate CL so the next
one is easier to review.

Change-Id: I428dc986b76370d8d3afc12cf19585f6384389d7
Reviewed-on: https://go-review.googlesource.com/c/go/+/283314
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
2021-01-12 23:22:11 +00:00
Matthew Dempsky
cc90e7a51e [dev.regabi] cmd/compile: always use the compile queue
The compiler currently has two modes for compilation: one where it
compiles each function as it sees them, and another where it enqueues
them all into a work queue. A subsequent CL is going to reorder
function compilation to ensure that functions are always compiled
before any non-trivial function literals they enclose, and this will
be easier if we always use the compile work queue.

Also, fewer compilation modes makes things simpler to reason about.

Change-Id: Ie090e81f7476c49486296f2b90911fa0a466a5dd
Reviewed-on: https://go-review.googlesource.com/c/go/+/283313
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
2021-01-12 23:22:04 +00:00
Matthew Dempsky
cd5b74d2df [dev.regabi] cmd/compile: call NeedFuncSym in InitLSym
InitLSym is where we're now generating ABI wrappers, so it seems as
good a place as any to make sure we're generating the degenerate
closure wrappers for declared functions and methods.

Change-Id: I097f34bbcee65dee87a97f9ed6f3f38e4cf2e2b5
Reviewed-on: https://go-review.googlesource.com/c/go/+/283312
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
2021-01-12 23:21:58 +00:00
Matthew Dempsky
95acd8121b [dev.regabi] cmd/compile: remove Name.Typegen
Just directly set Type.Vargen when declaring defined types within a
function.

Change-Id: Idcc0007084a660ce1c39da4a3697e158a1c615b5
Reviewed-on: https://go-review.googlesource.com/c/go/+/283212
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dan Scales <danscales@google.com>
2021-01-12 04:46:22 +00:00
Matthew Dempsky
12ee55ba7b [dev.regabi] cmd/compile: stop using Vargen for import/export
Historically, inline function bodies were exported as plain Go source
code, and symbol mangling was a convenient hack because it allowed
variables to be re-imported with largely the same names as they were
originally exported as.

However, nowadays we use a binary format that's more easily extended,
so we can simply serialize all of a function's declared objects up
front, and then refer to them by index later on. This also allows us
to easily report unmangled names all the time (e.g., error message
from issue7921.go).

Fixes #43633.

Change-Id: I46c88f5a47cb921f70ab140976ba9ddce38df216
Reviewed-on: https://go-review.googlesource.com/c/go/+/283193
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dan Scales <danscales@google.com>
Trust: Dan Scales <danscales@google.com>
Trust: Matthew Dempsky <mdempsky@google.com>
2021-01-12 03:15:18 +00:00
Matthew Dempsky
b4d2a0445b [dev.regabi] cmd/compile: refactor closure var setup/teardown
Creating closure vars is subtle and is also needed in both CL 281932
and CL 283112, so refactor out a common implementation that can be
used in all 3 places.

Passes toolstash -cmp.

Change-Id: Ib993eb90c895b52759bfbfbaad88921e391b0b4d
Reviewed-on: https://go-review.googlesource.com/c/go/+/283194
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dan Scales <danscales@google.com>
Trust: Dan Scales <danscales@google.com>
Trust: Matthew Dempsky <mdempsky@google.com>
2021-01-12 02:46:27 +00:00
Matthew Dempsky
f57f484053 [dev.regabi] cmd/compile: decouple escape analysis from Name.Vargen
Escape analysis needs to know the index of result parameters for
recording escape-flow information. It currently relies on Vargen for
this, but it can easily figure this out for itself. So just do that
instead, so that we can remove Vargen.

Passes toolstash -cmp.

For #43633.

Change-Id: I65dedc2d73bc25e85ff400f308e50b73dc503630
Reviewed-on: https://go-review.googlesource.com/c/go/+/283192
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dan Scales <danscales@google.com>
2021-01-12 01:25:32 +00:00
Matthew Dempsky
7fd84c6e46 [dev.regabi] cmd/compile: remove OCLOSUREREAD
After the previous CLs, all closure reads are handled during SSA
construction.

Change-Id: Iad67b01fa2d3798f50ea647be7ccf8195f189c27
Reviewed-on: https://go-review.googlesource.com/c/go/+/281512
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-10 08:02:23 +00:00
Matthew Dempsky
c9c26d7ffb [dev.regabi] cmd/compile: use ClosureVars for method value wrappers
Similar to with regular closures, we can change method value wrappers
to use ClosureVars and allow SSA construction to take care of wiring
it up appropriately.

Change-Id: I05c0b1bcec4e24305324755df35b7bc5b8a6ce7a
Reviewed-on: https://go-review.googlesource.com/c/go/+/281353
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
2021-01-10 08:02:16 +00:00
Matthew Dempsky
950cf4d46c [dev.regabi] cmd/compile: bind closure vars during SSA constructions
For function literals that aren't inlined or directly called, we need
to pass their arguments via a closure struct. This also means we need
to rewrite uses of closure variables to access from this closure
struct.

Currently we do this rewrite in a pass before walking begins. This CL
moves the code to SSA construction instead, alongside binding other
input parameters.

Change-Id: I13538ef3394e2d6f75d5b7b2d0adbb00db812dc2
Reviewed-on: https://go-review.googlesource.com/c/go/+/281352
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
2021-01-10 08:02:06 +00:00
Matthew Dempsky
8b2efa990b [dev.regabi] cmd/compile: deref PAUTOHEAPs during SSA construction
Currently, during walk we rewrite PAUTOHEAP uses into derefs of their
corresponding Heapaddr, but we can easily do this instead during SSA
construction. This does involve updating two test cases:

* nilptr3.go

This file had a test that we emit a "removed nil check" diagnostic for
the implicit dereference from accessing a PAUTOHEAP variable. This CL
removes this diagnostic, since it's not really useful to end users:
from the user's point of view, there's no pointer anyway, so they
needn't care about whether we check for nil or not. That's a purely
internal detail. And with the PAUTOHEAP dereference handled during SSA
construction, we can more robustly ensure this happens, rather than
relying on setting a flag in walk and hoping that SSA sees it.

* issue20780.go

Previously, when PAUTOHEAPs were dereferenced during walk, it had a
consequence that when they're passed as a function call argument, they
would first get copied to the stack before being copied to their
actual destination. Moving the dereferencing to SSA had a side-effect
of eliminating this unnecessary temporary, and copying directly to the
destination parameter.

The test is updated to instead call "g(h(), h())" where h() returns a
large value, as the first result will always need to be spilled
somewhere will calling the second function. Maybe eventually we're
smart enough to realize it can be spilled to the heap, but we don't do
that today.

Because I'm concerned that the direct copy-to-parameter optimization
could interfere with race-detector instrumentation (e.g., maybe the
copies were previously necessary to ensure they're not clobbered by
inserted raceread calls?), I've also added issue20780b.go to exercise
this in a few different ways.

Change-Id: I720598cb32b17518bc10a03e555620c0f25fd28d
Reviewed-on: https://go-review.googlesource.com/c/go/+/281293
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-10 08:01:49 +00:00
Robert Griesemer
6ee9b118a2 [dev.regabi] cmd/compile: remove fmt_test code; it has outlived its usefulness
With the recent compiler rewrites and cleanups to gc/fmt.go, the
"safety net" provided by fmt_test has become less important and
the test itself has become a burden (often breaks because of small
format changes elsewhere).

Eventually, the syntax and types2 packages will provide most error
and diagnostic compiler output at which point fmt.go can be further
simplified as well.

Change-Id: Ie93eefd3e1166f3548fed0199b732dbd6c81948a
Reviewed-on: https://go-review.googlesource.com/c/go/+/282560
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Austin Clements <austin@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-01-08 22:40:47 +00:00
Baokun Lee
b241938e04 [dev.regabi] cmd/compile: fix some methods error text
Change-Id: Ie9b034efba30d66a869c5e991b60c76198fd330f
Reviewed-on: https://go-review.googlesource.com/c/go/+/279444
Run-TryBot: Baokun Lee <bk@golangcn.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-08 11:05:29 +00:00
Matthew Dempsky
cb05a0aa6a [dev.regabi] cmd/compile: remove toolstash scaffolding
Now that CaptureVars is gone, we can remove the extra code in escape
analysis that only served to appease toolstash -cmp.

Change-Id: I8c811834f3d966e76702e2d362e3de414c94bea6
Reviewed-on: https://go-review.googlesource.com/c/go/+/281544
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-05 21:44:30 +00:00
Matthew Dempsky
9821838832 [dev.regabi] cmd/compile: remove CaptureVars
Capture analysis is now part of escape analysis.

Passes toolstash -cmp.

Change-Id: Ifcd3ecc342074c590e0db1ff0646dfa1ea2ff57b
Reviewed-on: https://go-review.googlesource.com/c/go/+/281543
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-05 21:12:11 +00:00
Matthew Dempsky
fd43831f44 [dev.regabi] cmd/compile: reimplement capture analysis
Currently we rely on the type-checker to do some basic data-flow
analysis to help decide whether function literals should capture
variables by value or reference. However, this analysis isn't done by
go/types, and escape analysis already has a better framework for doing
this more precisely.

This CL extends escape analysis to recalculate the same "byval" as
CaptureVars and check that it matches. A future CL will remove
CaptureVars in favor of escape analysis's calculation.

Notably, escape analysis happens after deadcode removes obviously
unreachable code, so it sees the AST without any unreachable
assignments. (Also without unreachable addrtakens, but
ComputeAddrtaken already happens after deadcode too.) There are two
test cases where a variable is only reassigned on certain CPUs. This
CL changes them to reassign the variables unconditionally (as no-op
reassignments that avoid triggering cmd/vet's self-assignment check),
at least until we remove CaptureVars.

Passes toolstash -cmp.

Change-Id: I7162619739fedaf861b478fb8d506f96a6ac21f3
Reviewed-on: https://go-review.googlesource.com/c/go/+/281535
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-05 21:11:38 +00:00
Matthew Dempsky
fb69c67cad [dev.regabi] test: enable finalizer tests on !amd64
The gc implementation has had precise GC for a while now, so we can
enable these tests more broadly.

Confirmed that they still fail with gccgo 10.2.1.

Change-Id: Ic1c0394ab832024a99e34163c422941a3706e1a2
Reviewed-on: https://go-review.googlesource.com/c/go/+/281542
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-05 21:11:31 +00:00
Matthew Dempsky
81f4f0e912 [dev.regabi] cmd/compile: remove race-y check in Name.Canonical
The backend doesn't synchronize compilation of functions with their
enclosed function literals, so it's not safe to double-check that
IsClosureVar isn't set on the underlying Name. Plenty of frontend
stuff would blow-up if this was wrong anyway, so it should be fine to
omit.

Change-Id: I3e97b64051fe56d97bf316c9b5dcce61f2082428
Reviewed-on: https://go-review.googlesource.com/c/go/+/281812
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-01-05 20:12:02 +00:00
Baokun Lee
4a9d9adea4 [dev.regabi] cmd/compile: remove initname function
Passes toolstash -cmp.

Change-Id: I84b99d6e636c7b867780389ad11dafc70d3628cd
Reviewed-on: https://go-review.googlesource.com/c/go/+/281313
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-01-05 18:46:39 +00:00
Matthew Dempsky
77365c5ed7 [dev.regabi] cmd/compile: add Name.Canonical and move Byval
There's a bunch of code that wants to map closure variables back to
their original name, so add a single Name.Canonical method that they
can all use.

Also, move the Byval flag from being stored on individual closure
variables to being stored on the canonical variable.

Passes toolstash -cmp.

Change-Id: Ia3ef81af5a15783d09f04b4e274ce33df94518e6
Reviewed-on: https://go-review.googlesource.com/c/go/+/281541
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-05 15:42:33 +00:00
Matthew Dempsky
e09783cbc0 [dev.regabi] cmd/compile: make ir.StaticValue safer
ir.StaticValue currently relies on CaptureVars setting Addrtaken for
variables that are assigned within nested function literals. We want
to move that logic to escape analysis, but ir.StaticValue is used in
inlining and devirtualization, which happen before escape
analysis.

The long-term solution here is to generalize escape analysis's precise
reassignment tracking for use by other optimization passes, but for
now we just generalize ir.StaticValue to not depend on Addrtaken
anymore. Instead, it now also pays attention to OADDR nodes as well as
recurses into OCLOSURE bodies.

Passes toolstash -cmp.

Change-Id: I6114e3277fb70b235f4423d2983d0433c881f79f
Reviewed-on: https://go-review.googlesource.com/c/go/+/281540
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-05 14:04:03 +00:00
Matthew Dempsky
9aa950c407 [dev.regabi] cmd/compile: make ir.OuterValue safer
For OINDEX expressions, ir.OuterValue depends on knowing the indexee's
type. Rather than silently acting as though it's not an array, make it
loudly fail.

The only code that needs to be fixed to support this is checkassign
during typechecking, which needs to avoid calling ir.OuterValue now if
typechecking the assigned operand already failed.

Passes toolstash -cmp.

Change-Id: I935cae0dacc837202bc6b63164dc2f0a6fde005c
Reviewed-on: https://go-review.googlesource.com/c/go/+/281539
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-05 14:03:51 +00:00
Matthew Dempsky
eb626409d1 [dev.regabi] cmd/compile: simplify CaptureVars
CaptureVars is responsible for deciding whether free variables should
be captured by value or by reference, but currently it also makes up
for some of the short-comings of the frontend symbol resolution /
type-checking algorithms. These are really separate responsibilities,
so move the latter into type-checking where it fits better.

Passes toolstash -cmp.

Change-Id: Iffbd53e83846a9ca9dfb54b597450b8543252850
Reviewed-on: https://go-review.googlesource.com/c/go/+/281534
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-05 12:31:18 +00:00
Matthew Dempsky
c28ca67a96 [dev.regabi] cmd/compile: fix ir.Dump for []*CaseClause, etc
Dump uses reflection to print IR nodes, and it only knew how to print
out the Nodes slice type itself. This CL adds support for printing any
slice whose element type implements Node, such as SwitchStmt and
SelectStmt's clause lists.

Change-Id: I2fd8defe11868b564d1d389ea3cd9b8abcefac62
Reviewed-on: https://go-review.googlesource.com/c/go/+/281537
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-05 07:36:40 +00:00
Matthew Dempsky
f24e40c14a [dev.regabi] cmd/compile: remove Name.Class_ accessors
These aren't part of the Node interface anymore, so no need to keep
them around.

Passes toolstash -cmp.

[git-generate]
cd src/cmd/compile/internal/ir

: Fix one off case that causes trouble for rf.
sed -i -e 's/n.SetClass(ir.PAUTO)/n.Class_ = ir.PAUTO/' ../ssa/export_test.go

pkgs=$(go list . ../...)
rf '
	ex '"$(echo $pkgs)"' {
		var n *Name
		var c Class
		n.Class() -> n.Class_
		n.SetClass(c) -> n.Class_ = c
	}

	rm Name.Class
	rm Name.SetClass
	mv Name.Class_ Name.Class
'

Change-Id: Ifb304bf4691a8c455456aabd8aa77178d4a49500
Reviewed-on: https://go-review.googlesource.com/c/go/+/281294
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-04 10:30:09 +00:00
Matthew Dempsky
d89705e087 [dev.regabi] cmd/compile: fix re-export of parameters
When exporting signature types, we include the originating package,
because it's exposed via go/types's API. And as a consistency check,
we ensure that the parameter names came from that same package.

However, we were getting this wrong in the case of exported variables
that were initialized with a method value using an imported method. In
this case, when we created the method value wrapper function's
type (which is reused as the variable's type if none is explicitly
provided in the variable declaration), we were reusing the
original (i.e., imported) parameter names, but the newly created
signature type was associated with the current package instead.

The correct fix here is really to preserve the original signature
type's package (along with position and name for its parameters), but
that's awkward to do at the moment because the DeclFunc API requires
an ir representation of the function signature, whereas we only
provide a way to explicitly set packages via the type constructor
APIs.

As an interim fix, we associate the parameters with the current
package, to be consistent with the signature type's package.

Fixes #43479.

Change-Id: Id45a10f8cf64165c9bc7d9598f0a0ee199a5e752
Reviewed-on: https://go-review.googlesource.com/c/go/+/281292
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-04 10:30:08 +00:00
Matthew Dempsky
290b4154b7 [dev.regabi] cmd/compile: fix ICE due to large uint64 constants
It's an error to call Int64Val on constants that don't fit into
int64. CL 272654 made the compiler stricter about detecting misuse,
and revealed that we were using it improperly in detecting consecutive
integer-switch cases. That particular usage actually did work in
practice, but it's easy and best to just fix it.

Fixes #43480.

Change-Id: I56f722d75e83091638ac43b80e45df0b0ad7d48d
Reviewed-on: https://go-review.googlesource.com/c/go/+/281272
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-04 10:30:07 +00:00
Cuong Manh Le
a30fd52884 [dev.regabi] cmd/compile: use ir.NewNameAt in SubstArgTypes
So we can remove Name.CloneName now.

Passes toolstash -cmp.

Change-Id: I63e57ba52a7031e06fe9c4ee9aee7de6dec70792
Reviewed-on: https://go-review.googlesource.com/c/go/+/281312
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-01-04 08:38:01 +00:00
Matthew Dempsky
8fc44cf0fa [dev.regabi] cmd/compile: remove a couple CloneName calls
In inl.go, that code path is unused, since we added ir.BasicLit to
represent unnamed OLITERALs.

In race.go, rather than cloning ir.RegFP, we can just create it from
scratch again.

Passes toolstash -cmp (incl. w/ -race).

Change-Id: I8e063e4898d2acf056ceca5bc03df6b40a14eca9
Reviewed-on: https://go-review.googlesource.com/c/go/+/281192
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-03 19:48:20 +00:00
Matthew Dempsky
907a4bfdc7 [dev.regabi] cmd/compile: fix map assignment order
After the previous cleanup/optimization CLs, ascompatee now correctly
handles map assignments too. So remove the code from order.mapAssign,
which causes us to assign to the map at the wrong point during
execution. It's not every day you get to fix an issue by only removing
code.

Thanks to Cuong Manh Le for test cases and continually following up on
this issue.

Passes toolstash -cmp. (Apparently the standard library never uses
tricky map assignments. Go figure.)

Fixes #23017.

Change-Id: Ie0728103d59d884d00c1c050251290a2a46150f9
Reviewed-on: https://go-review.googlesource.com/c/go/+/281172
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-03 19:48:18 +00:00
Matthew Dempsky
f2e6dab048 [dev.regabi] cmd/compile: remove walkReturn "common case" path
After the previous two optimization CLs, this code path now generates
the same code as ascompatee does anyway. So just use that and remove
some redundant code.

Passes toolstash -cmp.

Change-Id: I5e2e5c6dbea64d8e91abe0f2cf51aa5bb86576d2
Reviewed-on: https://go-review.googlesource.com/c/go/+/281154
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-03 19:48:17 +00:00
Matthew Dempsky
d36a6bf44d [dev.regabi] cmd/compile: improve walkReturn common case
Instead of evaluating all result expressions up front and then
assigning them to their result destinations, we can interleave
evaluation with assignment. This reduces how much temporary
stack/register space is needed to hold the values in flight.

Doesn't pass toolstash -cmp, because it allows better return statement
code to be generated. E.g., cmd/go's text segment on linux/ppc64le
shrinks another 1kB.

Change-Id: I3fe889342c80e947e0118704ec01f1682c577e6e
Reviewed-on: https://go-review.googlesource.com/c/go/+/281153
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-03 19:48:15 +00:00
Matthew Dempsky
a317067d65 [dev.regabi] cmd/compile: improve ascompatee
order.go has already ordered function calls, so ascompatee only needs
to worry about expressions that might access a variable after it's
already been re-assigned. It already handles this, so the safeExpr
calls simply result in unnecessarily pessimistic code.

Does not pass toolstash -cmp, because it allows more efficient code
generation. E.g., cmd/go on linux/ppc64le is about 2kB smaller.

Change-Id: Idde0588eabe7850fa13c4e281fc46bbeffb4f68c
Reviewed-on: https://go-review.googlesource.com/c/go/+/281152
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-03 19:20:20 +00:00
Matthew Dempsky
5d80a590a2 [dev.regabi] cmd/compile: simplify walkReturn
Just de-duplicating some logic and adding better comments.

Passes toolstash -cmp.

Change-Id: I15ec07070510692c6d4367880bc3d2d9847370ab
Reviewed-on: https://go-review.googlesource.com/c/go/+/281132
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-03 19:20:13 +00:00
Matthew Dempsky
bb1b6c95c2 [dev.regabi] cmd/compile: remove Node.{,Set}Walkdef
After the previous commit, we no longer access Walkdef on anything but
ir.Names, so we can remove them from the Node interface and miniNode.

The flag bits storage should also move from miniNode.bits to
Name.flags, but the latter is already full at the moment. Leaving as a
TODO for now.

Passes toolstash -cmp.

Change-Id: I2427e4cf7bc68dc1d1529f40fb93dd9f7a9149f6
Reviewed-on: https://go-review.googlesource.com/c/go/+/281005
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-03 06:16:36 +00:00
Matthew Dempsky
57c426c9a5 [dev.regabi] cmd/compile: tighten typecheckdef to *ir.Name
We only actually care about ir.Names in typecheckdef, so don't bother
calling it on anything else. Allows us to get rid of some more
superfluous .Name() calls and .(*ir.Name) assertions.

Passes toolstash -cmp.

Change-Id: I78c7cb680178991ea185958b47a36f101d4d5ef7
Reviewed-on: https://go-review.googlesource.com/c/go/+/281004
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-03 06:16:35 +00:00
Matthew Dempsky
b1747756e3 [dev.regabi] cmd/compile: reorganize escape analysis somewhat
To do closure conversion during escape analysis, we need to walk the
AST in order. So this CL makes a few changes:

1. Function literals are walked where they appear in their enclosing
function, rather than as independent functions.

2. Walking "range" and "switch" statements is reordered to visit the
X/Tag expression up front, before the body.

3. Most assignments are refactored to use a new assignList helper,
which handles 1:1, 2:1, and N:N assignments. N:1 function call
assignments are still handled directly by the OAS2FUNC case.

4. A latent missed-optimization in escape.addr is fixed: the
ONAMEOFFSET case was failing to update k with the result of calling
e.addr(n.Name_). In partice, this probably wasn't an issue because
ONAMEOFFSET is likely only used for PEXTERN variables (which are
treated as heap memory anyway) or code generated by walk (which has
already gone through escape analysis).

5. Finally, don't replace k with discardHole at the end of
escape.addr. This is already handled at the start of escape.expr, and
we'll want to be able to access the hole's location after escape.expr
returns.

Passes toolstash -cmp.

Change-Id: I2325234346b12b10056a360c489692bab8fdbd93
Reviewed-on: https://go-review.googlesource.com/c/go/+/281003
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-03 06:16:32 +00:00
Matthew Dempsky
f2538033c0 [dev.regabi] cmd/compile: remove Nodes.Set [generated]
Just "=". It's cleaner.

Passes toolstash -cmp.

[git-generate]
cd src/cmd/compile/internal/ir
pkgs=$(go list . ../...)
rf '
	ex '"$(echo $pkgs)"' {
		var l Nodes
		var p *Nodes

		p.Set(l) -> *p = l
	}

	ex '"$(echo $pkgs)"' {
		var n InitNode
		var l Nodes

		*n.PtrInit() = l -> n.SetInit(l)
	}

	rm Nodes.Set
'

Change-Id: Ic97219792243667146a02776553942ae1189ff7d
Reviewed-on: https://go-review.googlesource.com/c/go/+/281002
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-02 10:57:25 +00:00
Matthew Dempsky
2f2d4b4e68 [dev.regabi] cmd/compile: remove {Ptr,Set}Init from Node interface
This CL separates out PtrInit and SetInit into a new InitNode
extension interface, and adds a new TakeInit helper function for
taking and clearing the Init list (if any) from a Node.

This allows removing miniNode.SetInit and miniNode.PtrInit, which in
turn allow getting rid of immutableEmptyNodes, and will allow
simplification of the Nodes API.

It would be nice to get rid of the default Init method too, but
there's way more code that expects to be able to call that at the
moment, so that'll have to wait.

Passes toolstash -cmp.

Change-Id: Ia8c18fab9555b774376f7f43eeecfde4f07b5946
Reviewed-on: https://go-review.googlesource.com/c/go/+/281001
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-02 10:57:23 +00:00
Cuong Manh Le
1544a03198 [dev.regabi] cmd/compile: refactor redundant type conversion [generated]
Passes toolstash -cmp.

[git-generate]

cd src/cmd/compile
rf '
    ex . '"$(printf '%s\n' ./internal/* | paste -sd' ')"' {
        type T interface{}
        var t T
        strict t
        t.(T) -> t
    }
'
cd internal/ir
go generate

Change-Id: I492d50390e724a7216c3cd8b49d4aaf7d0c335da
Reviewed-on: https://go-review.googlesource.com/c/go/+/280716
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-01-01 18:25:46 +00:00
Cuong Manh Le
7958a23ea3 [dev.regabi] cmd/compile: use *ir.Name where possible in inl.go
Passes toolstash -cmp.

Change-Id: Ic99a5189ad0fca37bccb0e4b4d13793adc4f8fd8
Reviewed-on: https://go-review.googlesource.com/c/go/+/280715
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-01-01 18:25:00 +00:00
Matthew Dempsky
bfa97ba48f [dev.regabi] test: add another closure test case
When deciding whether a captured variable can be passed by value, the
compiler is sensitive to the order that the OCLOSURE node is
typechecked relative to the order that the variable is passed to
"checkassign". Today, for an assignment like:

    q, g = 2, func() int { return q }

we get this right because we always typecheck the full RHS expression
list before calling checkassign on any LHS expression.

But I nearly made a change that would interleave this ordering,
causing us to call checkassign on q before typechecking the function
literal. And alarmingly, there weren't any tests that caught this.

So this commit adds one.

Change-Id: I66cacd61066c7a229070861a7d973bcc434904cc
Reviewed-on: https://go-review.googlesource.com/c/go/+/280998
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-01 13:44:37 +00:00
Matthew Dempsky
67ad695416 [dev.regabi] cmd/compile: split escape analysis state
In a future CL, I plan to change escape analysis to walk function
literal bodies at the point they appear within the AST, rather than
separately as their own standalone function declaration. This means
escape analysis's AST-walking code will become reentrant.

To make this easier to get right, this CL splits escape analysis's
state into two separate types: one that holds all of the state shared
across the entire batch, and another that holds only the state that's
used within initFunc and walkFunc.

Incidentally, this CL reveals that a bunch of logopt code was using
e.curfn outside of the AST-walking code paths where it's actually set,
so it was always nil. That code is in need of refactoring anyway, so
I'll come back and figure out the correct values to pass later when I
address that.

Passes toolstash -cmp.

Change-Id: I1d13f47d06f7583401afa1b53fcc5ee2adaea6c8
Reviewed-on: https://go-review.googlesource.com/c/go/+/280997
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-01 13:44:35 +00:00
Matthew Dempsky
fad9a8b528 [dev.regabi] cmd/compile: simplify inlining of closures
Closures have their own ONAMEs for captured variables, which their
function bodies refer to. So during inlining, we need to account for
this and ensure the references still work.

The previous inlining handled this by actually declaring the variables
and then either copying the original value or creating a pointer to
them, as appropriate for variables captured by value or by reference.

But this is needlessly complicated. When inlining the function body,
we need to rewrite all variable references anyway. We can just detect
closure variables and change them to directly point to the enclosing
function's version of this variable. No need for copying or further
indirection.

Does not pass toolstash -cmp. Presumably because we're able to
generate better code in some circumstances.

Change-Id: I8f0ccf7b098f39b8cd33f3bcefb875c8132d2c62
Reviewed-on: https://go-review.googlesource.com/c/go/+/280996
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-01-01 11:30:21 +00:00
Matthew Dempsky
7d55669847 [dev.regabi] cmd/compile: simplify dwarfgen.declPos
The previous code was way overcomplicating things. To find out if a
variable is a closure pseudo-variable, one only needs to check
IsClosureVar. Checking Captured and Byval are only meant to be used by
closure conversion.

Passes toolstash -cmp.

Change-Id: I22622cba36ba7f60b3275d17999a8b6bb7c6719a
Reviewed-on: https://go-review.googlesource.com/c/go/+/280995
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-01-01 11:30:20 +00:00
Matthew Dempsky
9ed1577779 [dev.regabi] cmd/compile: remove Func.ClosureEnter
We can easily compute this on demand.

Passes toolstash -cmp.

Change-Id: I433d8adb2b1615ae05b2764e69904369a59542c5
Reviewed-on: https://go-review.googlesource.com/c/go/+/280994
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-01-01 11:30:18 +00:00
Matthew Dempsky
ece345aa69 [dev.regabi] cmd/compile: expand documentation for Func.Closure{Vars,Enter}
I keep getting these confused and having to look at how the code
actually uses them.

Change-Id: I86baf22b76e7dddada6830df0fac241092f716bf
Reviewed-on: https://go-review.googlesource.com/c/go/+/280993
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-01-01 11:30:16 +00:00
Matthew Dempsky
6ddbc75efd [dev.regabi] cmd/compile: earlier deadcode removal
This CL moves the general deadcode-removal pass to before computing
Addrtaken, which allows variables to still be converted to SSA if
their address is only taken in unreachable code paths (e.g., the "&mp"
expression in the "if false" block in runtime/os_linux.go:newosproc).

This doesn't pass toolstash -cmp, because it allows SSA to better
optimize some code.

Change-Id: I43e54acc02fdcbad8eb6493283f355aa1ee0de84
Reviewed-on: https://go-review.googlesource.com/c/go/+/280992
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-01 10:52:39 +00:00
Matthew Dempsky
68e6fa4f68 [dev.regabi] cmd/compile: fix package-initialization order
This CL fixes package initialization order by creating the init task
before the general deadcode-removal pass.

It also changes noder to emit zero-initialization assignments (i.e.,
OAS with nil RHS) for package-block variables, so that initOrder can
tell the variables still need initialization. To allow this, we need
to also extend the static-init code to recognize zero-initialization
assignments.

This doesn't pass toolstash -cmp, because it reorders some package
initialization routines.

Fixes #43444.

Change-Id: I0da7996a62c85e15e97ce965298127e075390a7e
Reviewed-on: https://go-review.googlesource.com/c/go/+/280976
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-01 10:52:33 +00:00
Matthew Dempsky
3a4474cdfd [dev.regabi] cmd/compile: some more manual shuffling
More minor reshuffling of passes.

Passes toolstash -cmp.

Change-Id: I22633b3741f668fc5ee8579d7d610035ed57df1f
Reviewed-on: https://go-review.googlesource.com/c/go/+/280975
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-01 10:52:27 +00:00
Matthew Dempsky
0f1d2129c4 [dev.regabi] cmd/compile: reshuffle type-checking code [generated]
This commit splits up typecheck.Package and moves the code
elsewhere. The type-checking code is moved into noder, so that it can
eventually be interleaved with the noding process. The
non-type-checking code is moved back into package gc, so that it can
be incorporated into appropriate compiler backend phases.

While here, deadcode removal is moved into its own package.

Passes toolstash -cmp.

[git-generate]
cd src/cmd/compile/internal/typecheck

: Split into two functions.
sed -i -e '/Phase 6/i}\n\nfunc postTypecheck() {' typecheck.go

rf '
	# Export needed identifiers.
	mv deadcode Deadcode
	mv loadsys InitRuntime
	mv declareUniverse DeclareUniverse
	mv dirtyAddrtaken DirtyAddrtaken
	mv computeAddrtaken ComputeAddrtaken
	mv incrementalAddrtaken IncrementalAddrtaken

	# Move into new package.
	mv Deadcode deadcodeslice deadcodeexpr deadcode.go
	mv deadcode.go cmd/compile/internal/deadcode

	# Move top-level type-checking code into noder.
	# Move DeclVars there too, now that nothing else uses it.
	mv DeclVars Package noder.go
	mv noder.go cmd/compile/internal/noder

	# Move non-type-checking code back into gc.
	mv postTypecheck main.go
	mv main.go cmd/compile/internal/gc
'

cd ../deadcode
rf '
	# Destutter names.
	mv Deadcode Func
	mv deadcodeslice stmts
	mv deadcodeexpr expr
'

cd ../noder
rf '
	# Move functions up, next to their related code.
	mv noder.go:/func Package/-1,$ \
		noder.go:/makeSrcPosBase translates/-1
	mv noder.go:/func DeclVars/-3,$ \
		noder.go:/constState tracks/-1
'

cd ../gc
rf '
	# Inline postTypecheck code back into gc.Main.
	mv main.go:/func postTypecheck/+0,/AllImportedBodies/+1 \
		main.go:/Build init task/-1
	rm postTypecheck
'

Change-Id: Ie5e992ece4a42204cce6aa98dd6eb52112d098c8
Reviewed-on: https://go-review.googlesource.com/c/go/+/280974
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-01 10:52:19 +00:00
Matthew Dempsky
b8fd3440cd [dev.regabi] cmd/compile: report unused variables during typecheck
Unused variables are a type-checking error, so they should be reported
during typecheck rather than walk.

One catch is that we only want to report unused-variable errors for
functions that type check successfully, but some errors are reported
during noding, so we don't have an easy way to detect that
currently. As an approximate solution, we simply check if we've
reported any errors yet.

Passes toolstash -cmp.

Change-Id: I9400bfc94312c71d0c908a491e85c16d62224c9c
Reviewed-on: https://go-review.googlesource.com/c/go/+/280973
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-01-01 10:52:10 +00:00
Matthew Dempsky
fd22df9905 [dev.regabi] cmd/compile: remove idempotent Name() calls [generated]
[git-generate]
cd src/cmd/compile/internal/ir
pkgs=$(grep -l -w Name ../*/*.go | xargs dirname | sort -u | grep -v '/ir$')
rf '
	ex . '"$(echo $pkgs)"' {
		var n *Name
		n.Name() -> n
	}
'

Change-Id: I6bfce6417a6dba833d2f652ae212a32c11bc5ef6
Reviewed-on: https://go-review.googlesource.com/c/go/+/280972
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
2021-01-01 03:02:29 +00:00
Cuong Manh Le
dfbcff80c6 [dev.regabi] cmd/compile: make copyExpr return *ir.Name directly
copyExpr just calls copyExpr1 with "clear" is false, so make it return
*ir.Name directly instead of ir.Node

Passes toolstash -cmp.

Change-Id: I31ca1d88d9eaf8ac37517022f1c74285ffce07d3
Reviewed-on: https://go-review.googlesource.com/c/go/+/280714
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-31 10:55:44 +00:00
Cuong Manh Le
77fd81a3e6 [dev.regabi] cmd/compile: use names for keep alive variables in function call
Back to pre Russquake, Node.Nbody of OCALL* node is used to attach
variables which must be kept alive during that call.

Now after Russquake, we have CallExpr to represent a function call,
so use a dedicated field for those variables instead.

Passes toolstash -cmp.

Change-Id: I4f40ebefcc7c41cdcc4e29c7a6d8496a083b68f4
Reviewed-on: https://go-review.googlesource.com/c/go/+/280733
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-31 09:43:13 +00:00
Cuong Manh Le
8fe1197654 [dev.regabi] cmd/compile: remove Name.orig
Passes toolstash -cmp.

Change-Id: Ie563ece7e4da14af46adc660b3d39757eb47c067
Reviewed-on: https://go-review.googlesource.com/c/go/+/280734
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-31 09:27:56 +00:00
Matthew Dempsky
477b049060 [dev.regabi] cmd/compile: fix printing of method expressions
OTYPE and OMETHEXPR were missing from OpPrec. So add them with the
same precedences as OT{ARRAY,MAP,STRUCT,etc} and
ODOT{,METH,INTER,etc}, respectively. However, ODEREF (which is also
used for pointer types *T) has a lower precedence than other types, so
pointer types need to be specially handled to assign them their
correct, lower precedence.

Incidentally, this also improves the error messages in issue15055.go,
where we were adding unnecessary parentheses around the types in
conversion expressions.

Thanks to Cuong Manh Le for writing the test cases for #43428.

Fixes #43428.

Change-Id: I57e7979babe3ed9ef8a8b5a2a3745e3737dd785f
Reviewed-on: https://go-review.googlesource.com/c/go/+/280873
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-31 05:52:09 +00:00
Keith Randall
178c667db2 [dev.regabi] cmd/compile: fix OSLICEARR comments
Change-Id: Ia6e734977a2cd80c91c28f4525be403f062dccc6
Reviewed-on: https://go-review.googlesource.com/c/go/+/280651
Trust: Keith Randall <khr@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-30 16:37:51 +00:00
Keith Randall
f0d99def5b [dev.regabi] cmd/compile: add newline to ir.Dump
If you do two ir.Dumps in a row, there's no newline between them.

Change-Id: I1a80dd22da68cb677eb9abd7a50571ea33584010
Reviewed-on: https://go-review.googlesource.com/c/go/+/280672
Trust: Keith Randall <khr@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-30 16:37:09 +00:00
Matthew Dempsky
451693af71 [dev.regabi] cmd/compile: simplify typecheckdef
Reorganize code to be a little clearer. Also allows tightening
typecheckdefstack from []ir.Node to []*ir.Name.

Passes toolstash -cmp.

Change-Id: I43df1a5e2a72dd3423b132d3afe363bf76700269
Reviewed-on: https://go-review.googlesource.com/c/go/+/280649
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-30 05:00:09 +00:00
Matthew Dempsky
0c1a899a6c [dev.regabi] cmd/compile: fix defined-pointer method call check
The compiler has logic to check whether we implicitly dereferenced a
defined pointer while trying to select a method. However, rather than
checking whether there were any implicit dereferences of a defined
pointer, it was finding the innermost dereference/selector expression
and checking whether that was dereferencing a named pointer. Moreover,
it was only checking defined pointer declared in the package block.

This CL restructures the code to match go/types and gccgo's behavior.

Fixes #43384.

Change-Id: I7bddfe2515776d9480eb2c7286023d4c15423888
Reviewed-on: https://go-review.googlesource.com/c/go/+/280392
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Robert Griesemer <gri@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-12-30 04:38:20 +00:00
Matthew Dempsky
f9b67f76a5 [dev.regabi] cmd/compile: change ir.DoChildren to use bool result type
After using the IR visitor code for a bit, it seems clear that a
simple boolean result type is adequate for tree traversals. This CL
updates ir.DoChildren to use the same calling convention as ir.Any,
and updates mknode.go to generate code accordingly.

There were only two places where the error-based DoChildren API was
used within the compiler:

1. Within typechecking, marking statements that contain "break". This
code never returns errors anyway, so it's trivially updated to return
false instead.

2. Within inlining, the "hairy visitor" actually does make use of
returning errors. However, it threads through a reference to the
hairyVisitor anyway, where it would be trivial to store any needed
information instead. For the purpose of this CL, we provide
"errChildren" and "errList" helper functions that provide the previous
error-based semantics on top of the new bool-based API.

Passes toolstash -cmp.

Change-Id: I4bac9a697b4dbfb5f66eeac37d4a2ced2073d7d0
Reviewed-on: https://go-review.googlesource.com/c/go/+/280675
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
2020-12-30 04:24:02 +00:00
Matthew Dempsky
499851bac8 [dev.regabi] cmd/compile: generalize ir/mknode.go
This CL generalizes ir/mknode.go to get rid of most of almost all of
its special cases for node field types. The only remaining speciale
case now is Field, which doesn't implement Node any more, but perhaps
should.

To help with removing special cases, node fields can now be tagged
with `mknode:"-"` so that mknode ignores them when generating its
helper methods. Further, to simplify skipping all of the orig fields,
a new origNode helper type is added which declares an orig field
marked as `mknode:"-"` and also provides the Orig and SetOrig methods
needed to implement the OrigNode interface.

Passes toolstash -cmp.

Change-Id: Ic68d4f0a9d2ef6e57e9fe87cdc641e5c4859830b
Reviewed-on: https://go-review.googlesource.com/c/go/+/280674
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-30 03:38:02 +00:00
Cuong Manh Le
82ab3d1448 [dev.regabi] cmd/compile: use *ir.Name for Decl.X
Passes toolstash -cmp.

Change-Id: I505577d067eda3512f6d78618fc0eff061a71e3c
Reviewed-on: https://go-review.googlesource.com/c/go/+/280732
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-30 00:48:19 +00:00
Cuong Manh Le
9958b7ed3e [dev.regabi] cmd/compile: unexport ir.FmtNode
It's only used inside package ir now.

[git-generate]

cd src/cmd/compile/internal/ir
rf 'mv FmtNode fmtNode'
sed -i 's/FmtNode/fmtNode/g' mknode.go
go generate

Change-Id: Ib8f6c6984905a4d4cfca1b23972a39c5ea30ff42
Reviewed-on: https://go-review.googlesource.com/c/go/+/279451
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-30 00:48:01 +00:00
Cuong Manh Le
f5816624cd [dev.regabi] cmd/compile: change AddrExpr.Alloc to AddrExpr.Prealloc
For being consistent with other Prealloc fields.

[git-generate]

cd src/cmd/compile/internal/ir
rf '
  mv AddrExpr.Alloc AddrExpr.Prealloc
'
go generate

Change-Id: Id1b05119092036e3f8208b73b63bd0ca6ceb7b15
Reviewed-on: https://go-review.googlesource.com/c/go/+/279450
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
2020-12-29 19:37:00 +00:00
Cuong Manh Le
850aa7c60c [dev.regabi] cmd/compile: use *ir.Name instead of ir.Node for CaseClause.Var
Passes toolstash -cmp.

Change-Id: Ib0b6ebf5751ffce2c9500dc67d78e54937ead208
Reviewed-on: https://go-review.googlesource.com/c/go/+/279449
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-29 19:21:18 +00:00
Cuong Manh Le
37babc97bb [dev.regabi] cmd/compile: allow visitor visits *ir.Name
So future CLs can refactor ir.Node to *ir.Name when possible.

Passes toolstash -cmp.

Change-Id: I91ae38417ba10de207ed84b65d1d69cf64f24456
Reviewed-on: https://go-review.googlesource.com/c/go/+/279448
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-29 19:21:07 +00:00
Cuong Manh Le
5cf3c87fa6 [dev.regabi] cmd/compile: generate case/comm clause functions in mknode.go
Passes toolstash -cmp.

Change-Id: I52e9d6f35f22d5d59ac6aad02011c5abaac45739
Reviewed-on: https://go-review.googlesource.com/c/go/+/279446
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-29 18:29:47 +00:00
Keith Randall
b3e1ec97fd [dev.regabi] cmd/compile: move new addrtaken bit back to the old name
Change-Id: I2732aefe95a21c23d73a907d5596fcb1626d6dd7
Reviewed-on: https://go-review.googlesource.com/c/go/+/275697
Trust: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dan Scales <danscales@google.com>
2020-12-29 18:05:07 +00:00
Keith Randall
0620c674dd [dev.regabi] cmd/compile: remove original addrtaken bit
Switch the source of truth to the new addrtaken bit. Remove the old one.

Change-Id: Ie53679ab14cfcd34b55e912e7ecb962a22db7db3
Reviewed-on: https://go-review.googlesource.com/c/go/+/275696
Trust: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dan Scales <danscales@google.com>
2020-12-29 18:04:37 +00:00
Keith Randall
0523d525ae [dev.regabi] cmd/compile: separate out address taken computation from typechecker
This CL computes a second parallel addrtaken bit that we check
against the old way of doing it. A subsequent CL will rip out the
typechecker code and just use the new way.

Change-Id: I62b7342c44f694144844695386f80088bbd40bf4
Reviewed-on: https://go-review.googlesource.com/c/go/+/275695
Trust: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dan Scales <danscales@google.com>
2020-12-29 18:04:10 +00:00
Matthew Dempsky
9ea272e5ec [dev.regabi] cmd/compile: simplify ir.Func somewhat
Two simplifications:

1. Statements (including ODCLFUNC) don't have types, and the
Func.Nname already has a type. There's no need for a second one.
However, there is a lot of code that expects to be able to call
Func.Type, so leave a forwarding method, like with Sym and Linksym.

2. Inline and remove ir.NewFuncNameAt. It doesn't really save any
code, and it's only used a handful of places.

Passes toolstash -cmp.

Change-Id: I51acaa341897dae0fcdf2fa576a10174a2ae4d1e
Reviewed-on: https://go-review.googlesource.com/c/go/+/280648
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-29 11:54:34 +00:00
Matthew Dempsky
e40cb4d4ae [dev.regabi] cmd/compile: remove more unused code
Change-Id: I60ac28e3ab376cb0dac23a9b4f481f8562ad8c56
Reviewed-on: https://go-review.googlesource.com/c/go/+/280647
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-29 11:54:25 +00:00
Matthew Dempsky
6f30c95048 [dev.regabi] cmd/compile: remove unneeded indirection
Thanks to package reorganizing, we can remove types.TypeLinkSym by
simply having its only callers use reflectdata.TypeLinksym directly.

Passes toolstash -cmp.

Change-Id: I5bc5dbb6bf0664af43ae5130cfe1f19bd23b2bfe
Reviewed-on: https://go-review.googlesource.com/c/go/+/280644
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-29 09:57:31 +00:00
Matthew Dempsky
171fc6f223 [dev.regabi] cmd/compile: remove workarounds for go/constant issues
These were fixed in CLs 273086 and 273126, which have been merged back
into dev.regabi already.

Passes toolstash -cmp.

Change-Id: I011e9ed7062bc034496a279e21cc163267bf83fd
Reviewed-on: https://go-review.googlesource.com/c/go/+/280643
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
2020-12-29 09:12:55 +00:00
Matthew Dempsky
33801cdc62 [dev.regabi] cmd/compile: use Ntype where possible
For nodes that are always a type expression, we can use Ntype instead
of Node.

Passes toolstash -cmp.

Change-Id: I28f9fa235015ab48d0da06b78b30c49d74c64e3a
Reviewed-on: https://go-review.googlesource.com/c/go/+/280642
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
2020-12-29 08:22:45 +00:00
Cuong Manh Le
82ad3083f8 [dev.regabi] cmd/compile: remove typ from AssignOpStmt
Previous detached logic of typechecking AssignOpStmt from tcArith, the
typ field of it is not used anymore.

Pass toolstash -cmp.

Change-Id: I407507a1c4c4f2958fca4d6899875564e54bf1f5
Reviewed-on: https://go-review.googlesource.com/c/go/+/279443
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-29 07:56:08 +00:00
Cuong Manh Le
e34c44a7c4 [dev.regabi] cmd/compile: refactoring typecheck arith
Currently, the tcArith logic is complicated and involes many
un-necessary checks for some ir.Op. This CL refactors how it works:

 - Add a new tcShiftOp function, which only does necessary works for
   typechecking OLSH/ORSH. That ends up moving OLSH/ORSH to a separated
   case in typecheck1.

 - Move OASOP to separated case, so its logic is detached from tcArith.

 - Move OANDAND/OOROR to separated case, which does some validation
   dedicated to logical operators only.

Passes toolstash -cmp.

Change-Id: I0db7b7c7a3e52d6f9e9d87eee6967871f1c32200
Reviewed-on: https://go-review.googlesource.com/c/go/+/279442
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-29 07:55:55 +00:00
Matthew Dempsky
a5ec920160 [dev.regabi] cmd/compile: more Linksym cleanup
This largely gets rid of the remaining direct Linksym calls, hopefully
enough to discourage people from following bad existing practice until
Sym.Linksym can be removed entirely.

Passes toolstash -cmp.

Change-Id: I5d8f8f703ace7256538fc79648891ede0d879dc2
Reviewed-on: https://go-review.googlesource.com/c/go/+/280641
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-29 07:55:00 +00:00
Matthew Dempsky
ec59b197d5 [dev.regabi] cmd/compile: rewrite to use linksym helpers [generated]
Passes toolstash -cmp.

[git-generate]
cd src/cmd/compile/internal/gc
pkgs=$(grep -l -w Linksym ../*/*.go | xargs dirname | grep -v '/gc$' | sort -u)
rf '
	ex . '"$(echo $pkgs)"' {
		import "cmd/compile/internal/ir"
		import "cmd/compile/internal/reflectdata"
		import "cmd/compile/internal/staticdata"
		import "cmd/compile/internal/types"

		avoid reflectdata.TypeLinksym
		avoid reflectdata.TypeLinksymLookup
		avoid reflectdata.TypeLinksymPrefix
		avoid staticdata.FuncLinksym

		var f *ir.Func
		var n *ir.Name
		var s string
		var t *types.Type

		f.Sym().Linksym() -> f.Linksym()
		n.Sym().Linksym() -> n.Linksym()

		reflectdata.TypeSym(t).Linksym() -> reflectdata.TypeLinksym(t)
		reflectdata.TypeSymPrefix(s, t).Linksym() -> reflectdata.TypeLinksymPrefix(s, t)
		staticdata.FuncSym(n.Sym()).Linksym() -> staticdata.FuncLinksym(n)
		types.TypeSymLookup(s).Linksym() -> reflectdata.TypeLinksymLookup(s)
	}
'

Change-Id: I7a3ae1dcd61bcdf4a29f708ff12f7f80c2b280c6
Reviewed-on: https://go-review.googlesource.com/c/go/+/280640
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-29 07:54:55 +00:00
Matthew Dempsky
25c613c02d [dev.regabi] cmd/compile: add Linksym helpers
Syms are meant to be just interned (pkg, name) tuples, and are a
purely abstract, Go-language concept. As such, associating them with
linker symbols (a low-level, implementation-oriented detail) is
inappropriate.

There's still work to be done before linker symbols can be directly
attached to their appropriate, higher-level objects instead. But in
the mean-time, we can at least add helper functions and discourage
folks from using Sym.Linksym directly. The next CL will mechanically
rewrite code to use these helpers where possible.

Passes toolstash -cmp.

Change-Id: I413bd1c80bce056304f9a7343526bd153f2b9c7d
Reviewed-on: https://go-review.googlesource.com/c/go/+/280639
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-29 07:54:40 +00:00
Matthew Dempsky
289da2b33e [dev.regabi] cmd/compile: move Node.Opt to Name
Escape analysis uses Node.Opt to map nodes to their "location", so
that other references to the same node use the same location
again. But in the current implementation of escape analysis, we never
need to refer back to a node's location except for named nodes (since
other nodes are anonymous, and have no way to be referenced).

This CL moves Opt from Node down to Name, turns it into a directly
accessed field, and cleans up escape analysis to avoid setting Opt on
non-named expressions.

One nit: in walkCheckPtrArithmetic, we were abusing Opt as a way to
detect/prevent loops. This CL adds a CheckPtr bit flag instead.

Passes toolstash -cmp.

Change-Id: If57d5ad8d972fa63bedbe69b9ebb6753e31aba85
Reviewed-on: https://go-review.googlesource.com/c/go/+/280638
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
2020-12-29 07:45:00 +00:00
Matthew Dempsky
6acbae4fcc [dev.regabi] cmd/compile: address some ir TODOs
Previously, ODOTTYPE/ODOTTYPE2 were forced to reuse some available
Node fields for storing pointers to runtime type descriptors. This
resulted in awkward field types for TypeAssertExpr and AddrExpr.

This CL gives TypeAssertExpr proper fields for the runtime type
descriptors, and also tightens the field types as
possible/appropriate.

Passes toolstash -cmp.

Change-Id: I521ee7a1462affc5459de33a0de6c68a7d6416ba
Reviewed-on: https://go-review.googlesource.com/c/go/+/280637
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dan Scales <danscales@google.com>
2020-12-29 02:49:00 +00:00
Matthew Dempsky
4629f6a51d [dev.regabi] cmd/compile: merge {Selector,CallPart,Method}Expr
These three expression nodes all represent the same syntax, and so
they're represented the same within types2. And also they're not
handled that meaningfully differently throughout the rest of the
compiler to merit unique representations.

Method expressions are somewhat unique today that they're very
frequently turned into plain function names. But eventually that can
be handled by a post-typecheck desugaring phase that reduces the
number of redundant AST forms.

Passes toolstash -cmp.

Change-Id: I20df91bbd0d885c1f18ec67feb61ae1558670719
Reviewed-on: https://go-review.googlesource.com/c/go/+/280636
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dan Scales <danscales@google.com>
2020-12-29 02:35:04 +00:00
Matthew Dempsky
e563715b30 [dev.regabi] cmd/compile: remove Sym.Importdef
Evidently it hasn't been needed since circa 2018, when we removed the
binary export data format.

Change-Id: I4e4c788d6b6233340fb0de0a56d035c31d96f761
Reviewed-on: https://go-review.googlesource.com/c/go/+/280634
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dan Scales <danscales@google.com>
2020-12-29 02:34:53 +00:00
Matthew Dempsky
3f370b75fb [dev.regabi] cmd/compile: cleanup //go:generate directives
During recent refactoring, we moved mkbuiltin.go to package typecheck,
but accidentally duplicated its //go:generate directive into a bunch
of other files/directories. This CL cleans up the unnecessary
duplicates.

Also, update all of the stringer invocations to use an explicit file
name, and regenerate their files.

Updates #43369.

Change-Id: I4e493c1fff103d742de0a839d7a3375659270b50
Reviewed-on: https://go-review.googlesource.com/c/go/+/280635
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Meng Zhuo <mzh@golangcn.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Meng Zhuo <mzh@golangcn.org>
2020-12-29 02:28:24 +00:00
Matthew Dempsky
07569dac4e [dev.regabi] all: merge master (1d78139) into dev.regabi
Merge List:

+ 2020-12-26 1d78139128 runtime/cgo: fix Android build with NDK 22
+ 2020-12-25 2018b68a65 net/mail: don't use MDT in test
+ 2020-12-23 b116404444 runtime: shift timeHistogram buckets and allow negative durations
+ 2020-12-23 8db7e2fecd runtime: fix allocs-by-size and frees-by-size buckets
+ 2020-12-23 fb96f07e1a runtime: fix nStackRoots comment about stack roots
+ 2020-12-23 d1502b3c72 lib/time, time/tzdata: update tzdata to 2020e
+ 2020-12-23 30c99cbb7a cmd/go: add the Retract field to 'go help mod edit' definition of the GoMod struct
+ 2020-12-23 49d0b239cb doc: fix a typo in contribute.html
+ 2020-12-23 98a73030b0 cmd/go: in 'go get', promote named implicit dependencies to explicit
+ 2020-12-23 fd6ba1c8a2 os/signal: fix a deadlock with syscall.AllThreadsSyscall() use
+ 2020-12-23 b0b0d98283 runtime: linux iscgo support for not blocking nptl signals
+ 2020-12-22 223331fc0c cmd/go/internal/modload: add hint for missing implicit dependency

Change-Id: I76d79f17c546cab03fab1facc36cc3f834d9d126
2020-12-28 00:12:16 -08:00
Matthew Dempsky
76136be027 [dev.regabi] cmd/compile: check for recursive import in ImportBody
After earlier importer refactorings, most of the importer is now
reentrant, so we don't need to guard against it at Resolve. The only
remaining part that is still not reentrant is inline body importing,
so move the recursive-import check there.

Passes toolstash -cmp.

Change-Id: Ia828f880a03e6125b102668c12a155d4c253d26b
Reviewed-on: https://go-review.googlesource.com/c/go/+/280515
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 08:09:20 +00:00
Matthew Dempsky
fda7ec3a3f [dev.regabi] cmd/compile: remove Name.IsDDD, etc
These are never used.

Change-Id: I58f7359f20252ca942f59bc7593c615a7b9de105
Reviewed-on: https://go-review.googlesource.com/c/go/+/280514
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 08:08:25 +00:00
Matthew Dempsky
098a6490b9 [dev.regabi] cmd/compile: remove Declare in makepartialcall
This is the only remaining late call to Declare. By changing it to use
Temp, we'll be able to move the legacy lexical scoping logic by moving
it to noder and iimport.

Passes toolstash -cmp.

Change-Id: Id7cf7a08e3138e50816f515fef3088785a10aaf4
Reviewed-on: https://go-review.googlesource.com/c/go/+/280513
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 08:07:51 +00:00
Matthew Dempsky
137f0d2e06 [dev.regabi] cmd/compile: remove unnecessary Name.Sym call
Since the introduction of ir.BasicLit, we no longer create Names
without Syms.

Passes toolstash -cmp.

Change-Id: I82de3fd65455e3756ff56e52febb512c0a2128f2
Reviewed-on: https://go-review.googlesource.com/c/go/+/280512
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 08:06:35 +00:00
Matthew Dempsky
3383b5c74a [dev.regabi] cmd/compile: flatten dependency graph [generated]
This CL shuffles a couple functions around to help flatten the package
dependency graph somewhat:

1. ssa.LosesStmtMark is only ever used in associated with an
objw.Prog, so we might as well move it to that package. This removes a
dependency from objw (a relatively low-level utility package that
wraps cmd/internal/obj) on ssa (a large and relatively high-level
package).

2. Moves liveness.SetTypeBits into a new package typebits. A
single-function package is a bit on the silly side, but reflectdata
shouldn't need to depend on liveness (nor vice versa).

[git-generate]
cd src/cmd/compile/internal/ssa
rf '
	mv LosesStmtMark prog.go
	mv prog.go cmd/compile/internal/objw
'

cd ../liveness
rf '
	mv SetTypeBits Set
	mv Set typebits.go
	rm typebits.go:/Copyright/+4,/^package/-0
	mv typebits.go cmd/compile/internal/typebits
'

Change-Id: Ic9a983f0ad6c0cf1a537f99889699a8444699e6e
Reviewed-on: https://go-review.googlesource.com/c/go/+/280447
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 08:06:26 +00:00
Matthew Dempsky
f8afb8216a [dev.regabi] cmd/compile: rename CommStmt and CaseStmt [generated]
Rename these two AST nodes to match their cmd/compile/internal/syntax
and go/ast counterparts.

Passes toolstash -cmp.

[git-generate]
cd src/cmd/compile/internal/ir
rf '
	mv CaseStmt CaseClause
	mv CommStmt CommClause
'
sed -E -i -e 's/(Case|Comm)Stmt/\1Clause/g' mknode.go

Change-Id: I19fba0323a5de1e71346622857011b2f7879bcef
Reviewed-on: https://go-review.googlesource.com/c/go/+/280446
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 08:03:43 +00:00
Matthew Dempsky
5f3bd59a0d [dev.regabi] cmd/compile: remove some unneeded code in package ir
The deepCopy functions haven't been needed since we switched to using
Edit everywhere, and AddStringExpr no longer has an Alloc field that
needs special casing.

Passes toolstash -cmp.

Change-Id: I5bcc8c73d5cb784f7e57fb3162ae6e288e6c9392
Reviewed-on: https://go-review.googlesource.com/c/go/+/280445
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 08:02:51 +00:00
Matthew Dempsky
3bdafb0d82 [dev.regabi] cmd/compile: remove CommStmt.List
Package syntax's parser already ensures that select communication
clauses only have one statement, so there's no need for ir's CommStmt
to need to represent more than one. Instead, noder can just directly
populate Comm in the first place.

Incidentally, this also revealed a latent issue in the inline-body
exporter: we were exporting List (where the case statement is before
type-checking), rather than Comm (where the case statement would be
after type-checking, when export happens).

Passes toolstash -cmp.

Change-Id: Ib4eb711527bed297c7332c79ed6e6562a1db2cfa
Reviewed-on: https://go-review.googlesource.com/c/go/+/280444
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 08:02:30 +00:00
Matthew Dempsky
2ecf52b841 [dev.regabi] cmd/compile: separate CommStmt from CaseStmt
Like go/ast and cmd/compile/internal/syntax before it, package ir now
has separate concrete representations for switch-case clauses and
select-communication clauses.

Passes toolstash -cmp.

Change-Id: I32667cbae251fe7881be0f434388478433b2414f
Reviewed-on: https://go-review.googlesource.com/c/go/+/280443
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 08:01:27 +00:00
Meng Zhuo
ed9772e130 [dev.regabi] cmd/compile: add explicit file name in types generation
The stringer using `go list` for the type detection, which depends on
GOROOT. Unfortunally by changing GOROOT to develop path will raise
version mismatch with internal packages.

Update #43369

Change-Id: Id81334ea5f1ecdbfa81eb2d162944d65664ce727
Reviewed-on: https://go-review.googlesource.com/c/go/+/280572
Trust: Meng Zhuo <mzh@golangcn.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2020-12-28 07:45:19 +00:00
Matthew Dempsky
a59d26603f [dev.regabi] cmd/compile: use []*CaseStmt in {Select,Switch}Stmt
Select and switch statements only ever contain case statements, so
change their Cases fields from Nodes to []*CaseStmt. This allows
removing a bunch of type assertions throughout the compiler.

CaseStmt should be renamed to CaseClause, and SelectStmt should
probably have its own CommClause type instead (like in go/ast and
cmd/compile/internal/syntax), but this is a good start.

Passes toolstash -cmp.

Change-Id: I2d41d616d44512c2be421e1e2ff13d0ee8b238ad
Reviewed-on: https://go-review.googlesource.com/c/go/+/280442
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 07:45:09 +00:00
Matthew Dempsky
fbc4458c06 [dev.regabi] cmd/compile: simplify some tree traversal code
When looking for referenced functions within bottomUpVisitor and
initDeps, the logic for ODOTMETH, OCALLPART, and OMETHEXPR are
basically identical, especially after previous refactorings to make
them use MethodExprName. This CL makes them exactly identical.

Passes toolstash -cmp.

Change-Id: I1f59c9be99aa9484d0397a0a6fb8ddd894a31c68
Reviewed-on: https://go-review.googlesource.com/c/go/+/280441
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 07:45:00 +00:00
Matthew Dempsky
6c67677541 [dev.regabi] cmd/compile: simplify FuncName and PkgFuncName
Now that we have proper types, these functions can be restricted to
only allowing *ir.Func, rather than any ir.Node. And even more
fortunately, all of their callers already happen to always
pass *ir.Func arguments, making this CL pretty simple.

Passes toolstash -cmp.

Change-Id: I21ecd4c8cee3ccb8ba86b17cedb2e71c56ffe87a
Reviewed-on: https://go-review.googlesource.com/c/go/+/280440
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 07:44:45 +00:00
Matthew Dempsky
676d794b81 [dev.regabi] cmd/compile: remove refersToCommonName
After reorder3's simplification, the only remaining use of
refersToCommonName is in oaslit, where the LHS expression is always a
single name. We can replace the now overly-generalized
refersToCommonName with a simple ir.Any traversal with ir.Uses.

Passes toolstash -cmp.

Change-Id: Ice3020cdbbf6083d52e07866a687580f4eb134b8
Reviewed-on: https://go-review.googlesource.com/c/go/+/280439
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 07:44:35 +00:00
Matthew Dempsky
c98548e110 [dev.regabi] cmd/compile: merge ascompatee, ascompatee1, and reorder3
These functions are interelated and have arbitrarily overlapping
responsibilities. By joining them together, we simplify the code and
remove some redundancy.

Passes toolstash -cmp.

Change-Id: I7c42cb7171b3006bc790199be3fd0991e6e985f2
Reviewed-on: https://go-review.googlesource.com/c/go/+/280438
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 07:44:25 +00:00
Matthew Dempsky
4c215c4fa9 [dev.regabi] cmd/compile: simplify and optimize reorder3
reorder3 is the code responsible for ensuring that evaluation of an
N:N parallel assignment statement respects the order of evaluation
rules specified for Go.

This CL simplifies the code and improves it from an O(N^2) algorithm
to O(N).

Passes toolstash -cmp.

Change-Id: I04cd31613af6924f637b042be8ad039ec6a924c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/280437
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 07:44:16 +00:00
Matthew Dempsky
e6c973198d [dev.regabi] cmd/compile: stop mangling SelectorExpr.Sel for ODOTMETH
ODOTMETH is unique among SelectorExpr expressions, in that Sel gets
mangled so that it no longer has the original identifier that was
selected (e.g., just "Foo"), but instead the qualified symbol name for
the selected method (e.g., "pkg.Type.Foo"). This is rarely useful, and
instead results in a lot of compiler code needing to worry about
undoing this change.

This CL changes ODOTMETH to leave the original symbol in place. The
handful of code locations where the mangled symbol name is actually
wanted are updated to use ir.MethodExprName(n).Sym() or (equivalently)
ir.MethodExprName(n).Func.Sym() instead.

Historically, the compiler backend has mistakenly used types.Syms
where it should have used ir.Name/ir.Funcs. And this change in
particular may risk breaking something, as the SelectorExpr.Sel will
no longer point at a symbol that uniquely identifies the called
method. However, I expect CL 280294 (desugar OCALLMETH into OCALLFUNC)
to have substantially reduced this risk, as ODOTMETH expressions are
now replaced entirely earlier in the compiler.

Passes toolstash -cmp.

Change-Id: If3c9c3b7df78ea969f135840574cf89e1d263876
Reviewed-on: https://go-review.googlesource.com/c/go/+/280436
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 07:44:07 +00:00
Matthew Dempsky
135ce1c485 [dev.regabi] cmd/compile: desugar OMETHEXPR into ONAME during walk
A subsequent CL will change FuncName to lazily create the ONAME nodes,
which isn't currently safe to do during SSA construction, because that
phase is concurrent.

Passes toolstash -cmp.

Change-Id: Ic24acc1d1160ad93b70ced3baa468f750e689ea6
Reviewed-on: https://go-review.googlesource.com/c/go/+/280435
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 07:43:31 +00:00
Matthew Dempsky
0f732f8c91 [dev.regabi] cmd/compile: minor walkExpr cleanups
This CL cleans up a few minor points in walkExpr:

1. We don't actually care about computing the type-size of all
expressions that are walked. We care about computing the type-size of
all expressions that are *returned* by walk, as these are the
expressions that will actually be seen by the back end.

2. There's no need to call typecheck.EvalConst anymore. EvalConst used
to be responsible for doing additional constant folding during walk;
but for a while a now, it has done only as much constant folding as is
required during type checking (because doing further constant folding
led to too many issues with Go spec compliance). Instead, more
aggressive constant folding is handled entirely by SSA.

3. The code for detecting string constants and generating their
symbols can be simplified somewhat.

Passes toolstash -cmp.

Change-Id: I464ef5bceb8a97689c8f55435369a3402a5ebc55
Reviewed-on: https://go-review.googlesource.com/c/go/+/280434
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 07:10:11 +00:00
Matthew Dempsky
0de8eafd98 [dev.regabi] cmd/compile: remove SelectorExpr.Offset field
Now that the previous CL ensures we always set SelectorExpr.Selection,
we can replace the SelectorExpr.Offset field with a helper method that
simply returns SelectorExpr.Selection.Offset.

Passes toolstash -cmp.

Change-Id: Id0f22b8b1980397b668f6860d27cb197b90ff52a
Reviewed-on: https://go-review.googlesource.com/c/go/+/280433
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 03:39:08 +00:00
Matthew Dempsky
a4f335f420 [dev.regabi] cmd/compile: always use a Field for ODOTPTR expressions
During walk, we create ODOTPTR expressions to access runtime struct
fields. But rather than using an actual Field for the selection, we
were just directly setting the ODOTPTR's Offset field.

This CL changes walk to create proper struct fields (albeit without
the rest of their enclosing struct type) and use them for creating the
ODOTPTR expressions.

Passes toolstash -cmp.

Change-Id: I08dbac3ed29141587feb0905d15adbcbcc4ca49e
Reviewed-on: https://go-review.googlesource.com/c/go/+/280432
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-28 03:38:58 +00:00
Matthew Dempsky
e4f293d853 [dev.regabi] cmd/compile: fix OCALLMETH desugaring
During walkCall, there's a half-hearted attempt at rewriting OCALLMETH
expressions into regular function calls by moving the receiver
argument into n.Args with the rest of the arguments. But the way it
does this leaves the AST in an inconsistent state (an ODOTMETH node
with no X expression), and leaves a lot of duplicate work for the rest
of the backend to deal with.

By simply rewriting OCALLMETH expressions into proper OCALLFUNC
expressions, we eliminate a ton of unnecessary code duplication during
SSA construction and avoid creation of invalid method-typed variables.

Passes toolstash -cmp.

Change-Id: I4d5c5f90a79f8994059b2d0ae472182e08096c0a
Reviewed-on: https://go-review.googlesource.com/c/go/+/280294
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-25 11:15:41 +00:00
Matthew Dempsky
1d9a1f67d5 [dev.regabi] cmd/compile: don't emit reflect data for method types
Within the compiler, we represent the type of methods as a special
"method" type, where the receiver parameter type is kept separate from
the other parameters. This is convenient for operations like testing
whether a type implements an interface, where we want to ignore the
receiver type.

These method types don't properly exist within the Go language though:
there are only "function" types. E.g., method expressions (expressions
of the form Type.Method) are simply functions with the receiver
parameter prepended to the regular parameter list.

However, the compiler backend is currently a little sloppy in its
handling of these types, which results in temporary variables being
declared as having "method" type, which then end up in DWARF
data. This is probably harmless in practice, but it's still wrong.

The proper solution is to fix the backend code so that we use correct
types everywhere, and the next CL does exactly this. But as it fixes
the DWARF output, so it fails toolstash -cmp. So this prelim CL
bandages over the issue in a way that generates the same output as
that proper fix.

Change-Id: I37a127bc8365c3a79ce513bdb3cfccb945912762
Reviewed-on: https://go-review.googlesource.com/c/go/+/280293
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-25 11:15:22 +00:00
Matthew Dempsky
396b6c2e7c [dev.regabi] cmd/compile: cleanup assignment typechecking
The assignment type-checking code previously bounced around a lot
between the LHS and RHS sides of the assignment. But there's actually
a very simple, consistent pattern to how to type check assignments:

1. Check the RHS expression.

2. If the LHS expression is an identifier that was declared in this
statement and it doesn't have an explicit type, give it the RHS
expression's default type.

3. Check the LHS expression.

4. Try assigning the RHS expression to the LHS expression, adding
implicit conversions as needed.

This CL implements this algorithm, and refactors tcAssign and
tcAssignList to use a common implementation. It also fixes the error
messages to consistently say just "1 variable" or "1 value", rather
than occasionally "1 variables" or "1 values".

Fixes #43348.

Passes toolstash -cmp.

Change-Id: I749cb8d6ccbc7d22cd7cb0a381f58a39fc2696b5
Reviewed-on: https://go-review.googlesource.com/c/go/+/280112
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-25 09:18:20 +00:00
Cuong Manh Le
e24d2f3d05 [dev.regabi] cmd/compile: remove typ from RangeStmt
We can use RangeStmt.X.Type() instead.

Passes buildall w/ toolstash -cmp.

Change-Id: Id63ce9cb046c3b39bcc35453b1602c986794dfe1
Reviewed-on: https://go-review.googlesource.com/c/go/+/279437
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-25 05:18:05 +00:00
Matthew Dempsky
2785c691c2 [dev.regabi] cmd/compile: cleanup devirtualization docs
Change-Id: I8e319f55fad6e9ed857aa020a96f3a89ccaadcea
Reviewed-on: https://go-review.googlesource.com/c/go/+/280213
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-25 00:39:43 +00:00
Matthew Dempsky
4b1d0fe66f [dev.regabi] cmd/compile: new devirtualization pkg [generated]
The devirtualization code was only in inl.go because it reused some of
the same helper functions as inlining (notably staticValue), but that
code all ended up in package ir instead anyway. Beyond that minor
commonality, it's entirely separate from inlining.

It's definitely on the small side, but consistent with the new
micropass-as-a-package approach we're trying.

[git-generate]
cd src/cmd/compile/internal/inline
rf '
  mv Devirtualize Func
  mv devirtualizeCall Call
  mv Func Call devirtualize.go
  mv devirtualize.go cmd/compile/internal/devirtualize
'

Change-Id: Iff7b9fe486856660a8107d5391c54b7e8d238706
Reviewed-on: https://go-review.googlesource.com/c/go/+/280212
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-25 00:39:24 +00:00
Cuong Manh Le
082cc8b7d9 [dev.regabi] cmd/compile: change ir.IsAssignable -> ir.IsAddressable
ir.IsAssignable does not include map index expression, so it should be
named ir.IsAddressable instead.

[git-generate]

cd src/cmd/compile/internal/ir
rf '
  mv IsAssignable IsAddressable
'

Change-Id: Ief6188e7b784ba9592d7b0cbec33b5f70d78f638
Reviewed-on: https://go-review.googlesource.com/c/go/+/279436
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-24 16:56:19 +00:00
Cuong Manh Le
27b248b307 [dev.regabi] cmd/compile: separate range stmt Vars to Key, Value nodes
Passes buildall w/ toolstash -cmp.

Change-Id: I9738fcabc8ebf3afa34d102afadf1b474b50db35
Reviewed-on: https://go-review.googlesource.com/c/go/+/279435
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2020-12-24 11:15:02 +00:00
Matthew Dempsky
40818038bf [dev.regabi] cmd/compile: change CaseStmt.Vars to Var
There's only ever one variable implicitly declared by a CaseStmt. It's
only a slice because we previous used Rlist for this.

Passes toolstash -cmp.

Change-Id: Idf747f3ec6dfbbe4e94d60546ba04a81754df3fe
Reviewed-on: https://go-review.googlesource.com/c/go/+/280012
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-23 20:54:11 +00:00
Cuong Manh Le
9eeed291bc [dev.regabi] cmd/compile: eliminate usage of ir.Node in liveness
All function parameters and return values in liveness have explicit
*ir.Name type, so use it directly instead of casting from ir.Node. While
at it, rename "affectedNode" to "affectedVar" to reflect this change.

Passes buildall w/ toolstash -cmp.

Change-Id: Id927e817a92ddb551a029064a2a54e020ca27074
Reviewed-on: https://go-review.googlesource.com/c/go/+/279434
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 15:15:29 +00:00
Matthew Dempsky
d1d64e4cea [dev.regabi] cmd/compile: split SliceExpr.List into separate fields
Passes toolstash -cmp.

Change-Id: I4e31154d04d99f2b80bec6a2c571a2a4a3f2ec99
Reviewed-on: https://go-review.googlesource.com/c/go/+/279959
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-12-23 14:38:23 +00:00
Matthew Dempsky
d19018e8f1 [dev.regabi] cmd/compile: split SliceHeaderExpr.LenCap into separate fields
Passes toolstash -cmp.

Change-Id: Ifc98a408c154a05997963e2c731466842ebbf50e
Reviewed-on: https://go-review.googlesource.com/c/go/+/279958
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-23 14:09:23 +00:00
Matthew Dempsky
53f082b0ee [dev.regabi] cmd/compile: cleanup export code further
This CL rips off a number of toolstash bandages:

- Fixes position information for string concatenation.

- Adds position information for struct literal fields.

- Removes unnecessary exprsOrNil calls or replaces them with plain
  expr calls when possible.

- Reorders conversion expressions to put type first, which matches
  source order and also the order the importer needs for calling the
  ConvExpr constructor.

Change-Id: I44cdc6035540d9ecefd9c1bcd92b8711d6ed813c
Reviewed-on: https://go-review.googlesource.com/c/go/+/279957
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-23 12:01:07 +00:00
Matthew Dempsky
31267f82e1 [dev.regabi] cmd/compile: simplify function/interface/struct typechecking
After the previous CL, the only callers to NewFuncType, tointerface,
or NewStructType are the functions for type-checking the type literal
ASTs. So just inline the code there.

While here, refactor the Field type-checking logic a little bit, to
reduce some duplication.

Passes toolstash -cmp.

Change-Id: Ie12d14b87ef8b6e528ac9dccd609604bd09b98ec
Reviewed-on: https://go-review.googlesource.com/c/go/+/279956
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-23 11:59:32 +00:00
Matthew Dempsky
addade2cce [dev.regabi] cmd/compile: prefer types constructors over typecheck
Similar to the earlier mkbuiltin cleanup, there's a bunch of code that
calls typecheck.NewFuncType or typecheck.NewStructType, which can now
just call types.NewSignature and types.NewStruct, respectively.

Passes toolstash -cmp.

Change-Id: Ie6e09f1a7efef84b9a2bb5daa7087a6879979668
Reviewed-on: https://go-review.googlesource.com/c/go/+/279955
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-23 11:59:23 +00:00
Matthew Dempsky
18ebfb49e9 [dev.regabi] cmd/compile: cleanup noder
Similar to previous CL: take advantage of better constructor APIs for
translating ASTs from syntax to ir.

Passes toolstash -cmp.

Change-Id: I40970775e7dd5afe2a0b7593ce3bd73237562457
Reviewed-on: https://go-review.googlesource.com/c/go/+/279972
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-23 10:46:45 +00:00
Matthew Dempsky
87a592b356 [dev.regabi] cmd/compile: cleanup import/export code
Now that we have concrete AST node types and better constructor APIs,
we can more cleanup a lot of the import code and some export code too.

Passes toolstash -cmp.

Change-Id: Ie3425d9dac11ac4245e5da675dd298984a926df4
Reviewed-on: https://go-review.googlesource.com/c/go/+/279954
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-23 10:29:05 +00:00
Matthew Dempsky
5898025026 [dev.regabi] cmd/compile: update mkbuiltin.go to use new type constructors
We recently added new functions to types like NewSignature and
NewField, so we can use these directly rather than depending on the
typecheck and ir wrappers.

Passes toolstash -cmp.

Change-Id: I32676aa9a4ea71892216017756e72bcf90297219
Reviewed-on: https://go-review.googlesource.com/c/go/+/279953
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-23 09:51:23 +00:00
Matthew Dempsky
63c96c2ee7 [dev.regabi] cmd/compile: update mkbuiltin.go and re-enable TestBuiltin
Update's mkbuiltin.go to match builtin.go after the recent rf
rewrites.

Change-Id: I80cf5d7c27b36fe28553406819cb4263de84e5ed
Reviewed-on: https://go-review.googlesource.com/c/go/+/279952
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-23 09:51:13 +00:00
Russ Cox
37f138df6b [dev.regabi] cmd/compile: split out package test [generated]
[git-generate]
cd src/cmd/compile/internal/gc
rf '
	mv bench_test.go constFold_test.go dep_test.go \
		fixedbugs_test.go iface_test.go float_test.go global_test.go \
		inl_test.go lang_test.go logic_test.go \
		reproduciblebuilds_test.go shift_test.go ssa_test.go \
		truncconst_test.go zerorange_test.go \
		cmd/compile/internal/test
'
mv testdata ../test

Change-Id: I041971b7e9766673f7a331679bfe1c8110dcda66
Reviewed-on: https://go-review.googlesource.com/c/go/+/279480
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:40:04 +00:00
Russ Cox
3d8a3cb06b [dev.regabi] cmd/compile: split out package pkginit [generated]
[git-generate]
cd src/cmd/compile/internal/gc
rf '
	mv fninit Task
	mv init.go initorder.go cmd/compile/internal/pkginit
'

Change-Id: Ie2a924784c7a6fa029eaef821384eef4b262e1af
Reviewed-on: https://go-review.googlesource.com/c/go/+/279479
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:39:57 +00:00
Russ Cox
3f04d964ab [dev.regabi] cmd/compile: split up walkexpr1, walkstmt [generated]
walkexpr1 is the second largest non-machine-generated function in the compiler.
weighing in at 1,164 lines. Since we are destroying the git blame history
anyway, now is a good time to split each different case into its own function,
making future work on this function more manageable.
Do the same to walkstmt too for consistency, even though it is a paltry 259 lines.

[git-generate]
cd src/cmd/compile/internal/walk
rf '
	mv addstr walkAddString
	mv walkCall walkCall1
	mv walkpartialcall walkCallPart
	mv walkclosure walkClosure
	mv walkrange walkRange
	mv walkselect walkSelect
	mv walkselectcases walkSelectCases
	mv walkswitch walkSwitch
	mv walkExprSwitch walkSwitchExpr
	mv walkTypeSwitch walkSwitchType
	mv walkstmt walkStmt
	mv walkstmtlist walkStmtList
	mv walkexprlist walkExprList
	mv walkexprlistsafe walkExprListSafe
	mv walkexprlistcheap walkExprListCheap
	mv walkexpr walkExpr
	mv walkexpr1 walkExpr1
	mv walkprint walkPrint
	mv walkappend walkAppend
	mv walkcompare walkCompare
	mv walkcompareInterface walkCompareInterface
	mv walkcompareString walkCompareString

	mv appendslice appendSlice
	mv cheapexpr cheapExpr
	mv copyany walkCopy
	mv copyexpr copyExpr
	mv eqfor eqFor
	mv extendslice extendSlice
	mv finishcompare finishCompare
	mv safeexpr safeExpr

	mv walkStmt:/^\tcase ir.ORECV:/+2,/^\tcase /-2 walkRecv
	add walk.go:/^func walkRecv/-0 \
		// walkRecv walks an ORECV node.
	mv walkStmt:/^\tcase ir.ODCL:/+2,/^\tcase /-2 walkDecl
	add walk.go:/^func walkDecl/-0 \
		// walkDecl walks an ODCL node.
	mv walkStmt:/^\tcase ir.OGO:/+2,/^\tcase /-2 walkGoDefer
	add walk.go:/^func walkGoDefer/-0 \
		// walkGoDefer walks an OGO or ODEFER node.
	mv walkStmt:/^\tcase ir.OFOR,/+2,/^\tcase /-2 walkFor
	add walk.go:/^func walkFor/-0 \
		// walkFor walks an OFOR or OFORUNTIL node.
	mv walkStmt:/^\tcase ir.OIF:/+2,/^\tcase /-2 walkIf
	add walk.go:/^func walkIf/-0 \
		// walkIf walks an OIF node.
	mv walkStmt:/^\tcase ir.ORETURN:/+2,/^\tcase /-2 walkReturn
	add walk.go:/^func walkReturn/-0 \
		// walkReturn walks an ORETURN node.

	mv walkExpr1:/^\tcase ir.ODOT,/+2,/^\tcase /-2 walkDot
	add walk.go:/^func walkDot/-0 \
		// walkDot walks an ODOT or ODOTPTR node.
	mv walkExpr1:/^\tcase ir.ODOTTYPE,/+2,/^\tcase /-2 walkDotType
	add walk.go:/^func walkDotType/-0 \
		// walkDotType walks an ODOTTYPE or ODOTTYPE2 node.
	mv walkExpr1:/^\tcase ir.OLEN,/+2,/^\tcase /-2 walkLenCap
	add walk.go:/^func walkLenCap/-0 \
		// walkLenCap walks an OLEN or OCAP node.
	mv walkExpr1:/^\tcase ir.OANDAND,/+2,/^\tcase /-2 walkLogical
	add walk.go:/^func walkLogical/-0 \
		// walkLogical walks an OANDAND or OOROR node.
	mv walkExpr1:/^\tcase ir.OCALLINTER,/+2,/^\tcase /-2 walkCall
	add walk.go:/^func walkCall/-0 \
		// walkCall walks an OCALLFUNC, OCALLINTER, or OCALLMETH node.
	mv walkExpr1:/^\tcase ir.OAS,/+1,/^\tcase /-2 walkAssign
	add walk.go:/^func walkAssign/-0 \
		// walkAssign walks an OAS (AssignExpr) or OASOP (AssignOpExpr) node.
	mv walkExpr1:/^\tcase ir.OAS2:/+2,/^\tcase /-3 walkAssignList
	add walk.go:/^func walkAssignList/-0 \
		// walkAssignList walks an OAS2 node.
	mv walkExpr1:/^\tcase ir.OAS2FUNC:/+2,/^\tcase /-4 walkAssignFunc
	add walk.go:/^func walkAssignFunc/-0 \
		// walkAssignFunc walks an OAS2FUNC node.
	mv walkExpr1:/^\tcase ir.OAS2RECV:/+2,/^\tcase /-3 walkAssignRecv
	add walk.go:/^func walkAssignRecv/-0 \
		// walkAssignRecv walks an OAS2RECV node.
	mv walkExpr1:/^\tcase ir.OAS2MAPR:/+2,/^\tcase /-2 walkAssignMapRead
	add walk.go:/^func walkAssignMapRead/-0 \
		// walkAssignMapRead walks an OAS2MAPR node.
	mv walkExpr1:/^\tcase ir.ODELETE:/+2,/^\tcase /-2 walkDelete
	add walk.go:/^func walkDelete/-0 \
		// walkDelete walks an ODELETE node.
	mv walkExpr1:/^\tcase ir.OAS2DOTTYPE:/+2,/^\tcase /-2 walkAssignDotType
	add walk.go:/^func walkAssignDotType/-0 \
		// walkAssignDotType walks an OAS2DOTTYPE node.
	mv walkExpr1:/^\tcase ir.OCONVIFACE:/+2,/^\tcase /-2 walkConvInterface
	add walk.go:/^func walkConvInterface/-0 \
		// walkConvInterface walks an OCONVIFACE node.
	mv walkExpr1:/^\tcase ir.OCONV,/+2,/^\tcase /-2 walkConv
	add walk.go:/^func walkConv/-0 \
		// walkConv walks an OCONV or OCONVNOP (but not OCONVIFACE) node.
	mv walkExpr1:/^\tcase ir.ODIV,/+2,/^\tcase /-2 walkDivMod
	add walk.go:/^func walkDivMod/-0 \
		// walkDivMod walks an ODIV or OMOD node.
	mv walkExpr1:/^\tcase ir.OINDEX:/+2,/^\tcase /-2 walkIndex
	add walk.go:/^func walkIndex/-0 \
		// walkIndex walks an OINDEX node.
	# move type assertion above comment
	mv walkExpr1:/^\tcase ir.OINDEXMAP:/+/n := n/-+ walkExpr1:/^\tcase ir.OINDEXMAP:/+0
	mv walkExpr1:/^\tcase ir.OINDEXMAP:/+2,/^\tcase /-2 walkIndexMap
	add walk.go:/^func walkIndexMap/-0 \
		// walkIndexMap walks an OINDEXMAP node.
	mv walkExpr1:/^\tcase ir.OSLICEHEADER:/+2,/^\tcase /-2 walkSliceHeader
	add walk.go:/^func walkSliceHeader/-0 \
		// walkSliceHeader walks an OSLICEHEADER node.
	mv walkExpr1:/^\tcase ir.OSLICE,/+2,/^\tcase /-2 walkSlice
	add walk.go:/^func walkSlice/-0 \
		// walkSlice walks an OSLICE, OSLICEARR, OSLICESTR, OSLICE3, or OSLICE3ARR node.
	mv walkExpr1:/^\tcase ir.ONEW:/+2,/^\tcase /-2 walkNew
	add walk.go:/^func walkNew/-0 \
		// walkNew walks an ONEW node.
	# move type assertion above comment
	mv walkExpr1:/^\tcase ir.OCLOSE:/+/n := n/-+ walkExpr1:/^\tcase ir.OCLOSE:/+0
	mv walkExpr1:/^\tcase ir.OCLOSE:/+2,/^\tcase /-2 walkClose
	add walk.go:/^func walkClose/-0 \
		// walkClose walks an OCLOSE node.
	# move type assertion above comment
	mv walkExpr1:/^\tcase ir.OMAKECHAN:/+/n := n/-+ walkExpr1:/^\tcase ir.OMAKECHAN:/+0
	mv walkExpr1:/^\tcase ir.OMAKECHAN:/+2,/^\tcase /-2 walkMakeChan
	add walk.go:/^func walkMakeChan/-0 \
		// walkMakeChan walks an OMAKECHAN node.
	mv walkExpr1:/^\tcase ir.OMAKEMAP:/+2,/^\tcase /-2 walkMakeMap
	add walk.go:/^func walkMakeMap/-0 \
		// walkMakeMap walks an OMAKEMAP node.
	mv walkExpr1:/^\tcase ir.OMAKESLICE:/+2,/^\tcase /-2 walkMakeSlice
	add walk.go:/^func walkMakeSlice/-0 \
		// walkMakeSlice walks an OMAKESLICE node.
	mv walkExpr1:/^\tcase ir.OMAKESLICECOPY:/+2,/^\tcase /-2 walkMakeSliceCopy
	add walk.go:/^func walkMakeSliceCopy/-0 \
		// walkMakeSliceCopy walks an OMAKESLICECOPY node.
	mv walkExpr1:/^\tcase ir.ORUNESTR:/+2,/^\tcase /-2 walkRuneToString
	add walk.go:/^func walkRuneToString/-0 \
		// walkRuneToString walks an ORUNESTR node.
	mv walkExpr1:/^\tcase ir.OBYTES2STR,/+2,/^\tcase /-2 walkBytesRunesToString
	add walk.go:/^func walkBytesRunesToString/-0 \
		// walkBytesRunesToString walks an OBYTES2STR or ORUNES2STR node.
	mv walkExpr1:/^\tcase ir.OBYTES2STRTMP:/+2,/^\tcase /-2 walkBytesToStringTemp
	add walk.go:/^func walkBytesToStringTemp/-0 \
		// walkBytesToStringTemp walks an OBYTES2STRTMP node.
	mv walkExpr1:/^\tcase ir.OSTR2BYTES:/+2,/^\tcase /-2 walkStringToBytes
	add walk.go:/^func walkStringToBytes/-0 \
		// walkStringToBytes walks an OSTR2BYTES node.
	# move type assertion above comment
	mv walkExpr1:/^\tcase ir.OSTR2BYTESTMP:/+/n := n/-+ walkExpr1:/^\tcase ir.OSTR2BYTESTMP:/+0
	mv walkExpr1:/^\tcase ir.OSTR2BYTESTMP:/+2,/^\tcase /-2 walkStringToBytesTemp
	add walk.go:/^func walkStringToBytesTemp/-0 \
		// walkStringToBytesTemp walks an OSTR2BYTESTMP node.
	mv walkExpr1:/^\tcase ir.OSTR2RUNES:/+2,/^\tcase /-2 walkStringToRunes
	add walk.go:/^func walkStringToRunes/-0 \
		// walkStringToRunes walks an OSTR2RUNES node.
	mv walkExpr1:/^\tcase ir.OARRAYLIT,/+1,/^\tcase /-2 walkCompLit
	add walk.go:/^func walkCompLit/-0 \
		// walkCompLit walks a composite literal node: \
		// OARRAYLIT, OSLICELIT, OMAPLIT, OSTRUCTLIT (all CompLitExpr), or OPTRLIT (AddrExpr).
	mv walkExpr1:/^\tcase ir.OSEND:/+2,/^\tcase /-2 walkSend
	add walk.go:/^func walkSend/-0 \
		// walkSend walks an OSEND node.

	mv walkStmt walkStmtList \
		walkDecl \
		walkFor \
		walkGoDefer \
		walkIf \
		wrapCall \
		stmt.go

	mv walkExpr walkExpr1 walkExprList walkExprListCheap walkExprListSafe \
		cheapExpr safeExpr copyExpr \
		walkAddString \
		walkCall \
		walkCall1 \
		walkDivMod \
		walkDot \
		walkDotType \
		walkIndex \
		walkIndexMap \
		walkLogical \
		walkSend \
		walkSlice \
		walkSliceHeader \
		reduceSlice \
		bounded \
		usemethod \
		usefield \
		expr.go

	mv \
		walkAssign \
		walkAssignDotType \
		walkAssignFunc \
		walkAssignList \
		walkAssignMapRead \
		walkAssignRecv \
		walkReturn \
		fncall \
		ascompatee \
		ascompatee1 \
		ascompatet \
		reorder3 \
		reorder3save \
		aliased \
		anyAddrTaken \
		refersToName \
		refersToCommonName \
		appendSlice \
		isAppendOfMake \
		extendSlice \
		assign.go

	mv \
		walkCompare \
		walkCompareInterface \
		walkCompareString \
		finishCompare \
		eqFor \
		brcom \
		brrev \
		tracecmpArg \
		canMergeLoads \
		compare.go

	mv \
		walkConv \
		walkConvInterface \
		walkBytesRunesToString \
		walkBytesToStringTemp \
		walkRuneToString \
		walkStringToBytes \
		walkStringToBytesTemp \
		walkStringToRunes \
		convFuncName \
		rtconvfn \
		byteindex \
		walkCheckPtrAlignment \
		walkCheckPtrArithmetic \
		convert.go

	mv \
		walkAppend \
		walkClose \
		walkCopy \
		walkDelete \
		walkLenCap \
		walkMakeChan \
		walkMakeMap \
		walkMakeSlice \
		walkMakeSliceCopy \
		walkNew \
		walkPrint \
		badtype \
		callnew \
		writebarrierfn \
		isRuneCount \
		builtin.go

	mv \
		walkCompLit \
		sinit.go \
		complit.go

	mv subr.go walk.go
'

Change-Id: Ie0cf3ba4adf363c120c134d57cb7ef37934eaab9
Reviewed-on: https://go-review.googlesource.com/c/go/+/279430
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:39:50 +00:00
Russ Cox
e4895ab4c0 [dev.regabi] cmd/compile: split out package walk [generated]
[git-generate]
cd src/cmd/compile/internal/gc
rf '
	# Late addition to package ir.
	mv closuredebugruntimecheck ClosureDebugRuntimeCheck
	mv hasemptycvars IsTrivialClosure
	mv ClosureDebugRuntimeCheck IsTrivialClosure func.go
	mv func.go cmd/compile/internal/ir

	# Late addition to package reflectdata.
	mv markTypeUsedInInterface MarkTypeUsedInInterface
	mv markUsedIfaceMethod MarkUsedIfaceMethod
	mv MarkTypeUsedInInterface MarkUsedIfaceMethod reflect.go
	mv reflect.go cmd/compile/internal/reflectdata

	# Late addition to package staticdata.
	mv litsym InitConst
	mv InitConst data.go
	mv data.go cmd/compile/internal/staticdata

	# Extract staticinit out of walk into its own package.
	mv InitEntry InitPlan InitSchedule InitSchedule.append InitSchedule.staticInit \
		InitSchedule.tryStaticInit InitSchedule.staticcopy \
		InitSchedule.staticassign InitSchedule.initplan InitSchedule.addvalue \
		statuniqgen staticname stataddr anySideEffects getlit isvaluelit \
		sched.go
	mv InitSchedule.initplans InitSchedule.Plans
	mv InitSchedule.inittemps InitSchedule.Temps
	mv InitSchedule.out InitSchedule.Out
	mv InitSchedule.staticInit InitSchedule.StaticInit
	mv InitSchedule.staticassign InitSchedule.StaticAssign
	mv InitSchedule Schedule
	mv InitPlan Plan
	mv InitEntry Entry
	mv anySideEffects AnySideEffects
	mv staticname StaticName
	mv stataddr StaticLoc
	mv sched.go cmd/compile/internal/staticinit

	# Export API and unexport non-API.
	mv transformclosure Closure
	mv walk Walk

	mv Order orderState

	mv swt.go switch.go
	mv racewalk.go race.go

	mv closure.go order.go range.go select.go switch.go race.go \
		sinit.go subr.go walk.go \
		cmd/compile/internal/walk
'

: # Update format test.
cd ../../
go install cmd/compile/... cmd/internal/archive
go test -u || go test -u
rm -rf ../../../pkg/darwin_amd64/cmd

Change-Id: I11c7a45f74d4a9e963da15c080e1018caaa99c05
Reviewed-on: https://go-review.googlesource.com/c/go/+/279478
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:39:43 +00:00
Russ Cox
01fd2d05c8 [dev.regabi] cmd/compile: split out package dwarfgen [generated]
[git-generate]
cd src/cmd/compile/internal/gc

rf '
	# Inline and remove ngotype.
	ex {
		import "cmd/compile/internal/ir"
		import "cmd/compile/internal/reflectdata"
		var n ir.Node
		ngotype(n) -> reflectdata.TypeSym(n.Type())
	}
	rm ngotype

	mv recordFlags RecordFlags
	mv recordPackageName RecordPackageName
	mv RecordFlags RecordPackageName dwarf.go

	mv debuginfo Info
	mv genAbstractFunc AbstractFunc
	mv scope.go scope_test.go dwarf.go dwinl.go cmd/compile/internal/dwarfgen
'

Change-Id: I31fa982900dbba2066ca4c7a706af922e5481c70
Reviewed-on: https://go-review.googlesource.com/c/go/+/279477
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:39:36 +00:00
Russ Cox
6c34d2f420 [dev.regabi] cmd/compile: split out package ssagen [generated]
[git-generate]

cd src/cmd/compile/internal/gc
rf '
	# maxOpenDefers is declared in ssa.go but used only by walk.
	mv maxOpenDefers walk.go

	# gc.Arch -> ssagen.Arch
	# It is not as nice but will do for now.
	mv Arch ArchInfo
	mv thearch Arch
	mv Arch ArchInfo arch.go

	# Pull dwarf out of pgen.go.
	mv debuginfo declPos createDwarfVars preInliningDcls \
		createSimpleVars createSimpleVar \
		createComplexVars createComplexVar \
		dwarf.go

	# Pull high-level compilation out of pgen.go,
	# leaving only the SSA code.
	mv compilequeue funccompile compile compilenow \
		compileFunctions isInlinableButNotInlined \
		initLSym \
		compile.go

	mv BoundsCheckFunc GCWriteBarrierReg ssa.go
	mv largeStack largeStackFrames CheckLargeStacks pgen.go

	# All that is left in dcl.go is the nowritebarrierrecCheck
	mv dcl.go nowb.go

	# Export API and unexport non-API.
	mv initssaconfig InitConfig
	mv isIntrinsicCall IsIntrinsicCall
	mv ssaDumpInline DumpInline
	mv initSSATables InitTables
	mv initSSAEnv InitEnv
	mv compileSSA Compile
	mv stackOffset StackOffset
	mv canSSAType TypeOK
	mv SSAGenState State
	mv FwdRefAux fwdRefAux

	mv cgoSymABIs CgoSymABIs
	mv readSymABIs ReadSymABIs
	mv initLSym InitLSym
	mv useABIWrapGen symabiDefs CgoSymABIs ReadSymABIs InitLSym selectLSym makeABIWrapper setupTextLSym abi.go

	mv arch.go abi.go nowb.go phi.go pgen.go pgen_test.go ssa.go cmd/compile/internal/ssagen
'
rm go.go gsubr.go

Change-Id: I47fad6cbf1d1e583fd9139003a08401d7cd048a1
Reviewed-on: https://go-review.googlesource.com/c/go/+/279476
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:39:29 +00:00
Russ Cox
de65151e50 [dev.regabi] cmd/compile: split out package reflectdata [generated]
[git-generate]

cd src/cmd/compile/internal/gc
rf '
	ex {
		import "cmd/compile/internal/base"
		thearch.LinkArch.Name -> base.Ctxt.Arch.Name
	}

	# Move out of reflect.go a few functions that should stay.
	mv addsignats obj.go
	mv deferstruct ssa.go

	# Export reflectdata API.
	mv zerosize ZeroSize
	mv hmap MapType
	mv bmap MapBucketType
	mv hiter MapIterType
	mv addsignat NeedRuntimeType
	mv typename TypePtr
	mv typenamesym TypeSym
	mv typesymprefix TypeSymPrefix
	mv itabsym ITabSym
	mv tracksym TrackSym
	mv zeroaddr ZeroAddr
	mv itabname ITabAddr
	mv ifaceMethodOffset InterfaceMethodOffset
	mv peekitabs CompileITabs
	mv addptabs CollectPTabs
	mv algtype AlgType
	mv dtypesym WriteType
	mv dumpbasictypes WriteBasicTypes
	mv dumpimportstrings WriteImportStrings
	mv dumpsignats WriteRuntimeTypes
	mv dumptabs WriteTabs
	mv eqinterface EqInterface
	mv eqstring EqString

	mv GCProg gcProg
	mv EqCanPanic eqCanPanic
	mv IsRegularMemory isRegularMemory
	mv Sig typeSig

	mv hashmem alg.go
	mv CollectPTabs genwrapper ZeroSize reflect.go
	mv alg.go reflect.go cmd/compile/internal/reflectdata
'

Change-Id: Iaae9da9e9fad5f772f5216004823ccff2ea8f139
Reviewed-on: https://go-review.googlesource.com/c/go/+/279475
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:39:23 +00:00
Russ Cox
4dfb5d91a8 [dev.regabi] cmd/compile: split out package staticdata [generated]
[git-generate]
cd src/cmd/compile/internal/gc

rf '
	# Export API and move to its own files.
	mv addrsym InitAddr
	mv pfuncsym InitFunc
	mv slicesym InitSlice
	mv slicebytes InitSliceBytes
	mv stringsym StringSym
	mv funcsym FuncSym
	mv makefuncsym NeedFuncSym
	mv dumpfuncsyms WriteFuncSyms
	mv InitAddr InitFunc InitSlice InitSliceBytes stringSymPrefix \
		StringSym fileStringSym slicedataGen slicedata dstringdata \
		funcsyms FuncSym NeedFuncSym WriteFuncSyms \
		data.go

	mv initEmbed WriteEmbed
	mv dumpembeds obj.go

	mv data.go embed.go cmd/compile/internal/staticdata
'

Change-Id: I209c5e597c8acfa29a48527695a9ddc1e9ea8e6a
Reviewed-on: https://go-review.googlesource.com/c/go/+/279474
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:39:16 +00:00
Russ Cox
fbc82f03b1 [dev.regabi] cmd/compile: split out package noder [generated]
[git-generate]
cd src/cmd/compile/internal/gc

rf '
	mv ArhdrSize HeaderSize
	mv arsize ReadHeader
	mv formathdr FormatHeader
	mv HeaderSize ReadHeader FormatHeader archive.go
	mv archive.go cmd/internal/archive

	mv makePos main.go

	mv checkDotImports CheckDotImports
	mv parseFiles ParseFiles

	mv Pragma pragmas
	mv PragmaEmbed pragmaEmbed
	mv PragmaPos pragmaPos
	mv FuncPragmas funcPragmas
	mv TypePragmas typePragmas

	mv fakeRecv noder.funcLit renameinitgen renameinit oldname varEmbed noder.go
	mv isDriveLetter islocalname findpkg myheight importfile \
		reservedimports isbadimport \
		pkgnotused \
		mkpackage clearImports \
		CheckDotImports dotImports importDot \
		importName \
		import.go

	mv noder _noder
	mv import.go lex.go lex_test.go noder.go cmd/compile/internal/noder
'
cd ../noder
rf '
	mv _noder noder
'

Change-Id: Iac2b856f7b86143c666d818e4b7c5b261cf387d5
Reviewed-on: https://go-review.googlesource.com/c/go/+/279473
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:39:06 +00:00
Russ Cox
de454eef5f [dev.regabi] cmd/compile: split out package escape [generated]
[git-generate]

cd src/cmd/compile/internal/gc
rf '
	# Trivial min, max defined in escape.go but only used in ssa.go.
	mv min8 max8 ssa.go

	# Export package escape API.
	mv escapes Funcs
	mv escapeFuncs Batch
	mv escFmt Fmt
	mv unsafeUintptrTag UnsafeUintptrNote
	mv uintptrEscapesTag UintptrEscapesNote
	mv heapAllocReason HeapAllocReason

	# Unexport non-API.
	mv EscEdge edge
	mv EscHole hole
	mv EscLeaks leaks
	mv ParseLeaks parseLeaks
	mv EscLocation location
	mv EscNote note
	mv Escape _escape # leave room for escape import, fixed below
	mv EscFuncUnknown escFuncUnknown
	mv EscFuncPlanned escFuncPlanned
	mv EscFuncStarted escFuncStarted
	mv EscFuncTagged escFuncTagged

	mv escape.go cmd/compile/internal/escape
'
cd ../escape
rf '
	mv _escape escape
'

Change-Id: I3a6d1bfb6eba12bea936354ea1fe9813cbde425c
Reviewed-on: https://go-review.googlesource.com/c/go/+/279472
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:39:00 +00:00
Russ Cox
071ab0a14c [dev.regabi] cmd/compile: split out package liveness [generated]
[git-generate]

cd src/cmd/compile/internal/gc
rf '
	# AutoVar is essentially an ssa helper; move it there.
	mv AutoVar value.go
	mv value.go cmd/compile/internal/ssa

	# Export liveness API and unexport non-API.
	mv LivenessMap Map
	mv Map.vals Map.Vals
	mv Map.deferreturn Map.DeferReturn
	mv livenessShouldTrack ShouldTrack
	mv onebitwalktype1 SetTypeBits
	mv allUnsafe IsUnsafe
	mv liveness Compute
	mv BlockEffects blockEffects
	mv Liveness liveness
	mv liveness _liveness # make room for import

	mv emitptrargsmap WriteFuncMap
	mv WriteFuncMap plive.go

	mv bvset.go plive.go cmd/compile/internal/liveness
'
cd ../liveness
rf '
	mv _liveness liveness
'

Change-Id: I3b86e5025bd9d32a7e19f44714fa16be4125059e
Reviewed-on: https://go-review.googlesource.com/c/go/+/279311
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:38:53 +00:00
Russ Cox
0ced54062e [dev.regabi] cmd/compile: split out package objw [generated]
Object file writing routines are used not just at the end
of the compilation but also during static data layout in walk.
Split them into their own package.

[git-generate]

cd src/cmd/compile/internal/gc
rf '
	# Move bit vector to new package bitvec
	mv bvec.n bvec.N
	mv bvec.b bvec.B
	mv bvec BitVec
	mv bvalloc New
	mv bvbulkalloc NewBulk
	mv bulkBvec.next bulkBvec.Next
	mv bulkBvec Bulk
	mv H0 h0
	mv Hp hp

	# Leave bvecSet and bitmap hashes behind - not needed as broadly.
	mv bvecSet.extractUniqe bvecSet.extractUnique
	mv h0 bvecSet bvecSet.grow bvecSet.add \
		bvecSet.extractUnique hashbitmap bvset.go

	mv bv.go cmd/compile/internal/bitvec

	ex . ../arm ../arm64 ../mips ../mips64 ../ppc64 ../s390x ../riscv64 {
		import "cmd/internal/obj"
		var a *obj.Addr
		var i int64
		Addrconst(a, i) -> a.SetConst(i)
		var p, to *obj.Prog
		Patch(p, to) -> p.To.SetTarget(to)
	}
	rm Addrconst Patch

	# Move object-writing API to new package objw
	mv duint8 Objw_Uint8
	mv duint16 Objw_Uint16
	mv duint32 Objw_Uint32
	mv duintptr Objw_Uintptr
	mv duintxx Objw_UintN
	mv dsymptr Objw_SymPtr
	mv dsymptrOff Objw_SymPtrOff
	mv dsymptrWeakOff Objw_SymPtrWeakOff
	mv ggloblsym Objw_Global
	mv dbvec Objw_BitVec
	mv newProgs NewProgs
	mv Progs.clearp Progs.Clear
	mv Progs.settext Progs.SetText
	mv Progs.next Progs.Next
	mv Progs.pc Progs.PC
	mv Progs.pos Progs.Pos
	mv Progs.curfn Progs.CurFunc
	mv Progs.progcache Progs.Cache
	mv Progs.cacheidx Progs.CacheIndex
	mv Progs.nextLive Progs.NextLive
	mv Progs.prevLive Progs.PrevLive
	mv Progs.Appendpp Progs.Append
	mv LivenessIndex.stackMapIndex LivenessIndex.StackMapIndex
	mv LivenessIndex.isUnsafePoint LivenessIndex.IsUnsafePoint

	mv Objw_Uint8 Objw_Uint16 Objw_Uint32 Objw_Uintptr Objw_UintN \
		Objw_SymPtr Objw_SymPtrOff Objw_SymPtrWeakOff Objw_Global \
		Objw_BitVec \
		objw.go
	mv sharedProgArray NewProgs Progs \
		LivenessIndex StackMapDontCare \
		LivenessDontCare LivenessIndex.StackMapValid \
		Progs.NewProg Progs.Flush Progs.Free Progs.Prog Progs.Clear Progs.Append Progs.SetText \
		prog.go
	mv prog.go objw.go cmd/compile/internal/objw

	# Move ggloblnod to obj with the rest of the non-objw higher-level writing.
	mv ggloblnod obj.go
'

cd ../objw
rf '
	mv Objw_Uint8 Uint8
	mv Objw_Uint16 Uint16
	mv Objw_Uint32 Uint32
	mv Objw_Uintptr Uintptr
	mv Objw_UintN UintN
	mv Objw_SymPtr SymPtr
	mv Objw_SymPtrOff SymPtrOff
	mv Objw_SymPtrWeakOff SymPtrWeakOff
	mv Objw_Global Global
	mv Objw_BitVec BitVec
'

Change-Id: I2b87085aa788564fb322e9c55bddd73347b4d5fd
Reviewed-on: https://go-review.googlesource.com/c/go/+/279310
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:38:47 +00:00
Russ Cox
575fd6ff0a [dev.regabi] cmd/compile: split out package inline [generated]
[git-generate]

cd src/cmd/compile/internal/gc
rf '
	mv numNonClosures inl.go
	mv inlFlood Inline_Flood
	mv inlcalls InlineCalls
	mv devirtualize Devirtualize
	mv caninl CanInline

	mv inl.go cmd/compile/internal/inline
'

Change-Id: Iee1f5b1e82d5cea6be4ecd91e6920500810f21de
Reviewed-on: https://go-review.googlesource.com/c/go/+/279309
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:38:41 +00:00
Russ Cox
0256ba99a8 [dev.regabi] cmd/compile: split up typecheck1 [generated]
typecheck1 is the largest non-machine-generated function in the compiler.
weighing in at 1,747 lines. Since we are destroying the git blame history
anyway, now is a good time to split each different case into its own function,
making future work on this function more manageable.

[git-generate]
cd src/cmd/compile/internal/typecheck
rf '
	# Remove tracing print from typecheck1 - the one in typecheck is fine.
	# Removing it lets us remove the named result.
	# That lets all the cut-out functions not have named results.
	rm typecheck.go:/^func typecheck1/+0,/^func typecheck1/+4
	sub typecheck.go:/^func typecheck1/+/\(res ir\.Node\)/ ir.Node

	mv typecheckselect tcSelect
	mv typecheckswitch tcSwitch
	mv typecheckrange tcRange
	mv typecheckfunc tcFunc
	mv checkdefergo tcGoDefer
	mv typecheckclosure tcClosure
	mv check typecheck
	mv typecheckcomplit tcCompLit
	mv typecheckas tcAssign
	mv typecheckas2 tcAssignList
	mv typecheckpartialcall tcCallPart
	mv typecheckExprSwitch tcSwitchExpr
	mv typecheckTypeSwitch tcSwitchType

	mv typecheck1:/^\tcase ir.ORETURN:/+2,/^\tcase /-2 tcReturn
	add typecheck.go:/^func tcReturn/-0 \
		// tcReturn typechecks an ORETURN node.
	mv typecheck1:/^\tcase ir.OIF:/+2,/^\tcase /-2 tcIf
	add typecheck.go:/^func tcIf/-0 \
		// tcIf typechecks an OIF node.
	mv typecheck1:/^\tcase ir.OFOR,/+2,/^\tcase /-2 tcFor
	add typecheck.go:/^func tcFor/-0 \
		// tcFor typechecks an OFOR node.
	mv typecheck1:/^\tcase ir.OSPTR:/+2,/^\tcase /-2 tcSPtr
	add typecheck.go:/^func tcSPtr/-0 \
		// tcSPtr typechecks an OSPTR node.
	mv typecheck1:/^\tcase ir.OITAB:/+2,/^\tcase /-2 tcITab
	add typecheck.go:/^func tcITab/-0 \
		// tcITab typechecks an OITAB node.
	mv typecheck1:/^\tcase ir.ORECOVER:/+2,/^\tcase /-2 tcRecover
	add typecheck.go:/^func tcRecover/-0 \
		// tcRecover typechecks an ORECOVER node.
	mv typecheck1:/^\tcase ir.OPANIC:/+2,/^\tcase /-2 tcPanic
	add typecheck.go:/^func tcPanic/-0 \
		// tcPanic typechecks an OPANIC node.
	mv typecheck1:/^\tcase ir.OPRINT,/+2,/^\tcase /-2 tcPrint
	add typecheck.go:/^func tcPrint/-0 \
		// tcPrint typechecks an OPRINT or OPRINTN node.
	mv typecheck1:/^\tcase ir.ONEW:/+2,/^\tcase /-2 tcNew
	add typecheck.go:/^func tcNew/-0 \
		// tcNew typechecks an ONEW node.
	mv typecheck1:/^\tcase ir.OMAKE:/+2,/^\tcase /-2 tcMake
	add typecheck.go:/^func tcMake/-0 \
		// tcMake typechecks an OMAKE node.
	mv typecheck1:/^\tcase ir.OCONV:/+2,/^\tcase /-2 tcConv
	add typecheck.go:/^func tcConv/-0 \
		// tcConv typechecks an OCONV node.
	mv typecheck1:/^\tcase ir.OCOPY:/+2,/^\tcase /-2 tcCopy
	add typecheck.go:/^func tcCopy/-0 \
		// tcCopy typechecks an OCOPY node.
	mv typecheck1:/^\tcase ir.OAPPEND:/+2,/^\tcase /-2 tcAppend
	add typecheck.go:/^func tcAppend/-0 \
		// tcAppend typechecks an OAPPEND node.
	mv typecheck1:/^\tcase ir.ODELETE:/+2,/^\tcase /-2 tcDelete
	add typecheck.go:/^func tcDelete/-0 \
		// tcDelete typechecks an ODELETE node.
	mv typecheck1:/^\tcase ir.OCLOSE:/+2,/^\tcase /-2 tcClose
	add typecheck.go:/^func tcClose/-0 \
		// tcClose typechecks an OCLOSE node.
	mv typecheck1:/^\tcase ir.OCOMPLEX:/+2,/^\tcase /-2 tcComplex
	add typecheck.go:/^func tcComplex/-0 \
		// tcComplex typechecks an OCOMPLEX node.
	mv typecheck1:/^\tcase ir.OREAL,/+2,/^\tcase /-2 tcRealImag
	add typecheck.go:/^func tcRealImag/-0 \
		// tcRealImag typechecks an OREAL or OIMAG node.
	mv typecheck1:/^\tcase ir.OCAP,/+2,/^\tcase /-2 tcLenCap
	add typecheck.go:/^func tcLenCap/-0 \
		// tcLenCap typechecks an OLEN or OCAP node.
	mv typecheck1:/^\tcase ir.OCALL:/+2,/^\tcase /-2 tcCall
	add typecheck.go:/^func tcCall/-0 \
		// tcCall typechecks an OCALL node.
	mv typecheck1:/^\tcase ir.OSLICE,/+2,/^\tcase /-3 tcSlice
	add typecheck.go:/^func tcSlice/-0 \
		// tcSlice typechecks an OSLICE or OSLICE3 node.
	# move type assertion above comment
	mv typecheck1:/^\tcase ir.OMAKESLICECOPY:/+/n := n/-+ typecheck1:/^\tcase ir.OMAKESLICECOPY:/+0
	mv typecheck1:/^\tcase ir.OMAKESLICECOPY:/+2,/^\tcase /-2 tcMakeSliceCopy
	add typecheck.go:/^func tcMakeSliceCopy/-0 \
		// tcMakeSliceCopy typechecks an OMAKESLICECOPY node.
	# move type assertion above comment
	mv typecheck1:/^\tcase ir.OSLICEHEADER:/+/n := n/-+ typecheck1:/^\tcase ir.OSLICEHEADER:/+0
	mv typecheck1:/^\tcase ir.OSLICEHEADER:/+2,/^\tcase /-2 tcSliceHeader
	add typecheck.go:/^func tcSliceHeader/-0 \
		// tcSliceHeader typechecks an OSLICEHEADER node.
	mv typecheck1:/^\tcase ir.OSEND:/+2,/^\tcase /-2 tcSend
	add typecheck.go:/^func tcSend/-0 \
		// tcSend typechecks an OSEND node.
	mv typecheck1:/^\tcase ir.ORECV:/+2,/^\tcase /-2 tcRecv
	add typecheck.go:/^func tcRecv/-0 \
		// tcRecv typechecks an ORECV node.
	mv typecheck1:/^\tcase ir.OINDEX:/+2,/^\tcase /-2 tcIndex
	add typecheck.go:/^func tcIndex/-0 \
		// tcIndex typechecks an OINDEX node.
	mv typecheck1:/^\tcase ir.ODOTTYPE:/+2,/^\tcase /-2 tcDotType
	add typecheck.go:/^func tcDotType/-0 \
		// tcDotType typechecks an ODOTTYPE node.
	mv typecheck1:/^\tcase ir.OXDOT,/+2,/^\tcase /-2 tcDot
	add typecheck.go:/^func tcDot/-0 \
		// tcDot typechecks an OXDOT or ODOT node.
	mv typecheck1:/^\tcase ir.OADDR:/+2,/^\tcase /-2 tcAddr
	add typecheck.go:/^func tcAddr/-0 \
		// tcAddr typechecks an OADDR node.
	mv typecheck1:/^\tcase ir.OBITNOT,/+2,/^\tcase /-3 tcUnaryArith
	add typecheck.go:/^func tcUnaryArith/-0 \
		// tcUnaryArith typechecks a unary arithmetic expression.
	mv typecheck1:/^\t\tir.OXOR:/+1,/^\tcase /-2 tcArith
	add typecheck.go:/^func tcArith/-0 \
		// tcArith typechecks a binary arithmetic expression.
	mv typecheck1:/^\tcase ir.ODEREF:/+2,/^\tcase /-2 tcStar
	add typecheck.go:/^func tcStar/-0 \
		// tcStar typechecks an ODEREF node, which may be an expression or a type.
	mv typecheck1:/^\tcase ir.OTFUNC:/+2,/^\tcase /-2 tcFuncType
	add typecheck.go:/^func tcFuncType/-0 \
		// tcFuncType typechecks an OTFUNC node.
	mv typecheck1:/^\tcase ir.OTINTER:/+2,/^\tcase /-2 tcInterfaceType
	add typecheck.go:/^func tcInterfaceType/-0 \
		// tcInterfaceType typechecks an OTINTER node.
	mv typecheck1:/^\tcase ir.OTSTRUCT:/+2,/^\tcase /-2 tcStructType
	add typecheck.go:/^func tcStructType/-0 \
		// tcStructType typechecks an OTSTRUCT node.
	mv typecheck1:/^\tcase ir.OTCHAN:/+2,/^\tcase /-2 tcChanType
	add typecheck.go:/^func tcChanType/-0 \
		// tcChanType typechecks an OTCHAN node.
	mv typecheck1:/^\tcase ir.OTMAP:/+2,/^\tcase /-2 tcMapType
	add typecheck.go:/^func tcMapType/-0 \
		// tcMapType typechecks an OTMAP node.
	mv typecheck1:/^\tcase ir.OTARRAY:/+2,/^\tcase /-2 tcArrayType
	add typecheck.go:/^func tcArrayType/-0 \
		// tcArrayType typechecks an OTARRAY node.
	mv typecheck1:/^\tcase ir.OTSLICE:/+2,/^\tcase /-2 tcSliceType
	add typecheck.go:/^func tcSliceType/-0 \
		// tcSliceType typechecks an OTSLICE node.
	mv \
		tcAssign \
		tcAssignList \
		tcFor \
		tcGoDefer \
		tcIf \
		tcRange \
		tcReturn \
		tcSelect \
		tcSend \
		tcSwitch \
		tcSwitchExpr \
		tcSwitchType \
		typeSet \
		typeSetEntry \
		typeSet.add \
		stmt1.go
	mv stmt1.go stmt.go

	mv \
		tcAddr \
		tcArith \
		tcArrayType \
		tcChanType \
		tcClosure \
		tcCompLit \
		tcConv \
		tcDot \
		tcDotType \
		tcFuncType \
		tcITab \
		tcIndex \
		tcInterfaceType \
		tcLenCap \
		tcMapType \
		tcRecv \
		tcSPtr \
		tcSlice \
		tcSliceHeader \
		tcSliceType \
		tcStar \
		tcStructType \
		tcUnaryArith \
		expr.go

	mv \
		tcClosure \
		tcCallPart \
		tcFunc \
		tcCall \
		tcAppend \
		tcClose \
		tcComplex \
		tcCopy \
		tcDelete \
		tcMake \
		tcMakeSliceCopy \
		tcNew \
		tcPanic \
		tcPrint \
		tcRealImag \
		tcRecover \
		func1.go
	mv func1.go func.go

	mv \
		tcArrayType \
		tcChanType \
		tcFuncType \
		tcInterfaceType \
		tcMapType \
		tcSliceType \
		tcStructType \
		type.go
'

Change-Id: I0fb0a3039005bc1783575291daff1e6c306895ff
Reviewed-on: https://go-review.googlesource.com/c/go/+/279429
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:38:34 +00:00
Russ Cox
b9693d7627 [dev.regabi] cmd/compile: split out package typecheck [generated]
This commit splits the typechecking logic into its own package,
the first of a sequence of CLs to break package gc into more
manageable units.

[git-generate]
cd src/cmd/compile/internal/gc
rf '
	# The binary import/export has to be part of typechecking,
	# because we load inlined function bodies lazily, but "exporter"
	# should not be. Move that out of bexport.go.
	mv exporter exporter.markObject exporter.markType export.go

	# Use the typechecking helpers, so that the calls left behind
	# in package gc do not need access to ctxExpr etc.
	ex {
		import "cmd/compile/internal/ir"

		# TODO(rsc): Should not be necessary.
		avoid TypecheckExpr
		avoid TypecheckStmt
		avoid TypecheckExprs
		avoid TypecheckStmts
		avoid TypecheckAssignExpr
		avoid TypecheckCallee

		var n ir.Node
		var ns []ir.Node
		typecheck(n, ctxExpr) -> TypecheckExpr(n)
		typecheck(n, ctxStmt) -> TypecheckStmt(n)
		typecheckslice(ns, ctxExpr) -> TypecheckExprs(ns)
		typecheckslice(ns, ctxStmt) -> TypecheckStmts(ns)
		typecheck(n, ctxExpr|ctxAssign) -> TypecheckAssignExpr(n)
		typecheck(n, ctxExpr|ctxCallee) -> TypecheckCallee(n)
	}

	# Move some typechecking API to typecheck.
	mv syslook LookupRuntime
	mv substArgTypes SubstArgTypes
	mv LookupRuntime SubstArgTypes syms.go

	mv conv Conv
	mv convnop ConvNop
	mv Conv ConvNop typecheck.go

	mv colasdefn AssignDefn
	mv colasname assignableName

	mv Target target.go

	mv initname autoexport exportsym dcl.go
	mv exportsym Export

	# Export API to be called from outside typecheck.
	# The ones with "Typecheck" prefixes will be renamed later to drop the prefix.
	mv adddot AddImplicitDots
	mv assignconv AssignConv
	mv expandmeth CalcMethods
	mv capturevarscomplete CaptureVarsComplete
	mv checkMapKeys CheckMapKeys
	mv checkreturn CheckReturn
	mv dclcontext DeclContext
	mv dclfunc DeclFunc
	mv declare Declare
	mv dotImportRefs DotImportRefs
	mv declImporter DeclImporter
	mv variter DeclVars
	mv defaultlit DefaultLit
	mv evalConst EvalConst
	mv expandInline ImportBody
	mv finishUniverse declareUniverse
	mv funcbody FinishFuncBody
	mv funchdr StartFuncBody
	mv indexconst IndexConst
	mv initTodo InitTodoFunc
	mv lookup Lookup
	mv resolve Resolve
	mv lookupN LookupNum
	mv nodAddr NodAddr
	mv nodAddrAt NodAddrAt
	mv nodnil NodNil
	mv origBoolConst OrigBool
	mv origConst OrigConst
	mv origIntConst OrigInt
	mv redeclare Redeclared
	mv tostruct NewStructType
	mv functype NewFuncType
	mv methodfunc NewMethodType
	mv structargs NewFuncParams
	mv temp Temp
	mv tempAt TempAt
	mv typecheckok TypecheckAllowed
	mv typecheck _typecheck # make room for typecheck pkg
	mv typecheckinl TypecheckImportedBody
	mv typecheckFunc TypecheckFunc
	mv iimport ReadImports
	mv iexport WriteExports
	mv sysfunc LookupRuntimeFunc
	mv sysvar LookupRuntimeVar

	# Move function constructors to typecheck.
	mv mkdotargslice MakeDotArgs
	mv fixVariadicCall FixVariadicCall
	mv closureType ClosureType
	mv partialCallType PartialCallType
	mv capturevars CaptureVars
	mv MakeDotArgs FixVariadicCall ClosureType PartialCallType CaptureVars typecheckclosure func.go

	mv autolabel AutoLabel
	mv AutoLabel syms.go

	mv Dlist dlist
	mv Symlink symlink

	mv \
		AssignDefn assignableName \
		AssignConv \
		CaptureVarsComplete \
		DeclContext \
		DeclFunc \
		DeclImporter \
		DeclVars \
		Declare \
		DotImportRefs \
		Export \
		InitTodoFunc \
		Lookup \
		LookupNum \
		LookupRuntimeFunc \
		LookupRuntimeVar \
		NewFuncParams \
		NewName \
		NodAddr \
		NodAddrAt \
		NodNil \
		Redeclared \
		StartFuncBody \
		FinishFuncBody \
		TypecheckImportedBody \
		AddImplicitDots \
		CalcMethods \
		CheckFuncStack \
		NewFuncType \
		NewMethodType \
		NewStructType \
		TypecheckAllowed \
		Temp \
		TempAt \
		adddot1 \
		dotlist \
		addmethod \
		assignconvfn \
		assignop \
		autotmpname \
		autoexport \
		bexport.go \
		checkdupfields \
		checkembeddedtype \
		closurename \
		convertop \
		declare_typegen \
		decldepth \
		dlist \
		dotpath \
		expand0 \
		expand1 \
		expandDecl \
		fakeRecvField \
		fnpkg \
		funcStack \
		funcStackEnt \
		funcarg \
		funcarg2 \
		funcargs \
		funcargs2 \
		globClosgen \
		ifacelookdot \
		implements \
		importalias \
		importconst \
		importfunc \
		importobj \
		importsym \
		importtype \
		importvar \
		inimport \
		initname \
		isptrto \
		loadsys \
		lookdot0 \
		lookdot1 \
		makepartialcall \
		okfor \
		okforlen \
		operandType \
		slist \
		symlink \
		tointerface \
		typeSet \
		typeSet.add \
		typeSetEntry \
		typecheckExprSwitch \
		typecheckTypeSwitch \
		typecheckpartialcall \
		typecheckrange \
		typecheckrangeExpr \
		typecheckselect \
		typecheckswitch \
		vargen \
		builtin.go \
		builtin_test.go \
		const.go \
		func.go \
		iexport.go \
		iimport.go \
		mapfile_mmap.go \
		syms.go \
		target.go \
		typecheck.go \
		unsafe.go \
		universe.go \
		cmd/compile/internal/typecheck
'
rm gen.go types.go types_acc.go

sed -i '' 's/package gc/package typecheck/' mapfile_read.go mkbuiltin.go
mv mapfile_read.go ../typecheck # not part of default build
mv mkbuiltin.go ../typecheck # package main helper
mv builtin ../typecheck

cd ../typecheck
mv dcl.go dcl1.go
mv typecheck.go typecheck1.go
mv universe.go universe1.go
rf '
	# Sweep some small files into larger ones.
	# "mv sym... file1.go file.go" (after the mv file1.go file.go above)
	# lets us insert sym... at the top of file.go.
	mv okfor okforeq universe1.go universe.go
	mv DeclContext vargen dcl1.go Temp TempAt autotmpname NewMethodType dcl.go
	mv InitTodoFunc inimport decldepth TypecheckAllowed typecheck1.go typecheck.go
	mv inl.go closure.go func.go
	mv range.go select.go swt.go stmt.go
	mv Lookup loadsys LookupRuntimeFunc LookupRuntimeVar syms.go
	mv unsafe.go const.go

	mv TypecheckAssignExpr AssignExpr
	mv TypecheckExpr Expr
	mv TypecheckStmt Stmt
	mv TypecheckExprs Exprs
	mv TypecheckStmts Stmts
	mv TypecheckCall Call
	mv TypecheckCallee Callee
	mv _typecheck check
	mv TypecheckFunc Func
	mv TypecheckFuncBody FuncBody
	mv TypecheckImports AllImportedBodies
	mv TypecheckImportedBody ImportedBody
	mv TypecheckInit Init
	mv TypecheckPackage Package
'
rm gen.go go.go init.go main.go reflect.go

Change-Id: Iea6a7aaf6407d690670ec58aeb36cc0b280f80b0
Reviewed-on: https://go-review.googlesource.com/c/go/+/279236
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:38:26 +00:00
Russ Cox
dac0de3748 [dev.regabi] cmd/compile: move type size calculations into package types [generated]
To break up package gc, we need to put these calculations somewhere
lower in the import graph, either an existing or new package. Package types
already needs this code and is using hacks to get it without an import cycle.
We can remove the hacks and set up for the new package gc by moving the
code into package types itself.

[git-generate]
cd src/cmd/compile/internal/gc
rf '
	# Remove old import cycle hacks in gc.
	rm TypecheckInit:/types.Widthptr =/-0,/types.Dowidth =/+0 \
		../ssa/export_test.go:/types.Dowidth =/-+
	ex {
		import "cmd/compile/internal/types"
		types.Widthptr -> Widthptr
		types.Dowidth -> dowidth
	}

	# Disable CalcSize in tests instead of base.Fatalf
	sub dowidth:/base.Fatalf\("dowidth without betypeinit"\)/ \
		// Assume this is a test. \
		return

	# Move size calculation into cmd/compile/internal/types
	mv Widthptr PtrSize
	mv Widthreg RegSize
	mv slicePtrOffset SlicePtrOffset
	mv sliceLenOffset SliceLenOffset
	mv sliceCapOffset SliceCapOffset
	mv sizeofSlice SliceSize
	mv sizeofString StringSize
	mv skipDowidthForTracing SkipSizeForTracing
	mv dowidth CalcSize
	mv checkwidth CheckSize
	mv widstruct calcStructOffset
	mv sizeCalculationDisabled CalcSizeDisabled
	mv defercheckwidth DeferCheckSize
	mv resumecheckwidth ResumeCheckSize
	mv typeptrdata PtrDataSize
	mv \
		PtrSize RegSize SlicePtrOffset SkipSizeForTracing typePos align.go PtrDataSize \
		size.go
	mv size.go cmd/compile/internal/types
'

: # Remove old import cycle hacks in types.
cd ../types
rf '
	ex {
		Widthptr -> PtrSize
		Dowidth -> CalcSize
	}
	rm Widthptr Dowidth
'

Change-Id: Ib96cdc6bda2617235480c29392ea5cfb20f60cd8
Reviewed-on: https://go-review.googlesource.com/c/go/+/279234
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:38:20 +00:00
Russ Cox
527a1895d6 [dev.regabi] cmd/compile: move helpers into package ir [generated]
[git-generate]
cd src/cmd/compile/internal/gc
sed -i '' 's/TestBuiltin.*/& t.Skip("mkbuiltin needs fixing")/' builtin_test.go
gofmt -w builtin_test.go
rf '
	# Inline a few little-used constructors to avoid bringing them.
	ex {
		import "cmd/compile/internal/base"
		import "cmd/compile/internal/ir"
		import "cmd/compile/internal/types"
		import "cmd/internal/src"

		var typ *types.Type
		var sym *types.Sym
		var str string
		symfield(sym, typ) -> ir.NewField(base.Pos, sym, nil, typ)
		anonfield(typ) -> ir.NewField(base.Pos, nil, nil, typ)
		namedfield(str, typ) -> ir.NewField(base.Pos, lookup(str), nil, typ)

		var cp *ir.CallPartExpr
		callpartMethod(cp) -> cp.Method
		var n ir.Node
		callpartMethod(n) -> n.(*ir.CallPartExpr).Method

		var ns []ir.Node
		liststmt(ns) -> ir.NewBlockStmt(src.NoXPos, ns)
	}
	rm symfield anonfield namedfield liststmt callpartMethod

	mv maxStackVarSize MaxStackVarSize
	mv maxImplicitStackVarSize MaxImplicitStackVarSize
	mv smallArrayBytes MaxSmallArraySize
	mv MaxStackVarSize cfg.go

	mv nodbool NewBool
	mv nodintconst NewInt
	mv nodstr NewString
	mv NewBool NewInt NewString const.go

	mv Mpprec ConstPrec
	mv bigFloatVal BigFloat
	mv doesoverflow ConstOverflow
	mv isGoConst IsConstNode
	mv smallintconst IsSmallIntConst

	mv isZero IsZero
	mv islvalue IsAssignable
	mv staticValue StaticValue
	mv samesafeexpr SameSafeExpr
	mv checkPtr ShouldCheckPtr
	mv isReflectHeaderDataField IsReflectHeaderDataField
	mv paramNnames ParamNames
	mv methodSym MethodSym
	mv methodSymSuffix MethodSymSuffix
	mv methodExprFunc MethodExprFunc
	mv methodExprName MethodExprName
	mv IsZero IsAssignable StaticValue staticValue1 reassigned \
		IsIntrinsicCall \
		SameSafeExpr ShouldCheckPtr IsReflectHeaderDataField \
		ParamNames MethodSym MethodSymSuffix \
		MethodExprName MethodExprFunc \
		expr.go

	mv Curfn CurFunc
	mv funcsymname FuncSymName
	mv newFuncNameAt NewFuncNameAt
	mv setNodeNameFunc MarkFunc
	mv CurFunc FuncSymName NewFuncNameAt MarkFunc func.go

	mv isParamStackCopy IsParamStackCopy
	mv isParamHeapCopy IsParamHeapCopy
	mv nodfp RegFP
	mv IsParamStackCopy IsParamHeapCopy RegFP name.go

	mv hasUniquePos HasUniquePos
	mv setlineno SetPos
	mv initExpr InitExpr
	mv hasNamedResults HasNamedResults
	mv outervalue OuterValue
	mv HasNamedResults HasUniquePos SetPos InitExpr OuterValue EscNever node.go

	mv visitBottomUp VisitFuncsBottomUp # scc.go

	mv cfg.go \
		NewBool NewInt NewString \ # parts of const.go
		ConstPrec BigFloat ConstOverflow IsConstNode IsSmallIntConst \
		expr.go func.go name.go node.go scc.go \
		cmd/compile/internal/ir

'

Change-Id: I13402c5a2cedbf78d993a1eae2940718f23ac166
Reviewed-on: https://go-review.googlesource.com/c/go/+/279421
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:38:14 +00:00
Russ Cox
65c4c6dfb2 [dev.regabi] cmd/compile: group known symbols, packages, names [generated]
There are a handful of pre-computed magic symbols known by
package gc, and we need a place to store them.

If we keep them together, the need for type *ir.Name means that
package ir is the lowest package in the import hierarchy that they
can go in. And package ir needs gopkg for methodSymSuffix
(in a later CL), so they can't go any higher either, at least not all together.
So package ir it is.

Rather than dump them all into the top-level package ir
namespace, however, we introduce global structs, Syms, Pkgs, and Names,
and make the known symbols, packages, and names fields of those.

[git-generate]
cd src/cmd/compile/internal/gc

rf '
	add go.go:$ \
		// Names holds known names. \
		var Names struct{} \
		\
		// Syms holds known symbols. \
		var Syms struct {} \
		\
		// Pkgs holds known packages. \
		var Pkgs struct {} \

	mv staticuint64s Names.Staticuint64s
	mv zerobase Names.Zerobase

	mv assertE2I Syms.AssertE2I
	mv assertE2I2 Syms.AssertE2I2
	mv assertI2I Syms.AssertI2I
	mv assertI2I2 Syms.AssertI2I2
	mv deferproc Syms.Deferproc
	mv deferprocStack Syms.DeferprocStack
	mv Deferreturn Syms.Deferreturn
	mv Duffcopy Syms.Duffcopy
	mv Duffzero Syms.Duffzero
	mv gcWriteBarrier Syms.GCWriteBarrier
	mv goschedguarded Syms.Goschedguarded
	mv growslice Syms.Growslice
	mv msanread Syms.Msanread
	mv msanwrite Syms.Msanwrite
	mv msanmove Syms.Msanmove
	mv newobject Syms.Newobject
	mv newproc Syms.Newproc
	mv panicdivide Syms.Panicdivide
	mv panicshift Syms.Panicshift
	mv panicdottypeE Syms.PanicdottypeE
	mv panicdottypeI Syms.PanicdottypeI
	mv panicnildottype Syms.Panicnildottype
	mv panicoverflow Syms.Panicoverflow
	mv raceread Syms.Raceread
	mv racereadrange Syms.Racereadrange
	mv racewrite Syms.Racewrite
	mv racewriterange Syms.Racewriterange
	mv SigPanic Syms.SigPanic
	mv typedmemclr Syms.Typedmemclr
	mv typedmemmove Syms.Typedmemmove
	mv Udiv Syms.Udiv
	mv writeBarrier Syms.WriteBarrier
	mv zerobaseSym Syms.Zerobase
	mv arm64HasATOMICS Syms.ARM64HasATOMICS
	mv armHasVFPv4 Syms.ARMHasVFPv4
	mv x86HasFMA Syms.X86HasFMA
	mv x86HasPOPCNT Syms.X86HasPOPCNT
	mv x86HasSSE41 Syms.X86HasSSE41
	mv WasmDiv Syms.WasmDiv
	mv WasmMove Syms.WasmMove
	mv WasmZero Syms.WasmZero
	mv WasmTruncS Syms.WasmTruncS
	mv WasmTruncU Syms.WasmTruncU

	mv gopkg Pkgs.Go
	mv itabpkg Pkgs.Itab
	mv itablinkpkg Pkgs.Itablink
	mv mappkg Pkgs.Map
	mv msanpkg Pkgs.Msan
	mv racepkg Pkgs.Race
	mv Runtimepkg Pkgs.Runtime
	mv trackpkg Pkgs.Track
	mv unsafepkg Pkgs.Unsafe

	mv Names Syms Pkgs symtab.go
	mv symtab.go cmd/compile/internal/ir
'

Change-Id: Ic143862148569a3bcde8e70b26d75421aa2d00f3
Reviewed-on: https://go-review.googlesource.com/c/go/+/279235
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:38:07 +00:00
Russ Cox
9ee309255a [dev.regabi] cmd/compile: move helpers into package types [generated]
[git-generate]
cd src/cmd/compile/internal/gc
rf '
	# Type hash (formatting).
	mv typehash TypeHash
	mv TypeHash fmt.go

	# Method sorting.
	mv methcmp MethodsByName
	mv MethodsByName MethodsByName.Len MethodsByName.Swap \
		MethodsByName.Less sort.go

	# Move version check into types.
	# A little surprising, but its keyed off the types.Pkg.
	ex {
		import "cmd/compile/internal/types"
		var p *types.Pkg
		var major, minor int
		langSupported(major, minor, p) -> AllowsGoVersion(p, major, minor)
	}
	rm langSupported
	mv checkLang ParseLangFlag
	mv lang langWant AllowsGoVersion ParseLangFlag \
		parseLang currentLang goVersionRE goversion.go

	mv testdclstack CheckDclstack
	mv CheckDclstack scope.go

	mv algtype1 AlgType
	mv isComplex IsComplex
	mv isFloat IsFloat
	mv isInt IsInt
	mv issimple IsSimple
	mv okforcmp IsOrdered
	mv floatForComplex FloatForComplex
	mv complexForFloat ComplexForFloat
	mv isdirectiface IsDirectIface
	mv isifacemethod IsInterfaceMethod
	mv isMethodApplicable IsMethodApplicable
	mv ispaddedfield IsPaddedField
	mv isRuntimePkg IsRuntimePkg
	mv isReflectPkg IsReflectPkg
	mv methtype ReceiverBaseType
	mv typesymname TypeSymName
	mv typesym TypeSym
	mv typeLookup TypeSymLookup
	mv IsAlias IsDotAlias
	mv isreflexive IsReflexive
	mv simtype SimType

	# The type1.go here is to avoid an undiagnosed bug in rf
	# that does not get the follow-up typechecking right if we
	# move directly to type.go during the mv into package types below.
	mv \
		IsInt IsOrdered IsReflexive \
		IsDirectIface IsInterfaceMethod IsMethodApplicable IsPaddedField \
		IsRuntimePkg IsReflectPkg ReceiverBaseType \
		FloatForComplex ComplexForFloat \
		TypeSym TypeSymLookup TypeSymName \
		typepkg SimType \
		type1.go

	# The alg1.go here is because we are only moving part of alg.go.
	mv typeHasNoAlg TypeHasNoAlg
	mv AlgKind ANOEQ AlgType TypeHasNoAlg IsComparable IncomparableField IsPaddedField alg1.go

	mv IsDotAlias pkg.go

	mv alg1.go algkind_string.go fmt.go goversion.go pkg.go \
		CheckDclstack \ # scope.go
		sort.go type1.go \
		cmd/compile/internal/types
'
cd ../types
rf '
	mv IsDclstackValid isDclstackValid
	mv alg1.go alg.go
	mv type1.go type.go
'

Change-Id: I8bd53b21c7bdd1770e1b525de32f136833e84c9d
Reviewed-on: https://go-review.googlesource.com/c/go/+/279307
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:38:01 +00:00
Russ Cox
ead4957892 [dev.regabi] cmd/compile: move helpers into package base [generated]
[git-generate]
cd src/cmd/compile/internal/gc
rf '
	# Move EnableTrace constant into base, with the other flags.
	mv enableTrace EnableTrace
	mv EnableTrace base.go

	# Move compilation checks to base.
	mv instrumenting Instrumenting
	mv ispkgin Compiling
	mv omit_pkgs NoInstrumentPkgs
	mv norace_inst_pkgs NoRacePkgs
	mv Instrumenting Compiling NoInstrumentPkgs NoRacePkgs base.go

	# Move AutogeneratedPos to package base, next to Pos.
	mv autogeneratedPos AutogeneratedPos
	mv AutogeneratedPos print.go

	mv timings Timer

	mv base.go print.go timings.go cmd/compile/internal/base
'
cd ../base
rf '
	mv Instrumenting Flag.Cfg.Instrumenting
'

Change-Id: I534437fa75857d31531fc499d833c9930c0a06d0
Reviewed-on: https://go-review.googlesource.com/c/go/+/279420
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:37:55 +00:00
Russ Cox
440308ffd7 [dev.regabi] cmd/compile: simplify Nodes usage [generated]
Now that Nodes is a slice, most of the methods can be removed
in favor of direct slice operations, reducing the new API that must
be understood to:

	Copy
	Take
	Append
	Prepend
	Format

Passes buildall w/ toolstash -cmp.

[git-generate]
cd src/cmd/compile/internal/ir
rf '
ex . ../gc {
	var ns Nodes
	var pns *Nodes
	var n, n2, n3 Node
	var i int
	var slice []Node

	ns.Len() -> len(ns)
	ns.Slice() -> ns
	ns.First() -> ns[0]
	ns.Second() -> ns[1]
	ns.Index(i) -> ns[i]
	ns.Addr(i) -> &ns[i]
	ns.SetIndex(i, n) -> ns[i] = n
	ns.SetFirst(n) -> ns[0] = n
	ns.SetSecond(n) -> ns[1] = n
	ns.Set1(n) -> ns = []Node{n}
	ns.Set2(n, n2) -> ns = []Node{n, n2}
	ns.Set3(n, n2, n3) -> ns = []Node{n, n2, n3}
	ns.Set1(n) -> ns = []Node{n}
	ns.Set2(n, n2) -> ns = []Node{n, n2}
	ns.Set3(n, n2, n3) -> ns = []Node{n, n2, n3}
	AsNodes(slice) -> Nodes(slice)
	ns.AppendNodes(pns) -> ns.Append(pns.Take()...)
	ns.MoveNodes(pns) -> ns = pns.Take()
}

rm \
	Nodes.Len Nodes.Slice \
	Nodes.First Nodes.Second Nodes.Index Nodes.Addr \
	Nodes.SetIndex Nodes.SetFirst Nodes.SetSecond \
	Nodes.Set1 Nodes.Set2 Nodes.Set3 \
	AsNodes \
	Nodes.AppendNodes Nodes.MoveNodes
'

Change-Id: Iee86434ced52e67861c3fa71bdd6d994a8cba735
Reviewed-on: https://go-review.googlesource.com/c/go/+/277936
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:37:48 +00:00
Russ Cox
f9d373720e [dev.regabi] cmd/compile: remove Left, Right etc methods [generated]
Now that the generic graph structure methods - Left, Right, and so on -
have been removed from the Node interface, each implementation's uses
can be replaced with direct field access, using more specific names,
and the methods themselves can be deleted.

Passes buildall w/ toolstash -cmp.

[git-generate]

cd src/cmd/compile/internal/ir
rf '
	mv Func.iota Func.Iota_
	mv Name.fn Name.Func_
'

cd ../gc
rf '
ex . ../ir {
        import "cmd/compile/internal/ir"
        import "cmd/compile/internal/types"

        var ns ir.Nodes
        var b bool
        var i64 int64
        var n ir.Node
        var op ir.Op
        var sym *types.Sym
        var class ir.Class

        var decl *ir.Decl
        decl.Left()         -> decl.X
        decl.SetLeft(n)     -> decl.X = n

        var asl *ir.AssignListStmt
        asl.List()          -> asl.Lhs
        asl.PtrList()       -> &asl.Lhs
        asl.SetList(ns)     -> asl.Lhs = ns
        asl.Rlist()         -> asl.Rhs
        asl.PtrRlist()      -> &asl.Rhs
        asl.SetRlist(ns)    -> asl.Rhs = ns
        asl.Colas()         -> asl.Def
        asl.SetColas(b)     -> asl.Def = b

        var as *ir.AssignStmt
        as.Left()           -> as.X
        as.SetLeft(n)       -> as.X = n
        as.Right()          -> as.Y
        as.SetRight(n)      -> as.Y = n
        as.Colas()          -> as.Def
        as.SetColas(b)      -> as.Def = b

        var ao *ir.AssignOpStmt
        ao.Left()           -> ao.X
        ao.SetLeft(n)       -> ao.X = n
        ao.Right()          -> ao.Y
        ao.SetRight(n)      -> ao.Y = n
        ao.SubOp()          -> ao.AsOp
        ao.SetSubOp(op)     -> ao.AsOp = op
        ao.Implicit()       -> ao.IncDec
        ao.SetImplicit(b)   -> ao.IncDec = b

        var bl *ir.BlockStmt
        bl.List()           -> bl.List_
        bl.PtrList()        -> &bl.List_
        bl.SetList(ns)      -> bl.List_ = ns

        var br *ir.BranchStmt
        br.Sym()            -> br.Label
        br.SetSym(sym)      -> br.Label = sym

        var cas *ir.CaseStmt
        cas.List()          -> cas.List_
        cas.PtrList()       -> &cas.List_
        cas.SetList(ns)     -> cas.List_ = ns
        cas.Body()          -> cas.Body_
        cas.PtrBody()       -> &cas.Body_
        cas.SetBody(ns)     -> cas.Body_ = ns
        cas.Rlist()         -> cas.Vars
        cas.PtrRlist()      -> &cas.Vars
        cas.SetRlist(ns)    -> cas.Vars = ns
        cas.Left()          -> cas.Comm
        cas.SetLeft(n)      -> cas.Comm = n

        var fr *ir.ForStmt
        fr.Sym()            -> fr.Label
        fr.SetSym(sym)      -> fr.Label = sym
        fr.Left()           -> fr.Cond
        fr.SetLeft(n)       -> fr.Cond = n
        fr.Right()          -> fr.Post
        fr.SetRight(n)      -> fr.Post = n
        fr.Body()           -> fr.Body_
        fr.PtrBody()        -> &fr.Body_
        fr.SetBody(ns)      -> fr.Body_ = ns
        fr.List()           -> fr.Late
        fr.PtrList()        -> &fr.Late
        fr.SetList(ns)      -> fr.Late = ns
        fr.HasBreak()       -> fr.HasBreak_
        fr.SetHasBreak(b)   -> fr.HasBreak_ = b

        var gs *ir.GoDeferStmt
        gs.Left()           -> gs.Call
        gs.SetLeft(n)       -> gs.Call = n

        var ifs *ir.IfStmt
        ifs.Left()          -> ifs.Cond
        ifs.SetLeft(n)      -> ifs.Cond = n
        ifs.Body()          -> ifs.Body_
        ifs.PtrBody()       -> &ifs.Body_
        ifs.SetBody(ns)     -> ifs.Body_ = ns
        ifs.Rlist()         -> ifs.Else
        ifs.PtrRlist()      -> &ifs.Else
        ifs.SetRlist(ns)    -> ifs.Else = ns
        ifs.Likely()        -> ifs.Likely_
        ifs.SetLikely(b)    -> ifs.Likely_ = b

        var im *ir.InlineMarkStmt
        im.Offset()         -> im.Index
        im.SetOffset(i64)   -> im.Index = i64

        var lab *ir.LabelStmt
        lab.Sym()           -> lab.Label
        lab.SetSym(sym)     -> lab.Label = sym

        var rng *ir.RangeStmt
        rng.Sym()           -> rng.Label
        rng.SetSym(sym)     -> rng.Label = sym
        rng.Right()         -> rng.X
        rng.SetRight(n)     -> rng.X = n
        rng.Body()          -> rng.Body_
        rng.PtrBody()       -> &rng.Body_
        rng.SetBody(ns)     -> rng.Body_ = ns
        rng.List()          -> rng.Vars
        rng.PtrList()       -> &rng.Vars
        rng.SetList(ns)     -> rng.Vars = ns
        rng.HasBreak()      -> rng.HasBreak_
        rng.SetHasBreak(b)  -> rng.HasBreak_ = b
        rng.Colas()         -> rng.Def
        rng.SetColas(b)     -> rng.Def = b

        var ret *ir.ReturnStmt
        ret.List()          -> ret.Results
        ret.PtrList()       -> &ret.Results
        ret.SetList(ns)     -> ret.Results = ns

        var sel *ir.SelectStmt
        sel.List()          -> sel.Cases
        sel.PtrList()       -> &sel.Cases
        sel.SetList(ns)     -> sel.Cases = ns
        sel.Sym()           -> sel.Label
        sel.SetSym(sym)     -> sel.Label = sym
        sel.HasBreak()      -> sel.HasBreak_
        sel.SetHasBreak(b)  -> sel.HasBreak_ = b
        sel.Body()          -> sel.Compiled
        sel.PtrBody()       -> &sel.Compiled
        sel.SetBody(ns)     -> sel.Compiled = ns

        var send *ir.SendStmt
        send.Left()         -> send.Chan
        send.SetLeft(n)     -> send.Chan = n
        send.Right()        -> send.Value
        send.SetRight(n)    -> send.Value = n

        var sw *ir.SwitchStmt
        sw.Left()           -> sw.Tag
        sw.SetLeft(n)       -> sw.Tag = n
        sw.List()           -> sw.Cases
        sw.PtrList()        -> &sw.Cases
        sw.SetList(ns)      -> sw.Cases = ns
        sw.Body()           -> sw.Compiled
        sw.PtrBody()        -> &sw.Compiled
        sw.SetBody(ns)      -> sw.Compiled = ns
        sw.Sym()            -> sw.Label
        sw.SetSym(sym)      -> sw.Label = sym
        sw.HasBreak()       -> sw.HasBreak_
        sw.SetHasBreak(b)   -> sw.HasBreak_ = b

        var tg *ir.TypeSwitchGuard
        tg.Left()           -> tg.Tag
        tg.SetLeft(nil)     -> tg.Tag = nil
        tg.SetLeft(n)       -> tg.Tag = n.(*ir.Ident)
        tg.Right()          -> tg.X
        tg.SetRight(n)      -> tg.X = n

        var adds *ir.AddStringExpr
        adds.List()         -> adds.List_
        adds.PtrList()      -> &adds.List_
        adds.SetList(ns)    -> adds.List_ = ns

        var addr *ir.AddrExpr
        addr.Left()         -> addr.X
        addr.SetLeft(n)     -> addr.X = n
        addr.Right()        -> addr.Alloc
        addr.SetRight(n)    -> addr.Alloc = n

        var bin *ir.BinaryExpr
        bin.Left()          -> bin.X
        bin.SetLeft(n)      -> bin.X = n
        bin.Right()         -> bin.Y
        bin.SetRight(n)     -> bin.Y = n

        var log *ir.LogicalExpr
        log.Left()          -> log.X
        log.SetLeft(n)      -> log.X = n
        log.Right()         -> log.Y
        log.SetRight(n)     -> log.Y = n

        var call *ir.CallExpr
        call.Left()         -> call.X
        call.SetLeft(n)     -> call.X = n
        call.List()         -> call.Args
        call.PtrList()      -> &call.Args
        call.SetList(ns)    -> call.Args = ns
        call.Rlist()        -> call.Rargs
        call.PtrRlist()     -> &call.Rargs
        call.SetRlist(ns)   -> call.Rargs = ns
        call.IsDDD()        -> call.DDD
        call.SetIsDDD(b)    -> call.DDD = b
        call.NoInline()     -> call.NoInline_
        call.SetNoInline(b) -> call.NoInline_ = b
        call.Body()         -> call.Body_
        call.PtrBody()      -> &call.Body_
        call.SetBody(ns)    -> call.Body_ = ns

        var cp *ir.CallPartExpr
        cp.Func()           -> cp.Func_
        cp.Left()           -> cp.X
        cp.SetLeft(n)       -> cp.X = n
        cp.Sym()            -> cp.Method.Sym

        var clo *ir.ClosureExpr
        clo.Func()          -> clo.Func_

        var cr *ir.ClosureReadExpr
        cr.Offset()         -> cr.Offset_

        var cl *ir.CompLitExpr
        cl.Right()          -> cl.Ntype
        cl.SetRight(nil)    -> cl.Ntype = nil
        cl.SetRight(n)      -> cl.Ntype = ir.Node(n).(ir.Ntype)
        cl.List()           -> cl.List_
        cl.PtrList()        -> &cl.List_
        cl.SetList(ns)      -> cl.List_ = ns

        var conv *ir.ConvExpr
        conv.Left()         -> conv.X
        conv.SetLeft(n)     -> conv.X = n

        var ix *ir.IndexExpr
        ix.Left()           -> ix.X
        ix.SetLeft(n)       -> ix.X = n
        ix.Right()          -> ix.Index
        ix.SetRight(n)      -> ix.Index = n
        ix.IndexMapLValue() -> ix.Assigned
        ix.SetIndexMapLValue(b) -> ix.Assigned = b

        var kv *ir.KeyExpr
        kv.Left()           -> kv.Key
        kv.SetLeft(n)       -> kv.Key = n
        kv.Right()          -> kv.Value
        kv.SetRight(n)      -> kv.Value = n

        var sk *ir.StructKeyExpr
        sk.Sym()            -> sk.Field
        sk.SetSym(sym)      -> sk.Field = sym
        sk.Left()           -> sk.Value
        sk.SetLeft(n)       -> sk.Value = n
        sk.Offset()         -> sk.Offset_
        sk.SetOffset(i64)   -> sk.Offset_ = i64

        var ic *ir.InlinedCallExpr
        ic.Body()           -> ic.Body_
        ic.PtrBody()        -> &ic.Body_
        ic.SetBody(ns)      -> ic.Body_ = ns
        ic.Rlist()          -> ic.ReturnVars
        ic.PtrRlist()       -> &ic.ReturnVars
        ic.SetRlist(ns)     -> ic.ReturnVars = ns

        var mak *ir.MakeExpr
        mak.Left()          -> mak.Len
        mak.SetLeft(n)      -> mak.Len = n
        mak.Right()         -> mak.Cap
        mak.SetRight(n)     -> mak.Cap = n

        var par *ir.ParenExpr
        par.Left()          -> par.X
        par.SetLeft(n)      -> par.X = n

        var res *ir.ResultExpr
        res.Offset()        -> res.Offset_
        res.SetOffset(i64)  -> res.Offset_ = i64

        var dot *ir.SelectorExpr
        dot.Left()          -> dot.X
        dot.SetLeft(n)      -> dot.X = n
        dot.Sym()           -> dot.Sel
        dot.SetSym(sym)     -> dot.Sel = sym
        dot.Offset()        -> dot.Offset_
        dot.SetOffset(i64)  -> dot.Offset_ = i64

        var sl *ir.SliceExpr
        sl.Left()           -> sl.X
        sl.SetLeft(n)       -> sl.X = n
        sl.List()           -> sl.List_
        sl.PtrList()        -> &sl.List_
        sl.SetList(ns)      -> sl.List_ = ns

        var sh *ir.SliceHeaderExpr
        sh.Left()           -> sh.Ptr
        sh.SetLeft(n)       -> sh.Ptr = n
        sh.List()           -> sh.LenCap_
        sh.PtrList()        -> &sh.LenCap_
        sh.SetList(ns)      -> sh.LenCap_ = ns

        var st *ir.StarExpr
        st.Left()           -> st.X
        st.SetLeft(n)       -> st.X = n

        var ta *ir.TypeAssertExpr
        ta.Left()           -> ta.X
        ta.SetLeft(n)       -> ta.X = n
        ta.Right()          -> ta.Ntype
        ta.SetRight(n)    -> ta.Ntype = n
        ta.List()           -> ta.Itab
        ta.PtrList()        -> &ta.Itab
        ta.SetList(ns)      -> ta.Itab = ns

        var u *ir.UnaryExpr
        u.Left()            -> u.X
        u.SetLeft(n)        -> u.X = n

        var fn *ir.Func
        fn.Body()           -> fn.Body_
        fn.PtrBody()        -> &fn.Body_
        fn.SetBody(ns)      -> fn.Body_ = ns
        fn.Iota()           -> fn.Iota_
        fn.SetIota(i64)     -> fn.Iota_ = i64
        fn.Func()           -> fn

        var nam *ir.Name
        nam.SubOp()         -> nam.BuiltinOp
        nam.SetSubOp(op)    -> nam.BuiltinOp = op
        nam.Class()         -> nam.Class_
        nam.SetClass(class) -> nam.Class_ = class
        nam.Func()          -> nam.Func_
        nam.Offset()        -> nam.Offset_
        nam.SetOffset(i64)  -> nam.Offset_ = i64
}

ex . ../ir {
        import "cmd/compile/internal/ir"

        var n ir.Nodes

        (&n).Append         -> n.Append
        (&n).AppendNodes    -> n.AppendNodes
        (&n).MoveNodes      -> n.MoveNodes
        (&n).Prepend        -> n.Prepend
        (&n).Set            -> n.Set
        (&n).Set1           -> n.Set1
        (&n).Set2           -> n.Set2
        (&n).Set3           -> n.Set3

        var ntype ir.Ntype
        ir.Node(ntype).(ir.Ntype) -> ntype
}
'

cd ../ir
rf '
rm \
        Decl.Left Decl.SetLeft \
        AssignListStmt.List AssignListStmt.PtrList AssignListStmt.SetList \
        AssignListStmt.Rlist AssignListStmt.PtrRlist AssignListStmt.SetRlist \
        AssignListStmt.Colas AssignListStmt.SetColas \
        AssignStmt.Left AssignStmt.SetLeft \
        AssignStmt.Right AssignStmt.SetRight \
        AssignStmt.Colas AssignStmt.SetColas \
        AssignOpStmt.Left AssignOpStmt.SetLeft \
        AssignOpStmt.Right AssignOpStmt.SetRight \
        AssignOpStmt.SubOp AssignOpStmt.SetSubOp \
        AssignOpStmt.Implicit AssignOpStmt.SetImplicit \
        BlockStmt.List BlockStmt.PtrList BlockStmt.SetList \
        BranchStmt.SetSym \
        CaseStmt.List CaseStmt.PtrList CaseStmt.SetList \
        CaseStmt.Body CaseStmt.PtrBody CaseStmt.SetBody \
        CaseStmt.Rlist CaseStmt.PtrRlist CaseStmt.SetRlist \
        CaseStmt.Left CaseStmt.SetLeft \
        ForStmt.Left ForStmt.SetLeft \
        ForStmt.Right ForStmt.SetRight \
        ForStmt.Body ForStmt.PtrBody ForStmt.SetBody \
        ForStmt.List ForStmt.PtrList ForStmt.SetList \
        ForStmt.HasBreak ForStmt.SetHasBreak \
        ForStmt.Sym ForStmt.SetSym \
        GoDeferStmt.Left GoDeferStmt.SetLeft \
        IfStmt.Left IfStmt.SetLeft \
        IfStmt.Body IfStmt.PtrBody IfStmt.SetBody \
        IfStmt.Rlist IfStmt.PtrRlist IfStmt.SetRlist \
        IfStmt.Likely IfStmt.SetLikely \
        LabelStmt.SetSym \
        RangeStmt.Right RangeStmt.SetRight \
        RangeStmt.Body RangeStmt.PtrBody RangeStmt.SetBody \
        RangeStmt.List RangeStmt.PtrList RangeStmt.SetList \
        RangeStmt.HasBreak RangeStmt.SetHasBreak \
        RangeStmt.Colas RangeStmt.SetColas \
        RangeStmt.Sym RangeStmt.SetSym \
        ReturnStmt.List ReturnStmt.PtrList ReturnStmt.SetList \
        SelectStmt.List SelectStmt.PtrList SelectStmt.SetList \
        SelectStmt.HasBreak SelectStmt.SetHasBreak \
        SelectStmt.Body SelectStmt.PtrBody SelectStmt.SetBody \
        SelectStmt.Sym SelectStmt.SetSym \
        SendStmt.Left SendStmt.SetLeft \
        SendStmt.Right SendStmt.SetRight \
        SwitchStmt.Left SwitchStmt.SetLeft \
        SwitchStmt.List SwitchStmt.PtrList SwitchStmt.SetList \
        SwitchStmt.Body SwitchStmt.PtrBody SwitchStmt.SetBody \
        SwitchStmt.HasBreak SwitchStmt.SetHasBreak \
        SwitchStmt.Sym SwitchStmt.SetSym \
        TypeSwitchGuard.Left TypeSwitchGuard.SetLeft \
        TypeSwitchGuard.Right TypeSwitchGuard.SetRight \
        AddStringExpr.List AddStringExpr.PtrList AddStringExpr.SetList \
        AddrExpr.Left AddrExpr.SetLeft \
        AddrExpr.Right AddrExpr.SetRight \
        BinaryExpr.Left BinaryExpr.SetLeft \
        BinaryExpr.Right BinaryExpr.SetRight \
        LogicalExpr.Left LogicalExpr.SetLeft \
        LogicalExpr.Right LogicalExpr.SetRight \
        CallExpr.Left CallExpr.SetLeft \
        CallExpr.List CallExpr.PtrList CallExpr.SetList \
        CallExpr.Rlist CallExpr.PtrRlist CallExpr.SetRlist \
        CallExpr.NoInline CallExpr.SetNoInline \
        CallExpr.Body CallExpr.PtrBody CallExpr.SetBody \
        CallExpr.IsDDD CallExpr.SetIsDDD \
        CallPartExpr.Left CallPartExpr.SetLeft \
        ClosureReadExpr.Offset \
        ClosureReadExpr.Type \ # provided by miniExpr already
        CompLitExpr.Right CompLitExpr.SetRight \
        CompLitExpr.List CompLitExpr.PtrList CompLitExpr.SetList \
        ConvExpr.Left ConvExpr.SetLeft \
        IndexExpr.Left IndexExpr.SetLeft \
        IndexExpr.Right IndexExpr.SetRight \
        IndexExpr.IndexMapLValue IndexExpr.SetIndexMapLValue \
        KeyExpr.Left KeyExpr.SetLeft \
        KeyExpr.Right KeyExpr.SetRight \
        StructKeyExpr.Left StructKeyExpr.SetLeft \
        StructKeyExpr.Offset StructKeyExpr.SetOffset \
        StructKeyExpr.SetSym \
        InlinedCallExpr.Body InlinedCallExpr.PtrBody InlinedCallExpr.SetBody \
        InlinedCallExpr.Rlist InlinedCallExpr.PtrRlist InlinedCallExpr.SetRlist \
        MakeExpr.Left MakeExpr.SetLeft \
        MakeExpr.Right MakeExpr.SetRight \
        MethodExpr.Left MethodExpr.SetLeft \
        MethodExpr.Right MethodExpr.SetRight \
        MethodExpr.Offset MethodExpr.SetOffset \
        MethodExpr.Class MethodExpr.SetClass \
        ParenExpr.Left ParenExpr.SetLeft \
        ResultExpr.Offset ResultExpr.SetOffset \
        ReturnStmt.IsDDD \
        SelectorExpr.Left SelectorExpr.SetLeft \
        SelectorExpr.Offset SelectorExpr.SetOffset \
        SelectorExpr.SetSym \
        SliceExpr.Left SliceExpr.SetLeft \
        SliceExpr.List SliceExpr.PtrList SliceExpr.SetList \
        SliceHeaderExpr.Left SliceHeaderExpr.SetLeft \
        SliceHeaderExpr.List SliceHeaderExpr.PtrList SliceHeaderExpr.SetList \
        StarExpr.Left StarExpr.SetLeft \
        TypeAssertExpr.Left TypeAssertExpr.SetLeft \
        TypeAssertExpr.Right TypeAssertExpr.SetRight \
        TypeAssertExpr.List TypeAssertExpr.PtrList TypeAssertExpr.SetList \
        UnaryExpr.Left UnaryExpr.SetLeft \
        Func.Body Func.PtrBody Func.SetBody \
        Func.Iota Func.SetIota \
        CallPartExpr.Func ClosureExpr.Func Func.Func Name.Func \

mv BlockStmt.List_ BlockStmt.List
mv CaseStmt.List_ CaseStmt.List
mv CaseStmt.Body_ CaseStmt.Body
mv ForStmt.Body_ ForStmt.Body
mv ForStmt.HasBreak_ ForStmt.HasBreak
mv Func.Iota_ Func.Iota
mv IfStmt.Body_ IfStmt.Body
mv IfStmt.Likely_ IfStmt.Likely
mv RangeStmt.Body_ RangeStmt.Body
mv RangeStmt.HasBreak_ RangeStmt.HasBreak
mv SelectStmt.HasBreak_ SelectStmt.HasBreak
mv SwitchStmt.HasBreak_ SwitchStmt.HasBreak
mv AddStringExpr.List_ AddStringExpr.List
mv CallExpr.NoInline_ CallExpr.NoInline
mv CallExpr.Body_ CallExpr.Body # TODO what is this?
mv CallExpr.DDD CallExpr.IsDDD
mv ClosureReadExpr.Offset_ ClosureReadExpr.Offset
mv CompLitExpr.List_ CompLitExpr.List
mv StructKeyExpr.Offset_ StructKeyExpr.Offset
mv InlinedCallExpr.Body_ InlinedCallExpr.Body
mv ResultExpr.Offset_ ResultExpr.Offset
mv SelectorExpr.Offset_ SelectorExpr.Offset
mv SliceExpr.List_ SliceExpr.List
mv SliceHeaderExpr.LenCap_ SliceHeaderExpr.LenCap
mv Func.Body_ Func.Body
mv CallPartExpr.Func_ CallPartExpr.Func
mv ClosureExpr.Func_ ClosureExpr.Func
mv Name.Func_ Name.Func
'

Change-Id: Ia2ee59649674f83eb123e63fda7a7781cf91cc56
Reviewed-on: https://go-review.googlesource.com/c/go/+/277935
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:37:41 +00:00
Russ Cox
14d667341f [dev.regabi] cmd/compile: remove Node.Left etc [generated]
This automated CL adds type assertions on the true branches of
n.Op() equality tests, to redeclare n with a more specific type, when
it is safe to do so. (That is, when n is not reassigned with a more
general type, when n is not reassigned and then used outside the
scope, and so on.) All the "unsafe" times that the automated tool
would avoid have been removed or rewritten in earlier CLs, so that
after this CL and the next one, which removes the use of ir.Nod,
every use of the Left, Right, and so on methods is done using concrete
types, never the Node interface.

Having done that, the CL locks in the progress by deleting many of
the access methods, including Left, SetLeft and so on, from the
Node interface.

There are still uses of Name, Func, Sym, some of the tracking
bits, and a few other miscellaneous fields, but all the main access
methods are gone from the Node interface. The others will be cleaned
up in smaller CLs.

Passes buildall w/ toolstash -cmp.

[git-generate]
cd src/cmd/compile/internal/gc
rf 'typeassert {
        import "cmd/compile/internal/ir"
        var n ir.Node

        n.Op() == ir.OADD -> n.(*ir.BinaryExpr)
        n.Op() == ir.OADDR -> n.(*ir.AddrExpr)
        n.Op() == ir.OADDSTR -> n.(*ir.AddStringExpr)
        n.Op() == ir.OALIGNOF -> n.(*ir.UnaryExpr)
        n.Op() == ir.OAND -> n.(*ir.BinaryExpr)
        n.Op() == ir.OANDAND -> n.(*ir.LogicalExpr)
        n.Op() == ir.OANDNOT -> n.(*ir.BinaryExpr)
        n.Op() == ir.OAPPEND -> n.(*ir.CallExpr)
        n.Op() == ir.OARRAYLIT -> n.(*ir.CompLitExpr)
        n.Op() == ir.OAS -> n.(*ir.AssignStmt)
        n.Op() == ir.OAS2 -> n.(*ir.AssignListStmt)
        n.Op() == ir.OAS2DOTTYPE -> n.(*ir.AssignListStmt)
        n.Op() == ir.OAS2FUNC -> n.(*ir.AssignListStmt)
        n.Op() == ir.OAS2MAPR -> n.(*ir.AssignListStmt)
        n.Op() == ir.OAS2RECV -> n.(*ir.AssignListStmt)
        n.Op() == ir.OASOP -> n.(*ir.AssignOpStmt)
        n.Op() == ir.OBITNOT -> n.(*ir.UnaryExpr)
        n.Op() == ir.OBLOCK -> n.(*ir.BlockStmt)
        n.Op() == ir.OBREAK -> n.(*ir.BranchStmt)
        n.Op() == ir.OBYTES2STR -> n.(*ir.ConvExpr)
        n.Op() == ir.OBYTES2STRTMP -> n.(*ir.ConvExpr)
        n.Op() == ir.OCALL -> n.(*ir.CallExpr)
        n.Op() == ir.OCALLFUNC -> n.(*ir.CallExpr)
        n.Op() == ir.OCALLINTER -> n.(*ir.CallExpr)
        n.Op() == ir.OCALLMETH -> n.(*ir.CallExpr)
        n.Op() == ir.OCALLPART -> n.(*ir.CallPartExpr)
        n.Op() == ir.OCAP -> n.(*ir.UnaryExpr)
        n.Op() == ir.OCASE -> n.(*ir.CaseStmt)
        n.Op() == ir.OCFUNC -> n.(*ir.UnaryExpr)
        n.Op() == ir.OCHECKNIL -> n.(*ir.UnaryExpr)
        n.Op() == ir.OCLOSE -> n.(*ir.UnaryExpr)
        n.Op() == ir.OCOMPLEX -> n.(*ir.BinaryExpr)
        n.Op() == ir.OCOMPLIT -> n.(*ir.CompLitExpr)
        n.Op() == ir.OCONTINUE -> n.(*ir.BranchStmt)
        n.Op() == ir.OCONV -> n.(*ir.ConvExpr)
        n.Op() == ir.OCONVIFACE -> n.(*ir.ConvExpr)
        n.Op() == ir.OCONVNOP -> n.(*ir.ConvExpr)
        n.Op() == ir.OCOPY -> n.(*ir.BinaryExpr)
        n.Op() == ir.ODCL -> n.(*ir.Decl)
        n.Op() == ir.ODCLCONST -> n.(*ir.Decl)
        n.Op() == ir.ODCLFUNC -> n.(*ir.Func)
        n.Op() == ir.ODCLTYPE -> n.(*ir.Decl)
        n.Op() == ir.ODEFER -> n.(*ir.GoDeferStmt)
        n.Op() == ir.ODELETE -> n.(*ir.CallExpr)
        n.Op() == ir.ODEREF -> n.(*ir.StarExpr)
        n.Op() == ir.ODIV -> n.(*ir.BinaryExpr)
        n.Op() == ir.ODOT -> n.(*ir.SelectorExpr)
        n.Op() == ir.ODOTINTER -> n.(*ir.SelectorExpr)
        n.Op() == ir.ODOTMETH -> n.(*ir.SelectorExpr)
        n.Op() == ir.ODOTPTR -> n.(*ir.SelectorExpr)
        n.Op() == ir.ODOTTYPE -> n.(*ir.TypeAssertExpr)
        n.Op() == ir.ODOTTYPE2 -> n.(*ir.TypeAssertExpr)
        n.Op() == ir.OEFACE -> n.(*ir.BinaryExpr)
        n.Op() == ir.OEQ -> n.(*ir.BinaryExpr)
        n.Op() == ir.OFALL -> n.(*ir.BranchStmt)
        n.Op() == ir.OFOR -> n.(*ir.ForStmt)
        n.Op() == ir.OFORUNTIL -> n.(*ir.ForStmt)
        n.Op() == ir.OGE -> n.(*ir.BinaryExpr)
        n.Op() == ir.OGETG -> n.(*ir.CallExpr)
        n.Op() == ir.OGO -> n.(*ir.GoDeferStmt)
        n.Op() == ir.OGOTO -> n.(*ir.BranchStmt)
        n.Op() == ir.OGT -> n.(*ir.BinaryExpr)
        n.Op() == ir.OIDATA -> n.(*ir.UnaryExpr)
        n.Op() == ir.OIF -> n.(*ir.IfStmt)
        n.Op() == ir.OIMAG -> n.(*ir.UnaryExpr)
        n.Op() == ir.OINDEX -> n.(*ir.IndexExpr)
        n.Op() == ir.OINDEXMAP -> n.(*ir.IndexExpr)
        n.Op() == ir.OINLCALL -> n.(*ir.InlinedCallExpr)
        n.Op() == ir.OINLMARK -> n.(*ir.InlineMarkStmt)
        n.Op() == ir.OITAB -> n.(*ir.UnaryExpr)
        n.Op() == ir.OKEY -> n.(*ir.KeyExpr)
        n.Op() == ir.OLABEL -> n.(*ir.LabelStmt)
        n.Op() == ir.OLE -> n.(*ir.BinaryExpr)
        n.Op() == ir.OLEN -> n.(*ir.UnaryExpr)
        n.Op() == ir.OLSH -> n.(*ir.BinaryExpr)
        n.Op() == ir.OLT -> n.(*ir.BinaryExpr)
        n.Op() == ir.OMAKE -> n.(*ir.CallExpr)
        n.Op() == ir.OMAKECHAN -> n.(*ir.MakeExpr)
        n.Op() == ir.OMAKEMAP -> n.(*ir.MakeExpr)
        n.Op() == ir.OMAKESLICE -> n.(*ir.MakeExpr)
        n.Op() == ir.OMAKESLICECOPY -> n.(*ir.MakeExpr)
        n.Op() == ir.OMAPLIT -> n.(*ir.CompLitExpr)
        n.Op() == ir.OMETHEXPR -> n.(*ir.MethodExpr)
        n.Op() == ir.OMOD -> n.(*ir.BinaryExpr)
        n.Op() == ir.OMUL -> n.(*ir.BinaryExpr)
        n.Op() == ir.ONAME -> n.(*ir.Name)
        n.Op() == ir.ONE -> n.(*ir.BinaryExpr)
        n.Op() == ir.ONEG -> n.(*ir.UnaryExpr)
        n.Op() == ir.ONEW -> n.(*ir.UnaryExpr)
        n.Op() == ir.ONEWOBJ -> n.(*ir.UnaryExpr)
        n.Op() == ir.ONIL -> n.(*ir.NilExpr)
        n.Op() == ir.ONOT -> n.(*ir.UnaryExpr)
        n.Op() == ir.OOFFSETOF -> n.(*ir.UnaryExpr)
        n.Op() == ir.OOR -> n.(*ir.BinaryExpr)
        n.Op() == ir.OOROR -> n.(*ir.LogicalExpr)
        n.Op() == ir.OPACK -> n.(*ir.PkgName)
        n.Op() == ir.OPANIC -> n.(*ir.UnaryExpr)
        n.Op() == ir.OPAREN -> n.(*ir.ParenExpr)
        n.Op() == ir.OPLUS -> n.(*ir.UnaryExpr)
        n.Op() == ir.OPRINT -> n.(*ir.CallExpr)
        n.Op() == ir.OPRINTN -> n.(*ir.CallExpr)
        n.Op() == ir.OPTRLIT -> n.(*ir.AddrExpr)
        n.Op() == ir.ORANGE -> n.(*ir.RangeStmt)
        n.Op() == ir.OREAL -> n.(*ir.UnaryExpr)
        n.Op() == ir.ORECOVER -> n.(*ir.CallExpr)
        n.Op() == ir.ORECV -> n.(*ir.UnaryExpr)
        n.Op() == ir.ORESULT -> n.(*ir.ResultExpr)
        n.Op() == ir.ORETJMP -> n.(*ir.BranchStmt)
        n.Op() == ir.ORETURN -> n.(*ir.ReturnStmt)
        n.Op() == ir.ORSH -> n.(*ir.BinaryExpr)
        n.Op() == ir.ORUNES2STR -> n.(*ir.ConvExpr)
        n.Op() == ir.ORUNESTR -> n.(*ir.ConvExpr)
        n.Op() == ir.OSELECT -> n.(*ir.SelectStmt)
        n.Op() == ir.OSELRECV2 -> n.(*ir.AssignListStmt)
        n.Op() == ir.OSEND -> n.(*ir.SendStmt)
        n.Op() == ir.OSIZEOF -> n.(*ir.UnaryExpr)
        n.Op() == ir.OSLICE -> n.(*ir.SliceExpr)
        n.Op() == ir.OSLICE3 -> n.(*ir.SliceExpr)
        n.Op() == ir.OSLICE3ARR -> n.(*ir.SliceExpr)
        n.Op() == ir.OSLICEARR -> n.(*ir.SliceExpr)
        n.Op() == ir.OSLICEHEADER -> n.(*ir.SliceHeaderExpr)
        n.Op() == ir.OSLICELIT -> n.(*ir.CompLitExpr)
        n.Op() == ir.OSLICESTR -> n.(*ir.SliceExpr)
        n.Op() == ir.OSPTR -> n.(*ir.UnaryExpr)
        n.Op() == ir.OSTR2BYTES -> n.(*ir.ConvExpr)
        n.Op() == ir.OSTR2BYTESTMP -> n.(*ir.ConvExpr)
        n.Op() == ir.OSTR2RUNES -> n.(*ir.ConvExpr)
        n.Op() == ir.OSTRUCTLIT -> n.(*ir.CompLitExpr)
        n.Op() == ir.OSUB -> n.(*ir.BinaryExpr)
        n.Op() == ir.OSWITCH -> n.(*ir.SwitchStmt)
        n.Op() == ir.OTYPESW -> n.(*ir.TypeSwitchGuard)
        n.Op() == ir.OVARDEF -> n.(*ir.UnaryExpr)
        n.Op() == ir.OVARKILL -> n.(*ir.UnaryExpr)
        n.Op() == ir.OVARLIVE -> n.(*ir.UnaryExpr)
        n.Op() == ir.OXDOT -> n.(*ir.SelectorExpr)
        n.Op() == ir.OXOR -> n.(*ir.BinaryExpr)
}
'

cd ../ir
rf '
        rm \
                Node.SetOp \
                miniNode.SetOp \
                Node.Func \
                miniNode.Func \
                Node.Left Node.SetLeft \
                miniNode.Left miniNode.SetLeft \
                Node.Right Node.SetRight \
                miniNode.Right miniNode.SetRight \
                Node.List Node.PtrList Node.SetList \
                miniNode.List miniNode.PtrList miniNode.SetList \
                Node.Rlist Node.PtrRlist Node.SetRlist \
                miniNode.Rlist miniNode.PtrRlist miniNode.SetRlist \
                Node.Body Node.PtrBody Node.SetBody \
                miniNode.Body miniNode.PtrBody miniNode.SetBody \
                Node.SubOp Node.SetSubOp \
                miniNode.SubOp miniNode.SetSubOp \
                Node.SetSym \
                miniNode.SetSym \
                Node.Offset Node.SetOffset \
                miniNode.Offset miniNode.SetOffset \
                Node.Class Node.SetClass \
                miniNode.Class miniNode.SetClass \
                Node.Iota Node.SetIota \
                miniNode.Iota miniNode.SetIota \
                Node.Colas Node.SetColas \
                miniNode.Colas miniNode.SetColas \
                Node.Transient Node.SetTransient \
                miniNode.Transient miniNode.SetTransient \
                Node.Implicit Node.SetImplicit \
                miniNode.Implicit miniNode.SetImplicit \
                Node.IsDDD Node.SetIsDDD \
                miniNode.IsDDD miniNode.SetIsDDD \
                Node.MarkReadonly \
                miniNode.MarkReadonly \
                Node.Likely Node.SetLikely \
                miniNode.Likely miniNode.SetLikely \
                Node.SliceBounds Node.SetSliceBounds \
                miniNode.SliceBounds miniNode.SetSliceBounds \
                Node.NoInline Node.SetNoInline \
                miniNode.NoInline miniNode.SetNoInline \
                Node.IndexMapLValue Node.SetIndexMapLValue \
                miniNode.IndexMapLValue miniNode.SetIndexMapLValue \
                Node.ResetAux \
                miniNode.ResetAux \
                Node.HasBreak Node.SetHasBreak \
                miniNode.HasBreak miniNode.SetHasBreak \
                Node.Bounded Node.SetBounded \
                miniNode.Bounded miniNode.SetBounded \
                miniNode.Embedded miniNode.SetEmbedded \
                miniNode.Int64Val miniNode.Uint64Val miniNode.CanInt64 \
                miniNode.BoolVal miniNode.StringVal \
                miniNode.TChanDir miniNode.SetTChanDir \
                miniNode.Format \
                miniNode.copy miniNode.doChildren miniNode.editChildren \

'

Change-Id: I2a05b535963b43f83b1849fcf653f82b99af6035
Reviewed-on: https://go-review.googlesource.com/c/go/+/277934
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:37:34 +00:00
Russ Cox
6f27d29be0 [dev.regabi] cmd/compile: remove ir.Nod [generated]
Rewrite all uses of ir.Nod and friends to call the IR constructors directly.
This gives the results a more specific type and will play nicely with
introduction of more specific types throughout the code in a followup CL.

Passes buildall w/ toolstash -cmp.

[git-generate]
cd src/cmd/compile/internal/gc
rf '
ex . ../ir {
	import "cmd/compile/internal/ir"
	import "cmd/compile/internal/types"
	import "cmd/compile/internal/syntax"
	import "cmd/internal/src"

	var p *noder
	var orig syntax.Node
	var op ir.Op
	var l, r ir.Node
	var sym *types.Sym
	p.nod(orig, op, l, r) -> ir.NodAt(p.pos(orig), op, l, r)
	p.nodSym(orig, op, l, sym) -> nodlSym(p.pos(orig), op, l, sym)

	var xpos src.XPos
	var ns ir.Nodes
	npos(xpos, nodSym(op, l, sym)) -> nodlSym(xpos, op, l, sym)
	npos(xpos, liststmt(ns)) -> ir.NewBlockStmt(xpos, ns)
}
ex . ../ir {
	import "cmd/compile/internal/base"
	import "cmd/compile/internal/ir"
	import "cmd/compile/internal/types"

	var op ir.Op
	var l, r ir.Node
	ir.Nod(op, l, r) -> ir.NodAt(base.Pos, op, l, r)

	var sym *types.Sym
	nodSym(op, l, sym) -> nodlSym(base.Pos, op, l, sym)
}
ex . ../ir {
	import "cmd/compile/internal/ir"
	import "cmd/internal/src"

	# rf overlapping match handling is not quite good enough
	# for certain nested rewrites, so handle these two - which often contain other ir.NodAt calls - early.
	var l, r ir.Node
	var xpos src.XPos
	ir.NodAt(xpos, ir.OAS, l, r)              -> ir.NewAssignStmt(xpos, l, r)
	ir.NodAt(xpos, ir.OIF, l, nil)            -> ir.NewIfStmt(xpos, l, nil, nil)
}
ex . ../ir {
	import "cmd/compile/internal/ir"
	import "cmd/compile/internal/types"
	import "cmd/internal/src"

	var l, r ir.Node
	var sym *types.Sym
	var xpos src.XPos

	nodlSym(xpos, ir.ODOT, l, sym)                   -> ir.NewSelectorExpr(xpos, ir.ODOT, l, sym)
	nodlSym(xpos, ir.OXDOT, l, sym)                  -> ir.NewSelectorExpr(xpos, ir.OXDOT, l, sym)
	nodlSym(xpos, ir.ODOTPTR, l, sym)                -> ir.NewSelectorExpr(xpos, ir.ODOTPTR, l, sym)
	nodlSym(xpos, ir.OGOTO, nil, sym)                -> ir.NewBranchStmt(xpos, ir.OGOTO, sym)
	nodlSym(xpos, ir.ORETJMP, nil, sym)              -> ir.NewBranchStmt(xpos, ir.ORETJMP, sym)
	nodlSym(xpos, ir.OLABEL, nil, sym)               -> ir.NewLabelStmt(xpos, sym)
	nodlSym(xpos, ir.OSTRUCTKEY, l, sym)             -> ir.NewStructKeyExpr(xpos, sym, l)

	ir.NodAt(xpos, ir.OADD, l, r)             -> ir.NewBinaryExpr(xpos, ir.OADD, l, r)
	ir.NodAt(xpos, ir.OAND, l, r)             -> ir.NewBinaryExpr(xpos, ir.OAND, l, r)
	ir.NodAt(xpos, ir.OANDNOT, l, r)          -> ir.NewBinaryExpr(xpos, ir.OANDNOT, l, r)
	ir.NodAt(xpos, ir.ODIV, l, r)             -> ir.NewBinaryExpr(xpos, ir.ODIV, l, r)
	ir.NodAt(xpos, ir.OEQ, l, r)              -> ir.NewBinaryExpr(xpos, ir.OEQ, l, r)
	ir.NodAt(xpos, ir.OGE, l, r)              -> ir.NewBinaryExpr(xpos, ir.OGE, l, r)
	ir.NodAt(xpos, ir.OGT, l, r)              -> ir.NewBinaryExpr(xpos, ir.OGT, l, r)
	ir.NodAt(xpos, ir.OLE, l, r)              -> ir.NewBinaryExpr(xpos, ir.OLE, l, r)
	ir.NodAt(xpos, ir.OLSH, l, r)             -> ir.NewBinaryExpr(xpos, ir.OLSH, l, r)
	ir.NodAt(xpos, ir.OLT, l, r)              -> ir.NewBinaryExpr(xpos, ir.OLT, l, r)
	ir.NodAt(xpos, ir.OMOD, l, r)             -> ir.NewBinaryExpr(xpos, ir.OMOD, l, r)
	ir.NodAt(xpos, ir.OMUL, l, r)             -> ir.NewBinaryExpr(xpos, ir.OMUL, l, r)
	ir.NodAt(xpos, ir.ONE, l, r)              -> ir.NewBinaryExpr(xpos, ir.ONE, l, r)
	ir.NodAt(xpos, ir.OOR, l, r)              -> ir.NewBinaryExpr(xpos, ir.OOR, l, r)
	ir.NodAt(xpos, ir.ORSH, l, r)             -> ir.NewBinaryExpr(xpos, ir.ORSH, l, r)
	ir.NodAt(xpos, ir.OSUB, l, r)             -> ir.NewBinaryExpr(xpos, ir.OSUB, l, r)
	ir.NodAt(xpos, ir.OXOR, l, r)             -> ir.NewBinaryExpr(xpos, ir.OXOR, l, r)
	ir.NodAt(xpos, ir.OCOPY, l, r)            -> ir.NewBinaryExpr(xpos, ir.OCOPY, l, r)
	ir.NodAt(xpos, ir.OCOMPLEX, l, r)         -> ir.NewBinaryExpr(xpos, ir.OCOMPLEX, l, r)
	ir.NodAt(xpos, ir.OEFACE, l, r)           -> ir.NewBinaryExpr(xpos, ir.OEFACE, l, r)
	ir.NodAt(xpos, ir.OADDR, l, nil)          -> ir.NewAddrExpr(xpos, l)
	ir.NodAt(xpos, ir.OADDSTR, nil, nil)      -> ir.NewAddStringExpr(xpos, nil)
	ir.NodAt(xpos, ir.OANDAND, l, r)          -> ir.NewLogicalExpr(xpos, ir.OANDAND, l, r)
	ir.NodAt(xpos, ir.OOROR, l, r)            -> ir.NewLogicalExpr(xpos, ir.OOROR, l, r)
	ir.NodAt(xpos, ir.OARRAYLIT, nil, nil)    -> ir.NewCompLitExpr(xpos, ir.OARRAYLIT, nil, nil)
	ir.NodAt(xpos, ir.OCOMPLIT, nil, nil)     -> ir.NewCompLitExpr(xpos, ir.OCOMPLIT, nil, nil)
	ir.NodAt(xpos, ir.OMAPLIT, nil, nil)      -> ir.NewCompLitExpr(xpos, ir.OMAPLIT, nil, nil)
	ir.NodAt(xpos, ir.OSTRUCTLIT, nil, nil)   -> ir.NewCompLitExpr(xpos, ir.OSTRUCTLIT, nil, nil)
	ir.NodAt(xpos, ir.OSLICELIT, nil, nil)    -> ir.NewCompLitExpr(xpos, ir.OSLICELIT, nil, nil)
	ir.NodAt(xpos, ir.OARRAYLIT, nil, r)      -> ir.NewCompLitExpr(xpos, ir.OARRAYLIT, r.(ir.Ntype), nil)
	ir.NodAt(xpos, ir.OCOMPLIT, nil, r)       -> ir.NewCompLitExpr(xpos, ir.OCOMPLIT, r.(ir.Ntype), nil)
	ir.NodAt(xpos, ir.OMAPLIT, nil, r)        -> ir.NewCompLitExpr(xpos, ir.OMAPLIT, r.(ir.Ntype), nil)
	ir.NodAt(xpos, ir.OSTRUCTLIT, nil, r)     -> ir.NewCompLitExpr(xpos, ir.OSTRUCTLIT, r.(ir.Ntype), nil)
	ir.NodAt(xpos, ir.OSLICELIT, nil, r)      -> ir.NewCompLitExpr(xpos, ir.OSLICELIT, r.(ir.Ntype), nil)
	ir.NodAt(xpos, ir.OAS2, nil, nil)         -> ir.NewAssignListStmt(xpos, ir.OAS2, nil, nil)
	ir.NodAt(xpos, ir.OAS2DOTTYPE, nil, nil)  -> ir.NewAssignListStmt(xpos, ir.OAS2DOTTYPE, nil, nil)
	ir.NodAt(xpos, ir.OAS2FUNC, nil, nil)     -> ir.NewAssignListStmt(xpos, ir.OAS2FUNC, nil, nil)
	ir.NodAt(xpos, ir.OAS2MAPR, nil, nil)     -> ir.NewAssignListStmt(xpos, ir.OAS2MAPR, nil, nil)
	ir.NodAt(xpos, ir.OAS2RECV, nil, nil)     -> ir.NewAssignListStmt(xpos, ir.OAS2RECV, nil, nil)
	ir.NodAt(xpos, ir.OSELRECV2, nil, nil)    -> ir.NewAssignListStmt(xpos, ir.OSELRECV2, nil, nil)
	ir.NodAt(xpos, ir.OASOP, l, r)            -> ir.NewAssignOpStmt(xpos, ir.OXXX, l, r)
	ir.NodAt(xpos, ir.OBITNOT, l, nil)        -> ir.NewUnaryExpr(xpos, ir.OBITNOT, l)
	ir.NodAt(xpos, ir.ONEG, l, nil)           -> ir.NewUnaryExpr(xpos, ir.ONEG, l)
	ir.NodAt(xpos, ir.ONOT, l, nil)           -> ir.NewUnaryExpr(xpos, ir.ONOT, l)
	ir.NodAt(xpos, ir.OPLUS, l, nil)          -> ir.NewUnaryExpr(xpos, ir.OPLUS, l)
	ir.NodAt(xpos, ir.ORECV, l, nil)          -> ir.NewUnaryExpr(xpos, ir.ORECV, l)
	ir.NodAt(xpos, ir.OALIGNOF, l, nil)       -> ir.NewUnaryExpr(xpos, ir.OALIGNOF, l)
	ir.NodAt(xpos, ir.OCAP, l, nil)           -> ir.NewUnaryExpr(xpos, ir.OCAP, l)
	ir.NodAt(xpos, ir.OCLOSE, l, nil)         -> ir.NewUnaryExpr(xpos, ir.OCLOSE, l)
	ir.NodAt(xpos, ir.OIMAG, l, nil)          -> ir.NewUnaryExpr(xpos, ir.OIMAG, l)
	ir.NodAt(xpos, ir.OLEN, l, nil)           -> ir.NewUnaryExpr(xpos, ir.OLEN, l)
	ir.NodAt(xpos, ir.ONEW, l, nil)           -> ir.NewUnaryExpr(xpos, ir.ONEW, l)
	ir.NodAt(xpos, ir.ONEWOBJ, l, nil)        -> ir.NewUnaryExpr(xpos, ir.ONEWOBJ, l)
	ir.NodAt(xpos, ir.OOFFSETOF, l, nil)      -> ir.NewUnaryExpr(xpos, ir.OOFFSETOF, l)
	ir.NodAt(xpos, ir.OPANIC, l, nil)         -> ir.NewUnaryExpr(xpos, ir.OPANIC, l)
	ir.NodAt(xpos, ir.OREAL, l, nil)          -> ir.NewUnaryExpr(xpos, ir.OREAL, l)
	ir.NodAt(xpos, ir.OSIZEOF, l, nil)        -> ir.NewUnaryExpr(xpos, ir.OSIZEOF, l)
	ir.NodAt(xpos, ir.OCHECKNIL, l, nil)      -> ir.NewUnaryExpr(xpos, ir.OCHECKNIL, l)
	ir.NodAt(xpos, ir.OCFUNC, l, nil)         -> ir.NewUnaryExpr(xpos, ir.OCFUNC, l)
	ir.NodAt(xpos, ir.OIDATA, l, nil)         -> ir.NewUnaryExpr(xpos, ir.OIDATA, l)
	ir.NodAt(xpos, ir.OITAB, l, nil)          -> ir.NewUnaryExpr(xpos, ir.OITAB, l)
	ir.NodAt(xpos, ir.OSPTR, l, nil)          -> ir.NewUnaryExpr(xpos, ir.OSPTR, l)
	ir.NodAt(xpos, ir.OVARDEF, l, nil)        -> ir.NewUnaryExpr(xpos, ir.OVARDEF, l)
	ir.NodAt(xpos, ir.OVARKILL, l, nil)       -> ir.NewUnaryExpr(xpos, ir.OVARKILL, l)
	ir.NodAt(xpos, ir.OVARLIVE, l, nil)       -> ir.NewUnaryExpr(xpos, ir.OVARLIVE, l)
	ir.NodAt(xpos, ir.OBLOCK, nil, nil)       -> ir.NewBlockStmt(xpos, nil)
	ir.NodAt(xpos, ir.OBREAK, nil, nil)       -> ir.NewBranchStmt(xpos, ir.OBREAK, nil)
	ir.NodAt(xpos, ir.OCONTINUE, nil, nil)    -> ir.NewBranchStmt(xpos, ir.OCONTINUE, nil)
	ir.NodAt(xpos, ir.OFALL, nil, nil)        -> ir.NewBranchStmt(xpos, ir.OFALL, nil)
	ir.NodAt(xpos, ir.OGOTO, nil, nil)        -> ir.NewBranchStmt(xpos, ir.OGOTO, nil)
	ir.NodAt(xpos, ir.ORETJMP, nil, nil)      -> ir.NewBranchStmt(xpos, ir.ORETJMP, nil)
	ir.NodAt(xpos, ir.OCALL, l, nil)          -> ir.NewCallExpr(xpos, ir.OCALL, l, nil)
	ir.NodAt(xpos, ir.OCALLFUNC, l, nil)      -> ir.NewCallExpr(xpos, ir.OCALLFUNC, l, nil)
	ir.NodAt(xpos, ir.OCALLINTER, l, nil)     -> ir.NewCallExpr(xpos, ir.OCALLINTER, l, nil)
	ir.NodAt(xpos, ir.OCALLMETH, l, nil)      -> ir.NewCallExpr(xpos, ir.OCALLMETH, l, nil)
	ir.NodAt(xpos, ir.OAPPEND, l, nil)        -> ir.NewCallExpr(xpos, ir.OAPPEND, l, nil)
	ir.NodAt(xpos, ir.ODELETE, l, nil)        -> ir.NewCallExpr(xpos, ir.ODELETE, l, nil)
	ir.NodAt(xpos, ir.OGETG, l, nil)          -> ir.NewCallExpr(xpos, ir.OGETG, l, nil)
	ir.NodAt(xpos, ir.OMAKE, l, nil)          -> ir.NewCallExpr(xpos, ir.OMAKE, l, nil)
	ir.NodAt(xpos, ir.OPRINT, l, nil)         -> ir.NewCallExpr(xpos, ir.OPRINT, l, nil)
	ir.NodAt(xpos, ir.OPRINTN, l, nil)        -> ir.NewCallExpr(xpos, ir.OPRINTN, l, nil)
	ir.NodAt(xpos, ir.ORECOVER, l, nil)       -> ir.NewCallExpr(xpos, ir.ORECOVER, l, nil)
	ir.NodAt(xpos, ir.OCASE, nil, nil)        -> ir.NewCaseStmt(xpos, nil, nil)
	ir.NodAt(xpos, ir.OCONV, l, nil)          -> ir.NewConvExpr(xpos, ir.OCONV, nil, l)
	ir.NodAt(xpos, ir.OCONVIFACE, l, nil)     -> ir.NewConvExpr(xpos, ir.OCONVIFACE, nil, l)
	ir.NodAt(xpos, ir.OCONVNOP, l, nil)       -> ir.NewConvExpr(xpos, ir.OCONVNOP, nil, l)
	ir.NodAt(xpos, ir.ORUNESTR, l, nil)       -> ir.NewConvExpr(xpos, ir.ORUNESTR, nil, l)
	ir.NodAt(xpos, ir.ODCL, l, nil)           -> ir.NewDecl(xpos, ir.ODCL, l)
	ir.NodAt(xpos, ir.ODCLCONST, l, nil)      -> ir.NewDecl(xpos, ir.ODCLCONST, l)
	ir.NodAt(xpos, ir.ODCLTYPE, l, nil)       -> ir.NewDecl(xpos, ir.ODCLTYPE, l)
	ir.NodAt(xpos, ir.ODCLFUNC, nil, nil)     -> ir.NewFunc(xpos)
	ir.NodAt(xpos, ir.ODEFER, l, nil)         -> ir.NewGoDeferStmt(xpos, ir.ODEFER, l)
	ir.NodAt(xpos, ir.OGO, l, nil)            -> ir.NewGoDeferStmt(xpos, ir.OGO, l)
	ir.NodAt(xpos, ir.ODEREF, l, nil)         -> ir.NewStarExpr(xpos, l)
	ir.NodAt(xpos, ir.ODOT, l, nil)           -> ir.NewSelectorExpr(xpos, ir.ODOT, l, nil)
	ir.NodAt(xpos, ir.ODOTPTR, l, nil)        -> ir.NewSelectorExpr(xpos, ir.ODOTPTR, l, nil)
	ir.NodAt(xpos, ir.ODOTMETH, l, nil)       -> ir.NewSelectorExpr(xpos, ir.ODOTMETH, l, nil)
	ir.NodAt(xpos, ir.ODOTINTER, l, nil)      -> ir.NewSelectorExpr(xpos, ir.ODOTINTER, l, nil)
	ir.NodAt(xpos, ir.OXDOT, l, nil)          -> ir.NewSelectorExpr(xpos, ir.OXDOT, l, nil)
	ir.NodAt(xpos, ir.ODOTTYPE, l, nil)       -> ir.NewTypeAssertExpr(xpos, l, nil)
	ir.NodAt(xpos, ir.ODOTTYPE, l, r)         -> ir.NewTypeAssertExpr(xpos, l, r.(ir.Ntype))
	ir.NodAt(xpos, ir.OFOR, l, r)             -> ir.NewForStmt(xpos, nil, l, r, nil)
	ir.NodAt(xpos, ir.OINDEX, l, r)           -> ir.NewIndexExpr(xpos, l, r)
	ir.NodAt(xpos, ir.OINLMARK, nil, nil)     -> ir.NewInlineMarkStmt(xpos, types.BADWIDTH)
	ir.NodAt(xpos, ir.OKEY, l, r)             -> ir.NewKeyExpr(xpos, l, r)
	ir.NodAt(xpos, ir.OLABEL, nil, nil)       -> ir.NewLabelStmt(xpos, nil)
	ir.NodAt(xpos, ir.OMAKECHAN, l, r)        -> ir.NewMakeExpr(xpos, ir.OMAKECHAN, l, r)
	ir.NodAt(xpos, ir.OMAKEMAP, l, r)         -> ir.NewMakeExpr(xpos, ir.OMAKEMAP, l, r)
	ir.NodAt(xpos, ir.OMAKESLICE, l, r)       -> ir.NewMakeExpr(xpos, ir.OMAKESLICE, l, r)
	ir.NodAt(xpos, ir.OMAKESLICECOPY, l, r)   -> ir.NewMakeExpr(xpos, ir.OMAKESLICECOPY, l, r)
	ir.NodAt(xpos, ir.ONIL, nil, nil)         -> ir.NewNilExpr(xpos)
	ir.NodAt(xpos, ir.OPACK, nil, nil)        -> ir.NewPkgName(xpos, nil, nil)
	ir.NodAt(xpos, ir.OPAREN, l, nil)         -> ir.NewParenExpr(xpos, l)
	ir.NodAt(xpos, ir.ORANGE, nil, r)         -> ir.NewRangeStmt(xpos, nil, r, nil)
	ir.NodAt(xpos, ir.ORESULT, nil, nil)      -> ir.NewResultExpr(xpos, nil, types.BADWIDTH)
	ir.NodAt(xpos, ir.ORETURN, nil, nil)      -> ir.NewReturnStmt(xpos, nil)
	ir.NodAt(xpos, ir.OSELECT, nil, nil)      -> ir.NewSelectStmt(xpos, nil)
	ir.NodAt(xpos, ir.OSEND, l, r)            -> ir.NewSendStmt(xpos, l, r)
	ir.NodAt(xpos, ir.OSLICE, l, nil)         -> ir.NewSliceExpr(xpos, ir.OSLICE, l)
	ir.NodAt(xpos, ir.OSLICEARR, l, nil)      -> ir.NewSliceExpr(xpos, ir.OSLICEARR, l)
	ir.NodAt(xpos, ir.OSLICESTR, l, nil)      -> ir.NewSliceExpr(xpos, ir.OSLICESTR, l)
	ir.NodAt(xpos, ir.OSLICE3, l, nil)        -> ir.NewSliceExpr(xpos, ir.OSLICE3, l)
	ir.NodAt(xpos, ir.OSLICE3ARR, l, nil)     -> ir.NewSliceExpr(xpos, ir.OSLICE3ARR, l)
	ir.NodAt(xpos, ir.OSLICEHEADER, l, nil)   -> ir.NewSliceHeaderExpr(xpos, nil, l, nil, nil)
	ir.NodAt(xpos, ir.OSWITCH, l, nil)        -> ir.NewSwitchStmt(xpos, l, nil)
	ir.NodAt(xpos, ir.OINLCALL, nil, nil)     -> ir.NewInlinedCallExpr(xpos, nil, nil)
}

rm noder.nod noder.nodSym nodSym nodlSym ir.NodAt ir.Nod
'

Change-Id: Ibf1eb708de8463ae74ccc47d7966cc263a18295e
Reviewed-on: https://go-review.googlesource.com/c/go/+/277933
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 06:37:28 +00:00
Russ Cox
69cf39089f [dev.regabi] cmd/compile: do not die in early base.FlushErrors
Change-Id: I72bac8a85db14494298059f8efddc5cbbf45f7ca
Reviewed-on: https://go-review.googlesource.com/c/go/+/279214
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 05:03:59 +00:00
Matthew Dempsky
6d03cde88a [dev.regabi] cmd/dist: automatically bootstrap cmd subdirs
We want almost all cmd subdirectories anyway, and relative to the cost
of the rest of toolchain bootstrapping, copying/rewriting a few extra
source files is way cheaper than the engineering cost of forgetting to
maintain these lists as we split out new packages.

While here, also add cmd/internal/archive (and make it compile with Go
1.4) because it'll be needed in subsequent refactorings anyway; and
skip files starting with # (emacs temporary files) and test files
ending with _test.go.

Change-Id: Ic86e680a5fdfaecd617c36d5d04413293b2d6f52
Reviewed-on: https://go-review.googlesource.com/c/go/+/279832
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-12-23 04:20:02 +00:00
Russ Cox
d1d1099c91 [dev.regabi] cmd/compile: fixes for big rewrite
Adjust the new regabi code a bit to make the rewrites apply cleanly.

Change-Id: Ice5378e94d94ab45ca0572f44ab8c94b847271b8
Reviewed-on: https://go-review.googlesource.com/c/go/+/279530
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-23 00:51:53 +00:00
Russ Cox
ec741b0447 [dev.regabi] all: merge master (c9fb4eb) into dev.regabi
Merge List:

* 2020-12-22 c9fb4eb0a2 cmd/link: handle grouped resource sections
* 2020-12-22 c06a354bcc test: trigger SIGSEGV instead of SIGTRAP in issue11656.go
* 2020-12-22 0aa9b4709a cmd/pack: r command create output file if not exist
* 2020-12-22 4d27c4c223 runtime: correct error handling in several FreeBSD syscall wrappers
* 2020-12-22 9b6147120a cmd/pack: treat compiler's -linkobj output as "compiler object"
* 2020-12-21 bc7e4d9257 syscall: don't generate ptrace on iOS
* 2020-12-21 6cff874c47 runtime/metrics: add Read examples
* 2020-12-21 8438a5779b runtime: use _exit on darwin
* 2020-12-21 cb95819cf6 runtime: detect netbsd netpoll overrun in sysmon
* 2020-12-21 53c984d976 runtime: skip wakep call in wakeNetPoller on Plan 9
* 2020-12-21 9abbe27710 test: skip issue11656.go on mips/mips64/ppc64

Change-Id: Ibb235fbf6a86ebcf50c686dc11f7c02d1865f845
2020-12-22 16:55:15 -05:00
Russ Cox
acc32ea124 [dev.regabi] codereview.cfg: add config for dev.regabi
Change-Id: Ida5cae7475bc19388fa46ceca25d983f560fa4e8
Reviewed-on: https://go-review.googlesource.com/c/go/+/279524
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2020-12-22 21:02:04 +00:00
Russ Cox
c40934b33d [dev.regabi] cmd/compile: adjust one case in walkexpr
The mid-case n := n.(*ir.AssignExpr) does not lend itself
well to pulling the code into a new function, because n will
be a function argument and will not be redeclarable.

Change-Id: I673f2aa37eea64b083725326ed3fa36447bcc7af
Reviewed-on: https://go-review.googlesource.com/c/go/+/279426
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-22 19:45:35 +00:00
Russ Cox
280e7fd1ee [dev.regabi] cmd/compile: only access Func method on concrete types
Sets up for removing Func from Node interface.
That means that once the Name reorg is done,
which will let us remove Name, Sym, and Val,
Node will be basically a minimal interface.

Passes buildall w/ toolstash -cmp.

Change-Id: I6e87897572debd7f8e29b4f5167763dc2792b408
Reviewed-on: https://go-review.googlesource.com/c/go/+/279484
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-22 19:32:21 +00:00
Russ Cox
51ba53f5c2 [dev.regabi] cmd/compile: separate misc for gc split
Misc cleanup for splitting package gc: API tweaks
and boundary adjustments.

The change in ir.NewBlockStmt makes it a drop-in
replacement for liststmt.

Change-Id: I9455fe8ccae7d71fe8ccf390ac96672389bf4f3d
Reviewed-on: https://go-review.googlesource.com/c/go/+/279305
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-22 19:32:16 +00:00
Russ Cox
572f168ed2 [dev.regabi] cmd/compile: separate various from Main
Move various code out of Main itself and into helper functions
that can be moved into other packages as package gc splits up.

Similarly, move order and instrument inside walk to reduce the amount
of API surface needed from the eventual package walk.

Change-Id: I7849258038c6e39625a0385af9c0edd6a3b654a1
Reviewed-on: https://go-review.googlesource.com/c/go/+/279304
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-22 19:32:10 +00:00
Russ Cox
3b12c6dc08 [dev.regabi] cmd/compile: separate typecheck more cleanly
Abstract the typecheck API a bit more so that it is
easier to move into a new package.

Change-Id: Ia0a0146151fa7f6073113e68a2c3f6e42a5d0ad8
Reviewed-on: https://go-review.googlesource.com/c/go/+/279303
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-22 19:32:03 +00:00
Russ Cox
7c8f5356ab [dev.regabi] cmd/compile: separate dowidth better
Having a global MaxWidth lets us avoid needing to
refer to thearch from split-out packages when all
they need is thearch.MAXWIDTH.

And make a couple interface changes to let dowidth
avoid importing package ir directly.
Then it can move into package types.

Change-Id: I2c12e8e22252597530e648848320e19bdd490a01
Reviewed-on: https://go-review.googlesource.com/c/go/+/279302
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-22 19:31:57 +00:00
Than McIntosh
cb28c96be8 [dev.regabi] cmd/compile,cmd/link: initial support for ABI wrappers
Add compiler support for emitting ABI wrappers by creating real IR as
opposed to introducing ABI aliases. At the moment these are "no-op"
wrappers in the sense that they make a simple call (using the existing
ABI) to their target. The assumption here is that once late call
expansion can handle both ABI0 and the "new" ABIInternal (register
version), it can expand the call to do the right thing.

Note that the runtime contains functions that do not strictly follow
the rules of the current Go ABI0; this has been handled in most cases
by treating these as ABIInternal instead (these changes have been made
in previous patches).

Generation of ABI wrappers (as opposed to ABI aliases) is currently
gated by GOEXPERIMENT=regabi -- wrapper generation is on by default if
GOEXPERIMENT=regabi is set and off otherwise (but can be turned on
using "-gcflags=all=-abiwrap -ldflags=-abiwrap"). Wrapper generation
currently only workd on AMD64; explicitly enabling wrapper for other
architectures (via the command line) is not supported.

Also in this patch are a few other command line options for debugging
(tracing and/or limiting wrapper creation). These will presumably go
away at some point.

Updates #27539, #40724.

Change-Id: I1ee3226fc15a3c32ca2087b8ef8e41dbe6df4a75
Reviewed-on: https://go-review.googlesource.com/c/go/+/270863
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Trust: Than McIntosh <thanm@google.com>
2020-12-22 18:13:48 +00:00
Matthew Dempsky
c8610e4700 [dev.regabi] cmd/compile: add ir.BasicLit to represent literals
This CL changes so that all literals are represented with a new,
smaller ir.BasicLit type, so that ir.Name is only used to represent
declared constants.

Passes buildall w/ toolstash -cmp.

Change-Id: I4702b8e3fa945617bd05881d7a2be1205f229633
Reviewed-on: https://go-review.googlesource.com/c/go/+/279153
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-22 17:04:46 +00:00
Matthew Dempsky
3512cde10a [dev.regabi] cmd/compile: stop reusing Ntype for OSLICELIT length
For OSLICELITs, we were reusing the Ntype field after type checking to
hold the length of the OSLICELIT's backing array. However, Ntype is
only meant for nodes that can represent types. Today, this works only
because we currently use Name for all OLITERAL constants (whether
declared or not), whereas we should be able to represent them more
compactly with a dedicated type that doesn't implement Ntype.

Passes buildall w/ toolstash -cmp.

Change-Id: I385f1d787c41b016f507a5bad9489d59ccfde7f2
Reviewed-on: https://go-review.googlesource.com/c/go/+/279152
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-22 17:04:18 +00:00
Matthew Dempsky
2755361e6a [dev.regabi] cmd/compile: change noder.declNames to returns ir.Names
declNames always returns a slice of *ir.Names, so return that directly
rather than as []ir.Node.

While here, also change iimport to directly create ir.ODCL/ir.OAS
statements, rather than calling variter. Allows eliminating a use of
ir.TypeNode.

Passes buildall w/ toolstash -cmp.

Change-Id: Icb75e993c4957b6050c797ba64ee71cfb7a19644
Reviewed-on: https://go-review.googlesource.com/c/go/+/279315
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-22 17:04:04 +00:00
Than McIntosh
301af2cb71 [dev.regabi] runtime/race: adjust test pattern match for ABI wrapper
Adjust the pattern matching in one of the race output test to allow
for the possible introduction of an ABI wrapper. Normally for tests
that match traceback output wrappers are not an issue since they
are screened out by Go's traceback mechanism, but in this case the
race runtime is doing the unwinding, so the wrapper may be visible.

Change-Id: I45413b5c4701d4c28cc760fccc8203493dbe2874
Reviewed-on: https://go-review.googlesource.com/c/go/+/278756
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Trust: Than McIntosh <thanm@google.com>
2020-12-22 16:35:20 +00:00
Than McIntosh
306b2451c8 [dev.regabi] runtime: fix ABI targets in runtime.panic{Index,Slice} shims
Fix up the assembly shim routines runtime.panic{Index,Slice} and
friends so that their tail calls target ABIInternal and not ABI0
functions. This is so as to ensure that these calls don't go through
an ABI0->ABIInternal wrapper (which would throw off the machinery in
the called routines designed to detect whether the violation happened
in the runtime).

Note that when the compiler starts emitting real register calls to
these routines, we'll need to rewrite them to update the arg size and
ensure that args are in the correct registers. For example, the
current shim

TEXT runtime·panicIndex<ABIInternal>(SB),NOSPLIT,$0-16
	MOVQ	AX, x+0(FP)
	MOVQ	CX, y+8(FP)
	JMP	runtime·goPanicIndex<ABIInternal>(SB)

will need to change to

TEXT runtime·panicIndex<ABIInternal>(SB),NOSPLIT,$0
	// AX already set up properly
	MOVQ	CX, BX // second argument expected in BX
	JMP	runtime·goPanicIndex<ABIInternal>(SB)

Change-Id: I48d1b5138fb4d229380ad12735cfaca5c50e6cc3
Reviewed-on: https://go-review.googlesource.com/c/go/+/278755
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Trust: Than McIntosh <thanm@google.com>
2020-12-22 14:50:50 +00:00
Matthew Dempsky
94cfeca0a5 [dev.regabi] cmd/compile: stop using ONONAME with Name
This CL changes NewDeclNameAt to take an Op argument to set the Op up
front, and updates all callers to provide the appropriate Op. This
allows dropping the Name.SetOp method.

Passes buildall w/ toolstash -cmp.

Change-Id: I20e580f62d3c8a81223d1c162327c11b37bbf3f0
Reviewed-on: https://go-review.googlesource.com/c/go/+/279314
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-21 19:42:21 +00:00
Matthew Dempsky
cb4898a77d [dev.regabi] cmd/compile: simplify declaration importing
Rather than creating Names w/ ONONAME earlier and later adding in the
details, this CL changes the import logic to create and add details at
the same time.

Passes buildall w/ toolstash -cmp.

Change-Id: Ifaabade3cef8cd80ddd6644bff79393b934255d9
Reviewed-on: https://go-review.googlesource.com/c/go/+/279313
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-21 19:42:00 +00:00
Matthew Dempsky
06915ac14d [dev.regabi] cmd/compile: move itabname call out of implements
We only need to call itabname when actually creating the OCONVIFACE
ops, not any time we test whether a type implements an
interface. Additionally, by moving this call out of implements, we
make it purely based on types, which makes it safe to move to package
types.

Does not pass toolstash -cmp, because it shuffles symbol creation
order.

Change-Id: Iea8e0c9374218f4d97b4339020ebd758d051bd03
Reviewed-on: https://go-review.googlesource.com/c/go/+/279333
Reviewed-by: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-12-21 19:41:21 +00:00
Gerrit Code Review
4e8f681eff Merge "[dev.regabi] all: merge master into dev.regabi" into dev.regabi 2020-12-21 19:23:41 +00:00
Russ Cox
1a523c8ab0 [dev.regabi] cmd/compile: separate nowritebarrierrec from main
Main knows a bit too much about nowritebarrierrec.
Abstract the API a little bit to make the package split easier.

Change-Id: I4b76bdb1fed73dfb0d44e1a6c86de8c2d29a9488
Reviewed-on: https://go-review.googlesource.com/c/go/+/279301
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-21 19:23:33 +00:00
Russ Cox
e999c17022 [dev.regabi] cmd/compile: separate ssa from other phases
isIntrinsicCall and ssaDumpInline are the only two "forward references"
to ssa by earlier phases. Make them a bit more explicit so that the
uses and the definitions can end up in different packages.

Change-Id: I02c7a27464fbedef9fee43c0e4094fa08b4d7a5c
Reviewed-on: https://go-review.googlesource.com/c/go/+/279300
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-21 19:23:31 +00:00
Russ Cox
4836e28ac0 [dev.regabi] cmd/compile: separate noder more cleanly
Separate embed, cgo pragmas, and Main trackScopes variable
from noder more cleanly.

This lets us split embed and noder into new packages.
It also assumes that the local embedded variables will be
removed and deletes them now for simplicity.

Change-Id: I9638bcc2c5f0e76440de056c6285b6aa2f73a00d
Reviewed-on: https://go-review.googlesource.com/c/go/+/279299
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-21 19:23:30 +00:00
Russ Cox
85ce6ecfe3 [dev.regabi] cmd/compile: separate exportsym more cleanly
Clean up a TODO (and make the package gc split easier)
by moving the exportsym walk out of iexport proper.

Also move exportsym call out of fninit.

Change-Id: Ie5887a68d325f7154201f4a35b9b4be4bf4b48dd
Reviewed-on: https://go-review.googlesource.com/c/go/+/279298
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-21 19:23:29 +00:00
Russ Cox
1a3b036b83 [dev.regabi] cmd/compile: collect global compilation state
There are various global variables tracking the state of the
compilation. Collect them in a single global struct instead.
The struct definition is in package ir, but the struct itself is
still in package gc. It may eventually be threaded through the
code, but in the short term will end up in package typecheck.

Change-Id: I019db07aaedaed2c9b67dd45a4e138dc6028e54c
Reviewed-on: https://go-review.googlesource.com/c/go/+/279297
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-21 19:23:28 +00:00
Russ Cox
2153a99914 [dev.regabi] cmd/compile: setup to move Addrconst, Patch into cmd/internal/obj
Deleting the Pc assignment from Patch is safe because the actual PCs
are not assigned until well after the compiler is done patching jumps.
And it proves that replacing uses of Patch with SetTarget will be safe later.

Change-Id: Iffcbe03f0b5949ccd4c91e79c1272cd06be0f434
Reviewed-on: https://go-review.googlesource.com/c/go/+/279296
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-21 19:23:27 +00:00
Russ Cox
0bb0baf683 [dev.regabi] cmd/compile: cleanup for concrete types - more
Accumulated fixes to recent changes, to make the code safe
for automated deinterfacing.

Change-Id: I200737046cea88f3356b2402f09e2ca477fb8456
Reviewed-on: https://go-review.googlesource.com/c/go/+/279232
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-21 19:23:26 +00:00
Matthew Dempsky
ca8e17164e [dev.regabi] all: merge master into dev.regabi
The list of conflicted files for this merge is:

   src/cmd/compile/internal/gc/inl.go
   src/cmd/compile/internal/gc/order.go
   src/cmd/compile/internal/gc/ssa.go
   test/fixedbugs/issue20415.go
   test/fixedbugs/issue22822.go
   test/fixedbugs/issue28079b.go

inl.go was updated for changes on dev.regabi: namely that OSELRECV has
been removed, and that OSELRECV2 now only uses List, rather than both
Left and List.

order.go was updated IsAutoTmp is now a standalone function, rather
than a method on Node.

ssa.go was similarly updated for new APIs involving package ir.

The tests are all merging upstream additions for gccgo error messages
with changes to cmd/compile's error messages on the dev.regabi branch.

Change-Id: Icaaf186d69da791b5994dbb6688ec989caabec42
2020-12-21 11:19:04 -08:00
Russ Cox
c45313bf45 [dev.regabi] cmd/compile: remove prealloc map
The prealloc map seems to exist to avoid adding a field to all nodes.
Now we can add a field to just the nodes that need the field,
so let's do that and avoid having a magic global with extra node state
that isn't preserved by operations like Copy nor printed by Dump.

This also makes clear which nodes can be prealloc'ed.
In particular, the code in walkstmt looked up an entry in
prealloc using an ONAME node, but there's no code that
ever stores such an entry, so the lookup never succeeded.
Having fields makes that kind of thing easier to see and fix.

Passes buildall w/ toolstash -cmp.

Change-Id: I418ad0e2847615c08868120c13ee719dc0b2eacb
Reviewed-on: https://go-review.googlesource.com/c/go/+/278915
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-18 17:52:54 +00:00
Russ Cox
ffb0cb7044 [dev.regabi] cmd/compile: remove uses of Name.Offset, Name.copy
For globals, Name.Offset is used as a way to address a field within
a global during static initialization. This CL replaces that use with
a separate NameOffsetExpr (ONAMEOFFSET) node.

For locals, Name.Offset is the stack frame offset. This CL calls it
that (FrameOffset, SetFrameOffset).

Now there is no longer any use of Name.Offset or Name.SetOffset.

And now that copies of Names are not being made to change their
offsets, we can lock down use of ir.Copy on Names. The only
remaining uses are during inlining and in handling generic system
functions. At both those times you do want to create a new name
and that can be made explicit by calling the new CloneName method
instead. ir.Copy on a name now panics.

Passes buildall w/ toolstash -cmp.

Change-Id: I0b0a25b9d93aeff7cf4e4025ac53faec7dc8603b
Reviewed-on: https://go-review.googlesource.com/c/go/+/278914
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-18 17:52:53 +00:00
Russ Cox
c76be2a24e [dev.regabi] cmd/compile: add ONAMEOFFSET, delete to-be-deleted fields
Breaks toolstash but clearly no effect.

Change-Id: Ic05bb7f74db170f140cf3b3cd7d629f159e3aae1
Reviewed-on: https://go-review.googlesource.com/c/go/+/278913
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-18 17:52:52 +00:00
Russ Cox
4e8f1e139f [dev.regabi] cmd/compile: cleanup for concrete types - sinit
An automated rewrite will add concrete type assertions after
a test of n.Op(), when n can be safely type-asserted
(meaning, n is not reassigned a different type, n is not reassigned
and then used outside the scope of the type assertion,
and so on).

This sequence of CLs handles the code that the automated
rewrite does not: adding specific types to function arguments,
adjusting code not to call n.Left() etc when n may have multiple
representations, and so on.

This CL focuses on sinit.go.

Passes buildall w/ toolstash -cmp.

Change-Id: I3e9458e69a7a9b3f2fe139382bf961bc4473cc42
Reviewed-on: https://go-review.googlesource.com/c/go/+/277928
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-18 17:52:51 +00:00
Russ Cox
27aba22651 [dev.regabi] cmd/compile: cleanup for concrete types - walk
An automated rewrite will add concrete type assertions after
a test of n.Op(), when n can be safely type-asserted
(meaning, n is not reassigned a different type, n is not reassigned
and then used outside the scope of the type assertion,
and so on).

This sequence of CLs handles the code that the automated
rewrite does not: adding specific types to function arguments,
adjusting code not to call n.Left() etc when n may have multiple
representations, and so on.

This CL focuses on walk.go.

Passes buildall w/ toolstash -cmp.

Change-Id: I7aab57e4077cf10da1994625575c5e42ad114a9c
Reviewed-on: https://go-review.googlesource.com/c/go/+/277921
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-18 17:52:49 +00:00
Russ Cox
0b9cb63b8d [dev.regabi] cmd/compile: rename ir.Find to ir.Any and update uses
ir.Find is called "any" in C#, Dart, Haskell, Python, R, Ruby, and Rust,
and "any_of" in C++, "anyMatch" in Java, "some" in JavaScript,
"exists in OCaml, and "existsb" in Coq.
(Thanks to Matthew Dempsky for the research.)

This CL changes Find to Any to use the mostly standard name.

It also updates wrapper helpers to use the any terminology:
	hasCall -> anyCall
	hasCallOrChan -> anyCallOrChan
	hasSideEffects -> anySideEffects

Unchanged are "hasNamedResults", "hasUniquePos", and "hasDefaultCase",
which are all about a single node, not any node in the IR tree.

I also renamed hasFall to endsInFallthrough, since its semantics are
neither that of "any" nor that of the remaining "has" functions.

So the new terminology helps separate different kinds of predicates nicely.

Change-Id: I9bb3c9ebf060a30447224be09a5c34ad5244ea0d
Reviewed-on: https://go-review.googlesource.com/c/go/+/278912
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-18 17:52:47 +00:00
Cuong Manh Le
aeedc9f804 [dev.regabi] cmd/compile: remove OSELRECV
Previous CL uses OSELRECV2 instead of OSELRECV, this CL removes it.

Make this a separated CL as it's not safe for toolstash.

Change-Id: I530ba33fd9311904545e40fe147829af629cf4a8
Reviewed-on: https://go-review.googlesource.com/c/go/+/275459
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-17 15:50:49 +00:00
Cuong Manh Le
0328c3b660 [dev.regabi] cmd/compile: use OSELRECV2 for all <-c variants
OSELRECV2 can represent all possible receive clauses that can appear
in a select statement, and it simplifies later code, so use it instead.

Follow up CL will remove OSELRECV.

Passes buildall w/ toolstash -cmp.

Change-Id: Ibbdae45287ffd888acd8dc89ca8d99e454277cd1
Reviewed-on: https://go-review.googlesource.com/c/go/+/275458
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-17 15:50:38 +00:00
Russ Cox
88e1415d08 [dev.regabi] cmd/compile: add type assertion in regabi test
Change-Id: I7da5165f3679736040be5bfbcea3d4a85deaff2e
Reviewed-on: https://go-review.googlesource.com/c/go/+/277957
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 04:43:59 +00:00
Russ Cox
9c384e881e [dev.regabi] cmd/compile: cleanup for concrete types - mop-up
An automated rewrite will add concrete type assertions after
a test of n.Op(), when n can be safely type-asserted
(meaning, n is not reassigned a different type, n is not reassigned
and then used outside the scope of the type assertion,
and so on).

This sequence of CLs handles the code that the automated
rewrite does not: adding specific types to function arguments,
adjusting code not to call n.Left() etc when n may have multiple
representations, and so on.

This CL handles all the little files that are left.

Passes buildall w/ toolstash -cmp.

Change-Id: I6588c92dbbdd37342a77b365d70e02134a033d2a
Reviewed-on: https://go-review.googlesource.com/c/go/+/277932
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 04:43:53 +00:00
Russ Cox
be64c8bece [dev.regabi] cmd/compile: cleanup for concrete types - noder
An automated rewrite will add concrete type assertions after
a test of n.Op(), when n can be safely type-asserted
(meaning, n is not reassigned a different type, n is not reassigned
and then used outside the scope of the type assertion,
and so on).

This sequence of CLs handles the code that the automated
rewrite does not: adding specific types to function arguments,
adjusting code not to call n.Left() etc when n may have multiple
representations, and so on.

This CL focuses on noder.go.

Passes buildall w/ toolstash -cmp.

Change-Id: Ie870126b51558e83c738add8e91a2804ed6d7f92
Reviewed-on: https://go-review.googlesource.com/c/go/+/277931
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 04:43:46 +00:00
Russ Cox
5024396563 [dev.regabi] cmd/compile: cleanup for concrete types - subr
An automated rewrite will add concrete type assertions after
a test of n.Op(), when n can be safely type-asserted
(meaning, n is not reassigned a different type, n is not reassigned
and then used outside the scope of the type assertion,
and so on).

This sequence of CLs handles the code that the automated
rewrite does not: adding specific types to function arguments,
adjusting code not to call n.Left() etc when n may have multiple
representations, and so on.

This CL focuses on subr.go.

Passes buildall w/ toolstash -cmp.

Change-Id: I435082167c91e20a4d490aa5d5945c7454f71d61
Reviewed-on: https://go-review.googlesource.com/c/go/+/277930
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 04:43:40 +00:00
Russ Cox
dd67b13d07 [dev.regabi] cmd/compile: cleanup for concrete types - range, select, swt
An automated rewrite will add concrete type assertions after
a test of n.Op(), when n can be safely type-asserted
(meaning, n is not reassigned a different type, n is not reassigned
and then used outside the scope of the type assertion,
and so on).

This sequence of CLs handles the code that the automated
rewrite does not: adding specific types to function arguments,
adjusting code not to call n.Left() etc when n may have multiple
representations, and so on.

This CL focuses on range.go, select.go, and swt.go: the big
control structures.

Passes buildall w/ toolstash -cmp.

Change-Id: I033fe056a7b815edb6e8a06f45c12ffd990f4d45
Reviewed-on: https://go-review.googlesource.com/c/go/+/277929
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 04:43:34 +00:00
Russ Cox
42fec2ded4 [dev.regabi] cmd/compile: cleanup for concrete types - const
An automated rewrite will add concrete type assertions after
a test of n.Op(), when n can be safely type-asserted
(meaning, n is not reassigned a different type, n is not reassigned
and then used outside the scope of the type assertion,
and so on).

This sequence of CLs handles the code that the automated
rewrite does not: adding specific types to function arguments,
adjusting code not to call n.Left() etc when n may have multiple
representations, and so on.

This CL focuses on const.go.

Passes buildall w/ toolstash -cmp.

Change-Id: I824f18fa0344ddde56df0522f9fa5e237114bbe2
Reviewed-on: https://go-review.googlesource.com/c/go/+/277927
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 04:43:27 +00:00
Russ Cox
389ae3d5ba [dev.regabi] cmd/compile: cleanup for concrete types - inl
An automated rewrite will add concrete type assertions after
a test of n.Op(), when n can be safely type-asserted
(meaning, n is not reassigned a different type, n is not reassigned
and then used outside the scope of the type assertion,
and so on).

This sequence of CLs handles the code that the automated
rewrite does not: adding specific types to function arguments,
adjusting code not to call n.Left() etc when n may have multiple
representations, and so on.

This CL focuses on inl.go.

Passes buildall w/ toolstash -cmp.

Change-Id: Iaaee7664cd43e264d9e49d252e3afa7cf719939b
Reviewed-on: https://go-review.googlesource.com/c/go/+/277926
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 04:43:21 +00:00
Russ Cox
5fe64298a4 [dev.regabi] cmd/compile: cleanup for concrete types - import/export
An automated rewrite will add concrete type assertions after
a test of n.Op(), when n can be safely type-asserted
(meaning, n is not reassigned a different type, n is not reassigned
and then used outside the scope of the type assertion,
and so on).

This sequence of CLs handles the code that the automated
rewrite does not: adding specific types to function arguments,
adjusting code not to call n.Left() etc when n may have multiple
representations, and so on.

This CL focuses on iimport.go and iexport.go.

Passes buildall w/ toolstash -cmp.

Change-Id: I63edee54991ae5d982e99efa7a2894478d511910
Reviewed-on: https://go-review.googlesource.com/c/go/+/277925
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 04:43:15 +00:00
Russ Cox
aa55d4e54b [dev.regabi] cmd/compile: cleanup for concrete types - escape
An automated rewrite will add concrete type assertions after
a test of n.Op(), when n can be safely type-asserted
(meaning, n is not reassigned a different type, n is not reassigned
and then used outside the scope of the type assertion,
and so on).

This sequence of CLs handles the code that the automated
rewrite does not: adding specific types to function arguments,
adjusting code not to call n.Left() etc when n may have multiple
representations, and so on.

This CL focuses on escape.go.

Passes buildall w/ toolstash -cmp.

Change-Id: I3e76e1ef9b72f28e3adad9633929699635d852dd
Reviewed-on: https://go-review.googlesource.com/c/go/+/277924
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 04:43:09 +00:00
Russ Cox
846740c17f [dev.regabi] cmd/compile: cleanup for concrete types - ssa
An automated rewrite will add concrete type assertions after
a test of n.Op(), when n can be safely type-asserted
(meaning, n is not reassigned a different type, n is not reassigned
and then used outside the scope of the type assertion,
and so on).

This sequence of CLs handles the code that the automated
rewrite does not: adding specific types to function arguments,
adjusting code not to call n.Left() etc when n may have multiple
representations, and so on.

This CL focuses on ssa.go.

Passes buildall w/ toolstash -cmp.

Change-Id: Iefacc7104dd9469e3c574149791ab0bff29f7fee
Reviewed-on: https://go-review.googlesource.com/c/go/+/277923
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 04:43:03 +00:00
Russ Cox
bf9bbbd6ed [dev.regabi] cmd/compile: cleanup for concrete types - order
An automated rewrite will add concrete type assertions after
a test of n.Op(), when n can be safely type-asserted
(meaning, n is not reassigned a different type, n is not reassigned
and then used outside the scope of the type assertion,
and so on).

This sequence of CLs handles the code that the automated
rewrite does not: adding specific types to function arguments,
adjusting code not to call n.Left() etc when n may have multiple
representations, and so on.

This CL focuses on order.go.

Passes buildall w/ toolstash -cmp.

Change-Id: Ib5731905a620175a6fe978f512da593e0dae9d87
Reviewed-on: https://go-review.googlesource.com/c/go/+/277922
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 04:42:58 +00:00
Russ Cox
4ac6a6317b [dev.regabi] cmd/compile: cleanup for concrete types - typecheck
An automated rewrite will add concrete type assertions after
a test of n.Op(), when n can be safely type-asserted
(meaning, n is not reassigned a different type, n is not reassigned
and then used outside the scope of the type assertion,
and so on).

This sequence of CLs handles the code that the automated
rewrite does not: adding specific types to function arguments,
adjusting code not to call n.Left() etc when n may have multiple
representations, and so on.

This CL focuses on typecheck.go.

Passes buildall w/ toolstash -cmp.

Change-Id: I32d1d3b813b0a088b1750c9fd28cd858ed813f1d
Reviewed-on: https://go-review.googlesource.com/c/go/+/277920
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 03:50:33 +00:00
Russ Cox
f6efa3d4a4 [dev.regabi] cmd/compile: simplify ir.Find, replace ir.Inspect with ir.Visit
It seems clear after using these for a week that Find need not return
anything other than a bool saying whether the target was found.
The main reason for not using the boolean earlier was to avoid confusion
with Inspect: for Find, returning true means "it was found! stop walking"
while for Inspect, returning true means "keep walking the children".

But it turns out that none of the uses of Inspect need the boolean.
This makes sense because types can contain expressions, expressions
can contain statements (inside function literals), and so on, so there
are essentially no times when you can say based on the current AST node
that the children are irrelevant to a particular operation.

So this CL makes two changes:

1) Change Find to return a boolean and to take a callback function
returning a boolean. This simplifies all existing calls to Find.

2) Rename Inspect to Visit and change it to take a callback with no
result at all. This simplifies all existing calls to Inspect.

Removing the boolean result from Inspect's callback avoids having
two callbacks with contradictory boolean results in different APIs.
Renaming Inspect to Visit avoids confusion with ast.Inspect.

Passes buildall w/ toolstash -cmp.

Change-Id: I344ebb5e00b6842012be33e779db483c28e5f350
Reviewed-on: https://go-review.googlesource.com/c/go/+/277919
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 03:50:26 +00:00
Russ Cox
f6d2834f8f [dev.regabi] cmd/compile: limit Implicit method to nodes where it is defined
The general concept of an "implicit" operation is provided by every
expr representation, but it really only makes sense for a few of them,
and worse the exact definition of what "implicit" means differs from
node to node.

This CL moves the method to each node implementation, although
they all share the same header bit instead of each defining a bool field
that would turn into 8 bytes on 64-bit systems.

Now we can say precisely which Nodes have a meaningful Implicit
method: AddrExpr, CompLitExpr, ConvExpr, ParenExpr, and StarExpr.

Passes buildall w/ toolstash -cmp.

Change-Id: I7d85cb0507a514cdcb6eed21347f362e5fb57a91
Reviewed-on: https://go-review.googlesource.com/c/go/+/277918
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 03:50:21 +00:00
Russ Cox
7fde0d2b50 [dev.regabi] cmd/compile: remove use of Initorder, Offset Node fields for initorder
The initorder pass is already making heavy use of maps,
and it is concerned with relatively few nodes (only the assignments
in package-level variable declarations). The tracking of init order
for these nodes can be done with another map instead of storing
the bits directly in the Node representations.

This will let us drop Offset_ from AssignStmt and AssignListStmt
and drop Initorder from all nodes.

Passes buildall w/ toolstash -cmp.

Change-Id: I151c64e84670292c2004da4e8e3d0660a88e3df3
Reviewed-on: https://go-review.googlesource.com/c/go/+/277917
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 03:50:15 +00:00
Russ Cox
114af2a044 [dev.regabi] cmd/compile: change Nodes to be a slice
The Nodes type originally served two purposes:
(1) It provided a representation optimized for empty slices,
allocating only a single word in that case instead of three,
at the cost of a non-empty slice being four words instead of three.
This was particularly important with the old Node representation,
in which most Nodes were full of unused fields.
(2) It provided a few useful helper methods beyond what can be
done with slices.

The downside of Nodes is that the API is a bit overwhelming,
with many ways to spell ordinary slice operations. For example,
reassigning the first node in the list can be done with:

	ns.Slice()[0] = n
	ns.SetIndex(0, n)
	ns.SetFirst(n)
	*ns.Addr(0) = n

And APIs must decide whether to use Nodes or []ir.Node and
then conversions must be inserted when crossing the boundary.

Now that Node structs are specialized to opcode and most Nodes
lists are actually non-empty, it makes sense to simplify Nodes
to make it actually a slice type, so that ordinary slice operations can
be used, and assignments can automatically convert between
Nodes and []ir.Node.

This CL changes the representation to be a slice and adds a new
Take method, which returns the old slice and clears the receiver.

In a future CL, the Nodes method set will simplify down to:

	Copy
	Take
	Append
	Prepend
	Format

with the current methods being rewritten:

	ns.Len() -> len(ns)
	ns.Slice() -> ns
	ns.First() -> ns[0]
	ns.Second() -> ns[1]
	ns.Index(i) -> ns[i]
	ns.Addr(i) -> &ns[i]
	ns.SetIndex(i, n) -> ns[i] = n
	ns.SetFirst(n) -> ns[0] = n
	ns.SetSecond(n) -> ns[1] = n
	ns.Set1(n) -> ns = []Node{n}
	ns.Set2(n, n2) -> ns = []Node{n, n2}
	ns.Set3(n, n2, n3) -> ns = []Node{n, n2, n3}
	AsNodes(slice) -> Nodes(slice)
	ns.AppendNodes(pns) -> ns.Append(pns.Take()...)
	ns.MoveNodes(pns) -> ns = pns.Take()

and then all those other methods will be deleted.

Simplifying the API down to just those five methods will also make it
more reasonable to introduce more specialized slices like Exprs and Stmts
at some point in the future.

But again this CL just changes the representation to a slice,
introduces Take, and leaves the rest alone.

Passes buildall w/ toolstash -cmp.

Change-Id: I309ab8335c69bb582d811c92c17f938dd6e0c4fe
Reviewed-on: https://go-review.googlesource.com/c/go/+/277916
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 03:50:09 +00:00
Russ Cox
4dfc7333f4 [dev.regabi] cmd/compile: update ir/fmt for concrete types
An automated rewrite will add concrete type assertions after
a test of n.Op(), when n can be safely type-asserted
(meaning, n is not reassigned a different type, n is not reassigned
and then used outside the scope of the type assertion,
and so on).

This sequence of CLs handles the code that the automated
rewrite does not: adding specific types to function arguments,
adjusting code not to call n.Left() etc when n may have multiple
representations, and so on.

This CL handles package fmt. There are various type assertions
but also some rewriting to lean more heavily on reflection.

Passes buildall w/ toolstash -cmp.

Change-Id: I503467468b42ace11bff2ba014b03cfa345e6d03
Reviewed-on: https://go-review.googlesource.com/c/go/+/277915
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 03:50:03 +00:00
Russ Cox
a997543292 [dev.regabi] cmd/compile: fix potential closure waste in Order
I haven't measured this, but it's the only use of EditChildren
where we aren't careful to allocate a closure once and use it
for the whole recursion. This one is allocating a closure at
every level of the recursion, and it was an oversight that it
wasn't cleaned up in the original CL.

Passes buildall w/ toolstash -cmp.

Change-Id: I5e3f1795c6f64c5867a19c077f797643aa1066a3
Reviewed-on: https://go-review.googlesource.com/c/go/+/277914
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 03:49:57 +00:00
Russ Cox
578fbbe3aa [dev.regabi] cmd/compile: rewrite some generic ir.Nod calls
An automated rewrite is going to remove the bulk of the calls
to ir.Nod and friends. This CL takes care of the ones that don't
have fixed opcodes and so aren't amenable to automatic rewriting.

Passes buildall w/ toolstash -cmp.

Replay of CL 275886, lost to the bad-merge history rewrite.

Change-Id: I5bf8d1d182f847f4ab44b7e278b752913e30e4c8
Reviewed-on: https://go-review.googlesource.com/c/go/+/277956
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 03:49:52 +00:00
Russ Cox
5ae70b85c6 [dev.regabi] cmd/compile: cleanup preparing for concrete types, 2
Avoid using the same variable for two different concrete
Node types in other files (beyond walk). This will smooth the
introduction of specific constructors, replacing ir.Nod and friends.

Passes buildall w/ toolstash -cmp.

Replay of CL 275885, lost to the bad-merge history rewrite.

Change-Id: I0da89502a0bd636b8766f01b6f843c7821b3e9ab
Reviewed-on: https://go-review.googlesource.com/c/go/+/277955
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 03:49:45 +00:00
Russ Cox
fa06894b36 [dev.regabi] cmd/compile: cleanup preparing for concrete types
Avoid using the same variable for two different concrete
Node types in walk. This will smooth the introduction of
specific constructors, replacing ir.Nod and friends.

Passes buildall w/ toolstash -cmp.

Replay of CL 275884, lost to the bad-merge history rewrite.

Change-Id: I05628e20a19c9559ed7478526ef6cb2613f735e5
Reviewed-on: https://go-review.googlesource.com/c/go/+/277954
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-17 03:49:41 +00:00
Matthew Dempsky
4c2d66f642 [dev.regabi] cmd/compile: use ir.Ident for imported identifiers
This CL substantially reworks how imported declarations are handled,
and fixes a number of issues with dot imports. In particular:

1. It eliminates the stub ir.Name declarations that are created
upfront during import-declaration processing, allowing this to be
deferred to when the declarations are actually needed. (Eventually,
this can be deferred even further so we never have to create ir.Names
w/ ONONAME, but this CL is already invasive/subtle enough.)

2. During noding, we now use ir.Idents to represent uses of imported
declarations, including of dot-imported declarations.

3. Unused dot imports are now reported after type checking, so that we
can correctly distinguish whether composite literal keys are a simple
identifier (struct literals) or expressions (array/slice/map literals)
and whether it might be a use of a dot-imported declaration.

4. It changes the "redeclared" error messages to report the previous
position information in the same style as other compiler error
messages that reference other source lines.

Passes buildall w/ toolstash -cmp.

Fixes #6428.
Fixes #43164.
Fixes #43167.
Updates #42990.

Change-Id: I40a0a780ec40daf5700fbc3cfeeb7300e1055981
Reviewed-on: https://go-review.googlesource.com/c/go/+/277713
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-12-15 07:53:10 +00:00
Matthew Dempsky
305d93ef84 [dev.regabi] cmd/compile: type check externdcl earlier
The next CL requires externdcl to be type checked earlier, but this
causes toolstash -cmp to complain because it causes src.PosBases to
get added in a different order. So split out into a separate CL.

Change-Id: Icab4eadd3fa8acffbd3e980bd8100924378351b3
Reviewed-on: https://go-review.googlesource.com/c/go/+/277732
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-15 07:16:12 +00:00
Matthew Dempsky
9f16620f46 [dev.regabi] cmd/compile: fix latent Sym.SetPkgDef issue
Sym.pkgDefPtr is supposed to return a pointer to the types.Object
variable currently holding the Sym's package-scope
definition. However, in the case of identifiers that were shadowed in
the current scope, it was incorrectly returning a pointer to a stack
copy of the dclstack variable, rather than a pointer into the dclstack
itself.

This doesn't affect PkgDef, because it only reads from the variable,
so it got the same result anyway. It also happens to not affect our
usage of SetPkgDef today, because we currently only call SetPkgDef for
the builtin/runtime.go symbols, and those are never shadowed.

However, it does affect my upcoming CL to lazily create the ir.Names
for imported objects, as that depends on the ability to use SetPkgDef
to set shadowed identifiers.

Passes buildall w/ toolstash -cmp.

Change-Id: I54fc48b33da0670d31725faa1df1170a8730750a
Reviewed-on: https://go-review.googlesource.com/c/go/+/277712
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-15 07:15:56 +00:00
Keith Randall
fea898a4b0 [dev.regabi] cmd/compile: intercept the making of OADDR nodes
This is a mechanical change to intercept the construction of
all OADDR nodes. We will use the new nodAddr and nodAddrAt
functions to compute the Addrtaken bit.

Change-Id: I90ee3acb8e32540a198a9999284573418729f422
Reviewed-on: https://go-review.googlesource.com/c/go/+/275694
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Dan Scales <danscales@google.com>
2020-12-14 23:35:06 +00:00
Keith Randall
617383377f [dev.regabi] cmd/compile: reorg generated array hash loop
The ORANGE structure that is being replaced by this CL was causing
trouble with another CL (CL 275695).

The problem occurs if you typecheck i in the middle of generating the
body of the ORANGE loop. If you typecheck i, it ends up typechecking
its definition, which secretly typechecks the containing ORANGE.  If
you then add other items to the ORANGE body, those items will never
get typechecked, as the ORANGE is already marked as typechecked.

Instead, just steal the loop we use for the equality code. Might as
well use the same pattern in both places.

Change-Id: Idb1ac77881d2cc9da08c7437a652b50d3ee45e2e
Reviewed-on: https://go-review.googlesource.com/c/go/+/275713
Trust: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dan Scales <danscales@google.com>
2020-12-14 21:39:00 +00:00
Than McIntosh
89f38323fa [dev.regabi] cmd/compile: add register ABI analysis utilities
Introduce a new utility routine for analyzing a given function
signature to how its various input and output parameters will be
passed (in registers or on the stack) for a given ABI description,
along with some unit tests.

Change-Id: Id64a98a0a142e42dd9c2dc9f6607c0d827ef84fb
Reviewed-on: https://go-review.googlesource.com/c/go/+/273011
Run-TryBot: Than McIntosh <thanm@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
Trust: Than McIntosh <thanm@google.com>
2020-12-14 17:34:36 +00:00
Matthew Dempsky
8ce37e4110 [dev.regabi] cmd/compile: fix noopt builder
The non-simple, phi-insertion algorithm can leave OpFwdRefs in the SSA
graph unresolved if they're in dead blocks. Normally, these would be
harmlessly removed later during SSA dead-code elimination, but those
passes are omitted for -N builds. And so they reach zcse, where the
Value.Aux is used within a hash map.

This became a problem after golang.org/cl/275788, which added
FwdRefAux to wrap OpFwdRef's ir.Node, and to ensure that it's not
compared for equality / used as a map key.

This CL adds a simple fix: if there are any OpFwdRefs remaining after
resolveFwdRef, then they must be dead code and we can simply replace
them with OpUnknown.

Change-Id: I72e4116d52d3f6441ebb0bf6160906617cd59513
Reviewed-on: https://go-review.googlesource.com/c/go/+/277075
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
2020-12-14 11:43:58 -05:00
Than McIntosh
7e17b46c58 [dev.regabi] cmd/compile/internal/types: add IsScalar query method
Add method Type.IsScalar() method, which returns TRUE
for numeric and pointer-shaped types, false for composites
such as string/array/slice/struct.

Change-Id: Ie53c71c07c5b3fbae11b48addd172343dc6bf3fd
Reviewed-on: https://go-review.googlesource.com/c/go/+/274857
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Than McIntosh <thanm@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-14 11:43:51 -05:00
Than McIntosh
2b76429eb0 [dev.regabi] cmd/compile: refactor type initialization code into helper
Create a helper routine for initializing the types package, so as make
it easier to use in unit testing (in a follow-on patch).

Change-Id: I0f937788dfd34ac6641a4f28c16e47008aa08116
Reviewed-on: https://go-review.googlesource.com/c/go/+/273010
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Than McIntosh <thanm@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-14 11:43:39 -05:00
Matthew Dempsky
9c5241e520 [dev.regabi] cmd/compile: remove unnecessary String methods
There were only a few places these were still used, none of which
justify generating all this code. Instead rewrite them to use
fmt.Sprint or simpler means.

Passes buildall w/ toolstash -cmp.

Change-Id: Ibd123a1696941a597f0cb4dcc96cda8ced672140
Reviewed-on: https://go-review.googlesource.com/c/go/+/276072
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-14 11:43:32 -05:00
Alexander Rakoczy
267975dc47 Merge branch 'master' into dev.regabi
Change-Id: I098acdbc5e2676aeb8700d935e796a9c29d04b88
2020-12-14 11:42:42 -05:00
Matthew Dempsky
63bc23b545 [dev.regabi] cmd/compile: first start towards using Ident
This CL adds Ident, which will eventually replace *Name and *PkgName
within the AST for representing uses of declared names. (Originally, I
intended to call it "IdentExpr", but neither go/ast nor
cmd/compile/internal/syntax include the "Expr" suffix for their
respective types.)

To start, this CL converts two uses of *Name to *Ident: the tag
identifier in a TypeSwitchGuard (which doesn't actually declare a
variable by itself), and the not-yet-known placeholder ONONAME
returned by oldname to stand-in for identifiers that might be declared
later in the package.

The TypeSwitchGuard's Name's Used flag was previously used for
detecting whether none of the per-clause variables were used. To avoid
bloating all Idents for this rare use, a "Used" bool is added to
TypeSwitchGuard instead. Eventually it could maybe be packed into
miniNode.bits, but for now this is good enough.

Passes buildall w/ toolstash -cmp.

Change-Id: I393284d86757cbbebd26e1320c7354e2bdcb30b0
Reviewed-on: https://go-review.googlesource.com/c/go/+/276113
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-09 17:17:53 +00:00
Matthew Dempsky
eae8fd519b [dev.regabi] cmd/compile: iexport debug crumbs for toolstash
Prints offsets for declarations, inline bodies, and strings when -v is
used. Still not much, but hopefully useful for narrowing down the
cause of export data differences.

Change-Id: I9b2e4a3d55b92823fa45a39923e8c4b25303693c
Reviewed-on: https://go-review.googlesource.com/c/go/+/276112
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-09 17:17:31 +00:00
Russ Cox
837b35cc55 [dev.regabi] cmd/compile: adjust IR representations
Based on actually using the IR when prototyping adding
type assertions, a few changes to improve it:

- Merge DeferStmt and GoStmt, since they are variants of one thing.
- Introduce LogicalExpr for && and ||, since they (alone) need an init list before Y.
- Add an explicit op to various constructors to make them easier to use.
- Add separate StructKeyExpr - it stores Value in a different abstract location (Left) than KeyExpr (Right).
- Export all fields for use by rewrites (and later reflection).

Passes buildall w/ toolstash -cmp.

Change-Id: Iefbff2386d2bb9ef511ce53b7f92ff6c709dc991
Reviewed-on: https://go-review.googlesource.com/c/go/+/275883
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-09 17:05:32 +00:00
Russ Cox
0c49440664 [dev.regabi] cmd/compile: arrange for walkstmt, walkexpr, to return from switch cases
Ending them in a returning switch makes it safe for each case
to do an appropriate type assertion.

Passes buildall w/ toolstash -cmp.

Change-Id: I55d8f0a555006104164d84d27822aa8c5ad68515
Reviewed-on: https://go-review.googlesource.com/c/go/+/275882
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-09 17:05:19 +00:00
Russ Cox
4090af83c5 [dev.regabi] cmd/compile: use reflection in ir.Dump
ir.Dump is the final (I think!) piece of the compiler that was walking
nodes using Left, Right etc without knowing what they meant.
This CL uses reflection to walk nodes without knowing what they mean instead.
One benefit is that we can print actual meanings (field names).

While we are here, I could not resist fixing a long-standing mental TODO:
make the line number more clearly a line number. I've forgotten where the
line number is in the dumps far too many times in the last decade.

As a small example, here is a fragment of go tool compile -W test/235.go:

.   FOR l(28) tc(1)
.   .   LT-init
.   .   .   AS l(28) tc(1)
.   .   .   .   NAME-main..autotmp_4 l(28) x(0) class(PAUTO) esc(N) tc(1) assigned used int
.   .   .   .   LEN l(28) tc(1) int
.   .   .   .   .   NAME-main.xs g(2) l(26) x(0) class(PPARAM) esc(no) tc(1) used SLICE-[]uint64
.   .   LT l(28) tc(1) hascall bool
.   .   .   NAME-main.i g(4) l(28) x(0) class(PAUTO) esc(no) tc(1) assigned used int
.   .   .   NAME-main..autotmp_4 l(28) x(0) class(PAUTO) esc(N) tc(1) assigned used int
.   .   BLOCK l(28)
.   .   BLOCK-list
.   .   .   ASOP-ADD l(28) tc(1) implicit(true) int
.   .   .   .   NAME-main.i g(4) l(28) x(0) class(PAUTO) esc(no) tc(1) assigned used int
.   .   .   .   LITERAL-1 l(28) tc(1) int
.   FOR-body
.   .   VARKILL l(28) tc(1)
.   .   .   NAME-main..autotmp_4 l(28) x(0) class(PAUTO) esc(N) tc(1) assigned used int
.   .   IF l(29) tc(1)
.   .   .   LT l(29) tc(1) bool
.   .   .   .   INDEX l(29) tc(1) uint64
.   .   .   .   .   NAME-main.xs g(2) l(26) x(0) class(PPARAM) esc(no) tc(1) used SLICE-[]uint64
.   .   .   .   .   NAME-main.i g(4) l(28) x(0) class(PAUTO) esc(no) tc(1) assigned used int
.   .   .   .   NAME-main.m g(3) l(27) x(0) class(PAUTO) esc(no) tc(1) assigned used uint64
.   .   IF-body
.   .   .   AS l(30) tc(1)
.   .   .   .   NAME-main.m g(3) l(27) x(0) class(PAUTO) esc(no) tc(1) assigned used uint64
.   .   .   .   INDEX l(30) tc(1) uint64
.   .   .   .   .   NAME-main.xs g(2) l(26) x(0) class(PPARAM) esc(no) tc(1) used SLICE-[]uint64
.   .   .   .   .   NAME-main.i g(4) l(28) x(0) class(PAUTO) esc(no) tc(1) assigned used int

and here it is after this CL:

.   FOR tc(1) # 235.go:28
.   FOR-Cond
.   .   LT-init
.   .   .   AS tc(1) # 235.go:28
.   .   .   .   NAME-main..autotmp_4 x(0) class(PAUTO) esc(N) tc(1) assigned used int # 235.go:28
.   .   .   .   LEN tc(1) int # 235.go:28 int
.   .   .   .   .   NAME-main.xs g(2) x(0) class(PPARAM) esc(no) tc(1) used SLICE-[]uint64 # 235.go:26
.   .   LT tc(1) hascall bool # 235.go:28 bool
.   .   .   NAME-main.i g(4) x(0) class(PAUTO) esc(no) tc(1) assigned used int # 235.go:28
.   .   .   NAME-main..autotmp_4 x(0) class(PAUTO) esc(N) tc(1) assigned used int # 235.go:28
.   FOR-Post
.   .   BLOCK # 235.go:28
.   .   BLOCK-List
.   .   .   ASOP-ADD tc(1) implicit(true) int # 235.go:28 int
.   .   .   .   NAME-main.i g(4) x(0) class(PAUTO) esc(no) tc(1) assigned used int # 235.go:28
.   .   .   .   LITERAL-1 tc(1) int # 235.go:28
.   FOR-Body
.   .   VARKILL tc(1) # 235.go:28
.   .   .   NAME-main..autotmp_4 x(0) class(PAUTO) esc(N) tc(1) assigned used int # 235.go:28
.   .   IF tc(1) # 235.go:29
.   .   IF-Cond
.   .   .   LT tc(1) bool # 235.go:29 bool
.   .   .   .   INDEX tc(1) uint64 # 235.go:29 uint64
.   .   .   .   .   NAME-main.xs g(2) x(0) class(PPARAM) esc(no) tc(1) used SLICE-[]uint64 # 235.go:26
.   .   .   .   .   NAME-main.i g(4) x(0) class(PAUTO) esc(no) tc(1) assigned used int # 235.go:28
.   .   .   .   NAME-main.m g(3) x(0) class(PAUTO) esc(no) tc(1) assigned used uint64 # 235.go:27
.   .   IF-Body
.   .   .   AS tc(1) # 235.go:30
.   .   .   .   NAME-main.m g(3) x(0) class(PAUTO) esc(no) tc(1) assigned used uint64 # 235.go:27
.   .   .   .   INDEX tc(1) uint64 # 235.go:30 uint64
.   .   .   .   .   NAME-main.xs g(2) x(0) class(PPARAM) esc(no) tc(1) used SLICE-[]uint64 # 235.go:26
.   .   .   .   .   NAME-main.i g(4) x(0) class(PAUTO) esc(no) tc(1) assigned used int # 235.go:28

Note in particular the clear marking of FOR-Cond, FOR-Post, FOR-Body compared to the original.

The only changes to a few test files are the improved field name lines, and of course the line numbers.

Passes buildall w/ toolstash -cmp.

Change-Id: I5b654d9d8ee898976d4c387742ea688a082bac78
Reviewed-on: https://go-review.googlesource.com/c/go/+/275785
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-09 17:05:13 +00:00
Russ Cox
e2d278bfeb [dev.regabi] cmd/compile: two small fixes
Addressing comments from CL 275434 and CL 275444.
I forgot to run "git rw" to rebase the fixup CLs down before
running "git submit".

Change-Id: Ideaa2340a81511491c096555c6834cd9bdb267d8
Reviewed-on: https://go-review.googlesource.com/c/go/+/275881
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-09 17:04:54 +00:00
Matthew Dempsky
dbf2fc8cff [dev.regabi] cmd/compile: replace many uses of ir.Node with *ir.Name
This commit adds exactly two "n := n.(*ir.Name)" statements, that are
each immediately preceded by a "case ir.ONAME:" clause in an n.Op()
switch. The rest of the changes are simply replacing "ir.Node" to
"*ir.Name" and removing now unnecessary "n.(*ir.Name)" type
assertions, exposing the latent typing details.

Passes buildall w/ toolstash -cmp.

Updates #42982.

Change-Id: I8ea3bbb7ddf0c7192245cafa49a19c0e7a556a39
Reviewed-on: https://go-review.googlesource.com/c/go/+/275791
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-08 01:47:13 +00:00
Matthew Dempsky
bb31c75343 [dev.regabi] cmd/compile: ir.Node is no longer an ssa.Aux
After the previous rewrite, we can now remove CanBeAnSSASym and
CanBeAnSSAAux from the generic Node interface, and declare them just
on *ir.Name.

Updates #42982.

Change-Id: I865771fd30c95c009740410844f20ade08648343
Reviewed-on: https://go-review.googlesource.com/c/go/+/275790
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-08 01:47:04 +00:00
Matthew Dempsky
6db970e20a [dev.regabi] cmd/compile: rewrite Aux uses of ir.Node to *ir.Name [generated]
Now that the only remaining ir.Node implementation that is stored
(directly) into ssa.Aux, we can rewrite all of the conversions between
ir.Node and ssa.Aux to use *ir.Name instead.

rf doesn't have a way to rewrite the type switch case clauses, so we
just use sed instead. There's only a handful, and they're the only
times that "case ir.Node" appears anyway.

The next CL will move the tag method declarations so that ir.Node no
longer implements ssa.Aux.

Passes buildall w/ toolstash -cmp.

Updates #42982.

[git-generate]
cd src/cmd/compile/internal
sed -i -e 's/case ir.Node/case *ir.Name/' gc/plive.go */ssa.go

cd ssa
rf '
ex . ../gc {
  import "cmd/compile/internal/ir"

  var v *Value
  v.Aux.(ir.Node) -> v.Aux.(*ir.Name)

  var n ir.Node
  var asAux func(Aux)
  strict n        # only match ir.Node-typed expressions; not *ir.Name
  implicit asAux  # match implicit assignments to ssa.Aux
  asAux(n)        -> n.(*ir.Name)
}
'

Change-Id: I3206ef5f12a7cfa37c5fecc67a1ca02ea4d52b32
Reviewed-on: https://go-review.googlesource.com/c/go/+/275789
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-08 01:46:57 +00:00
Matthew Dempsky
1c8943a6ad [dev.regabi] cmd/compile: introduce FwdRefAux for wrapping ir.Node as ssa.Aux
OpFwdRef is the only SSA value that needs the ability to store an
arbitrary ir.Node in its Aux field. Every other SSA value always uses
an *ir.Name.

This CL introduces FwdRefAux, which wraps an ir.Node and implements
the ssa.Aux tag interface, so that a subsequent refactoring can change
ir.Node to not implement ssa.Aux.

Passes buildall w/ toolstash -cmp.

Updates #42982.

Change-Id: Id1475b28847579573cd376e82f28761d84cd1c23
Reviewed-on: https://go-review.googlesource.com/c/go/+/275788
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-08 01:46:48 +00:00
Matthew Dempsky
dcec658f6c [dev.regabi] cmd/compile: change LocalSlot.N to *ir.Name
This was already documented as always being an ONAME, so it just
needed a few type assertion changes.

Passes buildall w/ toolstash -cmp.

Updates #42982.

Change-Id: I61f4b6ebd57c43b41977f4b37b81fe94fb11a723
Reviewed-on: https://go-review.googlesource.com/c/go/+/275757
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Russ Cox <rsc@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-12-08 01:46:40 +00:00
Matthew Dempsky
1a98ab0e2d [dev.regabi] cmd/compile: add ssa.Aux tag interface for Value.Aux
It's currently hard to automate refactorings around the Value.Aux
field, because we don't have any static typing information for it.
Adding a tag interface will make subsequent CLs easier and safer.

Passes buildall w/ toolstash -cmp.

Updates #42982.

Change-Id: I41ae8e411a66bda3195a0957b60c2fe8a8002893
Reviewed-on: https://go-review.googlesource.com/c/go/+/275756
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-12-08 01:46:31 +00:00
Keith Randall
63722da46b [dev.regabi] cmd/compile: fix comment
Russ, is this what you meant?

Change-Id: I27d2847811c6eabd94358e435eb3eb4bc8cfaa9e
Reviewed-on: https://go-review.googlesource.com/c/go/+/275712
Trust: Keith Randall <khr@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-07 21:43:46 +00:00
Russ Cox
6d783e7440 [dev.regabi] cmd/compile: export all Node fields [generated]
The plan was always to export them once we remove the getters
and setters, but do it a bit early, with _ suffixes as needed, so that
the reflection-based ir.Dump can access the fields.

Passes buildall w/ toolstash -cmp.

[git-generate]
cd src/cmd/compile/internal/ir
rf '
	mv AddStringExpr.list AddStringExpr.List_
	mv BlockStmt.list BlockStmt.List_
	mv CallExpr.body CallExpr.Body_
	mv CaseStmt.list CaseStmt.List_
	mv CaseStmt.body CaseStmt.Body_
	mv ClosureExpr.fn ClosureExpr.Func_
	mv CompLitExpr.list CompLitExpr.List_
	mv ForStmt.body ForStmt.Body_
	mv Func.body Func.Body_
	mv IfStmt.body IfStmt.Body_
	mv InlinedCallExpr.body InlinedCallExpr.Body_
	mv RangeStmt.body RangeStmt.Body_
	mv SliceExpr.list SliceExpr.List_
	mv SliceHeaderExpr.lenCap SliceHeaderExpr.LenCap_
	mv TypeSwitchGuard.name TypeSwitchGuard.Name_
'
go generate

Change-Id: I06e65920cecbcc51bea2254f52fcd7d5c5d0dc90
Reviewed-on: https://go-review.googlesource.com/c/go/+/275784
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-07 20:41:43 +00:00
Russ Cox
2de0af3b1b [dev.regabi] cmd/compile: prepare mknode for rename of Func.body
The next CL will rename Func.body to Func.Body_.
At some point in the future we will rename it to Func.Body.
Make the generator not get confused.

Passes buildall w/ toolstash -cmp.

Change-Id: Iee3f4915889a8287377bf3304d5b9250a909477e
Reviewed-on: https://go-review.googlesource.com/c/go/+/275783
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-07 20:41:37 +00:00
Russ Cox
724374f859 [dev.regabi] cmd/compile: rewrite stale format strings
On ir.Node, ir.Nodes, and ir.Op, # is ignored, so %#v is %v.
On ir.Node, %S is the same as %v.

On types.Type, # is ignored, so %#L is %L, %#v is %v.
On types.Type, 0 is ignored, so %0S is %S.

Rewrite all these using go test cmd/compile -r, plus a
few multiline formats mentioning %0S on types updated by hand.

Now the formats used in the compiler match the documentation
for the format methods, a minor miracle.

Passes buildall w/ toolstash -cmp.

Change-Id: I3d4a3fae543145a68da13eede91166632c5b1ceb
Reviewed-on: https://go-review.googlesource.com/c/go/+/275782
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-07 20:41:29 +00:00
Russ Cox
61889ba680 [dev.regabi] cmd/compile: simplify fmtmap
The format map is going to keep growing as we add more use of
concrete node types. Stop that by reporting all Node implementations
as Node.

Also, there's little point to reporting uses of %v, %p, %T, nor to reporting
formatting of basic types like int and []byte. Remove those too.
(Vet takes care of mistakes involving basic types now.)

Passes buildall w/ toolstash -cmp.

Change-Id: Ia9fb39b401c29bf0c76ffebaa24836c70acd773f
Reviewed-on: https://go-review.googlesource.com/c/go/+/275781
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-07 20:41:23 +00:00
Russ Cox
6ea2b8c54c [dev.regabi] cmd/compile: clean up and document formatting
Some cleanup left over from moving the Type and Sym formatting to types.
And then document what the type formats are, now that it's clear.

Passes buildall w/ toolstash -cmp.

Change-Id: I35cb8978f1627db1056cb8ab343ce6ba6c99afad
Reviewed-on: https://go-review.googlesource.com/c/go/+/275780
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-07 20:41:17 +00:00
Russ Cox
bb4a37bd93 [dev.regabi] cmd/compile: move Type, Sym printing to package types [generated]
Move the printing of types.Type and types.Sym out of ir
into package types, where it properly belongs. This wasn't
done originally (when the code was in gc) because the Type
and Sym printing was a bit tangled up with the Node printing.
But now they are untangled and can move into the correct
package.

This CL is automatically generated.
A followup CL will clean up a little bit more by hand.

Passes buildall w/ toolstash -cmp.

[git-generate]
cd src/cmd/compile/internal/ir
rf '
	mv FmtMode fmtMode
	mv FErr fmtGo
	mv FDbg fmtDebug
	mv FTypeId fmtTypeID
	mv FTypeIdName fmtTypeIDName
	mv methodSymName SymMethodName

	mv BuiltinPkg LocalPkg BlankSym OrigSym NumImport \
		fmtMode fmtGo symFormat sconv sconv2 symfmt SymMethodName \
		BasicTypeNames fmtBufferPool InstallTypeFormats typeFormat tconv tconv2 fldconv FmtConst \
		typefmt.go

	mv typefmt.go cmd/compile/internal/types
'
cd ../types
mv typefmt.go fmt.go

Change-Id: I6f3fd818323733ab8446f00594937c1628760b27
Reviewed-on: https://go-review.googlesource.com/c/go/+/275779
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-07 20:41:11 +00:00
Russ Cox
70155cca81 [dev.regabi] cmd/compile: untangle FmtFlag, FmtMode
It turns out that the FmtFlag is really only tracking the FmtLong and FmtShort bits,
and the others simply mirror the state of the FmtMode and are copied out and
back in repeatedly.

Simplify to FmtFlag being the verb itself ('S', 'L', or 'v').

Now there is only one formatting enumeration, making it a bit
easier to understand what's going on.

Passes buildall w/ toolstash -cmp.

Change-Id: I85bde2183eb22228fcf46d19d003401d588d9825
Reviewed-on: https://go-review.googlesource.com/c/go/+/275778
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-07 20:41:03 +00:00
Russ Cox
3904a62829 [dev.regabi] cmd/compile: remove mode.Sprintf etc in printer
This code is now hardly used and not worth the complexity.
It also tangles together Nodes and Types in a way that keeps
this code in package ir instead of package types.

Passes buildall w/ toolstash -cmp.

Change-Id: I2e829c1f6b602acbdc8ab4aac3b798f9ded762ef
Reviewed-on: https://go-review.googlesource.com/c/go/+/275777
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-07 20:40:58 +00:00
Russ Cox
fb17dfa43d [dev.regabi] cmd/compile: narrow interface between ir and types
Narrow the interface between package ir and package types
to make it easier to clean up the type formatting code all in one place.

Also introduce ir.BlankSym for use by OrigSym, so that later
OrigSym can move to package types without needing to reference
a variable of type ir.Node.

Passes buildall w/ toolstash -cmp.

Change-Id: I39fa419a1c8fb3318203e31cacc8d06399deeff9
Reviewed-on: https://go-review.googlesource.com/c/go/+/275776
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-07 20:40:52 +00:00
Russ Cox
3b25f3c150 [dev.regabi] cmd/compile: simplify Op, Node, Nodes printing
nconvFmt calls base.Fatalf if mode is anything but FErr,
proving that the only formats that matter for nodes are
plain %v, %S, and %L. And the nodes formatter can only get to %v.
(%S and %v are the same; we'll clean that up separately.)

Node and Nodes can therefore ignore mode, and all the mode
code can be removed from those implementations, removing
quite a few layers of abstraction.

Op similarly only runs in one mode and can be simplified.

Passes buildall w/ toolstash -cmp.

Change-Id: Ibfd845033e9c68181a20fb81c8f3dd428463920a
Reviewed-on: https://go-review.googlesource.com/c/go/+/275775
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-07 20:40:46 +00:00
Russ Cox
8ce2605c5b [dev.regabi] cmd/compile: untangle ir.Dump printing
The Node printing code is tangled up due to the multiple printing modes.
Split out the Dump mode into its own code, which clarifies it considerably.
We are going to have to change the code for the new Node representations,
so it is nice to have it in an understandable form first.

The output of Dump is unchanged except for the removal of spurious
mid-Dump blank lines that have been printed for a while but don't
really make sense and appear to be a bug.

The %+v verb on Op prints the name ("ADD" not "+"), matching
%+v on Node and %+v on Nodes to get Dump and DumpList formats.

Passes buildall w/ toolstash -cmp.

Change-Id: I07f0f245859f1f785e10bdd671855ca43c51b545
Reviewed-on: https://go-review.googlesource.com/c/go/+/275774
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-07 20:40:41 +00:00
Russ Cox
158c9dd131 [dev.regabi] cmd/compile: reorganize ir/fmt.go
This code is a few layer of abstraction stacked up on top
of each other, and they're hard to see all at the same time
because the file is pretty mixed up. As much as I try to avoid
code rearrangement to keep history, this one is long overdue.

A followup CL will cut out some of the layers, and the diff will be
much clearer what's going on with the code ordered with
callers near callees, as it is now.

Passes buildall w/ toolstash -cmp.

Change-Id: Iffc49d43cf4be9fab47e2dd59a5f98930573350f
Reviewed-on: https://go-review.googlesource.com/c/go/+/275773
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-07 20:40:34 +00:00
Russ Cox
a79742f39a [dev.regabi] cmd/compile: remove "short" node header mode
This is unreachable code - the only way short can be true is
if verb == 'S', but jconv is only called when verb == 'j'.
Simplify by removing.

Passes buildall w/ toolstash -cmp.

Change-Id: I27bd38319f72215069e940b320b5c82608e2651a
Reviewed-on: https://go-review.googlesource.com/c/go/+/275772
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-07 20:40:28 +00:00
Russ Cox
ef5964dd6b [dev.regabi] cmd/compile: arrange for typecheck1 to end in switch
Ending typecheck1 in the switch makes it safe for each case
to do an appropriate type assertion. The main change is dropping
the computation of "ok" and using the syntax nodes themselves
to decide what's OK.

Passes buildall w/ toolstash -cmp.

Change-Id: I2a1873a51e3f1194d74bb87a6653cb9857a02a1b
Reviewed-on: https://go-review.googlesource.com/c/go/+/275444
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-07 20:40:21 +00:00
Russ Cox
dcc640e839 [dev.regabi] test: add exhaustive test of evaluated but not used
Change-Id: I49db03c88b7595f1ea593df568244ad6aad3b024
Reviewed-on: https://go-review.googlesource.com/c/go/+/275443
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-07 20:40:14 +00:00
Russ Cox
2cec6c4a8c [dev.regabi] cmd/compile: generate Node methods using program
Add Node method generator by Matthew Dempsky,
lightly adapted to account for a few special cases.
No more writing these by hand.

Change-Id: I6933b895df666928b851bddf81b994799c0c97f7
Reviewed-on: https://go-review.googlesource.com/c/go/+/275434
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-07 20:40:08 +00:00
Matthew Dempsky
d90b199e9c [dev.regabi] cmd/compile: silence errors about missing blank methods
If an interface contains a blank method, that's already an error. No
need for useless follow-up error messages about not implementing them.

Fixes #42964.

Change-Id: I5bf53a8f27d75d4c86c61588c5e2e3e95563d320
Reviewed-on: https://go-review.googlesource.com/c/go/+/275294
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2020-12-07 06:40:04 +00:00
Matthew Dempsky
e885df2731 [dev.regabi] cmd/compile: change iexport to avoid map[ir.Node]
In the past, we had a lot of trouble with misusing *types.Sym
throughout the frontend, so I tried to push us towards always passing
around ONAMEs instead. But for constructing and writing out the symbol
indexes for the indexed export data, keying by *types.Sym is exactly
what we want.

Passes buildall w/ toolstash -cmp.

Updates #42982.

Change-Id: Idd8f1fb057d75a52a34ebc7788d9332fb49caf8d
Reviewed-on: https://go-review.googlesource.com/c/go/+/275755
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-06 21:06:05 +00:00
Matthew Dempsky
2d4c95565a [dev.regabi] cmd/compile: change nowritebarrierrec to use map[*ir.Func]
All of the uses were already using *ir.Func index operands, so only
needs the map type itself updated.

Passes buildall w/ toolstash -cmp.

Updates #42982.

Change-Id: I568d8601f3eb077e07e887f2071aa1a2667d803c
Reviewed-on: https://go-review.googlesource.com/c/go/+/275754
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
2020-12-06 21:05:50 +00:00
Matthew Dempsky
1b5eed8982 [dev.regabi] cmd/compile: replace NodeQueue with NameQueue
Similar to the previous CL, the only two users of NodeQueue only
needed it for tracking objects, not arbitrary AST nodes. So change
it's signature to use *Name instead of Node.

This does require a tweak to the nowritebarrierrec checker, because
previously it was pushing the ODCLFUNC *Func pointers into the queue,
whereas now we push the ONAME/PFUNC *Name pointers instead. However,
it's trivial and safe to flip between them.

Also, this changes a handful of export-related code from Node to
*Name, to avoid introducing type assertions within iexport.go.

Passes buildall w/ toolstash -cmp.

Updates #42982.

Change-Id: I867f9752121509fc3da753978c6a41d5015bc0ce
Reviewed-on: https://go-review.googlesource.com/c/go/+/275753
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2020-12-06 21:05:41 +00:00
Matthew Dempsky
6c5967e528 [dev.regabi] cmd/compile: change NodeSet to NameSet
The only user of NodeSet (computing initialization dependencies) only
needs to store *Names in this structure. So change its definition to
match that need, and update the code in initorder.go accordingly.

Passes buildall w/ toolstash -cmp.

Updates #42982.

Change-Id: I181a8aaf9bc71e88f4ac009c4f381a718080e48f
Reviewed-on: https://go-review.googlesource.com/c/go/+/275752
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
2020-12-06 21:05:34 +00:00
Cuong Manh Le
46b6e70e3b [dev.regabi] cmd/compile: replace ir.Node with *ir.Name in Order
Passes buildall w/ toolstash -cmp.

Updates #42982

Change-Id: I7121c37f72ccbc141a7dd17fba1753f2c6289908
Reviewed-on: https://go-review.googlesource.com/c/go/+/275353
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-04 20:55:42 +00:00
Cuong Manh Le
b75f51c645 [dev.regabi] cmd/compile: replace ir.Node with *ir.Name in Liveness
Passes buildall w/ toolstash -cmp.

Updates #42982

Change-Id: Iad8df321adfd576da070c13ed16a9651d4e59ad8
Reviewed-on: https://go-review.googlesource.com/c/go/+/275352
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-04 20:55:36 +00:00
Matthew Dempsky
133b03e1c3 [dev.regabi] cmd/compile: rewrite code to use DeclaredBy
Passes buildall w/ toolstash -cmp.

Updates #42990.

[git-generate]
cd src/cmd/compile/internal/gc
rf '
ex {
  import "cmd/compile/internal/ir"
  var x, stmt ir.Node
  x.Name() != nil && x.Name().Defn == stmt ->  ir.DeclaredBy(x, stmt)
  x.Name() == nil || x.Name().Defn != stmt -> !ir.DeclaredBy(x, stmt)
}
'

Change-Id: I222a757296dbcb5d0889d617d221a9d7319f2d74
Reviewed-on: https://go-review.googlesource.com/c/go/+/275306
Reviewed-by: Russ Cox <rsc@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-12-04 20:25:19 +00:00
Matthew Dempsky
d9cb84c84b [dev.regabi] cmd/compile: add SameSource, Uses, and DeclaredBy helpers
Currently, because we use the same *Name to represent both declaration
and uses of an object, it's ambiguous what "n1 == n2" means when
comparing two Node values. It can mean any of: Are these the same
syntactic element? Is n1 a use of declared variable n2? Are n1 and n2
both uses of the same declared variable?

We'd like to introduce a new IdentExpr node to replace use of Name
within the AST, but that means those three cases need to be handled
differently. The first case needs to stay "n1 == n2", but the other
cases need to become "n1.Name() == n2" and "n1.Name() == n2.Name()",
respectively. ("n1.Name() == n2.Name()" also currently works for the
second case, but eventually we'll want to get rid of the Name.Name
method.)

This CL introduces helper functions SameSource and Uses to handle
these cases. It also introduces DeclaredBy, which is another somewhat
common case that the next CL introduces uses of.

Passes buildall w/ toolstash -cmp.

Updates #42990.

Change-Id: Ia816c124446e9067645d5820a8163f295968794f
Reviewed-on: https://go-review.googlesource.com/c/go/+/275305
Reviewed-by: Russ Cox <rsc@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-12-04 20:25:07 +00:00
Russ Cox
5dbd2e8e44 [dev.regabi] cmd/compile: remove DeepCopyNode interface
The only reason for the DeepCopyNode interface was to
allow the type syntaxes to avoid being constrained by
Left, Right etc. methods. Now those are gone, so the
general traversal methods they implement (doChildren, editChildren)
do the right thing for DeepCopy.

Passes buildall w/ toolstash -cmp.

Change-Id: I54672c011114a95efabff32dbcf02e6071f91b9e
Reviewed-on: https://go-review.googlesource.com/c/go/+/275379
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-04 16:52:57 +00:00
Russ Cox
9ab3d854ad [dev.regabi] cmd/compile: avoid general traversal in deadcode
deadcode is trying to walk the statements it can find,
but it can sweep in other nodes too. Stop doing that:
only walk known statements containing statements.

Otherwise, if we put panics in expression accessors that
shouldn't be used anymore, deadcode can trip them.

deadcode would be a good candidate to rewrite using
EditChildren, but that would certainly cause toolstash
changes, since deadcode is so ad-hoc about exactly
which parts of the function it looks at. For now just
remove the general traversal and leave as is.

Passes buildall w/ toolstash -cmp.

Change-Id: I06481eb87350905597600203c4fa724d55645b46
Reviewed-on: https://go-review.googlesource.com/c/go/+/275377
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-04 16:52:56 +00:00
Russ Cox
bb5aa2b664 [dev.regabi] cmd/compile: implement editChildren for nodes
Put each node in charge of its EditChildren implementation.
This removes the final generic use of Left, SetLeft, Right, SetRight,
and so on in package ir.

Passes buildall w/ toolstash -cmp.

Change-Id: I9821cc20f5b91cc9b44eb1f386cc82f20cd6770c
Reviewed-on: https://go-review.googlesource.com/c/go/+/275376
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-04 16:52:54 +00:00
Russ Cox
4725c3ffd1 [dev.regabi] cmd/compile: implement doChildren for nodes
Put each node in charge of its DoChildren implementation.
This removes a generic use of Left, Right, and so on
in func DoChildren, heading toward removing those even from
being used in package ir.

Passes buildall w/ toolstash -cmp.

Change-Id: Ibdf56f36801217cf24549e063da0078c1820a56b
Reviewed-on: https://go-review.googlesource.com/c/go/+/275375
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-04 16:52:53 +00:00
Russ Cox
18f2df7e81 [dev.regabi] cmd/compile: implement copy for nodes
Put each node in charge of making copies of its own slices.
This removes a generic use of Body, SetBody, and so on
in func Copy, heading toward removing those even from
being used in package ir.

Passes buildall w/ toolstash -cmp.

Change-Id: I249b7fe54cf72e9d2f0467b10f3f257abf9b29b9
Reviewed-on: https://go-review.googlesource.com/c/go/+/275374
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-04 16:52:52 +00:00
Russ Cox
d855b30fe4 [dev.regabi] cmd/compile: use ir.EditChildren for inline rewriting
This CL rephrases the general inlining rewriter in terms of ir.EditChildren.
It is the final part of the code that was processing arbitrary nodes using
Left, SetLeft, and so on. After this CL, there should be none left except
for the implementations of DoChildren and EditChildren, which fall next.

Passes buildall w/ toolstash -cmp.

Change-Id: I9c36053360cd040710716f0b39397a80114be713
Reviewed-on: https://go-review.googlesource.com/c/go/+/275373
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-04 16:52:51 +00:00
Russ Cox
b9df26d7a8 [dev.regabi] cmd/compile: use ir.Find for "search" traversals
This CL converts all the generic searching traversal to use ir.Find
instead of relying on direct access to Left, Right, and so on.

Passes buildall w/ toolstash -cmp.

Change-Id: I4d951aef630c00bf333f24be79565cc564694d04
Reviewed-on: https://go-review.googlesource.com/c/go/+/275372
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-04 16:52:50 +00:00
Russ Cox
0d1b44c645 [dev.regabi] cmd/compile: introduce IR visitors
This CL introduces the general visitor functionality that will replace
the Left, SetLeft, Right, SetRight, etc methods in the Node interface.

For now, the CL defines the functionality in terms of those methods,
but eventually the Nodes themselves will implement DoChildren
and EditChildren and be relieved of implementing Left, SetLeft, and so on.

The CL also updates Inspect (which moved to visit.go) and DeepCopy
to use the new functionality.

The Find helper is not used in this CL but will be used in a future one.

Passes buildall w/ toolstash -cmp.

Change-Id: Id0eea654a884ab3ea25f48bd8bdd71712b5dcb44
Reviewed-on: https://go-review.googlesource.com/c/go/+/275311
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-04 16:52:48 +00:00
Russ Cox
7fcf5b994c [dev.regabi] cmd/compile: replace inlcopy with ir.DeepCopy
Now inlcopy and ir.DeepCopy are semantically the same,
so drop the inlcopy implementation.

Passes buildall w/ toolstash -cmp.

Change-Id: Id2abb39a412a8e57167a29be5ecf76e990dc9d3d
Reviewed-on: https://go-review.googlesource.com/c/go/+/275310
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-04 16:52:47 +00:00
Russ Cox
989a3f5041 [dev.regabi] cmd/compile: adjustments to Copy and DeepCopy
DeepCopy is not called DeepSepCopy, so it should use Copy, not SepCopy.

Also, the old gc.treecopy, which became ir.DeepCopy, only copied
the Left, Right, and List fields - not Init, Rlist, Body - and I didn't
notice when I moved it over. A general utility function should of
course copy the whole node, so do that.

Finally, the semantics of Copy should not depend on whether a
particular child node is held directly in a field or in a slice,
so make Copy duplicate the slice backing arrays as well.
(Logically, those backing arrays are part of the node storage.)

Passes buildall w/ toolstash -cmp.

Change-Id: I18fbe3f2b40078f566ed6370684d5585052b36a1
Reviewed-on: https://go-review.googlesource.com/c/go/+/275309
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-04 16:52:46 +00:00
Russ Cox
99ecfcae31 [dev.regabi] cmd/compile: swap inlining order of if then vs else blocks
The upcoming general iterators will process nodes in
source code order, meaning that the "then" block comes
before the "else" block. But for an if node, "then" is Body
while "else" is Rlist, and the inliner processes Rlist first.

The order of processing changes the order of inlining decisions,
which can affect which functions are inlined, but in general
won't affect much. (It's not like we know that we should prefer
to inline functions in else bodies over then bodies.)

Swapping these is not safe for toolstash -cmp.
Doing it in a separate CL lets the upcoming CLs all be toolstash-safe.

Change-Id: Id16172849239b0564930d2bbff1260ad6d03d5ab
Reviewed-on: https://go-review.googlesource.com/c/go/+/275308
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-04 16:52:45 +00:00
Matthew Dempsky
84cb51d7d7 [dev.regabi] cmd/compile: eliminate more SetOrig
This CL consolidates and cleans up fmt.go's logic for skipping past
Nodes introduced during typechecking. This allows eliminating SetOrig
on ConvExpr and Name. Also changes ConstExpr.SetOrig to a panic for
good measure.

The only remaining SetOrig uses now are for rewriting multi-value
"f(g())" calls and "return g()" statements, and type-checking
composite literals. It should be possible to eliminate both of those
as well.

Passes buildall w/ toolstash -cmp.

Change-Id: I478aea1a17dfb7a784293b930bf9081637eb2d7a
Reviewed-on: https://go-review.googlesource.com/c/go/+/275179
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-04 01:20:58 +00:00
Matthew Dempsky
351bc2f38c [dev.regabi] cmd/compile: store types.Field on {Selector,CallPart}Expr
It's useful to have quick access to the types.Field that a given
selector or method value expression refer to. Previously we abused Opt
for this, but couldn't do that for OCALLPART because escape analysis
uses Opt.

Now that we have more flexibility, we can simply add additional
pointer fields for this. This also allows getting rid of an unneeded
ONAME node for OCALLPART.

Passes buildall w/ toolstash -cmp.

Change-Id: I980d7bdb19abfd0b6f58a232876861b88dee1e47
Reviewed-on: https://go-review.googlesource.com/c/go/+/275034
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-12-03 19:33:13 +00:00
Matthew Dempsky
a2058bac21 [dev.regabi] cmd/compile: add ConstExpr
Currently, we represent constant-folded expressions with Name, which
is suboptimal because Name has a lot of fields to support declared
names (which are irrelevant to constant-folded expressions), while
constant expressions are fairly common.

This CL introduces a new lightweight ConstExpr type that simply wraps
an existing expression and associates it with a value.

Passes buildall w/ toolstash -cmp.

name                      old time/op       new time/op       delta
Template                        252ms ± 3%        254ms ± 1%     ~     (p=0.821 n=12+10)
Unicode                         120ms ± 2%        107ms ± 7%  -11.09%  (p=0.000 n=12+12)
GoTypes                         918ms ± 2%        918ms ± 1%     ~     (p=0.974 n=12+10)
Compiler                        5.19s ± 1%        5.18s ± 0%     ~     (p=0.190 n=12+11)
SSA                             12.4s ± 1%        12.3s ± 1%     ~     (p=0.283 n=10+12)
Flate                           152ms ± 2%        148ms ± 4%   -2.68%  (p=0.007 n=10+12)
GoParser                        212ms ± 1%        211ms ± 2%     ~     (p=0.674 n=10+12)
Reflect                         543ms ± 3%        542ms ± 3%     ~     (p=0.799 n=12+12)
Tar                             224ms ± 2%        225ms ± 2%     ~     (p=0.378 n=12+12)
XML                             292ms ± 1%        299ms ± 3%   +2.18%  (p=0.006 n=10+12)

name                      old user-time/op  new user-time/op  delta
Template                        243ms ± 4%        244ms ± 5%     ~     (p=0.887 n=12+12)
Unicode                         112ms ± 6%        100ms ±10%  -10.76%  (p=0.000 n=12+12)
GoTypes                         898ms ± 3%        895ms ± 3%     ~     (p=0.671 n=12+12)
Compiler                        5.10s ± 1%        5.08s ± 1%     ~     (p=0.104 n=12+11)
SSA                             12.2s ± 2%        12.1s ± 1%     ~     (p=0.487 n=11+12)
Flate                           144ms ± 6%        145ms ± 5%     ~     (p=0.695 n=12+11)
GoParser                        205ms ± 5%        204ms ± 3%     ~     (p=0.514 n=12+12)
Reflect                         528ms ± 3%        531ms ± 4%     ~     (p=0.630 n=12+12)
Tar                             218ms ± 4%        219ms ± 3%     ~     (p=0.843 n=12+12)
XML                             284ms ± 5%        291ms ± 5%     ~     (p=0.069 n=11+12)

name                      old alloc/op      new alloc/op      delta
Template                       37.0MB ± 0%       36.7MB ± 0%   -0.72%  (p=0.000 n=12+12)
Unicode                        31.9MB ± 0%       29.5MB ± 0%   -7.60%  (p=0.000 n=12+12)
GoTypes                         119MB ± 0%        118MB ± 0%   -0.40%  (p=0.000 n=12+12)
Compiler                        629MB ± 0%        626MB ± 0%   -0.36%  (p=0.000 n=11+12)
SSA                            1.45GB ± 0%       1.43GB ± 0%   -0.78%  (p=0.000 n=12+12)
Flate                          22.2MB ± 0%       21.9MB ± 0%   -1.12%  (p=0.000 n=12+12)
GoParser                       29.4MB ± 0%       29.3MB ± 0%   -0.36%  (p=0.000 n=12+12)
Reflect                        76.1MB ± 0%       75.8MB ± 0%   -0.38%  (p=0.000 n=12+12)
Tar                            33.4MB ± 0%       33.2MB ± 0%   -0.61%  (p=0.000 n=12+12)
XML                            43.2MB ± 0%       42.8MB ± 0%   -1.03%  (p=0.000 n=11+12)

name                      old allocs/op     new allocs/op     delta
Template                         375k ± 0%         375k ± 0%     ~     (p=0.854 n=12+12)
Unicode                          300k ± 0%         300k ± 0%     ~     (p=0.766 n=12+12)
GoTypes                         1.30M ± 0%        1.30M ± 0%     ~     (p=0.272 n=12+12)
Compiler                        5.89M ± 0%        5.89M ± 0%     ~     (p=0.478 n=12+12)
SSA                             14.0M ± 0%        14.0M ± 0%     ~     (p=0.266 n=12+12)
Flate                            226k ± 0%         226k ± 0%     ~     (p=0.898 n=12+12)
GoParser                         313k ± 0%         313k ± 0%   -0.01%  (p=0.042 n=12+11)
Reflect                          971k ± 0%         971k ± 0%     ~     (p=0.080 n=12+12)
Tar                              342k ± 0%         342k ± 0%     ~     (p=0.600 n=12+12)
XML                              416k ± 0%         416k ± 0%     ~     (p=0.217 n=11+12)

name                      old maxRSS/op     new maxRSS/op     delta
Template                        43.1M ± 5%        42.5M ± 5%     ~     (p=0.086 n=12+12)
Unicode                         49.4M ± 2%        47.0M ± 2%   -4.88%  (p=0.000 n=12+12)
GoTypes                         85.3M ± 2%        84.6M ± 2%   -0.84%  (p=0.047 n=11+11)
Compiler                         394M ± 3%         386M ± 2%   -1.97%  (p=0.000 n=10+11)
SSA                              847M ± 4%         821M ± 2%   -2.98%  (p=0.000 n=11+12)
Flate                           36.0M ± 7%        35.2M ± 7%     ~     (p=0.128 n=12+12)
GoParser                        39.4M ± 7%        39.5M ± 4%     ~     (p=0.413 n=12+11)
Reflect                         64.0M ± 3%        63.6M ± 3%     ~     (p=0.413 n=11+12)
Tar                             43.3M ± 5%        43.3M ± 5%     ~     (p=0.503 n=12+12)
XML                             47.6M ± 4%        46.4M ± 2%   -2.46%  (p=0.013 n=11+12)

Change-Id: If5781be346351c30b2228807211b5e57f777c506
Reviewed-on: https://go-review.googlesource.com/c/go/+/275033
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-12-03 18:04:03 +00:00
Matthew Dempsky
beb5e05404 [dev.regabi] cmd/compile: refactoring prep for ConstExpr
The next CL adds ConstExpr, which is a more memory efficient
representation for constant expressions than Name. However, currently
a bunch of Val helper methods are defined on Name. This CL changes
them into standalone functions that work with any Node.Val
implementation.

There's also an existing standalone function named Int64Val, which
takes a Type argument to specify what type of integer is expected. So
to avoid collisions, this CL renames it to IntVal.

Passes buildall w/ toolstash -cmp.

[git-generate]
cd src/cmd/compile/internal/ir
rf 'mv Int64Val IntVal'
sed -i -E -e 's/\(n \*Name\) (CanInt64|((I|Ui)nt64|Bool|String)Val)\(/\1(n Node/' name.go

cd ../gc
rf '
ex {
  import "cmd/compile/internal/ir"
  var n ir.Node
  n.CanInt64() -> ir.CanInt64(n)
  n.Int64Val() -> ir.Int64Val(n)
  n.Uint64Val() -> ir.Uint64Val(n)
  n.BoolVal() -> ir.BoolVal(n)
  n.StringVal() -> ir.StringVal(n)
}
'

cd ../ir
rf '
mv CanInt64 Int64Val Uint64Val BoolVal StringVal val.go
rm Node.CanInt64 Node.Int64Val Node.Uint64Val Node.BoolVal Node.StringVal
'

Change-Id: I003140bda1690d770fd608bdd087e6d4ff00fb1f
Reviewed-on: https://go-review.googlesource.com/c/go/+/275032
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-03 18:03:53 +00:00
Russ Cox
7e81135be7 [dev.regabi] cmd/compile: rename addinit(n, init) to initExpr(init, n)
Recreated manually to push below some CLs it depended on.

Change-Id: I1b3316fcdce39cbb33e5cbb471f5cd1cd2efc1f5
Reviewed-on: https://go-review.googlesource.com/c/go/+/274599
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-03 17:45:50 +00:00
Russ Cox
6e30fc10fc [dev.regabi] all: merge master (d0c0dc682c) into dev.regabi
Change-Id: Ia54d7306ca7550b8d5623f505070558d275faa23
2020-12-03 12:33:12 -05:00
Russ Cox
59b8916d48 [dev.regabi] cmd/compile: handle OCONVNOP better in ssa
This CL improves handling of OCONVNOP nodes during ssa generation,
so it is not toolstash safe.

An OCONVNOP wrapper is necessary for the "for" condition of
certain compiled range loops, and the boolean evaluator was
not looking through them properly, generating unnecessary
temporaries. That change saved 8k of the (13 MB) go binary.

The other changes just streamline the handling of OCONVNOP
to be more like what OSTMTEXPR will be like. They have no
effect on output size but do tweak the ssa graph a little, which
causes different register decisions and therefore different output.

Change-Id: I9e1dcd413b60944e21554c3e3f2bdc9adcee7634
Reviewed-on: https://go-review.googlesource.com/c/go/+/274598
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2020-12-03 16:22:40 +00:00
Cuong Manh Le
00e5727790 [dev.regabi] cmd/compile: remove okAs
The check for blank in okAs is redundant with what its callers already
done, so just inline the conversion in callers side instead.

Passes toolstash-check.

Change-Id: I606105e2d2cf8e80214722a13c3101c464d20d82
Reviewed-on: https://go-review.googlesource.com/c/go/+/274793
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-03 08:30:09 +00:00
Dan Scales
5a3b6796cd [dev.regabi] cmd/compile: remove extra typ field in Name struct
Noticed the typ field was duplicated, since it is also in miniExpr inside Name.

Also clarified the comments for Func, now that it is actually the ODCLFUNC node.

Change-Id: Ia483a0ad34bb409cd92c43d4ae0a6852f9e4f644
Reviewed-on: https://go-review.googlesource.com/c/go/+/274619
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-03 07:22:32 +00:00
Russ Cox
64bc656aed [dev.regabi] cmd/compile: use explicit block statements for init
For statements like goto that don't need an init, use an
explicit block statement instead of forcing them to have one.

There is also one call to addinit that is being replaced with
a block. That call is the source of much of my confusion
regarding init statements: walkstmt calls addinit on a statement,
whereas all the other uses of addinit are on expressions.

After this CL, they're all expressions.

Passes buildall w/ toolstash -cmp.

Change-Id: Ifdef9d318c236dc1a7567f9e9ef4a6bedd3fe81f
Reviewed-on: https://go-review.googlesource.com/c/go/+/274597
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-02 23:18:21 +00:00
Russ Cox
ecc8d15bc5 [dev.regabi] cmd/compile: delete OEMPTY
Not toolstash -cmp safe, so split into its own CL.

Change-Id: I523227514a95e19c9afe17556d754a03164bce76
Reviewed-on: https://go-review.googlesource.com/c/go/+/274595
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-02 20:12:53 +00:00
Russ Cox
ec5f349b22 [dev.regabi] cmd/compile: merge OBLOCK and OEMPTY
OEMPTY is an empty *statement*, but it confusingly
gets handled as an expression in a few places.
More confusingly, OEMPTY often has an init list,
making it not empty at all. Replace uses and analysis
of OEMPTY with OBLOCK instead.

Passes buildall w/ toolstash -cmp.

Change-Id: I8d4fcef151e4f441fa19b1b96da5272d778131d6
Reviewed-on: https://go-review.googlesource.com/c/go/+/274594
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-02 20:12:47 +00:00
Matthew Dempsky
c769d393de [dev.regabi] cmd/compile: add ir.NewDeclNameAt
This allows directly creating an ONONAME, which is a primordial Name
before having its Op initialized. Then after an Op is assigned, we
never allow it to be reassigned.

Passes buildall w/ toolstash -cmp.

Change-Id: Ibc2f413dc68c0af6a96abfe653c25ce31b184287
Reviewed-on: https://go-review.googlesource.com/c/go/+/274620
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-02 09:28:59 +00:00
Matthew Dempsky
c10b0ad628 [dev.regabi] cmd/compile: add Pkg parameter to type constructors
Allows getting rid of the SetPkg method and also addresses a
long-standing TODO in the exporter. Suggested by rsc@.

Passes buildall w/ toolstash -cmp.

Change-Id: Ib294f75f1350572efb2e0d993d49efef884de3d4
Reviewed-on: https://go-review.googlesource.com/c/go/+/274440
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-02 08:50:26 +00:00
Matthew Dempsky
42e46f4ae0 [dev.regabi] cmd/compile: comment out //go:linkname warning
It's noisy and not doing any harm, and we still have an entire release
cycle to revisit and address the issue properly.

Updates #42938

Change-Id: I1de5cfb495a8148c9c08b215deba38f2617fb467
Reviewed-on: https://go-review.googlesource.com/c/go/+/274732
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2020-12-02 06:49:37 +00:00
Matthew Dempsky
77a71e0057 [dev.regabi] cmd/compile: add Interface, Signature, and Struct constructors
This CL adds the remaining constructors needed to abstract away
construction of Types, and updates the compiler to use them
throughout. There's now just a couple uses within test cases to
remove.

While at it, I also replace the Func.Outnamed field with a simple
helper function, which reduces the size of function types somewhat.

Passes toolstash/buildall.

Change-Id: If1aa1095c98ae34b00380d0b3531bd63c10ce885
Reviewed-on: https://go-review.googlesource.com/c/go/+/274713
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-02 06:42:17 +00:00
Matthew Dempsky
15085f8974 [dev.regabi] cmd/compile: tweak hash bucket type descriptor
There's no need for the bucket type to be precise. The compiler
doesn't actually generate code that references these fields; it just
needs it for size and GC bitmap calculations.

However, changing the type field does alter the runtime type
descriptor and relocations emitted by the compiler, so this change
isn't safe for toolstash.

Change-Id: Icf79d6c4326515889b13435a575d618e3bbfbcd7
Reviewed-on: https://go-review.googlesource.com/c/go/+/274712
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-02 06:37:47 +00:00
Matthew Dempsky
1408d26ccc [dev.regabi] cmd/compile: cleanup some leftover cruft
Just clearing away some scaffolding artifacts from previous
refactorings.

[git-generate]
cd src/cmd/compile/internal/gc
rf '
ex {
  import "cmd/compile/internal/ir"
  import "cmd/compile/internal/types"

  var n *ir.Name; n.Name() -> n
  var f *ir.Func; f.Func() -> f

  var o types.Object
  ir.AsNode(o).Sym() -> o.Sym()
  ir.AsNode(o).Type() -> o.Type()
  ir.AsNode(o).(*ir.Name) -> o.(*ir.Name)
  ir.AsNode(o).(*ir.Func) -> o.(*ir.Func)

  var x ir.Node
  ir.AsNode(o) != x -> o != x
}
'

Change-Id: I946ec344bd7ee274900a392da53b95308ceaade4
Reviewed-on: https://go-review.googlesource.com/c/go/+/274592
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-01 22:09:25 +00:00
Matthew Dempsky
5ffa275f3c [dev.regabi] cmd/compile: first pass at abstracting Type
Passes toolstash/buildall.

[git-generate]
cd src/cmd/compile/internal/ssa
rf '
ex . ../ir ../gc {
  import "cmd/compile/internal/types"
  var t *types.Type
  t.Etype -> t.Kind()
  t.Sym -> t.GetSym()
  t.Orig -> t.Underlying()
}
'

cd ../types
rf '
mv EType Kind
mv IRNode Object

mv Type.Etype Type.kind
mv Type.Sym Type.sym
mv Type.Orig Type.underlying
mv Type.Cache Type.cache

mv Type.GetSym Type.Sym

mv Bytetype ByteType
mv Runetype RuneType
mv Errortype ErrorType
'

cd ../gc
sed -i 's/Bytetype/ByteType/; s/Runetype/RuneType/' mkbuiltin.go

git codereview gofmt
go install cmd/compile/internal/...
go test cmd/compile -u || go test cmd/compile

Change-Id: Ibecb2d7100d3318a49238eb4a78d70acb49eedca
Reviewed-on: https://go-review.googlesource.com/c/go/+/274437
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-12-01 19:24:00 +00:00
Matthew Dempsky
6ca23a45fe [dev.regabi] cmd/compile: only save ONAMEs on Curfn.Dcl
There's not really any use to tracking function-scoped constants and
types on Curfn.Dcl, and there's sloppy code that assumes all of the
declarations are variables (e.g., cmpstackvarlt).

Change-Id: I5d10dc681dac2c161c7b73ba808403052ca0608e
Reviewed-on: https://go-review.googlesource.com/c/go/+/274436
Reviewed-by: Russ Cox <rsc@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-12-01 17:16:53 +00:00
Matthew Dempsky
a17c5e2fce [dev.regabi] cmd/compile: add NewBasic and cleanup universe
This CL introduces types.NewBasic, for creating the predeclared
universal types, and reorganizes how the universe is initialized so
that all predeclared types are uniformly constructed.

There are now a bunch of Type fields that are no longer assigned
outside of the package, so this CL also introduces some new accessor
methods that a subsequent CL will mechanically introduce uses of.

Change-Id: Ie7996c3d5f1ca46cd5bfe45ecc91ebfa6a7b6c7d
Reviewed-on: https://go-review.googlesource.com/c/go/+/274435
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-01 17:16:39 +00:00
Matthew Dempsky
f37aa5e4e2 [dev.regabi] cmd/compile: add NewNamed
The start of abstracting away Type fields. This adds a new constructor
for named types, styled after go/types.NewNamed. Along with helper
methods for SetNod and Pos, this allows hiding Nod.

Change-Id: Ica107034b6346c7b523bf6ae2a34009e350a9aa8
Reviewed-on: https://go-review.googlesource.com/c/go/+/274434
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-01 17:16:25 +00:00
Matthew Dempsky
63a6f08b39 [dev.regabi] cmd/compile: move setUnderlying to package types
Now that setUnderlying is decoupled from Nodes, it can be moved into
package types, where it really belongs.

[git-generate]
cd src/cmd/compile/internal/gc
rf '
mv setUnderlying SetUnderlying
mv SetUnderlying typex.go
mv typex.go cmd/compile/internal/types
'

cd ../types
rf '
mv typex.go type.go
'

Change-Id: I76e2d4d8a6df599f24a731c4d8e5774ec83a119c
Reviewed-on: https://go-review.googlesource.com/c/go/+/274433
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-01 17:15:40 +00:00
Matthew Dempsky
f2311462ab [dev.regabi] cmd/compile: cleanup type-checking of defined types
The code for type-checking defined types was scattered between
typecheckdef, typecheckdeftype, and setUnderlying. There was redundant
work between them, and setUnderlying also needed to redo a lot of work
because of its brute-force solution of just copying all Type fields.

This CL reorders things so as many of the defined type's fields are
set in advance (in typecheckdeftype), and then setUnderlying only
copies over the details actually needed from the underlying type.

Incidentally, this evidently improves our error handling for an
existing test case, by allowing us to report an additional error.

Passes toolstash/buildall.

Change-Id: Id59a24341e7e960edd1f7366c3e2356da91b9fe7
Reviewed-on: https://go-review.googlesource.com/c/go/+/274432
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-12-01 17:15:12 +00:00
Matthew Dempsky
2d6ff998ed [dev.regabi] cmd/compile: process //go:linknames after declarations
Allows emitting errors about ineffectual //go:linkname directives.

In particular, this exposed: a typo in os2_aix.go; redundant (but
harmless) directives for libc_pipe in both os3_solaris.go and
syscall2_solaris.go; and a bunch of useless //go:linkname directives
in macOS wrapper code.

However, because there's also ineffectual directives in the vendored
macOS code from x/sys, that can't be an error just yet. So instead we
print a warning (including a heads up that it will be promoted to an
error in Go 1.17) to prevent backsliding while we fix and re-vendor
that code.

Passes toolstash-check.

Change-Id: I59badeab5df0d8b3abfd14c6066e9bb00e840f73
Reviewed-on: https://go-review.googlesource.com/c/go/+/273986
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-12-01 17:08:36 +00:00
Russ Cox
ecff7628ea [dev.regabi] cmd/compile: unexport Node.RawCopy
RawCopy breaks the invariant that ir.Orig depends on for
allowing nodes to omit keeping their own orig fields.
Avoid surprises by unexporting it.

The only use in package gc was removed in the previous CL.
This one is a straight global search and replace RawCopy -> rawCopy.

Change-Id: Ia99c0f4665bf7ed4f878cc44456d5fbdf33bab8d
Reviewed-on: https://go-review.googlesource.com/c/go/+/274293
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2020-12-01 12:34:56 +00:00
Russ Cox
4da41fb3f8 [dev.regabi] cmd/compile: use ir.Copy instead of direct use of RawCopy
The ONIL export bug happened because the logic about
maintaining an “implicit” orig pointer in the comments
around ir.Orig only applies to Copy and SepCopy, not to
direct use of RawCopy. I'd forgotten those could exist.

The sole direct use of RawCopy was for the OLITERAL/ONIL case.
The ONIL is now provably indistinguishable from Copy, since
NilExpr does not have an explicit Orig field, so for NilExpr
RawCopy and Copy are the same.
The OLITERAL is not, but we can reconstruct the effect
with Copy+SetOrig to be explicit that we need the orig link.

The next CL will unexport RawCopy.

Also fix a typo in MapType doc comment.

Passes buildall w/ toolstash -cmp.

Change-Id: I876a85ff188e6d1cd4c0dfa385be32482e0de0d4
Reviewed-on: https://go-review.googlesource.com/c/go/+/274292
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2020-12-01 12:34:46 +00:00
Matthew Dempsky
dadfc80bc1 [dev.regabi] cmd/compile: improve findTypeLoop
When checking if a defined type is part of a type loop, we can
short-circuit if it was defined in another package. We can assume any
package we import already successfully compiled, so any types it
contains cannot be part of a type loop.

This also allows us to simplify the logic for recursing into the type
used in the type declaration, because any defined type from this
package will have a properly setup node.

Change-Id: Ic024814d95533afd9e59f2103c8ddb22bd87e900
Reviewed-on: https://go-review.googlesource.com/c/go/+/274294
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
2020-12-01 05:14:26 +00:00
Russ Cox
45f3b646d4 [dev.regabi] cmd/compile: add OSTMTEXPR Op
This CL only adds the new constant, which is not safe for toolstash -cmp.

Change-Id: I774463a0ab5f57113d67a8888b6ac787be68510c
Reviewed-on: https://go-review.googlesource.com/c/go/+/274110
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-01 02:16:34 +00:00
Russ Cox
9a5a11adfa [dev.regabi] cmd/compile: add custom expression Node implementations
These are fairly rote implementations of structs appropriate to
each Op (or group of Ops).

The names of these are unknown except to ir.NodAt for now.
A later, automated change will introduce direct use of the types
throughout package gc.

(This CL is expressions; the previous one was statements.)

This is the last of the Ops that were previously handled by the
generic node struct, so that struct and its methods can be
and are deleted in this CL.

Passes buildall w/ toolstash -cmp.

Change-Id: I1703f35f24dcd3f7c5782a278e53c3fe04e87c37
Reviewed-on: https://go-review.googlesource.com/c/go/+/274109
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-12-01 02:16:26 +00:00
Matthew Dempsky
0f9f27287b [dev.regabi] cmd/compile: remove types.InitSyms
It's not types's responsibility to understand how package
initialization is implemented. Instead, have gc keep track of the
order that packages were imported, and then look for inittask
declarations.

Also, use resolve to force importing of the inittask's export data, so
that we can get the appropriate linker symbol index. (This is also why
this CL doesn't satisfy "toolstash -cmp".)

Change-Id: I5b706497d4a8d1c4439178863b4a8dba4da0f5a9
Reviewed-on: https://go-review.googlesource.com/c/go/+/274006
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2020-12-01 01:26:28 +00:00
Russ Cox
41ad4dec99 [dev.regabi] cmd/compile: fix -h
The compile -h flag is *meant* to panic, so you can see the stack
trace where the error is being printed. Make it do that again.

Change-Id: Ieb0042863582d7a4c5d08d2f866a144962915b06
Reviewed-on: https://go-review.googlesource.com/c/go/+/274116
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 23:48:42 +00:00
Russ Cox
ffa68716a0 [dev.regabi] cmd/compile: add custom statement Node implementations
These are fairly rote implementations of structs appropriate to
each Op (or group of Ops).

The names of these are unknown except to ir.NodAt for now.
A later, automated change will introduce direct use of the types
throughout package gc.

Passes buildall w/ toolstash -cmp.

Change-Id: Ie9835fcd2b214fda5b2149e187af369d76534487
Reviewed-on: https://go-review.googlesource.com/c/go/+/274108
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 23:48:34 +00:00
Russ Cox
2bc814cd18 [dev.regabi] cmd/compile: clean up ONEW node
The list is no longer needed and can be deleted.
Doing so reduces the inlining cost of any function containing
an explicit call to new by 1 point, so this change is not
toolstash -cmp safe.

Change-Id: Id29e115d68e466a353708ab4b8c1021e9c85a628
Reviewed-on: https://go-review.googlesource.com/c/go/+/274132
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 23:48:29 +00:00
Russ Cox
b7f67b75d2 [dev.regabi] cmd/compile: clean up in preparation for expression Nodes
Using expression nodes restricts the set of valid SetOp operations,
because you can't SetOp across representation. Rewrite various code
to avoid crossing those as-yet-unintroduced boundaries.

This also includes choosing a single representation for any given Op.
For example, OCLOSE starts out as an OCALL, so it starts with a List
of one node and then moves that node to Left. That's no good with
real data structures, so the code picks a single canonical implementation
and prepares it during the conversion from one Op to the next.
In this case, the conversion of an OCALL to an OCLOSE now creates
a new node with Left initialized from the start. This pattern repeats.

Passes buildall w/ toolstash -cmp.

Change-Id: I55a0872c614d883cac9d64976c46aeeaa639e25d
Reviewed-on: https://go-review.googlesource.com/c/go/+/274107
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 23:48:24 +00:00
Russ Cox
5fc192af56 [dev.regabi] cmd/compile: clean up Order.copyExpr TODO
Just a little cleaner to read.

Passes buildall w/ toolstash -cmp.

Change-Id: I27b9f09bf6756f74f1c01794444518ded1a7d625
Reviewed-on: https://go-review.googlesource.com/c/go/+/274106
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 23:48:18 +00:00
Russ Cox
7c9b6b1ca2 [dev.regabi] cmd/compile: clean up in preparation for statement Nodes
Using statement nodes restricts the set of valid SetOp operations,
because you can't SetOp across representation. Rewrite various
code to avoid crossing those as-yet-unintroduced boundaries.

In particular, code like

        x, y := v.(T)
        x, y := f()
        x, y := m[k]
        x, y := <-c

starts out with Op = OAS2, and then it turns into a specific Op
OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV, and then
later in walk is lowered to an OAS2 again.

In the middle, the specific forms move the right-hand side from
n.Rlist().First() to n.Right(), and then the conversion to OAS2 moves
it back. This is unnecessary and makes it hard for these all to
share an underlying Node implementation.

This CL changes these specific forms to leave the right-hand side
in n.Rlist().First().

Similarly, OSELRECV2 is really just a temporary form of OAS2.
This CL changes it to use same fields too.

Finally, this CL fixes the printing of OAS2 nodes in ir/fmt.go,
which formerly printed n.Right() instead of n.Rlist().
This results in a (correct!) update to cmd/compile/internal/logopt's
expected output: ~R0 = <N> becomes ~R0 = &y.b.

Passes buildall w/ toolstash -cmp.

Change-Id: I164aa2e17dc55bfb292024de53d7d250192ad64a
Reviewed-on: https://go-review.googlesource.com/c/go/+/274105
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 23:48:13 +00:00
Matthew Dempsky
c6de5d8d1f [dev.regabi] cmd/compile: simplify export data representation of nil
The handling of ONIL and Orig has been a mess for a while, and dates
back to how fmt.go used to print out typed nils. That hasn't applied
for a while, but we've kept dragging it along to appease toolstash
with the intention of someday finally removing it.

Today is that day.

Change-Id: I9a441628e53068ab1993cd2b67b977574d8117b7
Reviewed-on: https://go-review.googlesource.com/c/go/+/274212
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
2020-11-30 22:10:39 +00:00
Russ Cox
ae1a337809 [dev.regabi] cmd/compile: remove ODCLFIELD and ODDD ops
These are plain data now, not nodes (see previous CL).
The opcode deletions are not safe for toolstash -cmp,
so they are split into a separate CL.

Change-Id: Icef8a01e190195a7539a35b92f42835d823e314a
Reviewed-on: https://go-review.googlesource.com/c/go/+/274104
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 19:45:23 +00:00
Russ Cox
4e7685ef1a [dev.regabi] cmd/compile: add custom type syntax Node implementations
The type syntax is reused to stand in for the actual type once typechecked,
to avoid updating all the possible references to the original type syntax.
So all these implementations allow changing their Op from the raw syntax
like OTMAP to the finished form OTYPE, even though obviously the
representation does not change.

Passes buildall w/ toolstash -cmp.

Change-Id: I4acca1a5b35fa2f48ee08e8f1e5a330a004c284b
Reviewed-on: https://go-review.googlesource.com/c/go/+/274103
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2020-11-30 19:45:21 +00:00
Russ Cox
d40869fced [dev.regabi] cmd/compile: move gc.treecopy to ir.DeepCopy
This is a general operation on IR nodes, so it belongs in ir.
The copied implementation is adapted to support the
extension pattern, allowing nodes to implement their
own DeepCopy implementations if needed.

This is the first step toward higher-level operations instead
of Left, Right, etc. It will allow the new type syntax nodes
to be properly immutable and opt out of those fine-grained methods.

Passes buildall w/ toolstash -cmp.

Change-Id: Ibd64061e01daf14aebc6586cb2eb2b12057ca85a
Reviewed-on: https://go-review.googlesource.com/c/go/+/274102
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:34:07 +00:00
Russ Cox
f0001e8867 [dev.regabi] cmd/compile: add OTSLICE Op
This is not safe for toolstash -cmp and so is split into its own CL.

Change-Id: Ic3254e68fb84a90a11ac5f0b59ef252135c23658
Reviewed-on: https://go-review.googlesource.com/c/go/+/274101
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:34:06 +00:00
Russ Cox
1b84aabb01 [dev.regabi] cmd/compile: move typenod, typenodl to ir.TypeNode, ir.TypeNodeAt [generated]
[git-generate]
cd src/cmd/compile/internal/gc
rf '
	mv typenod TypeNode
	mv typenodl TypeNodeAt
	mv TypeNode TypeNodeAt type.go
	mv type.go cmd/compile/internal/ir
'

Passes buildall w/ toolstash -cmp.

Change-Id: Id546a8cfae93074ebb1496490da7635800807faf
Reviewed-on: https://go-review.googlesource.com/c/go/+/274100
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:34:05 +00:00
Russ Cox
e5c6463e20 [dev.regabi] cmd/compile: add ir.CallPartExpr
Now there are no longer any generic nodes with a non-nil
associated Func, so node.fn can be deleted. Also all manipulation
of func fields is done with concrete types, so Node.SetFunc can be
deleted, along with generic implementations.

Passes buildall w/ toolstash -cmp.

Change-Id: I4fee99870951ec9dc224f146d87b22e2bfe16889
Reviewed-on: https://go-review.googlesource.com/c/go/+/274099
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:34:03 +00:00
Russ Cox
4eaef981b5 [dev.regabi] cmd/compile: add ir.Closure, ir.ClosureRead
Closures are another reference to Funcs,
and it cleans up the code quite a bit to be clear about types.

OCLOSUREVAR is renamed to OCLOSUREREAD to make
clearer that it is unrelated to the list Func.ClosureVars.

Passes buildall w/ toolstash -cmp.

Change-Id: Id0d28df2d4d6e9954e34df7a39ea226995eee937
Reviewed-on: https://go-review.googlesource.com/c/go/+/274098
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:34:02 +00:00
Russ Cox
e84b27bec5 [dev.regabi] cmd/compile: clean up Name and Func uses
Now that we have specific types for ONAME and ODCLFUNC nodes
(*Name and *Func), use them throughout the compiler to be more
precise about what data is being operated on.

This is a somewhat large CL, but once you start applying the types
in a few places, you end up needing to apply them to many other
places to keep everything type-checking. A lot of code also melts
away as types are added.

Passes buildall w/ toolstash -cmp.

Change-Id: I21dd9b945d701c470332bac5394fca744a5b232d
Reviewed-on: https://go-review.googlesource.com/c/go/+/274097
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:34:01 +00:00
Russ Cox
c4bd0b7474 [dev.regabi] cmd/compile: make ir.Func the ODCLFUNC Node implementation
Before this CL, an ODCLFUNC Node was represented by both
a node struct and a Func struct (and a Name for the ONAME,
which isn't changing here). Now Func can be repurposed as
the ODCLFUNC implementation, replacing the two structs
totaling 280+144 = 424 bytes (64-bit) with a single 320-byte struct.

Using the *Func as the node also gives us a clear, typed answer to
“which node should we use to represent functions?”
The next CL will clean up uses. This CL is just the trivial
change in representation.

Passes buildall w/ toolstash -cmp.

Change-Id: Ie6d670da91d6eb8d67a85f8f83630b9586dc7443
Reviewed-on: https://go-review.googlesource.com/c/go/+/274096
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:34:00 +00:00
Russ Cox
65ae15ac5d [dev.regabi] cmd/compile: move func code from node.go to func.go
No code changes here, only copying of text.
This will make the diffs in a future CL readable.

Passes buildall w/ toolstash -cmp.

Change-Id: I325a62e79edd82f1437769891ea63a32f51c0170
Reviewed-on: https://go-review.googlesource.com/c/go/+/274095
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:33:59 +00:00
Russ Cox
862f638a89 [dev.regabi] cmd/compile: make ir.Name the ONAME Node implementation
Before this CL, an ONAME Node was represented by three structs
linked together: a node, a Name, and a Param. Previous CLs removed
OLABEL and OPACK from the set of nodes that knew about Name.
Now Name can be repurposed to *be* the ONAME Node implementation,
replacing three linked structs totaling 152+64+88 = 304 bytes (64-bit)
with a single 232-byte struct.

Many expressions in the code become simpler as well, without having
to use .Param. and sometimes even .Name().
(For a node n where n.Name() != nil, n.Name() == n.(*Name) now.)

Passes buildall w/ toolstash -cmp.

Change-Id: Ie719f1285c05623b9fd2faaa059e5b360a64b3be
Reviewed-on: https://go-review.googlesource.com/c/go/+/274094
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:33:57 +00:00
Russ Cox
f6106d195d [dev.regabi] cmd/compile: add ir.PkgName
OPACK was using a whole Node and Name and Param
to hold about three fields. Give it its own implementation.

Passes buildall w/ toolstash -cmp.

Change-Id: I85a28b43d37183b2062d337b0b1b2eea52884e8c
Reviewed-on: https://go-review.googlesource.com/c/go/+/274093
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:33:56 +00:00
Russ Cox
420809ab08 [dev.regabi] cmd/compile: move name code from node.go to name.go
No code changes here, only copying of text.
This will make the diffs in a future CL readable.

Passes buildall w/ toolstash -cmp.

Change-Id: I1b8d8b9ec9408859e36af5ff3bef7c6c10eac0d6
Reviewed-on: https://go-review.googlesource.com/c/go/+/274092
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:33:55 +00:00
Russ Cox
be3d8b40b5 [dev.regabi] cmd/compile: ir.BranchStmt, add ir.EmptyStmt, ir.LabelStmt
These are the first three specific implementations of Node.
They are both a bit of a warmup and also working toward
removing references to Name from Node types other than
the proper named things - ONAME, ONONAME, OTYPE, OLITERAL.
(In this case, BranchStmt and LabelStmt.)

Passes buildall w/ toolstash -cmp.

Change-Id: Ide816b162025ee4c858dd061d7c29ed633fb7baf
Reviewed-on: https://go-review.googlesource.com/c/go/+/274091
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:33:54 +00:00
Russ Cox
b09dbc6913 [dev.regabi] cmd/compile: remove SetOp(OEMPTY) calls
In preparation for OEMPTY being its own Node implementation,
remove SetOp(OEMPTY) calls that assume other implementations
can be turned into OEMPTY.

Passes buildall w/ toolstash -cmp.

Change-Id: Icac16d12548f35f52a5efa9d09dacf8260f42075
Reviewed-on: https://go-review.googlesource.com/c/go/+/274090
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:33:52 +00:00
Russ Cox
171787efcd [dev.regabi] cmd/compile: remove Orig, SetOrig from Node interface
These are only needed for a few opcodes, and we can avoid
wasting storage in every implementation by using the extension
interface pattern with a helper function for access.

Of course, in the current codebase, there is only one Node
implementation (*node) and it has these methods, so there
is no danger of a functional change in this particular CL.

Passes buildall w/ toolstash -cmp.

Change-Id: I440c6c232f1fe7b56b852a00dc530f8f49a6b12d
Reviewed-on: https://go-review.googlesource.com/c/go/+/274089
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:33:51 +00:00
Russ Cox
79a3d5ce15 [dev.regabi] cmd/compile: setup for new Node implementations
Start a list of which ops are valid for the default
node struct implementation (currently all of them).

Add a Node implementation helper for a minimal node.

Passes buildall w/ toolstash -cmp.

Change-Id: I7ae45f2cf2be85013cb71ab00524be53f243e13d
Reviewed-on: https://go-review.googlesource.com/c/go/+/274088
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:33:50 +00:00
Russ Cox
0c65a2f317 [dev.regabi] cmd/compile: drop Node.HasOpt method
Node.HasOpt is only used once, and that use can use Opt instead.
Interface is one method smaller.

Passes buildall w/ toolstash -cmp.

Change-Id: I6a9d5859a9977a8f4c9db70e166f50f0d8052160
Reviewed-on: https://go-review.googlesource.com/c/go/+/274087
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:33:49 +00:00
Russ Cox
65f4ec2fae [dev.regabi] cmd/compile: cleanup label handling
- The use of a label's Name.Defn to point at the named for/select/switch
  means that any rewrite of the for/select/switch must overwrite the original
  or else the pointer will dangle. Remove that pointer by adding the label
  name directly to the for/select/switch representation instead.

- The only uses of a label's Sym.Label were ephemeral values during
  markbreak and escape analysis. Use a map for each. Although in general
  we are not going to replace all computed fields with maps (too slow),
  the one in markbreak is only for labeled for/select/switch, and the one
  in escape is for all labels, but even so, labels are fairly rare.

In theory this cleanup should make it easy to allow labeled for/select/switch
in inlined bodies, but this CL does not attempt that. It's only concerned
with cleanup to enable a new Node representation.

Passes buildall w/ toolstash -cmp.

Change-Id: I7e36ee98d2ea40dbae94e6722d585f007b7afcfa
Reviewed-on: https://go-review.googlesource.com/c/go/+/274086
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-30 18:33:47 +00:00
Matthew Dempsky
88e33f6ecb [dev.regabi] cmd/compile: fix latent import/export issue with break/continue
In CL 145200, I changed OBREAK, OCONTINUE, OGOTO, and OLABEL to just
use Sym instead of Node. However, within the export data, I forgot to
update the code for OBREAK and OCONTINUE.

This isn't currently an issue because the inliner currently disallows
these anyway, but it'll be an issue in the future once we add support
for inlining them. Also, Russ independently ran into it in CL 273246.

Updates #14768.

Change-Id: I94575df59c08a750b0dce1d3ce612aba7bfeeb76
Reviewed-on: https://go-review.googlesource.com/c/go/+/273270
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-11-25 22:29:41 +00:00
Russ Cox
40f5bc4d55 [dev.regabi] merge master 4481ad6eb6 into dev.regabi
Change-Id: Ia2c251d3809a538c16d05e5dcef59932049b3913
2020-11-25 16:48:58 -05:00
Russ Cox
41f3af9d04 [dev.regabi] cmd/compile: replace *Node type with an interface Node [generated]
The plan is to introduce a Node interface that replaces the old *Node pointer-to-struct.

The previous CL defined an interface INode modeling a *Node.

This CL:
 - Changes all references outside internal/ir to use INode,
   along with many references inside internal/ir as well.
 - Renames Node to node.
 - Renames INode to Node

So now ir.Node is an interface implemented by *ir.node, which is otherwise inaccessible,
and the code outside package ir is now (clearly) using only the interface.

The usual rule is never to redefine an existing name with a new meaning,
so that old code that hasn't been updated gets a "unknown name" error
instead of more mysterious errors or silent misbehavior. That rule would
caution against replacing Node-the-struct with Node-the-interface,
as in this CL, because code that says *Node would now be using a pointer
to an interface. But this CL is being landed at the same time as another that
moves Node from gc to ir. So the net effect is to replace *gc.Node with ir.Node,
which does follow the rule: any lingering references to gc.Node will be told
it's gone, not silently start using pointers to interfaces. So the rule is followed
by the CL sequence, just not this specific CL.

Overall, the loss of inlining caused by using interfaces cuts the compiler speed
by about 6%, a not insignificant amount. However, as we convert the representation
to concrete structs that are not the giant Node over the next weeks, that speed
should come back as more of the compiler starts operating directly on concrete types
and the memory taken up by the graph of Nodes drops due to the more precise
structs. Honestly, I was expecting worse.

% benchstat bench.old bench.new
name                      old time/op       new time/op       delta
Template                        168ms ± 4%        182ms ± 2%   +8.34%  (p=0.000 n=9+9)
Unicode                        72.2ms ±10%       82.5ms ± 6%  +14.38%  (p=0.000 n=9+9)
GoTypes                         563ms ± 8%        598ms ± 2%   +6.14%  (p=0.006 n=9+9)
Compiler                        2.89s ± 4%        3.04s ± 2%   +5.37%  (p=0.000 n=10+9)
SSA                             6.45s ± 4%        7.25s ± 5%  +12.41%  (p=0.000 n=9+10)
Flate                           105ms ± 2%        115ms ± 1%   +9.66%  (p=0.000 n=10+8)
GoParser                        144ms ±10%        152ms ± 2%   +5.79%  (p=0.011 n=9+8)
Reflect                         345ms ± 9%        370ms ± 4%   +7.28%  (p=0.001 n=10+9)
Tar                             149ms ± 9%        161ms ± 5%   +8.05%  (p=0.001 n=10+9)
XML                             190ms ± 3%        209ms ± 2%   +9.54%  (p=0.000 n=9+8)
LinkCompiler                    327ms ± 2%        325ms ± 2%     ~     (p=0.382 n=8+8)
ExternalLinkCompiler            1.77s ± 4%        1.73s ± 6%     ~     (p=0.113 n=9+10)
LinkWithoutDebugCompiler        214ms ± 4%        211ms ± 2%     ~     (p=0.360 n=10+8)
StdCmd                          14.8s ± 3%        15.9s ± 1%   +6.98%  (p=0.000 n=10+9)
[Geo mean]                      480ms             510ms        +6.31%

name                      old user-time/op  new user-time/op  delta
Template                        223ms ± 3%        237ms ± 3%   +6.16%  (p=0.000 n=9+10)
Unicode                         103ms ± 6%        113ms ± 3%   +9.53%  (p=0.000 n=9+9)
GoTypes                         758ms ± 8%        800ms ± 2%   +5.55%  (p=0.003 n=10+9)
Compiler                        3.95s ± 2%        4.12s ± 2%   +4.34%  (p=0.000 n=10+9)
SSA                             9.43s ± 1%        9.74s ± 4%   +3.25%  (p=0.000 n=8+10)
Flate                           132ms ± 2%        141ms ± 2%   +6.89%  (p=0.000 n=9+9)
GoParser                        177ms ± 9%        183ms ± 4%     ~     (p=0.050 n=9+9)
Reflect                         467ms ±10%        495ms ± 7%   +6.17%  (p=0.029 n=10+10)
Tar                             183ms ± 9%        197ms ± 5%   +7.92%  (p=0.001 n=10+10)
XML                             249ms ± 5%        268ms ± 4%   +7.82%  (p=0.000 n=10+9)
LinkCompiler                    544ms ± 5%        544ms ± 6%     ~     (p=0.863 n=9+9)
ExternalLinkCompiler            1.79s ± 4%        1.75s ± 6%     ~     (p=0.075 n=10+10)
LinkWithoutDebugCompiler        248ms ± 6%        246ms ± 2%     ~     (p=0.965 n=10+8)
[Geo mean]                      483ms             504ms        +4.41%

[git-generate]
cd src/cmd/compile/internal/ir
: # We need to do the conversion in multiple steps, so we introduce
: # a temporary type alias that will start out meaning the pointer-to-struct
: # and then change to mean the interface.
rf '
	mv Node OldNode

	add node.go \
		type Node = *OldNode
'

: # It should work to do this ex in ir, but it misses test files, due to a bug in rf.
: # Run the command in gc to handle gc's tests, and then again in ssa for ssa's tests.
cd ../gc
rf '
        ex .  ../arm ../riscv64 ../arm64 ../mips64 ../ppc64 ../mips ../wasm {
                import "cmd/compile/internal/ir"
                *ir.OldNode -> ir.Node
        }
'
cd ../ssa
rf '
        ex {
                import "cmd/compile/internal/ir"
                *ir.OldNode -> ir.Node
        }
'

: # Back in ir, finish conversion clumsily with sed,
: # because type checking and circular aliases do not mix.
cd ../ir
sed -i '' '
	/type Node = \*OldNode/d
	s/\*OldNode/Node/g
	s/^func (n Node)/func (n *OldNode)/
	s/OldNode/node/g
	s/type INode interface/type Node interface/
	s/var _ INode = (Node)(nil)/var _ Node = (*node)(nil)/
' *.go
gofmt -w *.go

sed -i '' '
	s/{Func{}, 136, 248}/{Func{}, 152, 280}/
	s/{Name{}, 32, 56}/{Name{}, 44, 80}/
	s/{Param{}, 24, 48}/{Param{}, 44, 88}/
	s/{node{}, 76, 128}/{node{}, 88, 152}/
' sizeof_test.go

cd ../ssa
sed -i '' '
	s/{LocalSlot{}, 28, 40}/{LocalSlot{}, 32, 48}/
' sizeof_test.go

cd ../gc
sed -i '' 's/\*ir.Node/ir.Node/' mkbuiltin.go

cd ../../../..
go install std cmd
cd cmd/compile
go test -u || go test -u

Change-Id: I196bbe3b648e4701662e4a2bada40bf155e2a553
Reviewed-on: https://go-review.googlesource.com/c/go/+/272935
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 17:30:43 +00:00
Russ Cox
4d0d9c2c5c [dev.regabi] cmd/compile: introduce ir.INode interface for *ir.Node
Define the interface for an IR node.
The next CL will shuffle the names and leave us with ir.Node being the interface.

Change-Id: Ifc40f7846d522cf99efa6b4e558bebb6db5218f9
Reviewed-on: https://go-review.googlesource.com/c/go/+/272934
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 17:30:42 +00:00
Russ Cox
c26aead50c [dev.regabi] cmd/compile: convert types.Node (a pointer) to types.IRNode (an interface)
The pointer hack was nice and saved a word, but it's untenable
in a world where nodes are themselves interfaces with different
underlying types. Bite the bullet and use an interface to hold the
Node when in types.Sym and types.Type.

This has the nice benefit of removing AsTypesNode entirely.
AsNode is still useful because of its nil handling.

Change-Id: I298cba9ff788b956ee287283bec78010e8b601e5
Reviewed-on: https://go-review.googlesource.com/c/go/+/272933
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 17:30:40 +00:00
Russ Cox
acb4d1cef1 [dev.regabi] cmd/compile: use Node getters and setters [generated]
Now that we have all the getters and setters defined, use them
and unexport all the actual Node fields. This is the next step
toward replacing Node with an interface.

[git-generate]
cd src/cmd/compile/internal/gc
rf '
        ex . ../ir ../ssa {
                import "cmd/compile/internal/ir"
                import "cmd/compile/internal/types"
                import "cmd/internal/src"
                var n, x *ir.Node
                var op ir.Op
                var t *types.Type
                var f *ir.Func
                var m *ir.Name
                var s *types.Sym
                var p src.XPos
                var i int64
                var e uint16
                var nodes ir.Nodes

                n.Op = op    -> n.SetOp(op)
                n.Left = x   -> n.SetLeft(x)
                n.Right = x  -> n.SetRight(x)
                n.Orig = x -> n.SetOrig(x)
                n.Type = t -> n.SetType(t)
                n.Func = f -> n.SetFunc(f)
                n.Name = m -> n.SetName(m)
                n.Sym = s -> n.SetSym(s)
                n.Pos = p -> n.SetPos(p)
                n.Xoffset = i -> n.SetXoffset(i)
                n.Esc = e -> n.SetEsc(e)

                n.Ninit.Append -> n.PtrNinit().Append
                n.Ninit.AppendNodes -> n.PtrNinit().AppendNodes
                n.Ninit.MoveNodes -> n.PtrNinit().MoveNodes
                n.Ninit.Prepend -> n.PtrNinit().Prepend
                n.Ninit.Set -> n.PtrNinit().Set
                n.Ninit.Set1 -> n.PtrNinit().Set1
                n.Ninit.Set2 -> n.PtrNinit().Set2
                n.Ninit.Set3 -> n.PtrNinit().Set3
                &n.Ninit -> n.PtrNinit()
                n.Ninit = nodes -> n.SetNinit(nodes)

                n.Nbody.Append -> n.PtrNbody().Append
                n.Nbody.AppendNodes -> n.PtrNbody().AppendNodes
                n.Nbody.MoveNodes -> n.PtrNbody().MoveNodes
                n.Nbody.Prepend -> n.PtrNbody().Prepend
                n.Nbody.Set -> n.PtrNbody().Set
                n.Nbody.Set1 -> n.PtrNbody().Set1
                n.Nbody.Set2 -> n.PtrNbody().Set2
                n.Nbody.Set3 -> n.PtrNbody().Set3
                &n.Nbody -> n.PtrNbody()
                n.Nbody = nodes -> n.SetNbody(nodes)

                n.List.Append -> n.PtrList().Append
                n.List.AppendNodes -> n.PtrList().AppendNodes
                n.List.MoveNodes -> n.PtrList().MoveNodes
                n.List.Prepend -> n.PtrList().Prepend
                n.List.Set -> n.PtrList().Set
                n.List.Set1 -> n.PtrList().Set1
                n.List.Set2 -> n.PtrList().Set2
                n.List.Set3 -> n.PtrList().Set3
                &n.List -> n.PtrList()
                n.List = nodes -> n.SetList(nodes)

                n.Rlist.Append -> n.PtrRlist().Append
                n.Rlist.AppendNodes -> n.PtrRlist().AppendNodes
                n.Rlist.MoveNodes -> n.PtrRlist().MoveNodes
                n.Rlist.Prepend -> n.PtrRlist().Prepend
                n.Rlist.Set -> n.PtrRlist().Set
                n.Rlist.Set1 -> n.PtrRlist().Set1
                n.Rlist.Set2 -> n.PtrRlist().Set2
                n.Rlist.Set3 -> n.PtrRlist().Set3
                &n.Rlist -> n.PtrRlist()
                n.Rlist = nodes -> n.SetRlist(nodes)
        }
        ex . ../ir ../ssa {
                import "cmd/compile/internal/ir"

                var n *ir.Node
                n.Op         -> n.GetOp()
                n.Left       -> n.GetLeft()
                n.Right      -> n.GetRight()
                n.Orig -> n.GetOrig()
                n.Type -> n.GetType()
                n.Func -> n.GetFunc()
                n.Name -> n.GetName()
                n.Sym -> n.GetSym()
                n.Pos -> n.GetPos()
                n.Xoffset -> n.GetXoffset()
                n.Esc -> n.GetEsc()

                avoid (*ir.Node).PtrNinit
                avoid (*ir.Node).PtrNbody
                avoid (*ir.Node).PtrList
                avoid (*ir.Node).PtrRlist

                n.Ninit -> n.GetNinit()
                n.Nbody -> n.GetNbody()
                n.List -> n.GetList()
                n.Rlist -> n.GetRlist()
        }
'

cd ../ir
rf '
        mv Node.Op Node.op
        mv Node.GetOp Node.Op

        mv Node.Left Node.left
        mv Node.GetLeft Node.Left

        mv Node.Right Node.right
        mv Node.GetRight Node.Right

        mv Node.Orig Node.orig
        mv Node.GetOrig Node.Orig

        mv Node.Type Node.typ
        mv Node.GetType Node.Type

        mv Node.Func Node.fn
        mv Node.GetFunc Node.Func

        mv Node.Name Node.name
        mv Node.GetName Node.Name

        # All uses are in other Node methods already.
        mv Node.E Node.e

        mv Node.Sym Node.sym
        mv Node.GetSym Node.Sym

        mv Node.Pos Node.pos
        mv Node.GetPos Node.Pos

        mv Node.Esc Node.esc
        mv Node.GetEsc Node.Esc

	# While we are here, rename Xoffset to more idiomatic Offset.
        mv Node.Xoffset Node.offset
        mv Node.GetXoffset Node.Offset
	mv Node.SetXoffset Node.SetOffset

        # While we are here, rename Ninit, Nbody to more idiomatic Init, Body.
        mv Node.Ninit Node.init
        mv Node.GetNinit Node.Init
        mv Node.PtrNinit Node.PtrInit
        mv Node.SetNinit Node.SetInit
        mv Node.Nbody Node.body
        mv Node.GetNbody Node.Body
        mv Node.PtrNbody Node.PtrBody
        mv Node.SetNbody Node.SetBody
        mv Node.List Node.list
        mv Node.GetList Node.List
        mv Node.Rlist Node.rlist
        mv Node.GetRlist Node.Rlist

        # Unexport these
        mv Node.SetHasOpt Node.setHasOpt
        mv Node.SetHasVal Node.setHasVal
'

Change-Id: I9894f633375c5237a29b6d6d7b89ba181b56ca3a
Reviewed-on: https://go-review.googlesource.com/c/go/+/273009
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 17:30:39 +00:00
Russ Cox
41ab6689ed [dev.regabi] cmd/compile: rewrite a few ++/--/+=/-= to prep for getters/setters [generated]
These are trivial rewrites that are only OK because it turns out that n has no side effects.
Separated into a different CL for easy inspection.

[git-generate]
cd src/cmd/compile/internal/gc
rf '
        ex . ../ir ../ssa {
                import "cmd/compile/internal/ir"
                var n *ir.Node
		var i int64

                n.Xoffset++ -> n.Xoffset = n.Xoffset + 1
                n.Xoffset-- -> n.Xoffset = n.Xoffset - 1
                n.Xoffset += i -> n.Xoffset = n.Xoffset + i
                n.Xoffset -= i -> n.Xoffset = n.Xoffset - i
	}
'

Change-Id: If7b4b7f7cbdafeee988e04d03924ef0e1dd867b0
Reviewed-on: https://go-review.googlesource.com/c/go/+/272932
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 17:30:38 +00:00
Russ Cox
048debb224 [dev.regabi] cmd/compile: remove gc ↔ ssa cycle hacks
The cycle hacks existed because gc needed to import ssa
which need to know about gc.Node. But now that's ir.Node,
and there's no cycle anymore.

Don't know how much it matters but LocalSlot is now
one word shorter than before, because it holds a pointer
instead of an interface for the *Node. That won't last long.

Now that they're not necessary for interface satisfaction,
IsSynthetic and IsAutoTmp can move to top-level ir functions.

Change-Id: Ie511e93466cfa2b17d9a91afc4bd8d53fdb80453
Reviewed-on: https://go-review.googlesource.com/c/go/+/272931
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 17:30:36 +00:00
Russ Cox
84e2bd611f [dev.regabi] cmd/compile: introduce cmd/compile/internal/ir [generated]
If we want to break up package gc at all, we will need to move
the compiler IR it defines into a separate package that can be
imported by packages that gc itself imports. This CL does that.
It also removes the TINT8 etc aliases so that all code is clear
about which package things are coming from.

This CL is automatically generated by the script below.
See the comments in the script for details about the changes.

[git-generate]
cd src/cmd/compile/internal/gc

rf '
        # These names were never fully qualified
        # when the types package was added.
        # Do it now, to avoid confusion about where they live.
        inline -rm \
                Txxx \
                TINT8 \
                TUINT8 \
                TINT16 \
                TUINT16 \
                TINT32 \
                TUINT32 \
                TINT64 \
                TUINT64 \
                TINT \
                TUINT \
                TUINTPTR \
                TCOMPLEX64 \
                TCOMPLEX128 \
                TFLOAT32 \
                TFLOAT64 \
                TBOOL \
                TPTR \
                TFUNC \
                TSLICE \
                TARRAY \
                TSTRUCT \
                TCHAN \
                TMAP \
                TINTER \
                TFORW \
                TANY \
                TSTRING \
                TUNSAFEPTR \
                TIDEAL \
                TNIL \
                TBLANK \
                TFUNCARGS \
                TCHANARGS \
                NTYPE \
                BADWIDTH

        # esc.go and escape.go do not need to be split.
        # Append esc.go onto the end of escape.go.
        mv esc.go escape.go

        # Pull out the type format installation from func Main,
        # so it can be carried into package ir.
        mv Main:/Sconv.=/-0,/TypeLinkSym/-1 InstallTypeFormats

        # Names that need to be exported for use by code left in gc.
        mv Isconst IsConst
        mv asNode AsNode
        mv asNodes AsNodes
        mv asTypesNode AsTypesNode
        mv basicnames BasicTypeNames
        mv builtinpkg BuiltinPkg
        mv consttype ConstType
        mv dumplist DumpList
        mv fdumplist FDumpList
        mv fmtMode FmtMode
        mv goopnames OpNames
        mv inspect Inspect
        mv inspectList InspectList
        mv localpkg LocalPkg
        mv nblank BlankNode
        mv numImport NumImport
        mv opprec OpPrec
        mv origSym OrigSym
        mv stmtwithinit StmtWithInit
        mv dump DumpAny
        mv fdump FDumpAny
        mv nod Nod
        mv nodl NodAt
        mv newname NewName
        mv newnamel NewNameAt
        mv assertRepresents AssertValidTypeForConst
        mv represents ValidTypeForConst
        mv nodlit NewLiteral

        # Types and fields that need to be exported for use by gc.
        mv nowritebarrierrecCallSym SymAndPos
        mv SymAndPos.lineno SymAndPos.Pos
        mv SymAndPos.target SymAndPos.Sym

        mv Func.lsym Func.LSym
        mv Func.setWBPos Func.SetWBPos
        mv Func.numReturns Func.NumReturns
        mv Func.numDefers Func.NumDefers
        mv Func.nwbrCalls Func.NWBRCalls

        # initLSym is an algorithm left behind in gc,
        # not an operation on Func itself.
        mv Func.initLSym initLSym

        mv nodeQueue NodeQueue
        mv NodeQueue.empty NodeQueue.Empty
        mv NodeQueue.popLeft NodeQueue.PopLeft
        mv NodeQueue.pushRight NodeQueue.PushRight

        # Many methods on Node are actually algorithms that
        # would apply to any node implementation.
        # Those become plain functions.
        mv Node.funcname FuncName
        mv Node.isBlank IsBlank
        mv Node.isGoConst isGoConst
        mv Node.isNil IsNil
        mv Node.isParamHeapCopy isParamHeapCopy
        mv Node.isParamStackCopy isParamStackCopy
        mv Node.isSimpleName isSimpleName
        mv Node.mayBeShared MayBeShared
        mv Node.pkgFuncName PkgFuncName
        mv Node.backingArrayPtrLen backingArrayPtrLen
        mv Node.isterminating isTermNode
        mv Node.labeledControl labeledControl
        mv Nodes.isterminating isTermNodes
        mv Nodes.sigerr fmtSignature
        mv Node.MethodName methodExprName
        mv Node.MethodFunc methodExprFunc
        mv Node.IsMethod IsMethod

        # Every node will need to implement RawCopy;
        # Copy and SepCopy algorithms will use it.
        mv Node.rawcopy Node.RawCopy
        mv Node.copy Copy
        mv Node.sepcopy SepCopy

        # Extract Node.Format method body into func FmtNode,
        # but leave method wrapper behind.
        mv Node.Format:0,$ FmtNode

        # Formatting helpers that will apply to all node implementations.
        mv Node.Line Line
        mv Node.exprfmt exprFmt
        mv Node.jconv jconvFmt
        mv Node.modeString modeString
        mv Node.nconv nconvFmt
        mv Node.nodedump nodeDumpFmt
        mv Node.nodefmt nodeFmt
        mv Node.stmtfmt stmtFmt

	# Constant support needed for code moving to ir.
        mv okforconst OKForConst
        mv vconv FmtConst
        mv int64Val Int64Val
        mv float64Val Float64Val
        mv Node.ValueInterface ConstValue

        # Organize code into files.
        mv LocalPkg BuiltinPkg ir.go
        mv NumImport InstallTypeFormats Line fmt.go
        mv syntax.go Nod NodAt NewNameAt Class Pxxx PragmaFlag Nointerface SymAndPos \
                AsNode AsTypesNode BlankNode OrigSym \
                Node.SliceBounds Node.SetSliceBounds Op.IsSlice3 \
                IsConst Node.Int64Val Node.CanInt64 Node.Uint64Val Node.BoolVal Node.StringVal \
                Node.RawCopy SepCopy Copy \
                IsNil IsBlank IsMethod \
                Node.Typ Node.StorageClass node.go
        mv ConstType ConstValue Int64Val Float64Val AssertValidTypeForConst ValidTypeForConst NewLiteral idealType OKForConst val.go

        # Move files to new ir package.
        mv bitset.go class_string.go dump.go fmt.go \
                ir.go node.go op_string.go val.go \
                sizeof_test.go cmd/compile/internal/ir
'

: # fix mkbuiltin.go to generate the changes made to builtin.go during rf
sed -i '' '
        s/\[T/[types.T/g
        s/\*Node/*ir.Node/g
        /internal\/types/c \
                fmt.Fprintln(&b, `import (`) \
                fmt.Fprintln(&b, `      "cmd/compile/internal/ir"`) \
                fmt.Fprintln(&b, `      "cmd/compile/internal/types"`) \
                fmt.Fprintln(&b, `)`)
' mkbuiltin.go
gofmt -w mkbuiltin.go

: # update cmd/dist to add internal/ir
cd ../../../dist
sed -i '' '/compile.internal.gc/a\
        "cmd/compile/internal/ir",
' buildtool.go
gofmt -w buildtool.go

: # update cmd/compile TestFormats
cd ../..
go install std cmd
cd cmd/compile
go test -u || go test  # first one updates but fails; second passes

Change-Id: I5f7caf6b20629b51970279e81231a3574d5b51db
Reviewed-on: https://go-review.googlesource.com/c/go/+/273008
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 16:53:33 +00:00
Russ Cox
331b8b4797 [dev.regabi] cmd/compile: move okforconst into its own declaration
It needs to move into package ir, and we do not want all the rest.

Change-Id: Ibcfa1ebc0e63fe3659267bf2fa7069e8a93de4e9
Reviewed-on: https://go-review.googlesource.com/c/go/+/272930
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 16:40:01 +00:00
Russ Cox
26b66fd60b [dev.regabi] cmd/compile: introduce cmd/compile/internal/base [generated]
Move Flag, Debug, Ctxt, Exit, and error messages to
new package cmd/compile/internal/base.

These are the core functionality that everything in gc uses
and which otherwise prevent splitting any other code
out of gc into different packages.

A minor milestone: the compiler source code
no longer contains the string "yy".

[git-generate]
cd src/cmd/compile/internal/gc
rf '
        mv atExit AtExit
        mv Ctxt atExitFuncs AtExit Exit base.go

        mv lineno Pos
        mv linestr FmtPos
        mv flusherrors FlushErrors
        mv yyerror Errorf
        mv yyerrorl ErrorfAt
        mv yyerrorv ErrorfVers
        mv noder.yyerrorpos noder.errorAt
        mv Warnl WarnfAt
        mv errorexit ErrorExit

        mv base.go debug.go flag.go print.go cmd/compile/internal/base
'

: # update comments
sed -i '' 's/yyerrorl/ErrorfAt/g; s/yyerror/Errorf/g' *.go

: # bootstrap.go is not built by default so invisible to rf
sed -i '' 's/Fatalf/base.Fatalf/' bootstrap.go
goimports -w bootstrap.go

: # update cmd/dist to add internal/base
cd ../../../dist
sed -i '' '/internal.amd64/a\
	"cmd/compile/internal/base",
' buildtool.go
gofmt -w buildtool.go

Change-Id: I59903c7084222d6eaee38823fd222159ba24a31a
Reviewed-on: https://go-review.googlesource.com/c/go/+/272250
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 16:39:54 +00:00
Russ Cox
eb3086e5a8 [dev.regabi] cmd/compile: finish cleanup of Debug parsing
Now that the debug settings are in a struct, use struct tags to set
the usage messages and use reflection to populate debugtab,
much like we did for the Flag struct.

Change-Id: Id2ba30c30a9158c062527715a68bf4dd94679457
Reviewed-on: https://go-review.googlesource.com/c/go/+/272247
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 16:39:48 +00:00
Russ Cox
3c240f5d17 [dev.regabi] cmd/compile: clean up debug flag (-d) handling [generated]
The debug table is not as haphazard as flags, but there are still
a few mismatches between command-line names and variable names.
This CL moves them all into a consistent home (var Debug, like var Flag).

Code updated automatically using the rf command below.
A followup CL will make a few manual cleanups, leaving this CL
completely automated and easier to regenerate during merge
conflicts.

[git-generate]
cd src/cmd/compile/internal/gc
rf '
	add main.go var Debug struct{}
	mv Debug_append Debug.Append
	mv Debug_checkptr Debug.Checkptr
	mv Debug_closure Debug.Closure
	mv Debug_compilelater Debug.CompileLater
	mv disable_checknil Debug.DisableNil
	mv debug_dclstack Debug.DclStack
	mv Debug_gcprog Debug.GCProg
	mv Debug_libfuzzer Debug.Libfuzzer
	mv Debug_checknil Debug.Nil
	mv Debug_panic Debug.Panic
	mv Debug_slice Debug.Slice
	mv Debug_typeassert Debug.TypeAssert
	mv Debug_wb Debug.WB
	mv Debug_export Debug.Export
	mv Debug_pctab Debug.PCTab
	mv Debug_locationlist Debug.LocationLists
	mv Debug_typecheckinl Debug.TypecheckInl
	mv Debug_gendwarfinl Debug.DwarfInl
	mv Debug_softfloat Debug.SoftFloat
	mv Debug_defer Debug.Defer
	mv Debug_dumpptrs Debug.DumpPtrs

	mv flag.go:/parse.-d/-1,/unknown.debug/+2 parseDebug

	mv debugtab Debug parseDebug \
		debugHelpHeader debugHelpFooter \
		debug.go

	# Remove //go:generate line copied from main.go
	rm debug.go:/go:generate/-+
'

Change-Id: I625761ca5659be4052f7161a83baa00df75cca91
Reviewed-on: https://go-review.googlesource.com/c/go/+/272246
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 16:39:42 +00:00
Russ Cox
756661c82a [dev.regabi] cmd/compile: finish cleanup of Flag initialization
Now that all flags are in a struct, use struct tags to set the usage messages
and use reflection to walk the struct and register all the flags.

Also move some flag usage back into main.go that shouldn't
come with the rest of flag.go into package base.

Change-Id: Ie655582194906c9ab425c3d01ad8c304bc49bfe0
Reviewed-on: https://go-review.googlesource.com/c/go/+/271668
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 16:18:28 +00:00
Matthew Dempsky
259fd8adbb [dev.regabi] cmd/compile: fix reporting of overflow
In the previous CL, I had incorrectly removed one of the error
messages from issue20232.go, because I thought go/constant was just
handling it. But actually the compiler was panicking in nodlit,
because it didn't handle constant.Unknown. So this CL makes it leave
n.Type == nil for unknown constant.Values.

While here, also address #42732 by making sure to report an error
message when origConst is called with an unknown constant.Value (as
can happen when multiplying two floating-point constants overflows).

Finally, add OXOR and OBITNOT to the list of operations to report
errors about, since they're also constant expressions that can produce
a constant with a greater bit length than their operands.

Fixes #42732.

Change-Id: I4a538fbae9b3ac4c553d7de5625dc0c87d9acce3
Reviewed-on: https://go-review.googlesource.com/c/go/+/272928
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-11-25 15:47:36 +00:00
Russ Cox
18573aea3c [dev.regabi] cmd/compile: clean up flag handling [generated]
The flag values have grown fairly haphazard, with no organization
or even common naming convention. This CL moves all flag values
into the Flag struct (formerly misnamed Debug), except for a few
that live in Ctxt fields instead.

This CL is entirely automated changes.
A followup CL will make a few manual cleanups, leaving this CL
completely automated and easier to regenerate during merge
conflicts.

Cleaning up flags is necessary because the printing routines
look at some of them, and the printing routines need to move
out of package gc to a new package shared by gc and any
other packages that split out of gc.

[git-generate]

cd src/cmd/compile/internal/gc
rf '
	mv Debug Flag
	mv DebugFlags Flags
	mv Flags.e Flags.LowerE
	mv Flags.h Flags.LowerH
	mv Flags.j Flags.LowerJ
	mv Flags.l Flags.LowerL
	mv Flags.m Flags.LowerM
	mv Flags.r Flags.LowerR
	mv Flags.w Flags.LowerW
	mv Flags.P Flags.Percent
	mv compiling_runtime Flag.CompilingRuntime
	mv compiling_std Flag.Std
	mv localimport Flag.D
	mv asmhdr Flag.AsmHdr
	mv buildid Flag.BuildID
	mv nBackendWorkers Flag.LowerC
	mv pure_go Flag.Complete
	mv debugstr Flag.LowerD
	mv flagDWARF Flag.Dwarf
	mv genDwarfInline Flag.GenDwarfInl
	mv flag_installsuffix Flag.InstallSuffix
	mv flag_lang Flag.Lang
	mv linkobj Flag.LinkObj
	mv debuglive Flag.Live
	mv flag_msan Flag.MSan
	mv nolocalimports Flag.NoLocalImports
	mv outfile Flag.LowerO
	mv myimportpath Ctxt.Pkgpath
	mv writearchive Flag.Pack
	mv flag_race Flag.Race
	mv spectre Flag.Spectre
	mv trace Flag.LowerT
	mv pathPrefix Flag.TrimPath
	mv Debug_vlog Ctxt.Debugvlog
	mv use_writebarrier Flag.WB
	mv Main.flag_shared Flag.Shared
	mv Main.flag_dynlink Flag.Dynlink
	mv Main.goversion Flag.GoVersion
	mv Main.symabisPath Flag.SymABIs
	mv cpuprofile Flag.CPUProfile
	mv memprofile Flag.MemProfile
	mv traceprofile Flag.TraceProfile
	mv blockprofile Flag.BlockProfile
	mv mutexprofile Flag.MutexProfile
	mv benchfile Flag.Bench
	mv Main.smallFrames Flag.SmallFrames
	mv Main.jsonLogOpt Flag.JSON

	add Flag:$ \
		Cfg struct{}
	mv embedCfg Flag.Cfg.Embed
	mv idirs Flag.Cfg.ImportDirs
	mv importMap Flag.Cfg.ImportMap
	mv packageFile Flag.Cfg.PackageFile
	mv spectreIndex Flag.Cfg.SpectreIndex

	mv addidir addImportDir

	mv main.go:/Wasm/-0,/ssaDump/-3 ParseFlags

	mv usage Flag Flags ParseFlags \
		concurrentFlagOk concurrentBackendAllowed \
		addImportDir addImportMap \
		readImportCfg readEmbedCfg \
		flag.go

	# Remove //go:generate line copied from main.go
	# along with two self-assignments from the merge.
	rm flag.go:/go:generate/-+ \
		flag.go:/Ctxt.Pkgpath = Ctxt.Pkgpath/-+ \
		flag.go:/Ctxt.Debugvlog = Ctxt.Debugvlog/-+
'

Change-Id: I10431c15fe7d9f48024d53141d4224d957dbf334
Reviewed-on: https://go-review.googlesource.com/c/go/+/271667
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 15:08:55 +00:00
Russ Cox
6e583d65ab [dev.regabi] cmd/compile: simplify fmt handling of Nodes
The existing code introduces many types in what appears to be an
attempt to avoid allocation when converting formatting argument lists.
Simplify by accepting that allocation is going to happen, especially
when Node itself turns into an interface.

Change-Id: I3c0d45ca01eace4924deb43c0ea7dc6d65943d08
Reviewed-on: https://go-review.googlesource.com/c/go/+/272929
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 15:08:53 +00:00
Russ Cox
d166ef6876 [dev.regabi] cmd/compile: add Node field getters and setters
The goal is to move Node to being an interface and then break
up the one big struct into many implementations.

Step 1 is to convert all current uses of Node to only use methods,
so that the existing algorithms keep working even as the underlying
implementations are adjusted.

Step 0 - this CL - is to add the getters and setters for Step 1.

Change-Id: I0570d8727c3ccb64113627bb9bebcb0dc39da07a
Reviewed-on: https://go-review.googlesource.com/c/go/+/273007
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 15:08:52 +00:00
Russ Cox
9262909764 [dev.regabi] cmd/compile: rewrite problematic use of Node fields
For the upcoming rewrite to access methods, a few direct accesses
are problematic for the automated tool, most notably direct copies
or use of Node structs as opposed to pointers.

Fix these manually.

Passes toolstash -cmp.

Change-Id: I8bdbb33216737c09e1edda284d5c414422d86284
Reviewed-on: https://go-review.googlesource.com/c/go/+/273006
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 04:35:49 +00:00
Russ Cox
9e0e43d84d [dev.regabi] cmd/compile: remove uses of dummy
Per https://developers.google.com/style/inclusive-documentation,
since we are editing some of this code anyway and it is easier
to put the cleanup in a separate CL.

Change-Id: Ib6b851f43f9cc0a57676564477d4ff22abb1cee5
Reviewed-on: https://go-review.googlesource.com/c/go/+/273106
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-25 04:35:29 +00:00
Matthew Dempsky
4a6b4fd139 [dev.regabi] add FatalfAt and fix Fatalf docs
I've wanted a FatalfAt function for a while, but under the old "-l"
suffix naming convention it would have been called "Fatalfl", which is
just atrocious.

Change-Id: If87f692ecdff478769426d4b054ac396e5c1e42e
Reviewed-on: https://go-review.googlesource.com/c/go/+/273013
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-11-25 03:22:35 +00:00
Matthew Dempsky
484449c641 [dev.regabi] cmd/compile: remove file mistakenly added by CL 272248
Change-Id: Ib27a2ab499960cda3bedfd6c1d10a4038c519df5
Reviewed-on: https://go-review.googlesource.com/c/go/+/272986
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
2020-11-25 03:17:24 +00:00
Matthew Dempsky
7d72951229 [dev.regabi] cmd/compile: replace Val with go/constant.Value
This replaces the compiler's legacy constant representation with
go/constant, which is used by go/types. This should ease integrating
with the new go/types-based type checker in the future.

Performance difference is mixed, but there's still room for
improvement.

name                      old time/op       new time/op       delta
Template                        280ms ± 6%        281ms ± 6%    ~     (p=0.488 n=592+587)
Unicode                         132ms ±11%        129ms ±11%  -2.61%  (p=0.000 n=592+591)
GoTypes                         865ms ± 3%        866ms ± 3%  +0.16%  (p=0.019 n=572+577)
Compiler                        3.60s ± 3%        3.60s ± 3%    ~     (p=0.083 n=578+582)
SSA                             8.27s ± 2%        8.28s ± 2%  +0.14%  (p=0.002 n=575+580)
Flate                           177ms ± 8%        176ms ± 8%    ~     (p=0.133 n=580+590)
GoParser                        238ms ± 7%        237ms ± 6%    ~     (p=0.569 n=587+591)
Reflect                         542ms ± 4%        543ms ± 4%    ~     (p=0.064 n=581+579)
Tar                             244ms ± 6%        244ms ± 6%    ~     (p=0.880 n=586+584)
XML                             322ms ± 5%        322ms ± 5%    ~     (p=0.449 n=589+590)
LinkCompiler                    454ms ± 6%        453ms ± 6%    ~     (p=0.249 n=585+583)
ExternalLinkCompiler            1.35s ± 4%        1.35s ± 4%    ~     (p=0.968 n=590+588)
LinkWithoutDebugCompiler        279ms ± 7%        280ms ± 7%    ~     (p=0.270 n=589+586)
[Geo mean]                      535ms             534ms       -0.17%

name                      old user-time/op  new user-time/op  delta
Template                        599ms ±22%        602ms ±21%    ~     (p=0.377 n=588+590)
Unicode                         410ms ±43%        376ms ±39%  -8.36%  (p=0.000 n=596+586)
GoTypes                         1.96s ±15%        1.97s ±17%  +0.70%  (p=0.031 n=596+594)
Compiler                        7.47s ± 9%        7.50s ± 8%  +0.38%  (p=0.031 n=591+583)
SSA                             16.2s ± 4%        16.2s ± 5%    ~     (p=0.617 n=531+531)
Flate                           298ms ±25%        292ms ±30%  -2.14%  (p=0.001 n=594+596)
GoParser                        379ms ±20%        381ms ±21%    ~     (p=0.312 n=578+584)
Reflect                         1.24s ±20%        1.25s ±23%  +0.88%  (p=0.031 n=592+596)
Tar                             471ms ±23%        473ms ±21%    ~     (p=0.616 n=593+587)
XML                             674ms ±20%        681ms ±21%  +1.03%  (p=0.050 n=584+587)
LinkCompiler                    842ms ±10%        839ms ±10%    ~     (p=0.074 n=587+590)
ExternalLinkCompiler            1.65s ± 7%        1.65s ± 7%    ~     (p=0.767 n=590+585)
LinkWithoutDebugCompiler        378ms ±11%        379ms ±12%    ~     (p=0.677 n=591+586)
[Geo mean]                      1.02s             1.02s       -0.52%

name                      old alloc/op      new alloc/op      delta
Template                       37.4MB ± 0%       37.4MB ± 0%  +0.06%  (p=0.000 n=589+585)
Unicode                        29.6MB ± 0%       28.6MB ± 0%  -3.11%  (p=0.000 n=574+566)
GoTypes                         120MB ± 0%        120MB ± 0%  -0.01%  (p=0.000 n=594+593)
Compiler                        568MB ± 0%        568MB ± 0%  -0.02%  (p=0.000 n=588+591)
SSA                            1.45GB ± 0%       1.45GB ± 0%  -0.16%  (p=0.000 n=596+592)
Flate                          22.6MB ± 0%       22.5MB ± 0%  -0.36%  (p=0.000 n=593+595)
GoParser                       30.1MB ± 0%       30.1MB ± 0%  -0.01%  (p=0.000 n=590+594)
Reflect                        77.8MB ± 0%       77.8MB ± 0%    ~     (p=0.631 n=584+591)
Tar                            34.1MB ± 0%       34.1MB ± 0%  -0.04%  (p=0.000 n=584+588)
XML                            43.6MB ± 0%       43.6MB ± 0%  +0.07%  (p=0.000 n=593+591)
LinkCompiler                   98.6MB ± 0%       98.6MB ± 0%    ~     (p=0.096 n=590+589)
ExternalLinkCompiler           89.6MB ± 0%       89.6MB ± 0%    ~     (p=0.695 n=590+587)
LinkWithoutDebugCompiler       57.2MB ± 0%       57.2MB ± 0%    ~     (p=0.674 n=590+589)
[Geo mean]                     78.5MB            78.3MB       -0.28%

name                      old allocs/op     new allocs/op     delta
Template                         379k ± 0%         380k ± 0%  +0.33%  (p=0.000 n=593+590)
Unicode                          344k ± 0%         338k ± 0%  -1.67%  (p=0.000 n=594+589)
GoTypes                         1.30M ± 0%        1.31M ± 0%  +0.19%  (p=0.000 n=592+591)
Compiler                        5.40M ± 0%        5.41M ± 0%  +0.23%  (p=0.000 n=587+585)
SSA                             14.2M ± 0%        14.2M ± 0%  +0.08%  (p=0.000 n=594+591)
Flate                            231k ± 0%         230k ± 0%  -0.42%  (p=0.000 n=588+589)
GoParser                         314k ± 0%         315k ± 0%  +0.16%  (p=0.000 n=587+594)
Reflect                          975k ± 0%         976k ± 0%  +0.10%  (p=0.000 n=590+594)
Tar                              344k ± 0%         345k ± 0%  +0.24%  (p=0.000 n=595+590)
XML                              422k ± 0%         424k ± 0%  +0.57%  (p=0.000 n=590+589)
LinkCompiler                     538k ± 0%         538k ± 0%  -0.00%  (p=0.045 n=592+587)
ExternalLinkCompiler             593k ± 0%         593k ± 0%    ~     (p=0.171 n=588+587)
LinkWithoutDebugCompiler         172k ± 0%         172k ± 0%    ~     (p=0.996 n=590+585)
[Geo mean]                       685k              685k       -0.02%

name                      old maxRSS/op     new maxRSS/op     delta
Template                        53.7M ± 8%        53.8M ± 8%    ~     (p=0.666 n=576+574)
Unicode                         54.4M ±12%        55.0M ±10%  +1.15%  (p=0.000 n=591+588)
GoTypes                         95.1M ± 4%        95.1M ± 4%    ~     (p=0.948 n=589+591)
Compiler                         334M ± 6%         334M ± 6%    ~     (p=0.875 n=592+593)
SSA                              792M ± 5%         791M ± 5%    ~     (p=0.067 n=592+591)
Flate                           39.9M ±11%        40.0M ±10%    ~     (p=0.131 n=596+596)
GoParser                        45.2M ±11%        45.3M ±11%    ~     (p=0.353 n=592+590)
Reflect                         76.1M ± 5%        76.2M ± 5%    ~     (p=0.114 n=594+594)
Tar                             49.4M ±10%        49.6M ± 9%  +0.57%  (p=0.015 n=590+593)
XML                             57.4M ± 9%        57.7M ± 8%  +0.67%  (p=0.000 n=592+580)
LinkCompiler                     183M ± 2%         183M ± 2%    ~     (p=0.229 n=587+591)
ExternalLinkCompiler             187M ± 2%         187M ± 3%    ~     (p=0.362 n=571+562)
LinkWithoutDebugCompiler         143M ± 3%         143M ± 3%    ~     (p=0.350 n=584+586)
[Geo mean]                       103M              103M       +0.23%

Passes toolstash-check.

Fixes #4617.

Change-Id: Id4f6759b4afc5e002770091d0d4f6e272ee6cbdd
Reviewed-on: https://go-review.googlesource.com/c/go/+/272654
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-11-25 00:46:44 +00:00
Russ Cox
6826287c6b [dev.regabi] cmd/compile: replace evconst with non-mutating version
evconst is one of the largest sources of Op rewrites,
which prevent separating different kinds of nodes
(in this case, arithmetic nodes and OLITERAL nodes).

The change in swt.go is necessary because otherwise
the syntax graph ends up containing that OLEN expression
multiple times, which violates the invariant that it's a tree
except for ONAME, OLITERAL, and OTYPE nodes.
(Before, the OLEN was overwritten by an OLITERAL, so the
invariant still held, but now that we don't overwrite it,
we need a different copy for each instance.)

Passes toolstash -cmp.

Change-Id: Ia004774ab6852fb384805d0f9f9f234b40842811
Reviewed-on: https://go-review.googlesource.com/c/go/+/272869
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-24 20:58:42 +00:00
Russ Cox
c22bc745c3 [dev.regabi] cmd/compile: delete n.List after collapsing OADDSTR to OLITERAL
The leftover n.List is clearly unnecessary, but it makes the
inlining cost of the expression unnecessarily high.

This change breaks toolstash -cmp:

	# cmd/internal/src
	toolstash: compiler output differs, with optimizers disabled (-N)

	inconsistent log line:
	/tmp/go-build866291351/b230/_pkg_.a.log:77:
		/Users/rsc/go/src/cmd/internal/src/pos.go:275:6: can inline (*PosBase).SymFilename with cost 9 as: method(*PosBase) func() string { if b != nil { return b.symFilename }; return "gofile..??" }
	/tmp/go-build866291351/b230/_pkg_.a.stash.log:77:
		/Users/rsc/go/src/cmd/internal/src/pos.go:275:6: can inline (*PosBase).SymFilename with cost 11 as: method(*PosBase) func() string { if b != nil { return b.symFilename }; return "gofile..??" }

Separated from other constant work so that the bigger CL can pass toolstash -cmp.

Change-Id: I5c7ddbc8373207b5b9824eafb8639488da0ca1b7
Reviewed-on: https://go-review.googlesource.com/c/go/+/272868
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-24 20:58:36 +00:00
Russ Cox
ee6132a698 [dev.regabi] cmd/compile: introduce OMETHEXPR instead of overloading ONAME
A method expression today is an ONAME that has none of the
invariants or properties of other ONAMEs and is always a special case
(hence the Node.IsMethodExpression method).
Remove the special cases by making a separate Op.

Passes toolstash -cmp.

Change-Id: I7667693c9155d5486a6924dbf75ebb59891c4afc
Reviewed-on: https://go-review.googlesource.com/c/go/+/272867
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-24 20:58:29 +00:00
Russ Cox
4f9d54e41d [dev.regabi] cmd/compile: add OMETHEXPR
This CL is obviously OK but does not pass toolstash -cmp,
because it renumbers the Op codes. In a separate CL so that
we can use toolstash -cmp on the CL with real changes
related to OMETHEXPR.

Change-Id: I1db978e3f2652b3bdf51f7981a3ba5137641c8c7
Reviewed-on: https://go-review.googlesource.com/c/go/+/272866
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-24 20:58:19 +00:00
Russ Cox
fd11a32c92 [dev.regabi] cmd/compile: clean up Node.Func
The original meaning of type Func was "extra fields factored out
of a few cases of type Node having to do with functions",
but those specific cases didn't necessarily have any relation.
A typical declared function is represented by an ODCLFUNC Node
at its declaration and an ONAME node at its uses, and both those
have a .Func field, but they are *different* Funcs.
Similarly, a closure is represented both by an OCLOSURE Node for
the value itself and an ODCLFUNC Node for the underlying function
implementing the closure. Those too have *different* Funcs,
and the Func.Closure field in one points to the other and vice versa.
This has led to no end of confusion over the years.

This CL elevates type Func to be the canonical identifier for
a given Go function.

This looks like a trivial CL but in fact is the result of a lot of
scaffolding and rewriting, discarded once the result was achieved, to
separate out the three different kinds of Func nodes into three
separate fields, limited in use to each specific Node type, to
understand which Func fields are used by which Node types and what the
possible overlaps are. There were a few overlaps, most notably around
closures, which led to more fields being added to type Func to keep
them separate even though there is now a single Func instead of two
different ones for each function.

A future CL can and should change Curfn to be a *Func instead of
a *Node, finally eliminating the confusion about whether Curfn
is an ODCLFUNC node (as it is most of the time) or an ONAME node
(as it is when type-checking an inlined function body).

Although sizeof_test.go makes it look like Func is growing by two
words, there are now half as many Funcs in a running compilation,
so the memory footprint has actually been reduced substantially.

Change-Id: I598bd96c95728093dc769a835d48f2154a406a61
Reviewed-on: https://go-review.googlesource.com/c/go/+/272253
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-24 20:58:11 +00:00
Russ Cox
8e2106327c [dev.regabi] cmd/compile: clean up tests to know less about Node
We want to refactor a bit, and these tests know too much about
the layout of Nodes. Use standard constructors instead.

Change-Id: I91f0325c89ea60086655414468c53419ebeacea4
Reviewed-on: https://go-review.googlesource.com/c/go/+/272626
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
2020-11-24 20:05:54 +00:00
Matthew Dempsky
742c05e3bc [dev.regabi] cmd/compile: prep refactoring for switching to go/constant
This CL replaces gc.Ctype (along with its CTINT, etc. constants) with
constant.Kind; renames Val.Ctype to Val.Kind; and replaces a handful
of abstraction-violating patterns that can be readily expressed
differently.

The next commit will actually replace Val with constant.Value.

Passes toolstash-check.

[git-generate]
cd src/cmd/compile/internal/gc

sed -i 's/type Ctype uint8/type Ctype = constant.Kind/' const.go
goimports -w const.go

rf '
inline -rm Ctype
mv Val.Ctype Val.Kind

ex import "go/constant"; \
  CTxxx  -> constant.Unknown; \
  CTINT  -> constant.Int; \
  CTFLT  -> constant.Float; \
  CTCPLX -> constant.Complex; \
  CTBOOL -> constant.Bool; \
  CTSTR  -> constant.String

rm CTxxx CTINT CTFLT CTCPLX CTBOOL CTSTR

ex import "cmd/compile/internal/types"; \
  var t *types.Type; \
  var v, v2 Val; \
  v.U.(*Mpint).Cmp(maxintval[TINT]) > 0 -> doesoverflow(v, types.Types[TINT]); \
  v.U.(*Mpint).Cmp(v2.U.(*Mpint)) > 0 -> compareOp(v, OGT, v2); \
  maxintval[t.Etype].Cmp(maxintval[TUINT]) <= 0 -> t.Size() <= types.Types[TUINT].Size(); \
  maxintval[t.Etype].Cmp(maxintval[TUINT]) >  0 -> t.Size() >  types.Types[TUINT].Size();
'

go test cmd/compile -u

Change-Id: I6c22ec0597508845f88eee639a0d76cbaa66d08f
Reviewed-on: https://go-review.googlesource.com/c/go/+/272653
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-11-24 19:50:09 +00:00
Matthew Dempsky
015423a15b [dev.regabi] strconv: add to bootstrap packages
go/constant relies on strconv for parsing Go literals, while older
versions of strconv either lack recent Go language features (e.g., Go
1.13's new numeric literals) or have errors (e.g., mishandling of
carriage returns in raw string literals prior to Go 1.8).

This requires two changes:

1. Splitting out the internal/bytealg dependency into a separate file,
which can be easily substituted with a simple loop for bootstrap
builds.

2. Updating eisel_lemire.go to not utilize Go 1.13 functionality
(underscores in numeric literals and signed shift counts).

Change-Id: Ib48a858a03b155eebdcd08d577aec2254337e70e
Reviewed-on: https://go-review.googlesource.com/c/go/+/272749
Reviewed-by: Robert Griesemer <gri@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-11-24 19:42:42 +00:00
Matthew Dempsky
c767d73227 [dev.regabi] cmd/compile: remove CTRUNE
Since CL 255217, we've been able to rely on types.UntypedRune to
identify untyped rune literals, rather than needing Mpint.Rune /
CTRUNE. This makes way for switching to using go/constant, which
doesn't have a separate notion of rune constants distinct from integer
constants.

Passes toolstash-check.

Change-Id: I319861f4758aeea17345c101b167cb307e706a0e
Reviewed-on: https://go-review.googlesource.com/c/go/+/272652
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-11-24 19:41:59 +00:00
Matthew Dempsky
6dae48fb0b [dev.regabi] cmd/compile: refactor type/value assertions
Small refactoring to make subsequent CLs clearer.

Passes toolstash-check.

Change-Id: I1a6ae599f491220d44aaabae0b7bed4aff46ee92
Reviewed-on: https://go-review.googlesource.com/c/go/+/272651
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-11-24 19:35:46 +00:00
Matthew Dempsky
88a9e2f9ad [dev.regabi] cmd/compile: replace CTNIL with ONIL
Properly speaking, "nil" is a zero value, not a constant. So
go/constant does not have a representation for it. To allow replacing
Val with constant.Value, we split out ONIL separately from OLITERAL so
we can get rid of CTNIL.

Passes toolstash-check.

Change-Id: I4c8e60cae3b3c91bbac43b3b0cf2a4ade028d6cb
Reviewed-on: https://go-review.googlesource.com/c/go/+/272650
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-11-24 19:34:23 +00:00
Matthew Dempsky
4af2decf30 [dev.regabi] cmd/compile: add (unused) ONIL constant
Subsequent CL will make use of ONIL. Split out separately so that the
next CL can pass toolstash-check.

Change-Id: I49d77bedbe2cac4a5da149c925cda969e50b0b2d
Reviewed-on: https://go-review.googlesource.com/c/go/+/272649
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-11-24 19:33:02 +00:00
Matthew Dempsky
668e3a598f [dev.regabi] cmd/compile: cleanup type switch typechecking
Address outstanding TODO, which simplifies subsequent CLs.

Now the compiler always type checks type-switch case clauses (like
gccgo), but it treats clause variables as broken if an appropriate
type cannot be determined for it (like go/types).

Passes toolstash-check.

Change-Id: Iedfe9cdf38c6865211e4b93391f1cf72c1bed136
Reviewed-on: https://go-review.googlesource.com/c/go/+/272648
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
2020-11-24 19:32:42 +00:00
Matthew Dempsky
96f3fb7244 [dev.regabi] go/constant: avoid heap allocations in match
When type switching from interface{} to T, and then returning the T as
interface{} again, it's better to return the original interface{}
value. This avoids needing to heap allocate the T for
non-pointer-shaped types (i.e., int64Val, complexVal, stringVal).

Change-Id: I25c83b3f9ec9bd2ffeec5a65279b68f4fcef8a19
Reviewed-on: https://go-review.googlesource.com/c/go/+/272647
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2020-11-24 19:17:49 +00:00
Matthew Dempsky
1abb12fc97 [dev.regabi] go/constant: optimize BitLen
Avoids an unnecessary heap allocation when computing the bit length of
int64 values.

Change-Id: I69dfc510e461daf3e83b0b7b6c0707f6526a32d0
Reviewed-on: https://go-review.googlesource.com/c/go/+/272646
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2020-11-24 19:17:41 +00:00
Russ Cox
228b732ad9 [dev.regabi] cmd/compile: prepare for package ir
The next CL will introduce a package ir to hold the IR definitions.
This CL adjusts a few names and makes a few other minor changes
to make the next CL - an automated one - smoother.

Change-Id: Ie787a34732efd5b3d171bf0c1220b6dd91994ce3
Reviewed-on: https://go-review.googlesource.com/c/go/+/272251
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-24 15:07:48 +00:00
Russ Cox
e37597f7f0 [dev.regabi] cmd/compile: rename a few 'base' identifiers
We want to introduce a package cmd/compile/internal/base,
and these will shadow it at points where it is needed.

Change-Id: Ic936733fba1ccba8c2ca1fdedbd4d2989df4bbf4
Reviewed-on: https://go-review.googlesource.com/c/go/+/272249
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-24 15:07:30 +00:00
Russ Cox
357c576878 [dev.regabi] cmd/compile: clean up error API
Prepare for factoring the error API out of this package by
cleaning it up. The doc comments use the intended new names,
which will be introduced in the next CL.

Change-Id: Ie4c8d4262422da32a9a9f750fda42c225b6b42a8
Reviewed-on: https://go-review.googlesource.com/c/go/+/272248
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-24 15:07:09 +00:00
Russ Cox
5fd949e4bd [dev.regabi] cmd/compile: initialize importMap lazily
This sets up the next CL, moving importMap to a global zeroed struct.

Change-Id: I1acc91b440d3da6e28fb32bd275fb3cd36db4e97
Reviewed-on: https://go-review.googlesource.com/c/go/+/272046
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-24 14:58:20 +00:00
Russ Cox
7b144ed4f7 [dev.regabi] cmd/compile: rewrite concurrentFlagOk to be clearer
The current implementation copies Debug, clears a bunch of flags
that are meant to be considered OK, and then checks the result
against the zero value. But more flags are cleared than remain:
it's easier to write and to understand to just check the ones that
need checking.

This phrasing also makes it safe to move more flags into the struct.

It turns out that some of the flags being checked should probably
not be checked, but this CL is meant to be a strict semantic no-op,
so left a TODO to clean up the function a bit more later.

Change-Id: I7afe6d7b32b5b889c40dd339568e8602e02df9bc
Reviewed-on: https://go-review.googlesource.com/c/go/+/271666
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2020-11-24 14:58:10 +00:00
Matthew Dempsky
c754f25241 [dev.regabi] cmd/compile/internal/types: remove Func.Nname
Now that there's no code remaining that uses Func.Nname, we can get
rid of it along with the remaining code that uselessly assigns to it.

Passes toolstash-check.

Change-Id: I104ab3bb5122fb824c741bc6e4d9d54fefe5646e
Reviewed-on: https://go-review.googlesource.com/c/go/+/272390
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2020-11-24 01:34:39 +00:00
Matthew Dempsky
c50c7a8c06 [dev.regabi] cmd/compile/internal/gc: refactor to use stop using Func.Nname
Automated factoring produced by rf script below to replace uses of
Func.Nname with Field.Nname or Node.MethodName as appropriate.

Some dead assignments to Func.Nname are left behind; these will be
removed in a subequent remove-only CL.

Passes toolstash-check.

[git-generate]
cd src/cmd/compile/internal/gc
rf '
ex \
  import "cmd/compile/internal/types"; \
  var f *types.Field; \
  var n *types.Node; \
  f.Type.Nname() -> f.Nname; \
  f.Type.SetNname(n) -> f.Nname = n; \
  f.Type.FuncType().Nname -> f.Nname

ex \
  var n *Node; \
  asNode(n.Type.Nname()) -> n.MethodName(); \
  asNode(n.Type.FuncType().Nname) -> n.MethodName(); \
  asNode(callpartMethod(n).Type.Nname()) -> n.MethodName()
'

Change-Id: Iaae054324dfe7da6f5d8b8d57a1e05b58cc5968c
Reviewed-on: https://go-review.googlesource.com/c/go/+/272389
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2020-11-24 01:34:32 +00:00
Matthew Dempsky
d5928847de [dev.regabi] cmd/compile/internal/gc: prep for Func.Nname removal refactoring
There are three bits of method-handling code where we separately go
from Field->Type and then Type->Node. By shuffling the code around a
little to go Field->Type->Node in a single statement, we're able to
more easily remove Type from the operation.

Passes toolstash-check.

Change-Id: Ife98216d70d3b867fa153449abef0e56a4fb242a
Reviewed-on: https://go-review.googlesource.com/c/go/+/272388
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2020-11-24 01:34:20 +00:00
Matthew Dempsky
b30c7a8044 [dev.regabi] cmd/compile/internal/gc: add MethodName for getting referenced method
A common operation throughout the front end is getting the ONAME for a
method used in a method selector, method expression, or method value.
This CL adds MethodName as a uniform API for doing this for all of
these kinds of nodes.

For method selectors (ODOTMETH) and method expressions (ONAMEs where
isMethodExpression reports true), we take advantage of the Node.Opt
field to save the types.Field. This is the approach we already started
taking in golang.org/cl/271217 (caching types.Field in Node.Opt for
ODOT).

For method values (OCALLPART), we continue using the existing
callpartMethod helper function. Escape analysis already uses Node.Opt
for tracking the method value's closure's data flow.

A subsequent, automated refactoring CL will make more use of this
method. For now, we just address a few cases in inl.go that aren't
easily automated.

Passes toolstash-check.

Change-Id: Ic92b288b2d8b2fa7e18e3b68634326b8ef0d869b
Reviewed-on: https://go-review.googlesource.com/c/go/+/272387
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2020-11-24 01:34:13 +00:00
Matthew Dempsky
e1047302bd [dev.regabi] cmd/compile/internal/types: add pos/sym/typ params to NewField
These are almost always set, so might as well expect callers to
provide them. They're also all required by go/types's corresponding
New{Field,Func,Param,Var} functions, so this eases API compatibility.

Passes toolstash-check.

Change-Id: Ib3fa355d4961243cd285b41915e87652ae2c22f6
Reviewed-on: https://go-review.googlesource.com/c/go/+/272386
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2020-11-24 01:33:25 +00:00
511 changed files with 51253 additions and 45083 deletions

View File

@@ -3,7 +3,7 @@
Go is an open source programming language that makes it easy to build simple,
reliable, and efficient software.
![Gopher image](doc/gopher/fiveyears.jpg)
![Gopher image](https://golang.org/doc/gopher/fiveyears.jpg)
*Gopher image by [Renee French][rf], licensed under [Creative Commons 3.0 Attributions license][cc3-by].*
Our canonical Git repository is located at https://go.googlesource.com/go.

2
codereview.cfg Normal file
View File

@@ -0,0 +1,2 @@
branch: dev.regabi
parent-branch: master

View File

@@ -109,6 +109,10 @@ func archX86(linkArch *obj.LinkArch) *Arch {
register["SB"] = RSB
register["FP"] = RFP
register["PC"] = RPC
if linkArch == &x86.Linkamd64 {
// Alias g to R14
register["g"] = x86.REGG
}
// Register prefix not used on this architecture.
instructions := make(map[string]obj.As)

View File

@@ -259,6 +259,7 @@ var amd64OperandTests = []operandTest{
{"R15", "R15"},
{"R8", "R8"},
{"R9", "R9"},
{"g", "R14"},
{"SI", "SI"},
{"SP", "SP"},
{"X0", "X0"},

View File

@@ -305,7 +305,7 @@ func (p *Parser) pseudo(word string, operands [][]lex.Token) bool {
// references and writes symabis information to w.
//
// The symabis format is documented at
// cmd/compile/internal/gc.readSymABIs.
// cmd/compile/internal/ssagen.ReadSymABIs.
func (p *Parser) symDefRef(w io.Writer, word string, operands [][]lex.Token) {
switch word {
case "TEXT":

View File

@@ -1,599 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements TestFormats; a test that verifies
// format strings in the compiler (this directory and all
// subdirectories, recursively).
//
// TestFormats finds potential (Printf, etc.) format strings.
// If they are used in a call, the format verbs are verified
// based on the matching argument type against a precomputed
// map of valid formats (knownFormats). This map can be used to
// automatically rewrite format strings across all compiler
// files with the -r flag.
//
// The format map needs to be updated whenever a new (type,
// format) combination is found and the format verb is not
// 'v' or 'T' (as in "%v" or "%T"). To update the map auto-
// matically from the compiler source's use of format strings,
// use the -u flag. (Whether formats are valid for the values
// to be formatted must be verified manually, of course.)
//
// The -v flag prints out the names of all functions called
// with a format string, the names of files that were not
// processed, and any format rewrites made (with -r).
//
// Run as: go test -run Formats [-r][-u][-v]
//
// Known shortcomings:
// - indexed format strings ("%[2]s", etc.) are not supported
// (the test will fail)
// - format strings that are not simple string literals cannot
// be updated automatically
// (the test will fail with respective warnings)
// - format strings in _test packages outside the current
// package are not processed
// (the test will report those files)
//
package main_test
import (
"bytes"
"flag"
"fmt"
"go/ast"
"go/build"
"go/constant"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"internal/testenv"
"io"
"io/fs"
"io/ioutil"
"log"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"testing"
"unicode/utf8"
)
var (
rewrite = flag.Bool("r", false, "rewrite format strings")
update = flag.Bool("u", false, "update known formats")
)
// The following variables collect information across all processed files.
var (
fset = token.NewFileSet()
formatStrings = make(map[*ast.BasicLit]bool) // set of all potential format strings found
foundFormats = make(map[string]bool) // set of all formats found
callSites = make(map[*ast.CallExpr]*callSite) // map of all calls
)
// A File is a corresponding (filename, ast) pair.
type File struct {
name string
ast *ast.File
}
func TestFormats(t *testing.T) {
if testing.Short() && testenv.Builder() == "" {
t.Skip("Skipping in short mode")
}
testenv.MustHaveGoBuild(t) // more restrictive than necessary, but that's ok
// process all directories
filepath.WalkDir(".", func(path string, info fs.DirEntry, err error) error {
if info.IsDir() {
if info.Name() == "testdata" {
return filepath.SkipDir
}
importPath := filepath.Join("cmd/compile", path)
if ignoredPackages[filepath.ToSlash(importPath)] {
return filepath.SkipDir
}
pkg, err := build.Import(importPath, path, 0)
if err != nil {
if _, ok := err.(*build.NoGoError); ok {
return nil // nothing to do here
}
t.Fatal(err)
}
collectPkgFormats(t, pkg)
}
return nil
})
// test and rewrite formats
updatedFiles := make(map[string]File) // files that were rewritten
for _, p := range callSites {
// test current format literal and determine updated one
out := formatReplace(p.str, func(index int, in string) string {
if in == "*" {
return in // cannot rewrite '*' (as in "%*d")
}
// in != '*'
typ := p.types[index]
format := typ + " " + in // e.g., "*Node %n"
// check if format is known
out, known := knownFormats[format]
// record format if not yet found
_, found := foundFormats[format]
if !found {
foundFormats[format] = true
}
// report an error if the format is unknown and this is the first
// time we see it; ignore "%v" and "%T" which are always valid
if !known && !found && in != "%v" && in != "%T" {
t.Errorf("%s: unknown format %q for %s argument", posString(p.arg), in, typ)
}
if out == "" {
out = in
}
return out
})
// replace existing format literal if it changed
if out != p.str {
// we cannot replace the argument if it's not a string literal for now
// (e.g., it may be "foo" + "bar")
lit, ok := p.arg.(*ast.BasicLit)
if !ok {
delete(callSites, p.call) // treat as if we hadn't found this site
continue
}
if testing.Verbose() {
fmt.Printf("%s:\n\t- %q\n\t+ %q\n", posString(p.arg), p.str, out)
}
// find argument index of format argument
index := -1
for i, arg := range p.call.Args {
if p.arg == arg {
index = i
break
}
}
if index < 0 {
// we may have processed the same call site twice,
// but that shouldn't happen
panic("internal error: matching argument not found")
}
// replace literal
new := *lit // make a copy
new.Value = strconv.Quote(out) // this may introduce "-quotes where there were `-quotes
p.call.Args[index] = &new
updatedFiles[p.file.name] = p.file
}
}
// write dirty files back
var filesUpdated bool
if len(updatedFiles) > 0 && *rewrite {
for _, file := range updatedFiles {
var buf bytes.Buffer
if err := format.Node(&buf, fset, file.ast); err != nil {
t.Errorf("WARNING: gofmt %s failed: %v", file.name, err)
continue
}
if err := ioutil.WriteFile(file.name, buf.Bytes(), 0x666); err != nil {
t.Errorf("WARNING: writing %s failed: %v", file.name, err)
continue
}
fmt.Printf("updated %s\n", file.name)
filesUpdated = true
}
}
// report the names of all functions called with a format string
if len(callSites) > 0 && testing.Verbose() {
set := make(map[string]bool)
for _, p := range callSites {
set[nodeString(p.call.Fun)] = true
}
var list []string
for s := range set {
list = append(list, s)
}
fmt.Println("\nFunctions called with a format string")
writeList(os.Stdout, list)
}
// update formats
if len(foundFormats) > 0 && *update {
var list []string
for s := range foundFormats {
list = append(list, fmt.Sprintf("%q: \"\",", s))
}
var buf bytes.Buffer
buf.WriteString(knownFormatsHeader)
writeList(&buf, list)
buf.WriteString("}\n")
out, err := format.Source(buf.Bytes())
const outfile = "fmtmap_test.go"
if err != nil {
t.Errorf("WARNING: gofmt %s failed: %v", outfile, err)
out = buf.Bytes() // continue with unformatted source
}
if err = ioutil.WriteFile(outfile, out, 0644); err != nil {
t.Errorf("WARNING: updating format map failed: %v", err)
}
}
// check that knownFormats is up to date
if !*rewrite && !*update {
var mismatch bool
for s := range foundFormats {
if _, ok := knownFormats[s]; !ok {
mismatch = true
break
}
}
if !mismatch {
for s := range knownFormats {
if _, ok := foundFormats[s]; !ok {
mismatch = true
break
}
}
}
if mismatch {
t.Errorf("format map is out of date; run 'go test -u' to update and manually verify correctness of change'")
}
}
// all format strings of calls must be in the formatStrings set (self-verification)
for _, p := range callSites {
if lit, ok := p.arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
if formatStrings[lit] {
// ok
delete(formatStrings, lit)
} else {
// this should never happen
panic(fmt.Sprintf("internal error: format string not found (%s)", posString(lit)))
}
}
}
// if we have any strings left, we may need to update them manually
if len(formatStrings) > 0 && filesUpdated {
var list []string
for lit := range formatStrings {
list = append(list, fmt.Sprintf("%s: %s", posString(lit), nodeString(lit)))
}
fmt.Println("\nWARNING: Potentially missed format strings")
writeList(os.Stdout, list)
t.Fail()
}
fmt.Println()
}
// A callSite describes a function call that appears to contain
// a format string.
type callSite struct {
file File
call *ast.CallExpr // call containing the format string
arg ast.Expr // format argument (string literal or constant)
str string // unquoted format string
types []string // argument types
}
func collectPkgFormats(t *testing.T, pkg *build.Package) {
// collect all files
var filenames []string
filenames = append(filenames, pkg.GoFiles...)
filenames = append(filenames, pkg.CgoFiles...)
filenames = append(filenames, pkg.TestGoFiles...)
// TODO(gri) verify _test files outside package
for _, name := range pkg.XTestGoFiles {
// don't process this test itself
if name != "fmt_test.go" && testing.Verbose() {
fmt.Printf("WARNING: %s not processed\n", filepath.Join(pkg.Dir, name))
}
}
// make filenames relative to .
for i, name := range filenames {
filenames[i] = filepath.Join(pkg.Dir, name)
}
// parse all files
files := make([]*ast.File, len(filenames))
for i, filename := range filenames {
f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
if err != nil {
t.Fatal(err)
}
files[i] = f
}
// typecheck package
conf := types.Config{Importer: importer.Default()}
etypes := make(map[ast.Expr]types.TypeAndValue)
if _, err := conf.Check(pkg.ImportPath, fset, files, &types.Info{Types: etypes}); err != nil {
t.Fatal(err)
}
// collect all potential format strings (for extra verification later)
for _, file := range files {
ast.Inspect(file, func(n ast.Node) bool {
if s, ok := stringLit(n); ok && isFormat(s) {
formatStrings[n.(*ast.BasicLit)] = true
}
return true
})
}
// collect all formats/arguments of calls with format strings
for index, file := range files {
ast.Inspect(file, func(n ast.Node) bool {
if call, ok := n.(*ast.CallExpr); ok {
if ignoredFunctions[nodeString(call.Fun)] {
return true
}
// look for an arguments that might be a format string
for i, arg := range call.Args {
if s, ok := stringVal(etypes[arg]); ok && isFormat(s) {
// make sure we have enough arguments
n := numFormatArgs(s)
if i+1+n > len(call.Args) {
t.Errorf("%s: not enough format args (ignore %s?)", posString(call), nodeString(call.Fun))
break // ignore this call
}
// assume last n arguments are to be formatted;
// determine their types
argTypes := make([]string, n)
for i, arg := range call.Args[len(call.Args)-n:] {
if tv, ok := etypes[arg]; ok {
argTypes[i] = typeString(tv.Type)
}
}
// collect call site
if callSites[call] != nil {
panic("internal error: file processed twice?")
}
callSites[call] = &callSite{
file: File{filenames[index], file},
call: call,
arg: arg,
str: s,
types: argTypes,
}
break // at most one format per argument list
}
}
}
return true
})
}
}
// writeList writes list in sorted order to w.
func writeList(w io.Writer, list []string) {
sort.Strings(list)
for _, s := range list {
fmt.Fprintln(w, "\t", s)
}
}
// posString returns a string representation of n's position
// in the form filename:line:col: .
func posString(n ast.Node) string {
if n == nil {
return ""
}
return fset.Position(n.Pos()).String()
}
// nodeString returns a string representation of n.
func nodeString(n ast.Node) string {
var buf bytes.Buffer
if err := format.Node(&buf, fset, n); err != nil {
log.Fatal(err) // should always succeed
}
return buf.String()
}
// typeString returns a string representation of n.
func typeString(typ types.Type) string {
return filepath.ToSlash(typ.String())
}
// stringLit returns the unquoted string value and true if
// n represents a string literal; otherwise it returns ""
// and false.
func stringLit(n ast.Node) (string, bool) {
if lit, ok := n.(*ast.BasicLit); ok && lit.Kind == token.STRING {
s, err := strconv.Unquote(lit.Value)
if err != nil {
log.Fatal(err) // should not happen with correct ASTs
}
return s, true
}
return "", false
}
// stringVal returns the (unquoted) string value and true if
// tv is a string constant; otherwise it returns "" and false.
func stringVal(tv types.TypeAndValue) (string, bool) {
if tv.IsValue() && tv.Value != nil && tv.Value.Kind() == constant.String {
return constant.StringVal(tv.Value), true
}
return "", false
}
// formatIter iterates through the string s in increasing
// index order and calls f for each format specifier '%..v'.
// The arguments for f describe the specifier's index range.
// If a format specifier contains a "*", f is called with
// the index range for "*" alone, before being called for
// the entire specifier. The result of f is the index of
// the rune at which iteration continues.
func formatIter(s string, f func(i, j int) int) {
i := 0 // index after current rune
var r rune // current rune
next := func() {
r1, w := utf8.DecodeRuneInString(s[i:])
if w == 0 {
r1 = -1 // signal end-of-string
}
r = r1
i += w
}
flags := func() {
for r == ' ' || r == '#' || r == '+' || r == '-' || r == '0' {
next()
}
}
index := func() {
if r == '[' {
log.Fatalf("cannot handle indexed arguments: %s", s)
}
}
digits := func() {
index()
if r == '*' {
i = f(i-1, i)
next()
return
}
for '0' <= r && r <= '9' {
next()
}
}
for next(); r >= 0; next() {
if r == '%' {
i0 := i
next()
flags()
digits()
if r == '.' {
next()
digits()
}
index()
// accept any letter (a-z, A-Z) as format verb;
// ignore anything else
if 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' {
i = f(i0-1, i)
}
}
}
}
// isFormat reports whether s contains format specifiers.
func isFormat(s string) (yes bool) {
formatIter(s, func(i, j int) int {
yes = true
return len(s) // stop iteration
})
return
}
// oneFormat reports whether s is exactly one format specifier.
func oneFormat(s string) (yes bool) {
formatIter(s, func(i, j int) int {
yes = i == 0 && j == len(s)
return j
})
return
}
// numFormatArgs returns the number of format specifiers in s.
func numFormatArgs(s string) int {
count := 0
formatIter(s, func(i, j int) int {
count++
return j
})
return count
}
// formatReplace replaces the i'th format specifier s in the incoming
// string in with the result of f(i, s) and returns the new string.
func formatReplace(in string, f func(i int, s string) string) string {
var buf []byte
i0 := 0
index := 0
formatIter(in, func(i, j int) int {
if sub := in[i:j]; sub != "*" { // ignore calls for "*" width/length specifiers
buf = append(buf, in[i0:i]...)
buf = append(buf, f(index, sub)...)
i0 = j
}
index++
return j
})
return string(append(buf, in[i0:]...))
}
// ignoredPackages is the set of packages which can
// be ignored.
var ignoredPackages = map[string]bool{}
// ignoredFunctions is the set of functions which may have
// format-like arguments but which don't do any formatting and
// thus may be ignored.
var ignoredFunctions = map[string]bool{}
func init() {
// verify that knownFormats entries are correctly formatted
for key, val := range knownFormats {
// key must be "typename format", and format starts with a '%'
// (formats containing '*' alone are not collected in this map)
i := strings.Index(key, "%")
if i < 0 || !oneFormat(key[i:]) {
log.Fatalf("incorrect knownFormats key: %q", key)
}
// val must be "format" or ""
if val != "" && !oneFormat(val) {
log.Fatalf("incorrect knownFormats value: %q (key = %q)", val, key)
}
}
}
const knownFormatsHeader = `// 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.
// This file implements the knownFormats map which records the valid
// formats for a given type. The valid formats must correspond to
// supported compiler formats implemented in fmt.go, or whatever
// other format verbs are implemented for the given type. The map may
// also be used to change the use of a format verb across all compiler
// sources automatically (for instance, if the implementation of fmt.go
// changes), by using the -r option together with the new formats in the
// map. To generate this file automatically from the existing source,
// run: go test -run Formats -u.
//
// See the package comment in fmt_test.go for additional information.
package main_test
// knownFormats entries are of the form "typename format" -> "newformat".
// An absent entry means that the format is not recognized as valid.
// An empty new format means that the format should remain unchanged.
var knownFormats = map[string]string{
`

View File

@@ -1,211 +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.
// This file implements the knownFormats map which records the valid
// formats for a given type. The valid formats must correspond to
// supported compiler formats implemented in fmt.go, or whatever
// other format verbs are implemented for the given type. The map may
// also be used to change the use of a format verb across all compiler
// sources automatically (for instance, if the implementation of fmt.go
// changes), by using the -r option together with the new formats in the
// map. To generate this file automatically from the existing source,
// run: go test -run Formats -u.
//
// See the package comment in fmt_test.go for additional information.
package main_test
// knownFormats entries are of the form "typename format" -> "newformat".
// An absent entry means that the format is not recognized as valid.
// An empty new format means that the format should remain unchanged.
var knownFormats = map[string]string{
"*bytes.Buffer %s": "",
"*cmd/compile/internal/gc.EscLocation %v": "",
"*cmd/compile/internal/gc.Mpflt %v": "",
"*cmd/compile/internal/gc.Mpint %v": "",
"*cmd/compile/internal/gc.Node %#v": "",
"*cmd/compile/internal/gc.Node %+S": "",
"*cmd/compile/internal/gc.Node %+v": "",
"*cmd/compile/internal/gc.Node %L": "",
"*cmd/compile/internal/gc.Node %S": "",
"*cmd/compile/internal/gc.Node %j": "",
"*cmd/compile/internal/gc.Node %p": "",
"*cmd/compile/internal/gc.Node %v": "",
"*cmd/compile/internal/ssa.Block %s": "",
"*cmd/compile/internal/ssa.Block %v": "",
"*cmd/compile/internal/ssa.Func %s": "",
"*cmd/compile/internal/ssa.Func %v": "",
"*cmd/compile/internal/ssa.Register %s": "",
"*cmd/compile/internal/ssa.Register %v": "",
"*cmd/compile/internal/ssa.SparseTreeNode %v": "",
"*cmd/compile/internal/ssa.Value %s": "",
"*cmd/compile/internal/ssa.Value %v": "",
"*cmd/compile/internal/ssa.sparseTreeMapEntry %v": "",
"*cmd/compile/internal/types.Field %p": "",
"*cmd/compile/internal/types.Field %v": "",
"*cmd/compile/internal/types.Sym %0S": "",
"*cmd/compile/internal/types.Sym %S": "",
"*cmd/compile/internal/types.Sym %p": "",
"*cmd/compile/internal/types.Sym %v": "",
"*cmd/compile/internal/types.Type %#L": "",
"*cmd/compile/internal/types.Type %#v": "",
"*cmd/compile/internal/types.Type %+v": "",
"*cmd/compile/internal/types.Type %-S": "",
"*cmd/compile/internal/types.Type %0S": "",
"*cmd/compile/internal/types.Type %L": "",
"*cmd/compile/internal/types.Type %S": "",
"*cmd/compile/internal/types.Type %p": "",
"*cmd/compile/internal/types.Type %s": "",
"*cmd/compile/internal/types.Type %v": "",
"*cmd/internal/obj.Addr %v": "",
"*cmd/internal/obj.LSym %v": "",
"*math/big.Float %f": "",
"*math/big.Int %#x": "",
"*math/big.Int %s": "",
"*math/big.Int %v": "",
"[16]byte %x": "",
"[]*cmd/compile/internal/ssa.Block %v": "",
"[]*cmd/compile/internal/ssa.Value %v": "",
"[][]string %q": "",
"[]byte %s": "",
"[]byte %x": "",
"[]cmd/compile/internal/ssa.Edge %v": "",
"[]cmd/compile/internal/ssa.ID %v": "",
"[]cmd/compile/internal/ssa.posetNode %v": "",
"[]cmd/compile/internal/ssa.posetUndo %v": "",
"[]cmd/compile/internal/syntax.token %s": "",
"[]string %v": "",
"[]uint32 %v": "",
"bool %v": "",
"byte %08b": "",
"byte %c": "",
"byte %q": "",
"byte %v": "",
"cmd/compile/internal/arm.shift %d": "",
"cmd/compile/internal/gc.Class %d": "",
"cmd/compile/internal/gc.Class %s": "",
"cmd/compile/internal/gc.Class %v": "",
"cmd/compile/internal/gc.Ctype %d": "",
"cmd/compile/internal/gc.Ctype %v": "",
"cmd/compile/internal/gc.Nodes %#v": "",
"cmd/compile/internal/gc.Nodes %+v": "",
"cmd/compile/internal/gc.Nodes %.v": "",
"cmd/compile/internal/gc.Nodes %v": "",
"cmd/compile/internal/gc.Op %#v": "",
"cmd/compile/internal/gc.Op %v": "",
"cmd/compile/internal/gc.Val %#v": "",
"cmd/compile/internal/gc.Val %T": "",
"cmd/compile/internal/gc.Val %v": "",
"cmd/compile/internal/gc.fmtMode %d": "",
"cmd/compile/internal/gc.initKind %d": "",
"cmd/compile/internal/gc.itag %v": "",
"cmd/compile/internal/ssa.BranchPrediction %d": "",
"cmd/compile/internal/ssa.Edge %v": "",
"cmd/compile/internal/ssa.GCNode %v": "",
"cmd/compile/internal/ssa.ID %d": "",
"cmd/compile/internal/ssa.ID %v": "",
"cmd/compile/internal/ssa.LocalSlot %s": "",
"cmd/compile/internal/ssa.LocalSlot %v": "",
"cmd/compile/internal/ssa.Location %s": "",
"cmd/compile/internal/ssa.Op %s": "",
"cmd/compile/internal/ssa.Op %v": "",
"cmd/compile/internal/ssa.Sym %v": "",
"cmd/compile/internal/ssa.ValAndOff %s": "",
"cmd/compile/internal/ssa.domain %v": "",
"cmd/compile/internal/ssa.flagConstant %s": "",
"cmd/compile/internal/ssa.posetNode %v": "",
"cmd/compile/internal/ssa.posetTestOp %v": "",
"cmd/compile/internal/ssa.rbrank %d": "",
"cmd/compile/internal/ssa.regMask %d": "",
"cmd/compile/internal/ssa.register %d": "",
"cmd/compile/internal/ssa.relation %s": "",
"cmd/compile/internal/syntax.Error %q": "",
"cmd/compile/internal/syntax.Expr %#v": "",
"cmd/compile/internal/syntax.LitKind %d": "",
"cmd/compile/internal/syntax.Node %T": "",
"cmd/compile/internal/syntax.Operator %s": "",
"cmd/compile/internal/syntax.Pos %s": "",
"cmd/compile/internal/syntax.Pos %v": "",
"cmd/compile/internal/syntax.position %s": "",
"cmd/compile/internal/syntax.token %q": "",
"cmd/compile/internal/syntax.token %s": "",
"cmd/compile/internal/types.EType %d": "",
"cmd/compile/internal/types.EType %s": "",
"cmd/compile/internal/types.EType %v": "",
"cmd/internal/obj.ABI %v": "",
"error %v": "",
"float64 %.2f": "",
"float64 %.3f": "",
"float64 %.6g": "",
"float64 %g": "",
"int %#x": "",
"int %-12d": "",
"int %-6d": "",
"int %-8o": "",
"int %02d": "",
"int %6d": "",
"int %c": "",
"int %d": "",
"int %v": "",
"int %x": "",
"int16 %d": "",
"int16 %x": "",
"int32 %#x": "",
"int32 %d": "",
"int32 %v": "",
"int32 %x": "",
"int64 %#x": "",
"int64 %+d": "",
"int64 %-10d": "",
"int64 %.5d": "",
"int64 %d": "",
"int64 %v": "",
"int64 %x": "",
"int8 %d": "",
"int8 %v": "",
"int8 %x": "",
"interface{} %#v": "",
"interface{} %T": "",
"interface{} %p": "",
"interface{} %q": "",
"interface{} %s": "",
"interface{} %v": "",
"map[*cmd/compile/internal/gc.Node]*cmd/compile/internal/ssa.Value %v": "",
"map[*cmd/compile/internal/gc.Node][]*cmd/compile/internal/gc.Node %v": "",
"map[cmd/compile/internal/ssa.ID]uint32 %v": "",
"map[int64]uint32 %v": "",
"math/big.Accuracy %s": "",
"reflect.Type %s": "",
"rune %#U": "",
"rune %c": "",
"rune %q": "",
"string %-*s": "",
"string %-16s": "",
"string %-6s": "",
"string %q": "",
"string %s": "",
"string %v": "",
"time.Duration %d": "",
"time.Duration %v": "",
"uint %04x": "",
"uint %5d": "",
"uint %d": "",
"uint %x": "",
"uint16 %d": "",
"uint16 %x": "",
"uint32 %#U": "",
"uint32 %#x": "",
"uint32 %d": "",
"uint32 %v": "",
"uint32 %x": "",
"uint64 %08x": "",
"uint64 %b": "",
"uint64 %d": "",
"uint64 %x": "",
"uint8 %#x": "",
"uint8 %d": "",
"uint8 %v": "",
"uint8 %x": "",
"uintptr %d": "",
}

View File

@@ -0,0 +1,628 @@
# Go internal ABI specification
This document describes Gos internal application binary interface
(ABI), known as ABIInternal.
Go's ABI defines the layout of data in memory and the conventions for
calling between Go functions.
This ABI is *unstable* and will change between Go versions.
If youre writing assembly code, please instead refer to Gos
[assembly documentation](/doc/asm.html), which describes Gos stable
ABI, known as ABI0.
All functions defined in Go source follow ABIInternal.
However, ABIInternal and ABI0 functions are able to call each other
through transparent *ABI wrappers*, described in the [internal calling
convention proposal](https://golang.org/design/27539-internal-abi).
Go uses a common ABI design across all architectures.
We first describe the common ABI, and then cover per-architecture
specifics.
*Rationale*: For the reasoning behind using a common ABI across
architectures instead of the platform ABI, see the [register-based Go
calling convention proposal](https://golang.org/design/40724-register-calling).
## Memory layout
Go's built-in types have the following sizes and alignments.
Many, though not all, of these sizes are guaranteed by the [language
specification](/doc/go_spec.html#Size_and_alignment_guarantees).
Those that aren't guaranteed may change in future versions of Go (for
example, we've considered changing the alignment of int64 on 32-bit).
| Type | 64-bit | | 32-bit | |
| --- | --- | --- | --- | --- |
| | Size | Align | Size | Align |
| bool, uint8, int8 | 1 | 1 | 1 | 1 |
| uint16, int16 | 2 | 2 | 2 | 2 |
| uint32, int32 | 4 | 4 | 4 | 4 |
| uint64, int64 | 8 | 8 | 8 | 4 |
| int, uint | 8 | 8 | 4 | 4 |
| float32 | 4 | 4 | 4 | 4 |
| float64 | 8 | 8 | 8 | 4 |
| complex64 | 8 | 4 | 8 | 4 |
| complex128 | 16 | 8 | 16 | 4 |
| uintptr, *T, unsafe.Pointer | 8 | 8 | 4 | 4 |
The types `byte` and `rune` are aliases for `uint8` and `int32`,
respectively, and hence have the same size and alignment as these
types.
The layout of `map`, `chan`, and `func` types is equivalent to *T.
To describe the layout of the remaining composite types, we first
define the layout of a *sequence* S of N fields with types
t<sub>1</sub>, t<sub>2</sub>, ..., t<sub>N</sub>.
We define the byte offset at which each field begins relative to a
base address of 0, as well as the size and alignment of the sequence
as follows:
```
offset(S, i) = 0 if i = 1
= align(offset(S, i-1) + sizeof(t_(i-1)), alignof(t_i))
alignof(S) = 1 if N = 0
= max(alignof(t_i) | 1 <= i <= N)
sizeof(S) = 0 if N = 0
= align(offset(S, N) + sizeof(t_N), alignof(S))
```
Where sizeof(T) and alignof(T) are the size and alignment of type T,
respectively, and align(x, y) rounds x up to a multiple of y.
The `interface{}` type is a sequence of 1. a pointer to the runtime type
description for the interface's dynamic type and 2. an `unsafe.Pointer`
data field.
Any other interface type (besides the empty interface) is a sequence
of 1. a pointer to the runtime "itab" that gives the method pointers and
the type of the data field and 2. an `unsafe.Pointer` data field.
An interface can be "direct" or "indirect" depending on the dynamic
type: a direct interface stores the value directly in the data field,
and an indirect interface stores a pointer to the value in the data
field.
An interface can only be direct if the value consists of a single
pointer word.
An array type `[N]T` is a sequence of N fields of type T.
The slice type `[]T` is a sequence of a `*[cap]T` pointer to the slice
backing store, an `int` giving the `len` of the slice, and an `int`
giving the `cap` of the slice.
The `string` type is a sequence of a `*[len]byte` pointer to the
string backing store, and an `int` giving the `len` of the string.
A struct type `struct { f1 t1; ...; fM tM }` is laid out as the
sequence t1, ..., tM, tP, where tP is either:
- Type `byte` if sizeof(tM) = 0 and any of sizeof(t*i*) ≠ 0.
- Empty (size 0 and align 1) otherwise.
The padding byte prevents creating a past-the-end pointer by taking
the address of the final, empty fN field.
Note that user-written assembly code should generally not depend on Go
type layout and should instead use the constants defined in
[`go_asm.h`](/doc/asm.html#data-offsets).
## Function call argument and result passing
Function calls pass arguments and results using a combination of the
stack and machine registers.
Each argument or result is passed either entirely in registers or
entirely on the stack.
Because access to registers is generally faster than access to the
stack, arguments and results are preferentially passed in registers.
However, any argument or result that contains a non-trivial array or
does not fit entirely in the remaining available registers is passed
on the stack.
Each architecture defines a sequence of integer registers and a
sequence of floating-point registers.
At a high level, arguments and results are recursively broken down
into values of base types and these base values are assigned to
registers from these sequences.
Arguments and results can share the same registers, but do not share
the same stack space.
Beyond the arguments and results passed on the stack, the caller also
reserves spill space on the stack for all register-based arguments
(but does not populate this space).
The receiver, arguments, and results of function or method F are
assigned to registers or the stack using the following algorithm:
1. Let NI and NFP be the length of integer and floating-point register
sequences defined by the architecture.
Let I and FP be 0; these are the indexes of the next integer and
floating-pointer register.
Let S, the type sequence defining the stack frame, be empty.
1. If F is a method, assign Fs receiver.
1. For each argument A of F, assign A.
1. Add a pointer-alignment field to S. This has size 0 and the same
alignment as `uintptr`.
1. Reset I and FP to 0.
1. For each result R of F, assign R.
1. Add a pointer-alignment field to S.
1. For each register-assigned receiver and argument of F, let T be its
type and add T to the stack sequence S.
This is the argument's (or receiver's) spill space and will be
uninitialized at the call.
1. Add a pointer-alignment field to S.
Assigning a receiver, argument, or result V of underlying type T works
as follows:
1. Remember I and FP.
1. Try to register-assign V.
1. If step 2 failed, reset I and FP to the values from step 1, add T
to the stack sequence S, and assign V to this field in S.
Register-assignment of a value V of underlying type T works as follows:
1. If T is a boolean or integral type that fits in an integer
register, assign V to register I and increment I.
1. If T is an integral type that fits in two integer registers, assign
the least significant and most significant halves of V to registers
I and I+1, respectively, and increment I by 2
1. If T is a floating-point type and can be represented without loss
of precision in a floating-point register, assign V to register FP
and increment FP.
1. If T is a complex type, recursively register-assign its real and
imaginary parts.
1. If T is a pointer type, map type, channel type, or function type,
assign V to register I and increment I.
1. If T is a string type, interface type, or slice type, recursively
register-assign Vs components (2 for strings and interfaces, 3 for
slices).
1. If T is a struct type, recursively register-assign each field of V.
1. If T is an array type of length 0, do nothing.
1. If T is an array type of length 1, recursively register-assign its
one element.
1. If T is an array type of length > 1, fail.
1. If I > NI or FP > NFP, fail.
1. If any recursive assignment above fails, fail.
The above algorithm produces an assignment of each receiver, argument,
and result to registers or to a field in the stack sequence.
The final stack sequence looks like: stack-assigned receiver,
stack-assigned arguments, pointer-alignment, stack-assigned results,
pointer-alignment, spill space for each register-assigned argument,
pointer-alignment.
The following diagram shows what this stack frame looks like on the
stack, using the typical convention where address 0 is at the bottom:
+------------------------------+
| . . . |
| 2nd reg argument spill space |
| 1st reg argument spill space |
| <pointer-sized alignment> |
| . . . |
| 2nd stack-assigned result |
| 1st stack-assigned result |
| <pointer-sized alignment> |
| . . . |
| 2nd stack-assigned argument |
| 1st stack-assigned argument |
| stack-assigned receiver |
+------------------------------+ ↓ lower addresses
To perform a call, the caller reserves space starting at the lowest
address in its stack frame for the call stack frame, stores arguments
in the registers and argument stack fields determined by the above
algorithm, and performs the call.
At the time of a call, spill space, result stack fields, and result
registers are left uninitialized.
Upon return, the callee must have stored results to all result
registers and result stack fields determined by the above algorithm.
There are no callee-save registers, so a call may overwrite any
register that doesnt have a fixed meaning, including argument
registers.
### Example
Consider the function `func f(a1 uint8, a2 [2]uintptr, a3 uint8) (r1
struct { x uintptr; y [2]uintptr }, r2 string)` on a 64-bit
architecture with hypothetical integer registers R0R9.
On entry, `a1` is assigned to `R0`, `a3` is assigned to `R1` and the
stack frame is laid out in the following sequence:
a2 [2]uintptr
r1.x uintptr
r1.y [2]uintptr
a1Spill uint8
a2Spill uint8
_ [6]uint8 // alignment padding
In the stack frame, only the `a2` field is initialized on entry; the
rest of the frame is left uninitialized.
On exit, `r2.base` is assigned to `R0`, `r2.len` is assigned to `R1`,
and `r1.x` and `r1.y` are initialized in the stack frame.
There are several things to note in this example.
First, `a2` and `r1` are stack-assigned because they contain arrays.
The other arguments and results are register-assigned.
Result `r2` is decomposed into its components, which are individually
register-assigned.
On the stack, the stack-assigned arguments appear at lower addresses
than the stack-assigned results, which appear at lower addresses than
the argument spill area.
Only arguments, not results, are assigned a spill area on the stack.
### Rationale
Each base value is assigned to its own register to optimize
construction and access.
An alternative would be to pack multiple sub-word values into
registers, or to simply map an argument's in-memory layout to
registers (this is common in C ABIs), but this typically adds cost to
pack and unpack these values.
Modern architectures have more than enough registers to pass all
arguments and results this way for nearly all functions (see the
appendix), so theres little downside to spreading base values across
registers.
Arguments that cant be fully assigned to registers are passed
entirely on the stack in case the callee takes the address of that
argument.
If an argument could be split across the stack and registers and the
callee took its address, it would need to be reconstructed in memory,
a process that would be proportional to the size of the argument.
Non-trivial arrays are always passed on the stack because indexing
into an array typically requires a computed offset, which generally
isnt possible with registers.
Arrays in general are rare in function signatures (only 0.7% of
functions in the Go 1.15 standard library and 0.2% in kubelet).
We considered allowing array fields to be passed on the stack while
the rest of an arguments fields are passed in registers, but this
creates the same problems as other large structs if the callee takes
the address of an argument, and would benefit <0.1% of functions in
kubelet (and even these very little).
We make exceptions for 0 and 1-element arrays because these dont
require computed offsets, and 1-element arrays are already decomposed
in the compilers SSA representation.
The ABI assignment algorithm above is equivalent to Gos stack-based
ABI0 calling convention if there are zero architecture registers.
This is intended to ease the transition to the register-based internal
ABI and make it easy for the compiler to generate either calling
convention.
An architecture may still define register meanings that arent
compatible with ABI0, but these differences should be easy to account
for in the compiler.
The algorithm reserves spill space for arguments in the callers frame
so that the compiler can generate a stack growth path that spills into
this reserved space.
If the callee has to grow the stack, it may not be able to reserve
enough additional stack space in its own frame to spill these, which
is why its important that the caller do so.
These slots also act as the home location if these arguments need to
be spilled for any other reason, which simplifies traceback printing.
There are several options for how to lay out the argument spill space.
We chose to lay out each argument according to its type's usual memory
layout but to separate the spill space from the regular argument
space.
Using the usual memory layout simplifies the compiler because it
already understands this layout.
Also, if a function takes the address of a register-assigned argument,
the compiler must spill that argument to memory in its usual memory
layout and it's more convenient to use the argument spill space for
this purpose.
Alternatively, the spill space could be structured around argument
registers.
In this approach, the stack growth spill path would spill each
argument register to a register-sized stack word.
However, if the function takes the address of a register-assigned
argument, the compiler would have to reconstruct it in memory layout
elsewhere on the stack.
The spill space could also be interleaved with the stack-assigned
arguments so the arguments appear in order whether they are register-
or stack-assigned.
This would be close to ABI0, except that register-assigned arguments
would be uninitialized on the stack and there's no need to reserve
stack space for register-assigned results.
We expect separating the spill space to perform better because of
memory locality.
Separating the space is also potentially simpler for `reflect` calls
because this allows `reflect` to summarize the spill space as a single
number.
Finally, the long-term intent is to remove reserved spill slots
entirely allowing most functions to be called without any stack
setup and easing the introduction of callee-save registers and
separating the spill space makes that transition easier.
## Closures
A func value (e.g., `var x func()`) is a pointer to a closure object.
A closure object begins with a pointer-sized program counter
representing the entry point of the function, followed by zero or more
bytes containing the closed-over environment.
Closure calls follow the same conventions as static function and
method calls, with one addition. Each architecture specifies a
*closure context pointer* register and calls to closures store the
address of the closure object in the closure context pointer register
prior to the call.
## Software floating-point mode
In "softfloat" mode, the ABI simply treats the hardware as having zero
floating-point registers.
As a result, any arguments containing floating-point values will be
passed on the stack.
*Rationale*: Softfloat mode is about compatibility over performance
and is not commonly used.
Hence, we keep the ABI as simple as possible in this case, rather than
adding additional rules for passing floating-point values in integer
registers.
## Architecture specifics
This section describes per-architecture register mappings, as well as
other per-architecture special cases.
### amd64 architecture
The amd64 architecture uses the following sequence of 9 registers for
integer arguments and results:
RAX, RBX, RCX, RDI, RSI, R8, R9, R10, R11
It uses X0 X14 for floating-point arguments and results.
*Rationale*: These sequences are chosen from the available registers
to be relatively easy to remember.
Registers R12 and R13 are permanent scratch registers.
R15 is a scratch register except in dynamically linked binaries.
*Rationale*: Some operations such as stack growth and reflection calls
need dedicated scratch registers in order to manipulate call frames
without corrupting arguments or results.
Special-purpose registers are as follows:
| Register | Call meaning | Body meaning |
| --- | --- | --- |
| RSP | Stack pointer | Fixed |
| RBP | Frame pointer | Fixed |
| RDX | Closure context pointer | Scratch |
| R12 | None | Scratch |
| R13 | None | Scratch |
| R14 | Current goroutine | Scratch |
| R15 | GOT reference temporary | Fixed if dynlink |
| X15 | Zero value | Fixed |
TODO: We may start with the existing TLS-based g and move to R14
later.
*Rationale*: These register meanings are compatible with Gos
stack-based calling convention except for R14 and X15, which will have
to be restored on transitions from ABI0 code to ABIInternal code.
In ABI0, these are undefined, so transitions from ABIInternal to ABI0
can ignore these registers.
*Rationale*: For the current goroutine pointer, we chose a register
that requires an additional REX byte.
While this adds one byte to every function prologue, it is hardly ever
accessed outside the function prologue and we expect making more
single-byte registers available to be a net win.
*Rationale*: We designate X15 as a fixed zero register because
functions often have to bulk zero their stack frames, and this is more
efficient with a designated zero register.
#### Stack layout
The stack pointer, RSP, grows down and is always aligned to 8 bytes.
The amd64 architecture does not use a link register.
A function's stack frame is laid out as follows:
+------------------------------+
| return PC |
| RBP on entry |
| ... locals ... |
| ... outgoing arguments ... |
+------------------------------+ ↓ lower addresses
The "return PC" is pushed as part of the standard amd64 `CALL`
operation.
On entry, a function subtracts from RSP to open its stack frame and
saves the value of RBP directly below the return PC.
A leaf function that does not require any stack space may omit the
saved RBP.
The Go ABI's use of RBP as a frame pointer register is compatible with
amd64 platform conventions so that Go can inter-operate with platform
debuggers and profilers.
#### Flags
The direction flag (D) is always cleared (set to the “forward”
direction) at a call.
The arithmetic status flags are treated like scratch registers and not
preserved across calls.
All other bits in RFLAGS are system flags.
The CPU is always in MMX technology state (not x87 mode).
*Rationale*: Go on amd64 uses the XMM registers and never uses the x87
registers, so it makes sense to assume the CPU is in MMX mode.
Otherwise, any function that used the XMM registers would have to
execute an EMMS instruction before calling another function or
returning (this is the case in the SysV ABI).
At calls, the MXCSR control bits are always set as follows:
| Flag | Bit | Value | Meaning |
| --- | --- | --- | --- |
| FZ | 15 | 0 | Do not flush to zero |
| RC | 14/13 | 0 (RN) | Round to nearest |
| PM | 12 | 1 | Precision masked |
| UM | 11 | 1 | Underflow masked |
| OM | 10 | 1 | Overflow masked |
| ZM | 9 | 1 | Divide-by-zero masked |
| DM | 8 | 1 | Denormal operations masked |
| IM | 7 | 1 | Invalid operations masked |
| DAZ | 6 | 0 | Do not zero de-normals |
The MXCSR status bits are callee-save.
*Rationale*: Having a fixed MXCSR control configuration allows Go
functions to use SSE operations without modifying or saving the MXCSR.
Functions are allowed to modify it between calls (as long as they
restore it), but as of this writing Go code never does.
The above fixed configuration matches the process initialization
control bits specified by the ELF AMD64 ABI.
The x87 floating-point control word is not used by Go on amd64.
## Future directions
### Spill path improvements
The ABI currently reserves spill space for argument registers so the
compiler can statically generate an argument spill path before calling
into `runtime.morestack` to grow the stack.
This ensures there will be sufficient spill space even when the stack
is nearly exhausted and keeps stack growth and stack scanning
essentially unchanged from ABI0.
However, this wastes stack space (the median wastage is 16 bytes per
call), resulting in larger stacks and increased cache footprint.
A better approach would be to reserve stack space only when spilling.
One way to ensure enough space is available to spill would be for
every function to ensure there is enough space for the function's own
frame *as well as* the spill space of all functions it calls.
For most functions, this would change the threshold for the prologue
stack growth check.
For `nosplit` functions, this would change the threshold used in the
linker's static stack size check.
Allocating spill space in the callee rather than the caller may also
allow for faster reflection calls in the common case where a function
takes only register arguments, since it would allow reflection to make
these calls directly without allocating any frame.
The statically-generated spill path also increases code size.
It is possible to instead have a generic spill path in the runtime, as
part of `morestack`.
However, this complicates reserving the spill space, since spilling
all possible register arguments would, in most cases, take
significantly more space than spilling only those used by a particular
function.
Some options are to spill to a temporary space and copy back only the
registers used by the function, or to grow the stack if necessary
before spilling to it (using a temporary space if necessary), or to
use a heap-allocated space if insufficient stack space is available.
These options all add enough complexity that we will have to make this
decision based on the actual code size growth caused by the static
spill paths.
### Clobber sets
As defined, the ABI does not use callee-save registers.
This significantly simplifies the garbage collector and the compiler's
register allocator, but at some performance cost.
A potentially better balance for Go code would be to use *clobber
sets*: for each function, the compiler records the set of registers it
clobbers (including those clobbered by functions it calls) and any
register not clobbered by function F can remain live across calls to
F.
This is generally a good fit for Go because Go's package DAG allows
function metadata like the clobber set to flow up the call graph, even
across package boundaries.
Clobber sets would require relatively little change to the garbage
collector, unlike general callee-save registers.
One disadvantage of clobber sets over callee-save registers is that
they don't help with indirect function calls or interface method
calls, since static information isn't available in these cases.
### Large aggregates
Go encourages passing composite values by value, and this simplifies
reasoning about mutation and races.
However, this comes at a performance cost for large composite values.
It may be possible to instead transparently pass large composite
values by reference and delay copying until it is actually necessary.
## Appendix: Register usage analysis
In order to understand the impacts of the above design on register
usage, we
[analyzed](https://github.com/aclements/go-misc/tree/master/abi) the
impact of the above ABI on a large code base: cmd/kubelet from
[Kubernetes](https://github.com/kubernetes/kubernetes) at tag v1.18.8.
The following table shows the impact of different numbers of available
integer and floating-point registers on argument assignment:
```
| | | | stack args | spills | stack total |
| ints | floats | % fit | p50 | p95 | p99 | p50 | p95 | p99 | p50 | p95 | p99 |
| 0 | 0 | 6.3% | 32 | 152 | 256 | 0 | 0 | 0 | 32 | 152 | 256 |
| 0 | 8 | 6.4% | 32 | 152 | 256 | 0 | 0 | 0 | 32 | 152 | 256 |
| 1 | 8 | 21.3% | 24 | 144 | 248 | 8 | 8 | 8 | 32 | 152 | 256 |
| 2 | 8 | 38.9% | 16 | 128 | 224 | 8 | 16 | 16 | 24 | 136 | 240 |
| 3 | 8 | 57.0% | 0 | 120 | 224 | 16 | 24 | 24 | 24 | 136 | 240 |
| 4 | 8 | 73.0% | 0 | 120 | 216 | 16 | 32 | 32 | 24 | 136 | 232 |
| 5 | 8 | 83.3% | 0 | 112 | 216 | 16 | 40 | 40 | 24 | 136 | 232 |
| 6 | 8 | 87.5% | 0 | 112 | 208 | 16 | 48 | 48 | 24 | 136 | 232 |
| 7 | 8 | 89.8% | 0 | 112 | 208 | 16 | 48 | 56 | 24 | 136 | 232 |
| 8 | 8 | 91.3% | 0 | 112 | 200 | 16 | 56 | 64 | 24 | 136 | 232 |
| 9 | 8 | 92.1% | 0 | 112 | 192 | 16 | 56 | 72 | 24 | 136 | 232 |
| 10 | 8 | 92.6% | 0 | 104 | 192 | 16 | 56 | 72 | 24 | 136 | 232 |
| 11 | 8 | 93.1% | 0 | 104 | 184 | 16 | 56 | 80 | 24 | 128 | 232 |
| 12 | 8 | 93.4% | 0 | 104 | 176 | 16 | 56 | 88 | 24 | 128 | 232 |
| 13 | 8 | 94.0% | 0 | 88 | 176 | 16 | 56 | 96 | 24 | 128 | 232 |
| 14 | 8 | 94.4% | 0 | 80 | 152 | 16 | 64 | 104 | 24 | 128 | 232 |
| 15 | 8 | 94.6% | 0 | 80 | 152 | 16 | 64 | 112 | 24 | 128 | 232 |
| 16 | 8 | 94.9% | 0 | 16 | 152 | 16 | 64 | 112 | 24 | 128 | 232 |
| ∞ | 8 | 99.8% | 0 | 0 | 0 | 24 | 112 | 216 | 24 | 120 | 216 |
```
The first two columns show the number of available integer and
floating-point registers.
The first row shows the results for 0 integer and 0 floating-point
registers, which is equivalent to ABI0.
We found that any reasonable number of floating-point registers has
the same effect, so we fixed it at 8 for all other rows.
The “% fit” column gives the fraction of functions where all arguments
and results are register-assigned and no arguments are passed on the
stack.
The three “stack args” columns give the median, 95th and 99th
percentile number of bytes of stack arguments.
The “spills” columns likewise summarize the number of bytes in
on-stack spill space.
And “stack total” summarizes the sum of stack arguments and on-stack
spill slots.
Note that these are three different distributions; for example,
theres no single function that takes 0 stack argument bytes, 16 spill
bytes, and 24 total stack bytes.
From this, we can see that the fraction of functions that fit entirely
in registers grows very slowly once it reaches about 90%, though
curiously there is a small minority of functions that could benefit
from a huge number of registers.
Making 9 integer registers available on amd64 puts it in this realm.
We also see that the stack space required for most functions is fairly
small.
While the increasing space required for spills largely balances out
the decreasing space required for stack arguments as the number of
available registers increases, there is a general reduction in the
total stack space required with more available registers.
This does, however, suggest that eliminating spill slots in the future
would noticeably reduce stack requirements.

View File

@@ -0,0 +1,461 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package abi
import (
"cmd/compile/internal/types"
"cmd/internal/src"
"fmt"
"sync"
)
//......................................................................
//
// Public/exported bits of the ABI utilities.
//
// ABIParamResultInfo stores the results of processing a given
// function type to compute stack layout and register assignments. For
// each input and output parameter we capture whether the param was
// register-assigned (and to which register(s)) or the stack offset
// for the param if is not going to be passed in registers according
// to the rules in the Go internal ABI specification (1.17).
type ABIParamResultInfo struct {
inparams []ABIParamAssignment // Includes receiver for method calls. Does NOT include hidden closure pointer.
outparams []ABIParamAssignment
offsetToSpillArea int64
spillAreaSize int64
config *ABIConfig // to enable String() method
}
func (a *ABIParamResultInfo) InParams() []ABIParamAssignment {
return a.inparams
}
func (a *ABIParamResultInfo) OutParams() []ABIParamAssignment {
return a.outparams
}
func (a *ABIParamResultInfo) InParam(i int) ABIParamAssignment {
return a.inparams[i]
}
func (a *ABIParamResultInfo) OutParam(i int) ABIParamAssignment {
return a.outparams[i]
}
func (a *ABIParamResultInfo) SpillAreaOffset() int64 {
return a.offsetToSpillArea
}
func (a *ABIParamResultInfo) SpillAreaSize() int64 {
return a.spillAreaSize
}
// RegIndex stores the index into the set of machine registers used by
// the ABI on a specific architecture for parameter passing. RegIndex
// values 0 through N-1 (where N is the number of integer registers
// used for param passing according to the ABI rules) describe integer
// registers; values N through M (where M is the number of floating
// point registers used). Thus if the ABI says there are 5 integer
// registers and 7 floating point registers, then RegIndex value of 4
// indicates the 5th integer register, and a RegIndex value of 11
// indicates the 7th floating point register.
type RegIndex uint8
// ABIParamAssignment holds information about how a specific param or
// result will be passed: in registers (in which case 'Registers' is
// populated) or on the stack (in which case 'Offset' is set to a
// non-negative stack offset. The values in 'Registers' are indices (as
// described above), not architected registers.
type ABIParamAssignment struct {
Type *types.Type
Registers []RegIndex
offset int32
}
// Offset returns the stack offset for addressing the parameter that "a" describes.
// This will panic if "a" describes a register-allocated parameter.
func (a *ABIParamAssignment) Offset() int32 {
if len(a.Registers) > 0 {
panic("Register allocated parameters have no offset")
}
return a.offset
}
// SpillOffset returns the offset *within the spill area* for the parameter that "a" describes.
// Registers will be spilled here; if a memory home is needed (for a pointer method e.g.)
// then that will be the address.
// This will panic if "a" describes a stack-allocated parameter.
func (a *ABIParamAssignment) SpillOffset() int32 {
if len(a.Registers) == 0 {
panic("Stack-allocated parameters have no spill offset")
}
return a.offset
}
// RegAmounts holds a specified number of integer/float registers.
type RegAmounts struct {
intRegs int
floatRegs int
}
// ABIConfig captures the number of registers made available
// by the ABI rules for parameter passing and result returning.
type ABIConfig struct {
// Do we need anything more than this?
regAmounts RegAmounts
regsForTypeCache map[*types.Type]int
}
// NewABIConfig returns a new ABI configuration for an architecture with
// iRegsCount integer/pointer registers and fRegsCount floating point registers.
func NewABIConfig(iRegsCount, fRegsCount int) *ABIConfig {
return &ABIConfig{regAmounts: RegAmounts{iRegsCount, fRegsCount}, regsForTypeCache: make(map[*types.Type]int)}
}
// NumParamRegs returns the number of parameter registers used for a given type,
// without regard for the number available.
func (a *ABIConfig) NumParamRegs(t *types.Type) int {
if n, ok := a.regsForTypeCache[t]; ok {
return n
}
if t.IsScalar() || t.IsPtrShaped() {
var n int
if t.IsComplex() {
n = 2
} else {
n = (int(t.Size()) + types.RegSize - 1) / types.RegSize
}
a.regsForTypeCache[t] = n
return n
}
typ := t.Kind()
n := 0
switch typ {
case types.TARRAY:
n = a.NumParamRegs(t.Elem()) * int(t.NumElem())
case types.TSTRUCT:
for _, f := range t.FieldSlice() {
n += a.NumParamRegs(f.Type)
}
case types.TSLICE:
n = a.NumParamRegs(synthSlice)
case types.TSTRING:
n = a.NumParamRegs(synthString)
case types.TINTER:
n = a.NumParamRegs(synthIface)
}
a.regsForTypeCache[t] = n
return n
}
// ABIAnalyze takes a function type 't' and an ABI rules description
// 'config' and analyzes the function to determine how its parameters
// and results will be passed (in registers or on the stack), returning
// an ABIParamResultInfo object that holds the results of the analysis.
func (config *ABIConfig) ABIAnalyze(t *types.Type) ABIParamResultInfo {
setup()
s := assignState{
rTotal: config.regAmounts,
}
result := ABIParamResultInfo{config: config}
// Receiver
ft := t.FuncType()
if t.NumRecvs() != 0 {
rfsl := ft.Receiver.FieldSlice()
result.inparams = append(result.inparams,
s.assignParamOrReturn(rfsl[0].Type, false))
}
// Inputs
ifsl := ft.Params.FieldSlice()
for _, f := range ifsl {
result.inparams = append(result.inparams,
s.assignParamOrReturn(f.Type, false))
}
s.stackOffset = types.Rnd(s.stackOffset, int64(types.RegSize))
// Outputs
s.rUsed = RegAmounts{}
ofsl := ft.Results.FieldSlice()
for _, f := range ofsl {
result.outparams = append(result.outparams, s.assignParamOrReturn(f.Type, true))
}
// The spill area is at a register-aligned offset and its size is rounded up to a register alignment.
// TODO in theory could align offset only to minimum required by spilled data types.
result.offsetToSpillArea = alignTo(s.stackOffset, types.RegSize)
result.spillAreaSize = alignTo(s.spillOffset, types.RegSize)
return result
}
//......................................................................
//
// Non-public portions.
// regString produces a human-readable version of a RegIndex.
func (c *RegAmounts) regString(r RegIndex) string {
if int(r) < c.intRegs {
return fmt.Sprintf("I%d", int(r))
} else if int(r) < c.intRegs+c.floatRegs {
return fmt.Sprintf("F%d", int(r)-c.intRegs)
}
return fmt.Sprintf("<?>%d", r)
}
// toString method renders an ABIParamAssignment in human-readable
// form, suitable for debugging or unit testing.
func (ri *ABIParamAssignment) toString(config *ABIConfig) string {
regs := "R{"
offname := "spilloffset" // offset is for spill for register(s)
if len(ri.Registers) == 0 {
offname = "offset" // offset is for memory arg
}
for _, r := range ri.Registers {
regs += " " + config.regAmounts.regString(r)
}
return fmt.Sprintf("%s } %s: %d typ: %v", regs, offname, ri.offset, ri.Type)
}
// toString method renders an ABIParamResultInfo in human-readable
// form, suitable for debugging or unit testing.
func (ri *ABIParamResultInfo) String() string {
res := ""
for k, p := range ri.inparams {
res += fmt.Sprintf("IN %d: %s\n", k, p.toString(ri.config))
}
for k, r := range ri.outparams {
res += fmt.Sprintf("OUT %d: %s\n", k, r.toString(ri.config))
}
res += fmt.Sprintf("offsetToSpillArea: %d spillAreaSize: %d",
ri.offsetToSpillArea, ri.spillAreaSize)
return res
}
// assignState holds intermediate state during the register assigning process
// for a given function signature.
type assignState struct {
rTotal RegAmounts // total reg amounts from ABI rules
rUsed RegAmounts // regs used by params completely assigned so far
pUsed RegAmounts // regs used by the current param (or pieces therein)
stackOffset int64 // current stack offset
spillOffset int64 // current spill offset
}
// align returns a rounded up to t's alignment
func align(a int64, t *types.Type) int64 {
return alignTo(a, int(t.Align))
}
// alignTo returns a rounded up to t, where t must be 0 or a power of 2.
func alignTo(a int64, t int) int64 {
if t == 0 {
return a
}
return types.Rnd(a, int64(t))
}
// stackSlot returns a stack offset for a param or result of the
// specified type.
func (state *assignState) stackSlot(t *types.Type) int64 {
rv := align(state.stackOffset, t)
state.stackOffset = rv + t.Width
return rv
}
// allocateRegs returns a set of register indices for a parameter or result
// that we've just determined to be register-assignable. The number of registers
// needed is assumed to be stored in state.pUsed.
func (state *assignState) allocateRegs() []RegIndex {
regs := []RegIndex{}
// integer
for r := state.rUsed.intRegs; r < state.rUsed.intRegs+state.pUsed.intRegs; r++ {
regs = append(regs, RegIndex(r))
}
state.rUsed.intRegs += state.pUsed.intRegs
// floating
for r := state.rUsed.floatRegs; r < state.rUsed.floatRegs+state.pUsed.floatRegs; r++ {
regs = append(regs, RegIndex(r+state.rTotal.intRegs))
}
state.rUsed.floatRegs += state.pUsed.floatRegs
return regs
}
// regAllocate creates a register ABIParamAssignment object for a param
// or result with the specified type, as a final step (this assumes
// that all of the safety/suitability analysis is complete).
func (state *assignState) regAllocate(t *types.Type, isReturn bool) ABIParamAssignment {
spillLoc := int64(-1)
if !isReturn {
// Spill for register-resident t must be aligned for storage of a t.
spillLoc = align(state.spillOffset, t)
state.spillOffset = spillLoc + t.Size()
}
return ABIParamAssignment{
Type: t,
Registers: state.allocateRegs(),
offset: int32(spillLoc),
}
}
// stackAllocate creates a stack memory ABIParamAssignment object for
// a param or result with the specified type, as a final step (this
// assumes that all of the safety/suitability analysis is complete).
func (state *assignState) stackAllocate(t *types.Type) ABIParamAssignment {
return ABIParamAssignment{
Type: t,
offset: int32(state.stackSlot(t)),
}
}
// intUsed returns the number of integer registers consumed
// at a given point within an assignment stage.
func (state *assignState) intUsed() int {
return state.rUsed.intRegs + state.pUsed.intRegs
}
// floatUsed returns the number of floating point registers consumed at
// a given point within an assignment stage.
func (state *assignState) floatUsed() int {
return state.rUsed.floatRegs + state.pUsed.floatRegs
}
// regassignIntegral examines a param/result of integral type 't' to
// determines whether it can be register-assigned. Returns TRUE if we
// can register allocate, FALSE otherwise (and updates state
// accordingly).
func (state *assignState) regassignIntegral(t *types.Type) bool {
regsNeeded := int(types.Rnd(t.Width, int64(types.PtrSize)) / int64(types.PtrSize))
if t.IsComplex() {
regsNeeded = 2
}
// Floating point and complex.
if t.IsFloat() || t.IsComplex() {
if regsNeeded+state.floatUsed() > state.rTotal.floatRegs {
// not enough regs
return false
}
state.pUsed.floatRegs += regsNeeded
return true
}
// Non-floating point
if regsNeeded+state.intUsed() > state.rTotal.intRegs {
// not enough regs
return false
}
state.pUsed.intRegs += regsNeeded
return true
}
// regassignArray processes an array type (or array component within some
// other enclosing type) to determine if it can be register assigned.
// Returns TRUE if we can register allocate, FALSE otherwise.
func (state *assignState) regassignArray(t *types.Type) bool {
nel := t.NumElem()
if nel == 0 {
return true
}
if nel > 1 {
// Not an array of length 1: stack assign
return false
}
// Visit element
return state.regassign(t.Elem())
}
// regassignStruct processes a struct type (or struct component within
// some other enclosing type) to determine if it can be register
// assigned. Returns TRUE if we can register allocate, FALSE otherwise.
func (state *assignState) regassignStruct(t *types.Type) bool {
for _, field := range t.FieldSlice() {
if !state.regassign(field.Type) {
return false
}
}
return true
}
// synthOnce ensures that we only create the synth* fake types once.
var synthOnce sync.Once
// synthSlice, synthString, and syncIface are synthesized struct types
// meant to capture the underlying implementations of string/slice/interface.
var synthSlice *types.Type
var synthString *types.Type
var synthIface *types.Type
// setup performs setup for the register assignment utilities, manufacturing
// a small set of synthesized types that we'll need along the way.
func setup() {
synthOnce.Do(func() {
fname := types.BuiltinPkg.Lookup
nxp := src.NoXPos
unsp := types.Types[types.TUNSAFEPTR]
ui := types.Types[types.TUINTPTR]
synthSlice = types.NewStruct(types.NoPkg, []*types.Field{
types.NewField(nxp, fname("ptr"), unsp),
types.NewField(nxp, fname("len"), ui),
types.NewField(nxp, fname("cap"), ui),
})
synthString = types.NewStruct(types.NoPkg, []*types.Field{
types.NewField(nxp, fname("data"), unsp),
types.NewField(nxp, fname("len"), ui),
})
synthIface = types.NewStruct(types.NoPkg, []*types.Field{
types.NewField(nxp, fname("f1"), unsp),
types.NewField(nxp, fname("f2"), unsp),
})
})
}
// regassign examines a given param type (or component within some
// composite) to determine if it can be register assigned. Returns
// TRUE if we can register allocate, FALSE otherwise.
func (state *assignState) regassign(pt *types.Type) bool {
typ := pt.Kind()
if pt.IsScalar() || pt.IsPtrShaped() {
return state.regassignIntegral(pt)
}
switch typ {
case types.TARRAY:
return state.regassignArray(pt)
case types.TSTRUCT:
return state.regassignStruct(pt)
case types.TSLICE:
return state.regassignStruct(synthSlice)
case types.TSTRING:
return state.regassignStruct(synthString)
case types.TINTER:
return state.regassignStruct(synthIface)
default:
panic("not expected")
}
}
// assignParamOrReturn processes a given receiver, param, or result
// of type 'pt' to determine whether it can be register assigned.
// The result of the analysis is recorded in the result
// ABIParamResultInfo held in 'state'.
func (state *assignState) assignParamOrReturn(pt *types.Type, isReturn bool) ABIParamAssignment {
state.pUsed = RegAmounts{}
if pt.Width == types.BADWIDTH {
panic("should never happen")
} else if pt.Width == 0 {
return state.stackAllocate(pt)
} else if state.regassign(pt) {
return state.regAllocate(pt, isReturn)
} else {
return state.stackAllocate(pt)
}
}

View File

@@ -5,13 +5,13 @@
package amd64
import (
"cmd/compile/internal/gc"
"cmd/compile/internal/ssagen"
"cmd/internal/obj/x86"
)
var leaptr = x86.ALEAQ
func Init(arch *gc.Arch) {
func Init(arch *ssagen.ArchInfo) {
arch.LinkArch = &x86.Linkamd64
arch.REGSP = x86.REGSP
arch.MAXWIDTH = 1 << 50

View File

@@ -5,7 +5,10 @@
package amd64
import (
"cmd/compile/internal/gc"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/objw"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/x86"
"cmd/internal/objabi"
@@ -19,8 +22,8 @@ var isPlan9 = objabi.GOOS == "plan9"
const (
dzBlocks = 16 // number of MOV/ADD blocks
dzBlockLen = 4 // number of clears per block
dzBlockSize = 19 // size of instructions in a single block
dzMovSize = 4 // size of single MOV instruction w/ offset
dzBlockSize = 23 // size of instructions in a single block
dzMovSize = 5 // size of single MOV instruction w/ offset
dzLeaqSize = 4 // size of single LEAQ instruction
dzClearStep = 16 // number of bytes cleared by each MOV instruction
@@ -51,7 +54,7 @@ func dzDI(b int64) int64 {
return -dzClearStep * (dzBlockLen - tailSteps)
}
func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.Prog {
func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.Prog {
const (
ax = 1 << iota
x0
@@ -61,67 +64,67 @@ func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.Pr
return p
}
if cnt%int64(gc.Widthreg) != 0 {
if cnt%int64(types.RegSize) != 0 {
// should only happen with nacl
if cnt%int64(gc.Widthptr) != 0 {
gc.Fatalf("zerorange count not a multiple of widthptr %d", cnt)
if cnt%int64(types.PtrSize) != 0 {
base.Fatalf("zerorange count not a multiple of widthptr %d", cnt)
}
if *state&ax == 0 {
p = pp.Appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
p = pp.Append(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
*state |= ax
}
p = pp.Appendpp(p, x86.AMOVL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, off)
off += int64(gc.Widthptr)
cnt -= int64(gc.Widthptr)
p = pp.Append(p, x86.AMOVL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, off)
off += int64(types.PtrSize)
cnt -= int64(types.PtrSize)
}
if cnt == 8 {
if *state&ax == 0 {
p = pp.Appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
p = pp.Append(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
*state |= ax
}
p = pp.Appendpp(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, off)
} else if !isPlan9 && cnt <= int64(8*gc.Widthreg) {
p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, off)
} else if !isPlan9 && cnt <= int64(8*types.RegSize) {
if *state&x0 == 0 {
p = pp.Appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
p = pp.Append(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
*state |= x0
}
for i := int64(0); i < cnt/16; i++ {
p = pp.Appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, off+i*16)
p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, off+i*16)
}
if cnt%16 != 0 {
p = pp.Appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, off+cnt-int64(16))
p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, off+cnt-int64(16))
}
} else if !isPlan9 && (cnt <= int64(128*gc.Widthreg)) {
} else if !isPlan9 && (cnt <= int64(128*types.RegSize)) {
if *state&x0 == 0 {
p = pp.Appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
p = pp.Append(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
*state |= x0
}
p = pp.Appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, off+dzDI(cnt), obj.TYPE_REG, x86.REG_DI, 0)
p = pp.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, dzOff(cnt))
p.To.Sym = gc.Duffzero
p = pp.Append(p, leaptr, obj.TYPE_MEM, x86.REG_SP, off+dzDI(cnt), obj.TYPE_REG, x86.REG_DI, 0)
p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, dzOff(cnt))
p.To.Sym = ir.Syms.Duffzero
if cnt%16 != 0 {
p = pp.Appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_DI, -int64(8))
p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_DI, -int64(8))
}
} else {
if *state&ax == 0 {
p = pp.Appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
p = pp.Append(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
*state |= ax
}
p = pp.Appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, cnt/int64(gc.Widthreg), obj.TYPE_REG, x86.REG_CX, 0)
p = pp.Appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, off, obj.TYPE_REG, x86.REG_DI, 0)
p = pp.Appendpp(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
p = pp.Appendpp(p, x86.ASTOSQ, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
p = pp.Append(p, x86.AMOVQ, obj.TYPE_CONST, 0, cnt/int64(types.RegSize), obj.TYPE_REG, x86.REG_CX, 0)
p = pp.Append(p, leaptr, obj.TYPE_MEM, x86.REG_SP, off, obj.TYPE_REG, x86.REG_DI, 0)
p = pp.Append(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
p = pp.Append(p, x86.ASTOSQ, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
}
return p
}
func ginsnop(pp *gc.Progs) *obj.Prog {
func ginsnop(pp *objw.Progs) *obj.Prog {
// This is a hardware nop (1-byte 0x90) instruction,
// even though we describe it as an explicit XCHGL here.
// Particularly, this does not zero the high 32 bits

View File

@@ -8,16 +8,18 @@ import (
"fmt"
"math"
"cmd/compile/internal/gc"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
"cmd/compile/internal/ssa"
"cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/x86"
)
// markMoves marks any MOVXconst ops that need to avoid clobbering flags.
func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
func ssaMarkMoves(s *ssagen.State, b *ssa.Block) {
flive := b.FlagsLiveAtEnd
for _, c := range b.ControlValues() {
flive = c.Type.IsFlags() || flive
@@ -110,7 +112,7 @@ func moveByType(t *types.Type) obj.As {
// dest := dest(To) op src(From)
// and also returns the created obj.Prog so it
// may be further adjusted (offset, scale, etc).
func opregreg(s *gc.SSAGenState, op obj.As, dest, src int16) *obj.Prog {
func opregreg(s *ssagen.State, op obj.As, dest, src int16) *obj.Prog {
p := s.Prog(op)
p.From.Type = obj.TYPE_REG
p.To.Type = obj.TYPE_REG
@@ -164,7 +166,35 @@ func duff(size int64) (int64, int64) {
return off, adj
}
func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
func getgFromTLS(s *ssagen.State, r int16) {
// See the comments in cmd/internal/obj/x86/obj6.go
// near CanUse1InsnTLS for a detailed explanation of these instructions.
if x86.CanUse1InsnTLS(base.Ctxt) {
// MOVQ (TLS), r
p := s.Prog(x86.AMOVQ)
p.From.Type = obj.TYPE_MEM
p.From.Reg = x86.REG_TLS
p.To.Type = obj.TYPE_REG
p.To.Reg = r
} else {
// MOVQ TLS, r
// MOVQ (r)(TLS*1), r
p := s.Prog(x86.AMOVQ)
p.From.Type = obj.TYPE_REG
p.From.Reg = x86.REG_TLS
p.To.Type = obj.TYPE_REG
p.To.Reg = r
q := s.Prog(x86.AMOVQ)
q.From.Type = obj.TYPE_MEM
q.From.Reg = r
q.From.Index = x86.REG_TLS
q.From.Scale = 1
q.To.Type = obj.TYPE_REG
q.To.Reg = r
}
}
func ssaGenValue(s *ssagen.State, v *ssa.Value) {
switch v.Op {
case ssa.OpAMD64VFMADD231SD:
p := s.Prog(v.Op.Asm())
@@ -630,12 +660,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = o
}
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
case ssa.OpAMD64LEAQ, ssa.OpAMD64LEAL, ssa.OpAMD64LEAW:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64CMPQ, ssa.OpAMD64CMPL, ssa.OpAMD64CMPW, ssa.OpAMD64CMPB,
@@ -671,7 +701,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Args[1].Reg()
case ssa.OpAMD64CMPQconstload, ssa.OpAMD64CMPLconstload, ssa.OpAMD64CMPWconstload, ssa.OpAMD64CMPBconstload:
@@ -679,20 +709,20 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
gc.AddAux2(&p.From, v, sc.Off())
ssagen.AddAux2(&p.From, v, sc.Off())
p.To.Type = obj.TYPE_CONST
p.To.Offset = sc.Val()
case ssa.OpAMD64CMPQloadidx8, ssa.OpAMD64CMPQloadidx1, ssa.OpAMD64CMPLloadidx4, ssa.OpAMD64CMPLloadidx1, ssa.OpAMD64CMPWloadidx2, ssa.OpAMD64CMPWloadidx1, ssa.OpAMD64CMPBloadidx1:
p := s.Prog(v.Op.Asm())
memIdx(&p.From, v)
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Args[2].Reg()
case ssa.OpAMD64CMPQconstloadidx8, ssa.OpAMD64CMPQconstloadidx1, ssa.OpAMD64CMPLconstloadidx4, ssa.OpAMD64CMPLconstloadidx1, ssa.OpAMD64CMPWconstloadidx2, ssa.OpAMD64CMPWconstloadidx1, ssa.OpAMD64CMPBconstloadidx1:
sc := v.AuxValAndOff()
p := s.Prog(v.Op.Asm())
memIdx(&p.From, v)
gc.AddAux2(&p.From, v, sc.Off())
ssagen.AddAux2(&p.From, v, sc.Off())
p.To.Type = obj.TYPE_CONST
p.To.Offset = sc.Val()
case ssa.OpAMD64MOVLconst, ssa.OpAMD64MOVQconst:
@@ -732,14 +762,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64MOVBloadidx1, ssa.OpAMD64MOVWloadidx1, ssa.OpAMD64MOVLloadidx1, ssa.OpAMD64MOVQloadidx1, ssa.OpAMD64MOVSSloadidx1, ssa.OpAMD64MOVSDloadidx1,
ssa.OpAMD64MOVQloadidx8, ssa.OpAMD64MOVSDloadidx8, ssa.OpAMD64MOVLloadidx8, ssa.OpAMD64MOVLloadidx4, ssa.OpAMD64MOVSSloadidx4, ssa.OpAMD64MOVWloadidx2:
p := s.Prog(v.Op.Asm())
memIdx(&p.From, v)
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64MOVQstore, ssa.OpAMD64MOVSSstore, ssa.OpAMD64MOVSDstore, ssa.OpAMD64MOVLstore, ssa.OpAMD64MOVWstore, ssa.OpAMD64MOVBstore, ssa.OpAMD64MOVOstore,
@@ -751,7 +781,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
gc.AddAux(&p.To, v)
ssagen.AddAux(&p.To, v)
case ssa.OpAMD64MOVBstoreidx1, ssa.OpAMD64MOVWstoreidx1, ssa.OpAMD64MOVLstoreidx1, ssa.OpAMD64MOVQstoreidx1, ssa.OpAMD64MOVSSstoreidx1, ssa.OpAMD64MOVSDstoreidx1,
ssa.OpAMD64MOVQstoreidx8, ssa.OpAMD64MOVSDstoreidx8, ssa.OpAMD64MOVLstoreidx8, ssa.OpAMD64MOVSSstoreidx4, ssa.OpAMD64MOVLstoreidx4, ssa.OpAMD64MOVWstoreidx2,
ssa.OpAMD64ADDLmodifyidx1, ssa.OpAMD64ADDLmodifyidx4, ssa.OpAMD64ADDLmodifyidx8, ssa.OpAMD64ADDQmodifyidx1, ssa.OpAMD64ADDQmodifyidx8,
@@ -763,7 +793,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[2].Reg()
memIdx(&p.To, v)
gc.AddAux(&p.To, v)
ssagen.AddAux(&p.To, v)
case ssa.OpAMD64ADDQconstmodify, ssa.OpAMD64ADDLconstmodify:
sc := v.AuxValAndOff()
off := sc.Off()
@@ -786,7 +816,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(asm)
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
gc.AddAux2(&p.To, v, off)
ssagen.AddAux2(&p.To, v, off)
break
}
fallthrough
@@ -801,7 +831,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = val
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
gc.AddAux2(&p.To, v, off)
ssagen.AddAux2(&p.To, v, off)
case ssa.OpAMD64MOVQstoreconst, ssa.OpAMD64MOVLstoreconst, ssa.OpAMD64MOVWstoreconst, ssa.OpAMD64MOVBstoreconst:
p := s.Prog(v.Op.Asm())
@@ -810,7 +840,21 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = sc.Val()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
gc.AddAux2(&p.To, v, sc.Off())
ssagen.AddAux2(&p.To, v, sc.Off())
case ssa.OpAMD64MOVOstorezero:
if s.ABI != obj.ABIInternal {
v.Fatalf("MOVOstorezero can be only used in ABIInternal functions")
}
if !base.Flag.ABIWrap {
// zeroing X15 manually if wrappers are not used
opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
}
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = x86.REG_X15
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
ssagen.AddAux(&p.To, v)
case ssa.OpAMD64MOVQstoreconstidx1, ssa.OpAMD64MOVQstoreconstidx8, ssa.OpAMD64MOVLstoreconstidx1, ssa.OpAMD64MOVLstoreconstidx4, ssa.OpAMD64MOVWstoreconstidx1, ssa.OpAMD64MOVWstoreconstidx2, ssa.OpAMD64MOVBstoreconstidx1,
ssa.OpAMD64ADDLconstmodifyidx1, ssa.OpAMD64ADDLconstmodifyidx4, ssa.OpAMD64ADDLconstmodifyidx8, ssa.OpAMD64ADDQconstmodifyidx1, ssa.OpAMD64ADDQconstmodifyidx8,
ssa.OpAMD64ANDLconstmodifyidx1, ssa.OpAMD64ANDLconstmodifyidx4, ssa.OpAMD64ANDLconstmodifyidx8, ssa.OpAMD64ANDQconstmodifyidx1, ssa.OpAMD64ANDQconstmodifyidx8,
@@ -835,7 +879,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Type = obj.TYPE_NONE
}
memIdx(&p.To, v)
gc.AddAux2(&p.To, v, sc.Off())
ssagen.AddAux2(&p.To, v, sc.Off())
case ssa.OpAMD64MOVLQSX, ssa.OpAMD64MOVWQSX, ssa.OpAMD64MOVBQSX, ssa.OpAMD64MOVLQZX, ssa.OpAMD64MOVWQZX, ssa.OpAMD64MOVBQZX,
ssa.OpAMD64CVTTSS2SL, ssa.OpAMD64CVTTSD2SL, ssa.OpAMD64CVTTSS2SQ, ssa.OpAMD64CVTTSD2SQ,
ssa.OpAMD64CVTSS2SD, ssa.OpAMD64CVTSD2SS:
@@ -865,7 +909,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[1].Reg()
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
if v.Reg() != v.Args[0].Reg() {
@@ -891,13 +935,20 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = r
p.From.Index = i
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
if v.Reg() != v.Args[0].Reg() {
v.Fatalf("input[0] and output not in same register %s", v.LongString())
}
case ssa.OpAMD64DUFFZERO:
if s.ABI != obj.ABIInternal {
v.Fatalf("MOVOconst can be only used in ABIInternal functions")
}
if !base.Flag.ABIWrap {
// zeroing X15 manually if wrappers are not used
opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
}
off := duffStart(v.AuxInt)
adj := duffAdj(v.AuxInt)
var p *obj.Prog
@@ -911,18 +962,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
}
p = s.Prog(obj.ADUFFZERO)
p.To.Type = obj.TYPE_ADDR
p.To.Sym = gc.Duffzero
p.To.Sym = ir.Syms.Duffzero
p.To.Offset = off
case ssa.OpAMD64MOVOconst:
if v.AuxInt != 0 {
v.Fatalf("MOVOconst can only do constant=0")
}
r := v.Reg()
opregreg(s, x86.AXORPS, r, r)
case ssa.OpAMD64DUFFCOPY:
p := s.Prog(obj.ADUFFCOPY)
p.To.Type = obj.TYPE_ADDR
p.To.Sym = gc.Duffcopy
p.To.Sym = ir.Syms.Duffcopy
if v.AuxInt%16 != 0 {
v.Fatalf("bad DUFFCOPY AuxInt %v", v.AuxInt)
}
@@ -949,7 +994,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
return
}
p := s.Prog(loadByType(v.Type))
gc.AddrAuto(&p.From, v.Args[0])
ssagen.AddrAuto(&p.From, v.Args[0])
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -961,44 +1006,37 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(storeByType(v.Type))
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
gc.AddrAuto(&p.To, v)
ssagen.AddrAuto(&p.To, v)
case ssa.OpAMD64LoweredHasCPUFeature:
p := s.Prog(x86.AMOVBQZX)
p.From.Type = obj.TYPE_MEM
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64LoweredGetClosurePtr:
// Closure pointer is DX.
gc.CheckLoweredGetClosurePtr(v)
ssagen.CheckLoweredGetClosurePtr(v)
case ssa.OpAMD64LoweredGetG:
r := v.Reg()
// See the comments in cmd/internal/obj/x86/obj6.go
// near CanUse1InsnTLS for a detailed explanation of these instructions.
if x86.CanUse1InsnTLS(gc.Ctxt) {
// MOVQ (TLS), r
p := s.Prog(x86.AMOVQ)
p.From.Type = obj.TYPE_MEM
p.From.Reg = x86.REG_TLS
p.To.Type = obj.TYPE_REG
p.To.Reg = r
} else {
// MOVQ TLS, r
// MOVQ (r)(TLS*1), r
p := s.Prog(x86.AMOVQ)
p.From.Type = obj.TYPE_REG
p.From.Reg = x86.REG_TLS
p.To.Type = obj.TYPE_REG
p.To.Reg = r
q := s.Prog(x86.AMOVQ)
q.From.Type = obj.TYPE_MEM
q.From.Reg = r
q.From.Index = x86.REG_TLS
q.From.Scale = 1
q.To.Type = obj.TYPE_REG
q.To.Reg = r
if base.Flag.ABIWrap {
v.Fatalf("LoweredGetG should not appear in new ABI")
}
case ssa.OpAMD64CALLstatic, ssa.OpAMD64CALLclosure, ssa.OpAMD64CALLinter:
r := v.Reg()
getgFromTLS(s, r)
case ssa.OpAMD64CALLstatic:
if s.ABI == obj.ABI0 && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABIInternal {
// zeroing X15 when entering ABIInternal from ABI0
opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
// set G register from TLS
getgFromTLS(s, x86.REG_R14)
}
s.Call(v)
if s.ABI == obj.ABIInternal && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABI0 {
// zeroing X15 when entering ABIInternal from ABI0
opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
// set G register from TLS
getgFromTLS(s, x86.REG_R14)
}
case ssa.OpAMD64CALLclosure, ssa.OpAMD64CALLinter:
s.Call(v)
case ssa.OpAMD64LoweredGetCallerPC:
@@ -1012,12 +1050,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
case ssa.OpAMD64LoweredGetCallerSP:
// caller's SP is the address of the first arg
mov := x86.AMOVQ
if gc.Widthptr == 4 {
if types.PtrSize == 4 {
mov = x86.AMOVL
}
p := s.Prog(mov)
p.From.Type = obj.TYPE_ADDR
p.From.Offset = -gc.Ctxt.FixedFrameSize() // 0 on amd64, just to be consistent with other architectures
p.From.Offset = -base.Ctxt.FixedFrameSize() // 0 on amd64, just to be consistent with other architectures
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -1027,14 +1065,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
// arg0 is in DI. Set sym to match where regalloc put arg1.
p.To.Sym = gc.GCWriteBarrierReg[v.Args[1].Reg()]
p.To.Sym = ssagen.GCWriteBarrierReg[v.Args[1].Reg()]
case ssa.OpAMD64LoweredPanicBoundsA, ssa.OpAMD64LoweredPanicBoundsB, ssa.OpAMD64LoweredPanicBoundsC:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = gc.BoundsCheckFunc[v.AuxInt]
s.UseArgs(int64(2 * gc.Widthptr)) // space used in callee args area by assembly stubs
p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
s.UseArgs(int64(2 * types.PtrSize)) // space used in callee args area by assembly stubs
case ssa.OpAMD64NEGQ, ssa.OpAMD64NEGL,
ssa.OpAMD64BSWAPQ, ssa.OpAMD64BSWAPL,
@@ -1115,7 +1153,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
gc.AddAux(&p.To, v)
ssagen.AddAux(&p.To, v)
case ssa.OpAMD64SETNEF:
p := s.Prog(v.Op.Asm())
@@ -1164,14 +1202,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
if logopt.Enabled() {
logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
}
if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
gc.Warnl(v.Pos, "generated nil check")
if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
base.WarnfAt(v.Pos, "generated nil check")
}
case ssa.OpAMD64MOVBatomicload, ssa.OpAMD64MOVLatomicload, ssa.OpAMD64MOVQatomicload:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0()
case ssa.OpAMD64XCHGB, ssa.OpAMD64XCHGL, ssa.OpAMD64XCHGQ:
@@ -1184,7 +1222,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = r
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[1].Reg()
gc.AddAux(&p.To, v)
ssagen.AddAux(&p.To, v)
case ssa.OpAMD64XADDLlock, ssa.OpAMD64XADDQlock:
r := v.Reg0()
if r != v.Args[0].Reg() {
@@ -1196,7 +1234,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = r
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[1].Reg()
gc.AddAux(&p.To, v)
ssagen.AddAux(&p.To, v)
case ssa.OpAMD64CMPXCHGLlock, ssa.OpAMD64CMPXCHGQlock:
if v.Args[1].Reg() != x86.REG_AX {
v.Fatalf("input[1] not in AX %s", v.LongString())
@@ -1207,7 +1245,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[2].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
gc.AddAux(&p.To, v)
ssagen.AddAux(&p.To, v)
p = s.Prog(x86.ASETEQ)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0()
@@ -1218,20 +1256,20 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
gc.AddAux(&p.To, v)
ssagen.AddAux(&p.To, v)
case ssa.OpClobber:
p := s.Prog(x86.AMOVL)
p.From.Type = obj.TYPE_CONST
p.From.Offset = 0xdeaddead
p.To.Type = obj.TYPE_MEM
p.To.Reg = x86.REG_SP
gc.AddAux(&p.To, v)
ssagen.AddAux(&p.To, v)
p = s.Prog(x86.AMOVL)
p.From.Type = obj.TYPE_CONST
p.From.Offset = 0xdeaddead
p.To.Type = obj.TYPE_MEM
p.To.Reg = x86.REG_SP
gc.AddAux(&p.To, v)
ssagen.AddAux(&p.To, v)
p.To.Offset += 4
default:
v.Fatalf("genValue not implemented: %s", v.LongString())
@@ -1257,22 +1295,22 @@ var blockJump = [...]struct {
ssa.BlockAMD64NAN: {x86.AJPS, x86.AJPC},
}
var eqfJumps = [2][2]gc.IndexJump{
var eqfJumps = [2][2]ssagen.IndexJump{
{{Jump: x86.AJNE, Index: 1}, {Jump: x86.AJPS, Index: 1}}, // next == b.Succs[0]
{{Jump: x86.AJNE, Index: 1}, {Jump: x86.AJPC, Index: 0}}, // next == b.Succs[1]
}
var nefJumps = [2][2]gc.IndexJump{
var nefJumps = [2][2]ssagen.IndexJump{
{{Jump: x86.AJNE, Index: 0}, {Jump: x86.AJPC, Index: 1}}, // next == b.Succs[0]
{{Jump: x86.AJNE, Index: 0}, {Jump: x86.AJPS, Index: 0}}, // next == b.Succs[1]
}
func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
case ssa.BlockPlain:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockDefer:
// defer returns in rax:
@@ -1285,16 +1323,22 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
p.To.Reg = x86.REG_AX
p = s.Prog(x86.AJNE)
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockExit:
case ssa.BlockRet:
s.Prog(obj.ARET)
case ssa.BlockRetJmp:
if s.ABI == obj.ABI0 && b.Aux.(*obj.LSym).ABI() == obj.ABIInternal {
// zeroing X15 when entering ABIInternal from ABI0
opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
// set G register from TLS
getgFromTLS(s, x86.REG_R14)
}
p := s.Prog(obj.ARET)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN

View File

@@ -5,13 +5,13 @@
package arm
import (
"cmd/compile/internal/gc"
"cmd/compile/internal/ssa"
"cmd/compile/internal/ssagen"
"cmd/internal/obj/arm"
"cmd/internal/objabi"
)
func Init(arch *gc.Arch) {
func Init(arch *ssagen.ArchInfo) {
arch.LinkArch = &arm.Linkarm
arch.REGSP = arm.REGSP
arch.MAXWIDTH = (1 << 32) - 1
@@ -20,7 +20,7 @@ func Init(arch *gc.Arch) {
arch.Ginsnop = ginsnop
arch.Ginsnopdefer = ginsnop
arch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {}
arch.SSAGenValue = ssaGenValue
arch.SSAGenBlock = ssaGenBlock
}

View File

@@ -5,49 +5,51 @@
package arm
import (
"cmd/compile/internal/gc"
"cmd/compile/internal/ir"
"cmd/compile/internal/objw"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/arm"
)
func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, r0 *uint32) *obj.Prog {
func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, r0 *uint32) *obj.Prog {
if cnt == 0 {
return p
}
if *r0 == 0 {
p = pp.Appendpp(p, arm.AMOVW, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, arm.REG_R0, 0)
p = pp.Append(p, arm.AMOVW, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, arm.REG_R0, 0)
*r0 = 1
}
if cnt < int64(4*gc.Widthptr) {
for i := int64(0); i < cnt; i += int64(gc.Widthptr) {
p = pp.Appendpp(p, arm.AMOVW, obj.TYPE_REG, arm.REG_R0, 0, obj.TYPE_MEM, arm.REGSP, 4+off+i)
if cnt < int64(4*types.PtrSize) {
for i := int64(0); i < cnt; i += int64(types.PtrSize) {
p = pp.Append(p, arm.AMOVW, obj.TYPE_REG, arm.REG_R0, 0, obj.TYPE_MEM, arm.REGSP, 4+off+i)
}
} else if cnt <= int64(128*gc.Widthptr) {
p = pp.Appendpp(p, arm.AADD, obj.TYPE_CONST, 0, 4+off, obj.TYPE_REG, arm.REG_R1, 0)
} else if cnt <= int64(128*types.PtrSize) {
p = pp.Append(p, arm.AADD, obj.TYPE_CONST, 0, 4+off, obj.TYPE_REG, arm.REG_R1, 0)
p.Reg = arm.REGSP
p = pp.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
p.To.Name = obj.NAME_EXTERN
p.To.Sym = gc.Duffzero
p.To.Offset = 4 * (128 - cnt/int64(gc.Widthptr))
p.To.Sym = ir.Syms.Duffzero
p.To.Offset = 4 * (128 - cnt/int64(types.PtrSize))
} else {
p = pp.Appendpp(p, arm.AADD, obj.TYPE_CONST, 0, 4+off, obj.TYPE_REG, arm.REG_R1, 0)
p = pp.Append(p, arm.AADD, obj.TYPE_CONST, 0, 4+off, obj.TYPE_REG, arm.REG_R1, 0)
p.Reg = arm.REGSP
p = pp.Appendpp(p, arm.AADD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, arm.REG_R2, 0)
p = pp.Append(p, arm.AADD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, arm.REG_R2, 0)
p.Reg = arm.REG_R1
p = pp.Appendpp(p, arm.AMOVW, obj.TYPE_REG, arm.REG_R0, 0, obj.TYPE_MEM, arm.REG_R1, 4)
p = pp.Append(p, arm.AMOVW, obj.TYPE_REG, arm.REG_R0, 0, obj.TYPE_MEM, arm.REG_R1, 4)
p1 := p
p.Scond |= arm.C_PBIT
p = pp.Appendpp(p, arm.ACMP, obj.TYPE_REG, arm.REG_R1, 0, obj.TYPE_NONE, 0, 0)
p = pp.Append(p, arm.ACMP, obj.TYPE_REG, arm.REG_R1, 0, obj.TYPE_NONE, 0, 0)
p.Reg = arm.REG_R2
p = pp.Appendpp(p, arm.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
gc.Patch(p, p1)
p = pp.Append(p, arm.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
p.To.SetTarget(p1)
}
return p
}
func ginsnop(pp *gc.Progs) *obj.Prog {
func ginsnop(pp *objw.Progs) *obj.Prog {
p := pp.Prog(arm.AAND)
p.From.Type = obj.TYPE_REG
p.From.Reg = arm.REG_R0

View File

@@ -9,9 +9,11 @@ import (
"math"
"math/bits"
"cmd/compile/internal/gc"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
"cmd/compile/internal/ssa"
"cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/arm"
@@ -91,7 +93,7 @@ func makeshift(reg int16, typ int64, s int64) shift {
}
// genshift generates a Prog for r = r0 op (r1 shifted by n)
func genshift(s *gc.SSAGenState, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
func genshift(s *ssagen.State, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
p := s.Prog(as)
p.From.Type = obj.TYPE_SHIFT
p.From.Offset = int64(makeshift(r1, typ, n))
@@ -109,7 +111,7 @@ func makeregshift(r1 int16, typ int64, r2 int16) shift {
}
// genregshift generates a Prog for r = r0 op (r1 shifted by r2)
func genregshift(s *gc.SSAGenState, as obj.As, r0, r1, r2, r int16, typ int64) *obj.Prog {
func genregshift(s *ssagen.State, as obj.As, r0, r1, r2, r int16, typ int64) *obj.Prog {
p := s.Prog(as)
p.From.Type = obj.TYPE_SHIFT
p.From.Offset = int64(makeregshift(r1, typ, r2))
@@ -143,7 +145,7 @@ func getBFC(v uint32) (uint32, uint32) {
return 0xffffffff, 0
}
func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
func ssaGenValue(s *ssagen.State, v *ssa.Value) {
switch v.Op {
case ssa.OpCopy, ssa.OpARMMOVWreg:
if v.Type.IsMemory() {
@@ -181,7 +183,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
return
}
p := s.Prog(loadByType(v.Type))
gc.AddrAuto(&p.From, v.Args[0])
ssagen.AddrAuto(&p.From, v.Args[0])
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpStoreReg:
@@ -192,7 +194,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(storeByType(v.Type))
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
gc.AddrAuto(&p.To, v)
ssagen.AddrAuto(&p.To, v)
case ssa.OpARMADD,
ssa.OpARMADC,
ssa.OpARMSUB,
@@ -543,10 +545,10 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
v.Fatalf("aux is of unknown type %T", v.Aux)
case *obj.LSym:
wantreg = "SB"
gc.AddAux(&p.From, v)
case *gc.Node:
ssagen.AddAux(&p.From, v)
case *ir.Name:
wantreg = "SP"
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
case nil:
// No sym, just MOVW $off(SP), R
wantreg = "SP"
@@ -566,7 +568,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpARMMOVBstore,
@@ -579,7 +581,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
gc.AddAux(&p.To, v)
ssagen.AddAux(&p.To, v)
case ssa.OpARMMOVWloadidx, ssa.OpARMMOVBUloadidx, ssa.OpARMMOVBloadidx, ssa.OpARMMOVHUloadidx, ssa.OpARMMOVHloadidx:
// this is just shift 0 bits
fallthrough
@@ -700,7 +702,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = gc.Udiv
p.To.Sym = ir.Syms.Udiv
case ssa.OpARMLoweredWB:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
@@ -710,39 +712,39 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = gc.BoundsCheckFunc[v.AuxInt]
p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
s.UseArgs(8) // space used in callee args area by assembly stubs
case ssa.OpARMLoweredPanicExtendA, ssa.OpARMLoweredPanicExtendB, ssa.OpARMLoweredPanicExtendC:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = gc.ExtendCheckFunc[v.AuxInt]
p.To.Sym = ssagen.ExtendCheckFunc[v.AuxInt]
s.UseArgs(12) // space used in callee args area by assembly stubs
case ssa.OpARMDUFFZERO:
p := s.Prog(obj.ADUFFZERO)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = gc.Duffzero
p.To.Sym = ir.Syms.Duffzero
p.To.Offset = v.AuxInt
case ssa.OpARMDUFFCOPY:
p := s.Prog(obj.ADUFFCOPY)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = gc.Duffcopy
p.To.Sym = ir.Syms.Duffcopy
p.To.Offset = v.AuxInt
case ssa.OpARMLoweredNilCheck:
// Issue a load which will fault if arg is nil.
p := s.Prog(arm.AMOVB)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = arm.REGTMP
if logopt.Enabled() {
logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
}
if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
gc.Warnl(v.Pos, "generated nil check")
if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
base.WarnfAt(v.Pos, "generated nil check")
}
case ssa.OpARMLoweredZero:
// MOVW.P Rarg2, 4(R1)
@@ -777,7 +779,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p2.Reg = arm.REG_R1
p3 := s.Prog(arm.ABLE)
p3.To.Type = obj.TYPE_BRANCH
gc.Patch(p3, p)
p3.To.SetTarget(p)
case ssa.OpARMLoweredMove:
// MOVW.P 4(R1), Rtmp
// MOVW.P Rtmp, 4(R2)
@@ -818,7 +820,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.Reg = arm.REG_R1
p4 := s.Prog(arm.ABLE)
p4.To.Type = obj.TYPE_BRANCH
gc.Patch(p4, p)
p4.To.SetTarget(p)
case ssa.OpARMEqual,
ssa.OpARMNotEqual,
ssa.OpARMLessThan,
@@ -844,12 +846,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Reg = v.Reg()
case ssa.OpARMLoweredGetClosurePtr:
// Closure pointer is R7 (arm.REGCTXT).
gc.CheckLoweredGetClosurePtr(v)
ssagen.CheckLoweredGetClosurePtr(v)
case ssa.OpARMLoweredGetCallerSP:
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(arm.AMOVW)
p.From.Type = obj.TYPE_ADDR
p.From.Offset = -gc.Ctxt.FixedFrameSize()
p.From.Offset = -base.Ctxt.FixedFrameSize()
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -899,24 +901,24 @@ var blockJump = map[ssa.BlockKind]struct {
}
// To model a 'LEnoov' ('<=' without overflow checking) branching
var leJumps = [2][2]gc.IndexJump{
var leJumps = [2][2]ssagen.IndexJump{
{{Jump: arm.ABEQ, Index: 0}, {Jump: arm.ABPL, Index: 1}}, // next == b.Succs[0]
{{Jump: arm.ABMI, Index: 0}, {Jump: arm.ABEQ, Index: 0}}, // next == b.Succs[1]
}
// To model a 'GTnoov' ('>' without overflow checking) branching
var gtJumps = [2][2]gc.IndexJump{
var gtJumps = [2][2]ssagen.IndexJump{
{{Jump: arm.ABMI, Index: 1}, {Jump: arm.ABEQ, Index: 1}}, // next == b.Succs[0]
{{Jump: arm.ABEQ, Index: 1}, {Jump: arm.ABPL, Index: 0}}, // next == b.Succs[1]
}
func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
case ssa.BlockPlain:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockDefer:
@@ -929,11 +931,11 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
p.Reg = arm.REG_R0
p = s.Prog(arm.ABNE)
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockExit:

View File

@@ -5,12 +5,12 @@
package arm64
import (
"cmd/compile/internal/gc"
"cmd/compile/internal/ssa"
"cmd/compile/internal/ssagen"
"cmd/internal/obj/arm64"
)
func Init(arch *gc.Arch) {
func Init(arch *ssagen.ArchInfo) {
arch.LinkArch = &arm64.Linkarm64
arch.REGSP = arm64.REGSP
arch.MAXWIDTH = 1 << 50
@@ -20,7 +20,7 @@ func Init(arch *gc.Arch) {
arch.Ginsnop = ginsnop
arch.Ginsnopdefer = ginsnop
arch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {}
arch.SSAGenValue = ssaGenValue
arch.SSAGenBlock = ssaGenBlock
}

View File

@@ -5,7 +5,9 @@
package arm64
import (
"cmd/compile/internal/gc"
"cmd/compile/internal/ir"
"cmd/compile/internal/objw"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/arm64"
"cmd/internal/objabi"
@@ -22,52 +24,52 @@ func padframe(frame int64) int64 {
return frame
}
func zerorange(pp *gc.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog {
if cnt == 0 {
return p
}
if cnt < int64(4*gc.Widthptr) {
for i := int64(0); i < cnt; i += int64(gc.Widthptr) {
p = pp.Appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+off+i)
if cnt < int64(4*types.PtrSize) {
for i := int64(0); i < cnt; i += int64(types.PtrSize) {
p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+off+i)
}
} else if cnt <= int64(128*gc.Widthptr) && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend
if cnt%(2*int64(gc.Widthptr)) != 0 {
p = pp.Appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+off)
off += int64(gc.Widthptr)
cnt -= int64(gc.Widthptr)
} else if cnt <= int64(128*types.PtrSize) && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend
if cnt%(2*int64(types.PtrSize)) != 0 {
p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+off)
off += int64(types.PtrSize)
cnt -= int64(types.PtrSize)
}
p = pp.Appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REG_R20, 0)
p = pp.Appendpp(p, arm64.AADD, obj.TYPE_CONST, 0, 8+off, obj.TYPE_REG, arm64.REG_R20, 0)
p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REG_R20, 0)
p = pp.Append(p, arm64.AADD, obj.TYPE_CONST, 0, 8+off, obj.TYPE_REG, arm64.REG_R20, 0)
p.Reg = arm64.REG_R20
p = pp.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
p.To.Name = obj.NAME_EXTERN
p.To.Sym = gc.Duffzero
p.To.Offset = 4 * (64 - cnt/(2*int64(gc.Widthptr)))
p.To.Sym = ir.Syms.Duffzero
p.To.Offset = 4 * (64 - cnt/(2*int64(types.PtrSize)))
} else {
// Not using REGTMP, so this is async preemptible (async preemption clobbers REGTMP).
// We are at the function entry, where no register is live, so it is okay to clobber
// other registers
const rtmp = arm64.REG_R20
p = pp.Appendpp(p, arm64.AMOVD, obj.TYPE_CONST, 0, 8+off-8, obj.TYPE_REG, rtmp, 0)
p = pp.Appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0)
p = pp.Appendpp(p, arm64.AADD, obj.TYPE_REG, rtmp, 0, obj.TYPE_REG, arm64.REGRT1, 0)
p = pp.Append(p, arm64.AMOVD, obj.TYPE_CONST, 0, 8+off-8, obj.TYPE_REG, rtmp, 0)
p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0)
p = pp.Append(p, arm64.AADD, obj.TYPE_REG, rtmp, 0, obj.TYPE_REG, arm64.REGRT1, 0)
p.Reg = arm64.REGRT1
p = pp.Appendpp(p, arm64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, rtmp, 0)
p = pp.Appendpp(p, arm64.AADD, obj.TYPE_REG, rtmp, 0, obj.TYPE_REG, arm64.REGRT2, 0)
p = pp.Append(p, arm64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, rtmp, 0)
p = pp.Append(p, arm64.AADD, obj.TYPE_REG, rtmp, 0, obj.TYPE_REG, arm64.REGRT2, 0)
p.Reg = arm64.REGRT1
p = pp.Appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGRT1, int64(gc.Widthptr))
p = pp.Append(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGRT1, int64(types.PtrSize))
p.Scond = arm64.C_XPRE
p1 := p
p = pp.Appendpp(p, arm64.ACMP, obj.TYPE_REG, arm64.REGRT1, 0, obj.TYPE_NONE, 0, 0)
p = pp.Append(p, arm64.ACMP, obj.TYPE_REG, arm64.REGRT1, 0, obj.TYPE_NONE, 0, 0)
p.Reg = arm64.REGRT2
p = pp.Appendpp(p, arm64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
gc.Patch(p, p1)
p = pp.Append(p, arm64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
p.To.SetTarget(p1)
}
return p
}
func ginsnop(pp *gc.Progs) *obj.Prog {
func ginsnop(pp *objw.Progs) *obj.Prog {
p := pp.Prog(arm64.AHINT)
p.From.Type = obj.TYPE_CONST
return p

View File

@@ -7,9 +7,11 @@ package arm64
import (
"math"
"cmd/compile/internal/gc"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
"cmd/compile/internal/ssa"
"cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/arm64"
@@ -81,7 +83,7 @@ func makeshift(reg int16, typ int64, s int64) int64 {
}
// genshift generates a Prog for r = r0 op (r1 shifted by n)
func genshift(s *gc.SSAGenState, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
func genshift(s *ssagen.State, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
p := s.Prog(as)
p.From.Type = obj.TYPE_SHIFT
p.From.Offset = makeshift(r1, typ, n)
@@ -110,7 +112,7 @@ func genIndexedOperand(v *ssa.Value) obj.Addr {
return mop
}
func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
func ssaGenValue(s *ssagen.State, v *ssa.Value) {
switch v.Op {
case ssa.OpCopy, ssa.OpARM64MOVDreg:
if v.Type.IsMemory() {
@@ -148,7 +150,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
return
}
p := s.Prog(loadByType(v.Type))
gc.AddrAuto(&p.From, v.Args[0])
ssagen.AddrAuto(&p.From, v.Args[0])
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpStoreReg:
@@ -159,7 +161,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(storeByType(v.Type))
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
gc.AddrAuto(&p.To, v)
ssagen.AddrAuto(&p.To, v)
case ssa.OpARM64ADD,
ssa.OpARM64SUB,
ssa.OpARM64AND,
@@ -393,10 +395,10 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
v.Fatalf("aux is of unknown type %T", v.Aux)
case *obj.LSym:
wantreg = "SB"
gc.AddAux(&p.From, v)
case *gc.Node:
ssagen.AddAux(&p.From, v)
case *ir.Name:
wantreg = "SP"
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
case nil:
// No sym, just MOVD $off(SP), R
wantreg = "SP"
@@ -417,7 +419,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpARM64MOVBloadidx,
@@ -444,7 +446,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0()
case ssa.OpARM64MOVBstore,
@@ -461,7 +463,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
gc.AddAux(&p.To, v)
ssagen.AddAux(&p.To, v)
case ssa.OpARM64MOVBstoreidx,
ssa.OpARM64MOVHstoreidx,
ssa.OpARM64MOVWstoreidx,
@@ -482,7 +484,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = int64(v.Args[2].Reg())
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
gc.AddAux(&p.To, v)
ssagen.AddAux(&p.To, v)
case ssa.OpARM64MOVBstorezero,
ssa.OpARM64MOVHstorezero,
ssa.OpARM64MOVWstorezero,
@@ -492,7 +494,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = arm64.REGZERO
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
gc.AddAux(&p.To, v)
ssagen.AddAux(&p.To, v)
case ssa.OpARM64MOVBstorezeroidx,
ssa.OpARM64MOVHstorezeroidx,
ssa.OpARM64MOVWstorezeroidx,
@@ -511,7 +513,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Offset = int64(arm64.REGZERO)
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
gc.AddAux(&p.To, v)
ssagen.AddAux(&p.To, v)
case ssa.OpARM64BFI,
ssa.OpARM64BFXIL:
r := v.Reg()
@@ -580,7 +582,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p2.From.Type = obj.TYPE_REG
p2.From.Reg = arm64.REGTMP
p2.To.Type = obj.TYPE_BRANCH
gc.Patch(p2, p)
p2.To.SetTarget(p)
case ssa.OpARM64LoweredAtomicExchange64Variant,
ssa.OpARM64LoweredAtomicExchange32Variant:
swap := arm64.ASWPALD
@@ -634,7 +636,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.From.Type = obj.TYPE_REG
p3.From.Reg = arm64.REGTMP
p3.To.Type = obj.TYPE_BRANCH
gc.Patch(p3, p)
p3.To.SetTarget(p)
case ssa.OpARM64LoweredAtomicAdd64Variant,
ssa.OpARM64LoweredAtomicAdd32Variant:
// LDADDAL Rarg1, (Rarg0), Rout
@@ -698,13 +700,13 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p4.From.Type = obj.TYPE_REG
p4.From.Reg = arm64.REGTMP
p4.To.Type = obj.TYPE_BRANCH
gc.Patch(p4, p)
p4.To.SetTarget(p)
p5 := s.Prog(arm64.ACSET)
p5.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg
p5.From.Reg = arm64.COND_EQ
p5.To.Type = obj.TYPE_REG
p5.To.Reg = out
gc.Patch(p2, p5)
p2.To.SetTarget(p5)
case ssa.OpARM64LoweredAtomicCas64Variant,
ssa.OpARM64LoweredAtomicCas32Variant:
// Rarg0: ptr
@@ -792,7 +794,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.From.Type = obj.TYPE_REG
p3.From.Reg = arm64.REGTMP
p3.To.Type = obj.TYPE_BRANCH
gc.Patch(p3, p)
p3.To.SetTarget(p)
case ssa.OpARM64LoweredAtomicAnd8Variant,
ssa.OpARM64LoweredAtomicAnd32Variant:
atomic_clear := arm64.ALDCLRALW
@@ -959,7 +961,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(obj.ADUFFZERO)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = gc.Duffzero
p.To.Sym = ir.Syms.Duffzero
p.To.Offset = v.AuxInt
case ssa.OpARM64LoweredZero:
// STP.P (ZR,ZR), 16(R16)
@@ -980,12 +982,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p2.Reg = arm64.REG_R16
p3 := s.Prog(arm64.ABLE)
p3.To.Type = obj.TYPE_BRANCH
gc.Patch(p3, p)
p3.To.SetTarget(p)
case ssa.OpARM64DUFFCOPY:
p := s.Prog(obj.ADUFFCOPY)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = gc.Duffcopy
p.To.Sym = ir.Syms.Duffcopy
p.To.Offset = v.AuxInt
case ssa.OpARM64LoweredMove:
// MOVD.P 8(R16), Rtmp
@@ -1013,7 +1015,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p3.Reg = arm64.REG_R16
p4 := s.Prog(arm64.ABLE)
p4.To.Type = obj.TYPE_BRANCH
gc.Patch(p4, p)
p4.To.SetTarget(p)
case ssa.OpARM64CALLstatic, ssa.OpARM64CALLclosure, ssa.OpARM64CALLinter:
s.Call(v)
case ssa.OpARM64LoweredWB:
@@ -1025,21 +1027,21 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = gc.BoundsCheckFunc[v.AuxInt]
p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
s.UseArgs(16) // space used in callee args area by assembly stubs
case ssa.OpARM64LoweredNilCheck:
// Issue a load which will fault if arg is nil.
p := s.Prog(arm64.AMOVB)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
gc.AddAux(&p.From, v)
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = arm64.REGTMP
if logopt.Enabled() {
logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
}
if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Line==1 in generated wrappers
gc.Warnl(v.Pos, "generated nil check")
if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Line==1 in generated wrappers
base.WarnfAt(v.Pos, "generated nil check")
}
case ssa.OpARM64Equal,
ssa.OpARM64NotEqual,
@@ -1067,12 +1069,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Reg = v.Reg()
case ssa.OpARM64LoweredGetClosurePtr:
// Closure pointer is R26 (arm64.REGCTXT).
gc.CheckLoweredGetClosurePtr(v)
ssagen.CheckLoweredGetClosurePtr(v)
case ssa.OpARM64LoweredGetCallerSP:
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(arm64.AMOVD)
p.From.Type = obj.TYPE_ADDR
p.From.Offset = -gc.Ctxt.FixedFrameSize()
p.From.Offset = -base.Ctxt.FixedFrameSize()
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -1142,24 +1144,24 @@ var blockJump = map[ssa.BlockKind]struct {
}
// To model a 'LEnoov' ('<=' without overflow checking) branching
var leJumps = [2][2]gc.IndexJump{
var leJumps = [2][2]ssagen.IndexJump{
{{Jump: arm64.ABEQ, Index: 0}, {Jump: arm64.ABPL, Index: 1}}, // next == b.Succs[0]
{{Jump: arm64.ABMI, Index: 0}, {Jump: arm64.ABEQ, Index: 0}}, // next == b.Succs[1]
}
// To model a 'GTnoov' ('>' without overflow checking) branching
var gtJumps = [2][2]gc.IndexJump{
var gtJumps = [2][2]ssagen.IndexJump{
{{Jump: arm64.ABMI, Index: 1}, {Jump: arm64.ABEQ, Index: 1}}, // next == b.Succs[0]
{{Jump: arm64.ABEQ, Index: 1}, {Jump: arm64.ABPL, Index: 0}}, // next == b.Succs[1]
}
func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
case ssa.BlockPlain:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockDefer:
@@ -1172,11 +1174,11 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
p.Reg = arm64.REG_R0
p = s.Prog(arm64.ABNE)
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockExit:

View File

@@ -1,15 +1,43 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Copyright 2009 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 gc
package base
import (
"cmd/compile/internal/types"
"cmd/internal/src"
"cmd/internal/sys"
"os"
)
var atExitFuncs []func()
func AtExit(f func()) {
atExitFuncs = append(atExitFuncs, f)
}
func Exit(code int) {
for i := len(atExitFuncs) - 1; i >= 0; i-- {
f := atExitFuncs[i]
atExitFuncs = atExitFuncs[:i]
f()
}
os.Exit(code)
}
// To enable tracing support (-t flag), set EnableTrace to true.
const EnableTrace = false
func Compiling(pkgs []string) bool {
if Ctxt.Pkgpath != "" {
for _, p := range pkgs {
if Ctxt.Pkgpath == p {
return true
}
}
}
return false
}
// The racewalk pass is currently handled in three parts.
//
// First, for flag_race, it inserts calls to racefuncenter and
@@ -32,7 +60,7 @@ import (
// Do not instrument the following packages at all,
// at best instrumentation would cause infinite recursion.
var omit_pkgs = []string{
var NoInstrumentPkgs = []string{
"runtime/internal/atomic",
"runtime/internal/sys",
"runtime/internal/math",
@@ -44,50 +72,4 @@ var omit_pkgs = []string{
// Don't insert racefuncenterfp/racefuncexit into the following packages.
// Memory accesses in the packages are either uninteresting or will cause false positives.
var norace_inst_pkgs = []string{"sync", "sync/atomic"}
func ispkgin(pkgs []string) bool {
if myimportpath != "" {
for _, p := range pkgs {
if myimportpath == p {
return true
}
}
}
return false
}
func instrument(fn *Node) {
if fn.Func.Pragma&Norace != 0 {
return
}
if !flag_race || !ispkgin(norace_inst_pkgs) {
fn.Func.SetInstrumentBody(true)
}
if flag_race {
lno := lineno
lineno = src.NoXPos
if thearch.LinkArch.Arch.Family != sys.AMD64 {
fn.Func.Enter.Prepend(mkcall("racefuncenterfp", nil, nil))
fn.Func.Exit.Append(mkcall("racefuncexit", nil, nil))
} else {
// nodpc is the PC of the caller as extracted by
// getcallerpc. We use -widthptr(FP) for x86.
// This only works for amd64. This will not
// work on arm or others that might support
// race in the future.
nodpc := nodfp.copy()
nodpc.Type = types.Types[TUINTPTR]
nodpc.Xoffset = int64(-Widthptr)
fn.Func.Dcl = append(fn.Func.Dcl, nodpc)
fn.Func.Enter.Prepend(mkcall("racefuncenter", nil, nil, nodpc))
fn.Func.Exit.Append(mkcall("racefuncexit", nil, nil))
}
lineno = lno
}
}
var NoRacePkgs = []string{"sync", "sync/atomic"}

View File

@@ -0,0 +1,194 @@
// Copyright 2009 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.
// Debug arguments, set by -d flag.
package base
import (
"fmt"
"log"
"os"
"reflect"
"strconv"
"strings"
"cmd/internal/objabi"
)
// Debug holds the parsed debugging configuration values.
var Debug = DebugFlags{
Fieldtrack: &objabi.Fieldtrack_enabled,
}
// DebugFlags defines the debugging configuration values (see var Debug).
// Each struct field is a different value, named for the lower-case of the field name.
// Each field must be an int or string and must have a `help` struct tag.
//
// The -d option takes a comma-separated list of settings.
// Each setting is name=value; for ints, name is short for name=1.
type DebugFlags struct {
Append int `help:"print information about append compilation"`
Checkptr int `help:"instrument unsafe pointer conversions"`
Closure int `help:"print information about closure compilation"`
DclStack int `help:"run internal dclstack check"`
Defer int `help:"print information about defer compilation"`
DisableNil int `help:"disable nil checks"`
DumpPtrs int `help:"show Node pointers values in dump output"`
DwarfInl int `help:"print information about DWARF inlined function creation"`
Export int `help:"print export data"`
Fieldtrack *int `help:"enable field tracking"`
GCProg int `help:"print dump of GC programs"`
Libfuzzer int `help:"enable coverage instrumentation for libfuzzer"`
LocationLists int `help:"print information about DWARF location list creation"`
Nil int `help:"print information about nil checks"`
PCTab string `help:"print named pc-value table"`
Panic int `help:"show all compiler panics"`
Slice int `help:"print information about slice compilation"`
SoftFloat int `help:"force compiler to emit soft-float code"`
TypeAssert int `help:"print information about type assertion inlining"`
TypecheckInl int `help:"eager typechecking of inline function bodies"`
WB int `help:"print information about write barriers"`
ABIWrap int `help:"print information about ABI wrapper generation"`
any bool // set when any of the values have been set
}
// Any reports whether any of the debug flags have been set.
func (d *DebugFlags) Any() bool { return d.any }
type debugField struct {
name string
help string
val interface{} // *int or *string
}
var debugTab []debugField
func init() {
v := reflect.ValueOf(&Debug).Elem()
t := v.Type()
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Name == "any" {
continue
}
name := strings.ToLower(f.Name)
help := f.Tag.Get("help")
if help == "" {
panic(fmt.Sprintf("base.Debug.%s is missing help text", f.Name))
}
ptr := v.Field(i).Addr().Interface()
switch ptr.(type) {
default:
panic(fmt.Sprintf("base.Debug.%s has invalid type %v (must be int or string)", f.Name, f.Type))
case *int, *string:
// ok
case **int:
ptr = *ptr.(**int) // record the *int itself
}
debugTab = append(debugTab, debugField{name, help, ptr})
}
}
// DebugSSA is called to set a -d ssa/... option.
// If nil, those options are reported as invalid options.
// If DebugSSA returns a non-empty string, that text is reported as a compiler error.
var DebugSSA func(phase, flag string, val int, valString string) string
// parseDebug parses the -d debug string argument.
func parseDebug(debugstr string) {
// parse -d argument
if debugstr == "" {
return
}
Debug.any = true
Split:
for _, name := range strings.Split(debugstr, ",") {
if name == "" {
continue
}
// display help about the -d option itself and quit
if name == "help" {
fmt.Print(debugHelpHeader)
maxLen := len("ssa/help")
for _, t := range debugTab {
if len(t.name) > maxLen {
maxLen = len(t.name)
}
}
for _, t := range debugTab {
fmt.Printf("\t%-*s\t%s\n", maxLen, t.name, t.help)
}
// ssa options have their own help
fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging")
fmt.Print(debugHelpFooter)
os.Exit(0)
}
val, valstring, haveInt := 1, "", true
if i := strings.IndexAny(name, "=:"); i >= 0 {
var err error
name, valstring = name[:i], name[i+1:]
val, err = strconv.Atoi(valstring)
if err != nil {
val, haveInt = 1, false
}
}
for _, t := range debugTab {
if t.name != name {
continue
}
switch vp := t.val.(type) {
case nil:
// Ignore
case *string:
*vp = valstring
case *int:
if !haveInt {
log.Fatalf("invalid debug value %v", name)
}
*vp = val
default:
panic("bad debugtab type")
}
continue Split
}
// special case for ssa for now
if DebugSSA != nil && strings.HasPrefix(name, "ssa/") {
// expect form ssa/phase/flag
// e.g. -d=ssa/generic_cse/time
// _ in phase name also matches space
phase := name[4:]
flag := "debug" // default flag is debug
if i := strings.Index(phase, "/"); i >= 0 {
flag = phase[i+1:]
phase = phase[:i]
}
err := DebugSSA(phase, flag, val, valstring)
if err != "" {
log.Fatalf(err)
}
continue Split
}
log.Fatalf("unknown debug key -d %s\n", name)
}
}
const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>]
<key> is one of:
`
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"
`

View File

@@ -0,0 +1,459 @@
// Copyright 2009 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 base
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"reflect"
"runtime"
"strings"
"cmd/internal/objabi"
"cmd/internal/sys"
)
func usage() {
fmt.Fprintf(os.Stderr, "usage: compile [options] file.go...\n")
objabi.Flagprint(os.Stderr)
Exit(2)
}
// Flag holds the parsed command-line flags.
// See ParseFlag for non-zero defaults.
var Flag CmdFlags
// A CountFlag is a counting integer flag.
// It accepts -name=value to set the value directly,
// but it also accepts -name with no =value to increment the count.
type CountFlag int
// CmdFlags defines the command-line flags (see var Flag).
// Each struct field is a different flag, by default named for the lower-case of the field name.
// If the flag name is a single letter, the default flag name is left upper-case.
// If the flag name is "Lower" followed by a single letter, the default flag name is the lower-case of the last letter.
//
// If this default flag name can't be made right, the `flag` struct tag can be used to replace it,
// but this should be done only in exceptional circumstances: it helps everyone if the flag name
// is obvious from the field name when the flag is used elsewhere in the compiler sources.
// The `flag:"-"` struct tag makes a field invisible to the flag logic and should also be used sparingly.
//
// Each field must have a `help` struct tag giving the flag help message.
//
// The allowed field types are bool, int, string, pointers to those (for values stored elsewhere),
// CountFlag (for a counting flag), and func(string) (for a flag that uses special code for parsing).
type CmdFlags struct {
// Single letters
B CountFlag "help:\"disable bounds checking\""
C CountFlag "help:\"disable printing of columns in error messages\""
D string "help:\"set relative `path` for local imports\""
E CountFlag "help:\"debug symbol export\""
I func(string) "help:\"add `directory` to import search path\""
K CountFlag "help:\"debug missing line numbers\""
L CountFlag "help:\"show full file names in error messages\""
N CountFlag "help:\"disable optimizations\""
S CountFlag "help:\"print assembly listing\""
// V is added by objabi.AddVersionFlag
W CountFlag "help:\"debug parse tree after type checking\""
LowerC int "help:\"concurrency during compilation (1 means no concurrency)\""
LowerD func(string) "help:\"enable debugging settings; try -d help\""
LowerE CountFlag "help:\"no limit on number of errors reported\""
LowerH CountFlag "help:\"halt on error\""
LowerJ CountFlag "help:\"debug runtime-initialized variables\""
LowerL CountFlag "help:\"disable inlining\""
LowerM CountFlag "help:\"print optimization decisions\""
LowerO string "help:\"write output to `file`\""
LowerP *string "help:\"set expected package import `path`\"" // &Ctxt.Pkgpath, set below
LowerR CountFlag "help:\"debug generated wrappers\""
LowerT bool "help:\"enable tracing for debugging the compiler\""
LowerW CountFlag "help:\"debug type checking\""
LowerV *bool "help:\"increase debug verbosity\""
// Special characters
Percent int "flag:\"%\" help:\"debug non-static initializers\""
CompilingRuntime bool "flag:\"+\" help:\"compiling runtime\""
// Longer names
ABIWrap bool "help:\"enable generation of ABI wrappers\""
ABIWrapLimit int "help:\"emit at most N ABI wrappers (for debugging)\""
AsmHdr string "help:\"write assembly header to `file`\""
Bench string "help:\"append benchmark times to `file`\""
BlockProfile string "help:\"write block profile to `file`\""
BuildID string "help:\"record `id` as the build id in the export metadata\""
CPUProfile string "help:\"write cpu profile to `file`\""
Complete bool "help:\"compiling complete package (no C or assembly)\""
Dwarf bool "help:\"generate DWARF symbols\""
DwarfBASEntries *bool "help:\"use base address selection entries in DWARF\"" // &Ctxt.UseBASEntries, set below
DwarfLocationLists *bool "help:\"add location lists to DWARF in optimized mode\"" // &Ctxt.Flag_locationlists, set below
Dynlink *bool "help:\"support references to Go symbols defined in other shared libraries\"" // &Ctxt.Flag_dynlink, set below
EmbedCfg func(string) "help:\"read go:embed configuration from `file`\""
GenDwarfInl int "help:\"generate DWARF inline info records\"" // 0=disabled, 1=funcs, 2=funcs+formals/locals
GoVersion string "help:\"required version of the runtime\""
ImportCfg func(string) "help:\"read import configuration from `file`\""
ImportMap func(string) "help:\"add `definition` of the form source=actual to import map\""
InstallSuffix string "help:\"set pkg directory `suffix`\""
JSON string "help:\"version,file for JSON compiler/optimizer detail output\""
Lang string "help:\"Go language version source code expects\""
LinkObj string "help:\"write linker-specific object to `file`\""
LinkShared *bool "help:\"generate code that will be linked against Go shared libraries\"" // &Ctxt.Flag_linkshared, set below
Live CountFlag "help:\"debug liveness analysis\""
MSan bool "help:\"build code compatible with C/C++ memory sanitizer\""
MemProfile string "help:\"write memory profile to `file`\""
MemProfileRate int64 "help:\"set runtime.MemProfileRate to `rate`\""
MutexProfile string "help:\"write mutex profile to `file`\""
NoLocalImports bool "help:\"reject local (relative) imports\""
Pack bool "help:\"write to file.a instead of file.o\""
Race bool "help:\"enable race detector\""
Shared *bool "help:\"generate code that can be linked into a shared library\"" // &Ctxt.Flag_shared, set below
SmallFrames bool "help:\"reduce the size limit for stack allocated objects\"" // small stacks, to diagnose GC latency; see golang.org/issue/27732
Spectre string "help:\"enable spectre mitigations in `list` (all, index, ret)\""
Std bool "help:\"compiling standard library\""
SymABIs string "help:\"read symbol ABIs from `file`\""
TraceProfile string "help:\"write an execution trace to `file`\""
TrimPath string "help:\"remove `prefix` from recorded source file paths\""
WB bool "help:\"enable write barrier\"" // TODO: remove
// Configuration derived from flags; not a flag itself.
Cfg struct {
Embed struct { // set by -embedcfg
Patterns map[string][]string
Files map[string]string
}
ImportDirs []string // appended to by -I
ImportMap map[string]string // set by -importmap OR -importcfg
PackageFile map[string]string // set by -importcfg; nil means not in use
SpectreIndex bool // set by -spectre=index or -spectre=all
// Whether we are adding any sort of code instrumentation, such as
// when the race detector is enabled.
Instrumenting bool
}
}
// ParseFlags parses the command-line flags into Flag.
func ParseFlags() {
Flag.I = addImportDir
Flag.LowerC = 1
Flag.LowerD = parseDebug
Flag.LowerP = &Ctxt.Pkgpath
Flag.LowerV = &Ctxt.Debugvlog
Flag.ABIWrap = objabi.Regabi_enabled != 0
Flag.Dwarf = objabi.GOARCH != "wasm"
Flag.DwarfBASEntries = &Ctxt.UseBASEntries
Flag.DwarfLocationLists = &Ctxt.Flag_locationlists
*Flag.DwarfLocationLists = true
Flag.Dynlink = &Ctxt.Flag_dynlink
Flag.EmbedCfg = readEmbedCfg
Flag.GenDwarfInl = 2
Flag.ImportCfg = readImportCfg
Flag.ImportMap = addImportMap
Flag.LinkShared = &Ctxt.Flag_linkshared
Flag.Shared = &Ctxt.Flag_shared
Flag.WB = true
Flag.Cfg.ImportMap = make(map[string]string)
objabi.AddVersionFlag() // -V
registerFlags()
objabi.Flagparse(usage)
if Flag.MSan && !sys.MSanSupported(objabi.GOOS, objabi.GOARCH) {
log.Fatalf("%s/%s does not support -msan", objabi.GOOS, objabi.GOARCH)
}
if Flag.Race && !sys.RaceDetectorSupported(objabi.GOOS, objabi.GOARCH) {
log.Fatalf("%s/%s does not support -race", objabi.GOOS, objabi.GOARCH)
}
if (*Flag.Shared || *Flag.Dynlink || *Flag.LinkShared) && !Ctxt.Arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.RISCV64, sys.S390X) {
log.Fatalf("%s/%s does not support -shared", objabi.GOOS, objabi.GOARCH)
}
parseSpectre(Flag.Spectre) // left as string for RecordFlags
Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared
Ctxt.Flag_optimize = Flag.N == 0
Ctxt.Debugasm = int(Flag.S)
if flag.NArg() < 1 {
usage()
}
if Flag.GoVersion != "" && Flag.GoVersion != runtime.Version() {
fmt.Printf("compile: version %q does not match go tool version %q\n", runtime.Version(), Flag.GoVersion)
Exit(2)
}
if Flag.LowerO == "" {
p := flag.Arg(0)
if i := strings.LastIndex(p, "/"); i >= 0 {
p = p[i+1:]
}
if runtime.GOOS == "windows" {
if i := strings.LastIndex(p, `\`); i >= 0 {
p = p[i+1:]
}
}
if i := strings.LastIndex(p, "."); i >= 0 {
p = p[:i]
}
suffix := ".o"
if Flag.Pack {
suffix = ".a"
}
Flag.LowerO = p + suffix
}
if Flag.Race && Flag.MSan {
log.Fatal("cannot use both -race and -msan")
}
if Flag.Race || Flag.MSan {
// -race and -msan imply -d=checkptr for now.
Debug.Checkptr = 1
}
if Flag.CompilingRuntime && Flag.N != 0 {
log.Fatal("cannot disable optimizations while compiling runtime")
}
if Flag.LowerC < 1 {
log.Fatalf("-c must be at least 1, got %d", Flag.LowerC)
}
if Flag.LowerC > 1 && !concurrentBackendAllowed() {
log.Fatalf("cannot use concurrent backend compilation with provided flags; invoked as %v", os.Args)
}
if Flag.CompilingRuntime {
// Runtime can't use -d=checkptr, at least not yet.
Debug.Checkptr = 0
// Fuzzing the runtime isn't interesting either.
Debug.Libfuzzer = 0
}
// set via a -d flag
Ctxt.Debugpcln = Debug.PCTab
}
// registerFlags adds flag registrations for all the fields in Flag.
// See the comment on type CmdFlags for the rules.
func registerFlags() {
var (
boolType = reflect.TypeOf(bool(false))
intType = reflect.TypeOf(int(0))
stringType = reflect.TypeOf(string(""))
ptrBoolType = reflect.TypeOf(new(bool))
ptrIntType = reflect.TypeOf(new(int))
ptrStringType = reflect.TypeOf(new(string))
countType = reflect.TypeOf(CountFlag(0))
funcType = reflect.TypeOf((func(string))(nil))
)
v := reflect.ValueOf(&Flag).Elem()
t := v.Type()
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Name == "Cfg" {
continue
}
var name string
if len(f.Name) == 1 {
name = f.Name
} else if len(f.Name) == 6 && f.Name[:5] == "Lower" && 'A' <= f.Name[5] && f.Name[5] <= 'Z' {
name = string(rune(f.Name[5] + 'a' - 'A'))
} else {
name = strings.ToLower(f.Name)
}
if tag := f.Tag.Get("flag"); tag != "" {
name = tag
}
help := f.Tag.Get("help")
if help == "" {
panic(fmt.Sprintf("base.Flag.%s is missing help text", f.Name))
}
if k := f.Type.Kind(); (k == reflect.Ptr || k == reflect.Func) && v.Field(i).IsNil() {
panic(fmt.Sprintf("base.Flag.%s is uninitialized %v", f.Name, f.Type))
}
switch f.Type {
case boolType:
p := v.Field(i).Addr().Interface().(*bool)
flag.BoolVar(p, name, *p, help)
case intType:
p := v.Field(i).Addr().Interface().(*int)
flag.IntVar(p, name, *p, help)
case stringType:
p := v.Field(i).Addr().Interface().(*string)
flag.StringVar(p, name, *p, help)
case ptrBoolType:
p := v.Field(i).Interface().(*bool)
flag.BoolVar(p, name, *p, help)
case ptrIntType:
p := v.Field(i).Interface().(*int)
flag.IntVar(p, name, *p, help)
case ptrStringType:
p := v.Field(i).Interface().(*string)
flag.StringVar(p, name, *p, help)
case countType:
p := (*int)(v.Field(i).Addr().Interface().(*CountFlag))
objabi.Flagcount(name, help, p)
case funcType:
f := v.Field(i).Interface().(func(string))
objabi.Flagfn1(name, help, f)
}
}
}
// concurrentFlagOk reports whether the current compiler flags
// are compatible with concurrent compilation.
func concurrentFlagOk() bool {
// TODO(rsc): Many of these are fine. Remove them.
return Flag.Percent == 0 &&
Flag.E == 0 &&
Flag.K == 0 &&
Flag.L == 0 &&
Flag.LowerH == 0 &&
Flag.LowerJ == 0 &&
Flag.LowerM == 0 &&
Flag.LowerR == 0
}
func concurrentBackendAllowed() bool {
if !concurrentFlagOk() {
return false
}
// Debug.S by itself is ok, because all printing occurs
// while writing the object file, and that is non-concurrent.
// Adding Debug_vlog, however, causes Debug.S to also print
// while flushing the plist, which happens concurrently.
if Ctxt.Debugvlog || Debug.Any() || Flag.Live > 0 {
return false
}
// TODO: Test and delete this condition.
if objabi.Fieldtrack_enabled != 0 {
return false
}
// TODO: fix races and enable the following flags
if Ctxt.Flag_shared || Ctxt.Flag_dynlink || Flag.Race {
return false
}
return true
}
func addImportDir(dir string) {
if dir != "" {
Flag.Cfg.ImportDirs = append(Flag.Cfg.ImportDirs, dir)
}
}
func addImportMap(s string) {
if Flag.Cfg.ImportMap == nil {
Flag.Cfg.ImportMap = make(map[string]string)
}
if strings.Count(s, "=") != 1 {
log.Fatal("-importmap argument must be of the form source=actual")
}
i := strings.Index(s, "=")
source, actual := s[:i], s[i+1:]
if source == "" || actual == "" {
log.Fatal("-importmap argument must be of the form source=actual; source and actual must be non-empty")
}
Flag.Cfg.ImportMap[source] = actual
}
func readImportCfg(file string) {
if Flag.Cfg.ImportMap == nil {
Flag.Cfg.ImportMap = make(map[string]string)
}
Flag.Cfg.PackageFile = map[string]string{}
data, err := ioutil.ReadFile(file)
if err != nil {
log.Fatalf("-importcfg: %v", err)
}
for lineNum, line := range strings.Split(string(data), "\n") {
lineNum++ // 1-based
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
var verb, args string
if i := strings.Index(line, " "); i < 0 {
verb = line
} else {
verb, args = line[:i], strings.TrimSpace(line[i+1:])
}
var before, after string
if i := strings.Index(args, "="); i >= 0 {
before, after = args[:i], args[i+1:]
}
switch verb {
default:
log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
case "importmap":
if before == "" || after == "" {
log.Fatalf(`%s:%d: invalid importmap: syntax is "importmap old=new"`, file, lineNum)
}
Flag.Cfg.ImportMap[before] = after
case "packagefile":
if before == "" || after == "" {
log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
}
Flag.Cfg.PackageFile[before] = after
}
}
}
func readEmbedCfg(file string) {
data, err := ioutil.ReadFile(file)
if err != nil {
log.Fatalf("-embedcfg: %v", err)
}
if err := json.Unmarshal(data, &Flag.Cfg.Embed); err != nil {
log.Fatalf("%s: %v", file, err)
}
if Flag.Cfg.Embed.Patterns == nil {
log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
}
if Flag.Cfg.Embed.Files == nil {
log.Fatalf("%s: invalid embedcfg: missing Files", file)
}
}
// parseSpectre parses the spectre configuration from the string s.
func parseSpectre(s string) {
for _, f := range strings.Split(s, ",") {
f = strings.TrimSpace(f)
switch f {
default:
log.Fatalf("unknown setting -spectre=%s", f)
case "":
// nothing
case "all":
Flag.Cfg.SpectreIndex = true
Ctxt.Retpoline = true
case "index":
Flag.Cfg.SpectreIndex = true
case "ret":
Ctxt.Retpoline = true
}
}
if Flag.Cfg.SpectreIndex {
switch objabi.GOARCH {
case "amd64":
// ok
default:
log.Fatalf("GOARCH=%s does not support -spectre=index", objabi.GOARCH)
}
}
}

View File

@@ -0,0 +1,36 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package base
import (
"cmd/internal/obj"
)
var Ctxt *obj.Link
// TODO(mdempsky): These should probably be obj.Link methods.
// PkgLinksym returns the linker symbol for name within the given
// package prefix. For user packages, prefix should be the package
// path encoded with objabi.PathToPrefix.
func PkgLinksym(prefix, name string, abi obj.ABI) *obj.LSym {
if name == "_" {
// TODO(mdempsky): Cleanup callers and Fatalf instead.
return linksym(prefix, "_", abi)
}
return linksym(prefix, prefix+"."+name, abi)
}
// Linkname returns the linker symbol for the given name as it might
// appear within a //go:linkname directive.
func Linkname(name string, abi obj.ABI) *obj.LSym {
return linksym("_", name, abi)
}
// linksym is an internal helper function for implementing the above
// exported APIs.
func linksym(pkg, name string, abi obj.ABI) *obj.LSym {
return Ctxt.LookupABIInit(name, abi, func(r *obj.LSym) { r.Pkg = pkg })
}

View File

@@ -0,0 +1,264 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package base
import (
"fmt"
"os"
"runtime/debug"
"sort"
"strings"
"cmd/internal/objabi"
"cmd/internal/src"
)
// An errorMsg is a queued error message, waiting to be printed.
type errorMsg struct {
pos src.XPos
msg string
}
// Pos is the current source position being processed,
// printed by Errorf, ErrorfLang, Fatalf, and Warnf.
var Pos src.XPos
var (
errorMsgs []errorMsg
numErrors int // number of entries in errorMsgs that are errors (as opposed to warnings)
numSyntaxErrors int
)
// Errors returns the number of errors reported.
func Errors() int {
return numErrors
}
// SyntaxErrors returns the number of syntax errors reported
func SyntaxErrors() int {
return numSyntaxErrors
}
// addErrorMsg adds a new errorMsg (which may be a warning) to errorMsgs.
func addErrorMsg(pos src.XPos, format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
// Only add the position if know the position.
// See issue golang.org/issue/11361.
if pos.IsKnown() {
msg = fmt.Sprintf("%v: %s", FmtPos(pos), msg)
}
errorMsgs = append(errorMsgs, errorMsg{
pos: pos,
msg: msg + "\n",
})
}
// FmtPos formats pos as a file:line string.
func FmtPos(pos src.XPos) string {
if Ctxt == nil {
return "???"
}
return Ctxt.OutermostPos(pos).Format(Flag.C == 0, Flag.L == 1)
}
// byPos sorts errors by source position.
type byPos []errorMsg
func (x byPos) Len() int { return len(x) }
func (x byPos) Less(i, j int) bool { return x[i].pos.Before(x[j].pos) }
func (x byPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
// FlushErrors sorts errors seen so far by line number, prints them to stdout,
// and empties the errors array.
func FlushErrors() {
if Ctxt != nil && Ctxt.Bso != nil {
Ctxt.Bso.Flush()
}
if len(errorMsgs) == 0 {
return
}
sort.Stable(byPos(errorMsgs))
for i, err := range errorMsgs {
if i == 0 || err.msg != errorMsgs[i-1].msg {
fmt.Printf("%s", err.msg)
}
}
errorMsgs = errorMsgs[:0]
}
// lasterror keeps track of the most recently issued error,
// to avoid printing multiple error messages on the same line.
var lasterror struct {
syntax src.XPos // source position of last syntax error
other src.XPos // source position of last non-syntax error
msg string // error message of last non-syntax error
}
// sameline reports whether two positions a, b are on the same line.
func sameline(a, b src.XPos) bool {
p := Ctxt.PosTable.Pos(a)
q := Ctxt.PosTable.Pos(b)
return p.Base() == q.Base() && p.Line() == q.Line()
}
// Errorf reports a formatted error at the current line.
func Errorf(format string, args ...interface{}) {
ErrorfAt(Pos, format, args...)
}
// ErrorfAt reports a formatted error message at pos.
func ErrorfAt(pos src.XPos, format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
if strings.HasPrefix(msg, "syntax error") {
numSyntaxErrors++
// only one syntax error per line, no matter what error
if sameline(lasterror.syntax, pos) {
return
}
lasterror.syntax = pos
} else {
// only one of multiple equal non-syntax errors per line
// (FlushErrors shows only one of them, so we filter them
// here as best as we can (they may not appear in order)
// so that we don't count them here and exit early, and
// then have nothing to show for.)
if sameline(lasterror.other, pos) && lasterror.msg == msg {
return
}
lasterror.other = pos
lasterror.msg = msg
}
addErrorMsg(pos, "%s", msg)
numErrors++
hcrash()
if numErrors >= 10 && Flag.LowerE == 0 {
FlushErrors()
fmt.Printf("%v: too many errors\n", FmtPos(pos))
ErrorExit()
}
}
// ErrorfVers reports that a language feature (format, args) requires a later version of Go.
func ErrorfVers(lang string, format string, args ...interface{}) {
Errorf("%s requires %s or later (-lang was set to %s; check go.mod)", fmt.Sprintf(format, args...), lang, Flag.Lang)
}
// UpdateErrorDot is a clumsy hack that rewrites the last error,
// if it was "LINE: undefined: NAME", to be "LINE: undefined: NAME in EXPR".
// It is used to give better error messages for dot (selector) expressions.
func UpdateErrorDot(line string, name, expr string) {
if len(errorMsgs) == 0 {
return
}
e := &errorMsgs[len(errorMsgs)-1]
if strings.HasPrefix(e.msg, line) && e.msg == fmt.Sprintf("%v: undefined: %v\n", line, name) {
e.msg = fmt.Sprintf("%v: undefined: %v in %v\n", line, name, expr)
}
}
// Warnf reports a formatted warning at the current line.
// In general the Go compiler does NOT generate warnings,
// so this should be used only when the user has opted in
// to additional output by setting a particular flag.
func Warn(format string, args ...interface{}) {
WarnfAt(Pos, format, args...)
}
// WarnfAt reports a formatted warning at pos.
// In general the Go compiler does NOT generate warnings,
// so this should be used only when the user has opted in
// to additional output by setting a particular flag.
func WarnfAt(pos src.XPos, format string, args ...interface{}) {
addErrorMsg(pos, format, args...)
if Flag.LowerM != 0 {
FlushErrors()
}
}
// Fatalf reports a fatal error - an internal problem - at the current line and exits.
// If other errors have already been printed, then Fatalf just quietly exits.
// (The internal problem may have been caused by incomplete information
// after the already-reported errors, so best to let users fix those and
// try again without being bothered about a spurious internal error.)
//
// But if no errors have been printed, or if -d panic has been specified,
// Fatalf prints the error as an "internal compiler error". In a released build,
// it prints an error asking to file a bug report. In development builds, it
// prints a stack trace.
//
// If -h has been specified, Fatalf panics to force the usual runtime info dump.
func Fatalf(format string, args ...interface{}) {
FatalfAt(Pos, format, args...)
}
// FatalfAt reports a fatal error - an internal problem - at pos and exits.
// If other errors have already been printed, then FatalfAt just quietly exits.
// (The internal problem may have been caused by incomplete information
// after the already-reported errors, so best to let users fix those and
// try again without being bothered about a spurious internal error.)
//
// But if no errors have been printed, or if -d panic has been specified,
// FatalfAt prints the error as an "internal compiler error". In a released build,
// it prints an error asking to file a bug report. In development builds, it
// prints a stack trace.
//
// If -h has been specified, FatalfAt panics to force the usual runtime info dump.
func FatalfAt(pos src.XPos, format string, args ...interface{}) {
FlushErrors()
if Debug.Panic != 0 || numErrors == 0 {
fmt.Printf("%v: internal compiler error: ", FmtPos(pos))
fmt.Printf(format, args...)
fmt.Printf("\n")
// If this is a released compiler version, ask for a bug report.
if strings.HasPrefix(objabi.Version, "go") {
fmt.Printf("\n")
fmt.Printf("Please file a bug report including a short program that triggers the error.\n")
fmt.Printf("https://golang.org/issue/new\n")
} else {
// Not a release; dump a stack trace, too.
fmt.Println()
os.Stdout.Write(debug.Stack())
fmt.Println()
}
}
hcrash()
ErrorExit()
}
// hcrash crashes the compiler when -h is set, to find out where a message is generated.
func hcrash() {
if Flag.LowerH != 0 {
FlushErrors()
if Flag.LowerO != "" {
os.Remove(Flag.LowerO)
}
panic("-h")
}
}
// ErrorExit handles an error-status exit.
// It flushes any pending errors, removes the output file, and exits.
func ErrorExit() {
FlushErrors()
if Flag.LowerO != "" {
os.Remove(Flag.LowerO)
}
os.Exit(2)
}
// ExitIfErrors calls ErrorExit if any errors have been reported.
func ExitIfErrors() {
if Errors() > 0 {
ErrorExit()
}
}
var AutogeneratedPos src.XPos

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gc
package base
import (
"fmt"
@@ -11,6 +11,8 @@ import (
"time"
)
var Timer Timings
// Timings collects the execution times of labeled phases
// which are added trough a sequence of Start/Stop calls.
// Events may be associated with each phase via AddEvent.

View File

@@ -0,0 +1,190 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bitvec
import (
"math/bits"
"cmd/compile/internal/base"
)
const (
wordBits = 32
wordMask = wordBits - 1
wordShift = 5
)
// A BitVec is a bit vector.
type BitVec struct {
N int32 // number of bits in vector
B []uint32 // words holding bits
}
func New(n int32) BitVec {
nword := (n + wordBits - 1) / wordBits
return BitVec{n, make([]uint32, nword)}
}
type Bulk struct {
words []uint32
nbit int32
nword int32
}
func NewBulk(nbit int32, count int32) Bulk {
nword := (nbit + wordBits - 1) / wordBits
size := int64(nword) * int64(count)
if int64(int32(size*4)) != size*4 {
base.Fatalf("NewBulk too big: nbit=%d count=%d nword=%d size=%d", nbit, count, nword, size)
}
return Bulk{
words: make([]uint32, size),
nbit: nbit,
nword: nword,
}
}
func (b *Bulk) Next() BitVec {
out := BitVec{b.nbit, b.words[:b.nword]}
b.words = b.words[b.nword:]
return out
}
func (bv1 BitVec) Eq(bv2 BitVec) bool {
if bv1.N != bv2.N {
base.Fatalf("bvequal: lengths %d and %d are not equal", bv1.N, bv2.N)
}
for i, x := range bv1.B {
if x != bv2.B[i] {
return false
}
}
return true
}
func (dst BitVec) Copy(src BitVec) {
copy(dst.B, src.B)
}
func (bv BitVec) Get(i int32) bool {
if i < 0 || i >= bv.N {
base.Fatalf("bvget: index %d is out of bounds with length %d\n", i, bv.N)
}
mask := uint32(1 << uint(i%wordBits))
return bv.B[i>>wordShift]&mask != 0
}
func (bv BitVec) Set(i int32) {
if i < 0 || i >= bv.N {
base.Fatalf("bvset: index %d is out of bounds with length %d\n", i, bv.N)
}
mask := uint32(1 << uint(i%wordBits))
bv.B[i/wordBits] |= mask
}
func (bv BitVec) Unset(i int32) {
if i < 0 || i >= bv.N {
base.Fatalf("bvunset: index %d is out of bounds with length %d\n", i, bv.N)
}
mask := uint32(1 << uint(i%wordBits))
bv.B[i/wordBits] &^= mask
}
// bvnext returns the smallest index >= i for which bvget(bv, i) == 1.
// If there is no such index, bvnext returns -1.
func (bv BitVec) Next(i int32) int32 {
if i >= bv.N {
return -1
}
// Jump i ahead to next word with bits.
if bv.B[i>>wordShift]>>uint(i&wordMask) == 0 {
i &^= wordMask
i += wordBits
for i < bv.N && bv.B[i>>wordShift] == 0 {
i += wordBits
}
}
if i >= bv.N {
return -1
}
// Find 1 bit.
w := bv.B[i>>wordShift] >> uint(i&wordMask)
i += int32(bits.TrailingZeros32(w))
return i
}
func (bv BitVec) IsEmpty() bool {
for _, x := range bv.B {
if x != 0 {
return false
}
}
return true
}
func (bv BitVec) Not() {
for i, x := range bv.B {
bv.B[i] = ^x
}
}
// union
func (dst BitVec) Or(src1, src2 BitVec) {
if len(src1.B) == 0 {
return
}
_, _ = dst.B[len(src1.B)-1], src2.B[len(src1.B)-1] // hoist bounds checks out of the loop
for i, x := range src1.B {
dst.B[i] = x | src2.B[i]
}
}
// intersection
func (dst BitVec) And(src1, src2 BitVec) {
if len(src1.B) == 0 {
return
}
_, _ = dst.B[len(src1.B)-1], src2.B[len(src1.B)-1] // hoist bounds checks out of the loop
for i, x := range src1.B {
dst.B[i] = x & src2.B[i]
}
}
// difference
func (dst BitVec) AndNot(src1, src2 BitVec) {
if len(src1.B) == 0 {
return
}
_, _ = dst.B[len(src1.B)-1], src2.B[len(src1.B)-1] // hoist bounds checks out of the loop
for i, x := range src1.B {
dst.B[i] = x &^ src2.B[i]
}
}
func (bv BitVec) String() string {
s := make([]byte, 2+bv.N)
copy(s, "#*")
for i := int32(0); i < bv.N; i++ {
ch := byte('0')
if bv.Get(i) {
ch = '1'
}
s[2+i] = ch
}
return string(s)
}
func (bv BitVec) Clear() {
for i := range bv.B {
bv.B[i] = 0
}
}

View File

@@ -0,0 +1,152 @@
// Copyright 2009 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 deadcode
import (
"go/constant"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
)
func Func(fn *ir.Func) {
stmts(&fn.Body)
if len(fn.Body) == 0 {
return
}
for _, n := range fn.Body {
if len(n.Init()) > 0 {
return
}
switch n.Op() {
case ir.OIF:
n := n.(*ir.IfStmt)
if !ir.IsConst(n.Cond, constant.Bool) || len(n.Body) > 0 || len(n.Else) > 0 {
return
}
case ir.OFOR:
n := n.(*ir.ForStmt)
if !ir.IsConst(n.Cond, constant.Bool) || ir.BoolVal(n.Cond) {
return
}
default:
return
}
}
fn.Body = []ir.Node{ir.NewBlockStmt(base.Pos, nil)}
}
func stmts(nn *ir.Nodes) {
var lastLabel = -1
for i, n := range *nn {
if n != nil && n.Op() == ir.OLABEL {
lastLabel = i
}
}
for i, n := range *nn {
// Cut is set to true when all nodes after i'th position
// should be removed.
// In other words, it marks whole slice "tail" as dead.
cut := false
if n == nil {
continue
}
if n.Op() == ir.OIF {
n := n.(*ir.IfStmt)
n.Cond = expr(n.Cond)
if ir.IsConst(n.Cond, constant.Bool) {
var body ir.Nodes
if ir.BoolVal(n.Cond) {
n.Else = ir.Nodes{}
body = n.Body
} else {
n.Body = ir.Nodes{}
body = n.Else
}
// If "then" or "else" branch ends with panic or return statement,
// it is safe to remove all statements after this node.
// isterminating is not used to avoid goto-related complications.
// We must be careful not to deadcode-remove labels, as they
// might be the target of a goto. See issue 28616.
if body := body; len(body) != 0 {
switch body[(len(body) - 1)].Op() {
case ir.ORETURN, ir.OTAILCALL, ir.OPANIC:
if i > lastLabel {
cut = true
}
}
}
}
}
if len(n.Init()) != 0 {
stmts(n.(ir.InitNode).PtrInit())
}
switch n.Op() {
case ir.OBLOCK:
n := n.(*ir.BlockStmt)
stmts(&n.List)
case ir.OFOR:
n := n.(*ir.ForStmt)
stmts(&n.Body)
case ir.OIF:
n := n.(*ir.IfStmt)
stmts(&n.Body)
stmts(&n.Else)
case ir.ORANGE:
n := n.(*ir.RangeStmt)
stmts(&n.Body)
case ir.OSELECT:
n := n.(*ir.SelectStmt)
for _, cas := range n.Cases {
stmts(&cas.Body)
}
case ir.OSWITCH:
n := n.(*ir.SwitchStmt)
for _, cas := range n.Cases {
stmts(&cas.Body)
}
}
if cut {
*nn = (*nn)[:i+1]
break
}
}
}
func expr(n ir.Node) ir.Node {
// Perform dead-code elimination on short-circuited boolean
// expressions involving constants with the intent of
// producing a constant 'if' condition.
switch n.Op() {
case ir.OANDAND:
n := n.(*ir.LogicalExpr)
n.X = expr(n.X)
n.Y = expr(n.Y)
if ir.IsConst(n.X, constant.Bool) {
if ir.BoolVal(n.X) {
return n.Y // true && x => x
} else {
return n.X // false && x => false
}
}
case ir.OOROR:
n := n.(*ir.LogicalExpr)
n.X = expr(n.X)
n.Y = expr(n.Y)
if ir.IsConst(n.X, constant.Bool) {
if ir.BoolVal(n.X) {
return n.X // true || x => true
} else {
return n.Y // false || x => x
}
}
}
return n
}

View File

@@ -0,0 +1,85 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package devirtualize implements a simple "devirtualization"
// optimization pass, which replaces interface method calls with
// direct concrete-type method calls where possible.
package devirtualize
import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
)
// Func devirtualizes calls within fn where possible.
func Func(fn *ir.Func) {
ir.CurFunc = fn
ir.VisitList(fn.Body, func(n ir.Node) {
if call, ok := n.(*ir.CallExpr); ok {
Call(call)
}
})
}
// Call devirtualizes the given call if possible.
func Call(call *ir.CallExpr) {
if call.Op() != ir.OCALLINTER {
return
}
sel := call.X.(*ir.SelectorExpr)
r := ir.StaticValue(sel.X)
if r.Op() != ir.OCONVIFACE {
return
}
recv := r.(*ir.ConvExpr)
typ := recv.X.Type()
if typ.IsInterface() {
return
}
dt := ir.NewTypeAssertExpr(sel.Pos(), sel.X, nil)
dt.SetType(typ)
x := typecheck.Callee(ir.NewSelectorExpr(sel.Pos(), ir.OXDOT, dt, sel.Sel))
switch x.Op() {
case ir.ODOTMETH:
x := x.(*ir.SelectorExpr)
if base.Flag.LowerM != 0 {
base.WarnfAt(call.Pos(), "devirtualizing %v to %v", sel, typ)
}
call.SetOp(ir.OCALLMETH)
call.X = x
case ir.ODOTINTER:
// Promoted method from embedded interface-typed field (#42279).
x := x.(*ir.SelectorExpr)
if base.Flag.LowerM != 0 {
base.WarnfAt(call.Pos(), "partially devirtualizing %v to %v", sel, typ)
}
call.SetOp(ir.OCALLINTER)
call.X = x
default:
// TODO(mdempsky): Turn back into Fatalf after more testing.
if base.Flag.LowerM != 0 {
base.WarnfAt(call.Pos(), "failed to devirtualize %v (%v)", x, x.Op())
}
return
}
// Duplicated logic from typecheck for function call return
// value types.
//
// Receiver parameter size may have changed; need to update
// call.Type to get correct stack offsets for result
// parameters.
types.CheckSize(x.Type())
switch ft := x.Type(); ft.NumResults() {
case 0:
case 1:
call.SetType(ft.Results().Field(0).Type)
default:
call.SetType(ft.Results())
}
}

View File

@@ -0,0 +1,458 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package dwarfgen
import (
"bytes"
"flag"
"fmt"
"sort"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/reflectdata"
"cmd/compile/internal/ssa"
"cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
"cmd/internal/dwarf"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/src"
)
func Info(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls) {
fn := curfn.(*ir.Func)
if fn.Nname != nil {
expect := fn.Linksym()
if fnsym.ABI() == obj.ABI0 {
expect = fn.LinksymABI(obj.ABI0)
}
if fnsym != expect {
base.Fatalf("unexpected fnsym: %v != %v", fnsym, expect)
}
}
// Back when there were two different *Funcs for a function, this code
// was not consistent about whether a particular *Node being processed
// was an ODCLFUNC or ONAME node. Partly this is because inlined function
// bodies have no ODCLFUNC node, which was it's own inconsistency.
// In any event, the handling of the two different nodes for DWARF purposes
// was subtly different, likely in unintended ways. CL 272253 merged the
// two nodes' Func fields, so that code sees the same *Func whether it is
// holding the ODCLFUNC or the ONAME. This resulted in changes in the
// DWARF output. To preserve the existing DWARF output and leave an
// intentional change for a future CL, this code does the following when
// fn.Op == ONAME:
//
// 1. Disallow use of createComplexVars in createDwarfVars.
// It was not possible to reach that code for an ONAME before,
// because the DebugInfo was set only on the ODCLFUNC Func.
// Calling into it in the ONAME case causes an index out of bounds panic.
//
// 2. Do not populate apdecls. fn.Func.Dcl was in the ODCLFUNC Func,
// not the ONAME Func. Populating apdecls for the ONAME case results
// in selected being populated after createSimpleVars is called in
// createDwarfVars, and then that causes the loop to skip all the entries
// in dcl, meaning that the RecordAutoType calls don't happen.
//
// These two adjustments keep toolstash -cmp working for now.
// Deciding the right answer is, as they say, future work.
//
// We can tell the difference between the old ODCLFUNC and ONAME
// cases by looking at the infosym.Name. If it's empty, DebugInfo is
// being called from (*obj.Link).populateDWARF, which used to use
// the ODCLFUNC. If it's non-empty (the name will end in $abstract),
// DebugInfo is being called from (*obj.Link).DwarfAbstractFunc,
// which used to use the ONAME form.
isODCLFUNC := infosym.Name == ""
var apdecls []*ir.Name
// Populate decls for fn.
if isODCLFUNC {
for _, n := range fn.Dcl {
if n.Op() != ir.ONAME { // might be OTYPE or OLITERAL
continue
}
switch n.Class {
case ir.PAUTO:
if !n.Used() {
// Text == nil -> generating abstract function
if fnsym.Func().Text != nil {
base.Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)")
}
continue
}
case ir.PPARAM, ir.PPARAMOUT:
default:
continue
}
apdecls = append(apdecls, n)
fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
}
}
decls, dwarfVars := createDwarfVars(fnsym, isODCLFUNC, fn, apdecls)
// For each type referenced by the functions auto vars but not
// already referenced by a dwarf var, attach an R_USETYPE relocation to
// the function symbol to insure that the type included in DWARF
// processing during linking.
typesyms := []*obj.LSym{}
for t, _ := range fnsym.Func().Autot {
typesyms = append(typesyms, t)
}
sort.Sort(obj.BySymName(typesyms))
for _, sym := range typesyms {
r := obj.Addrel(infosym)
r.Sym = sym
r.Type = objabi.R_USETYPE
}
fnsym.Func().Autot = nil
var varScopes []ir.ScopeID
for _, decl := range decls {
pos := declPos(decl)
varScopes = append(varScopes, findScope(fn.Marks, pos))
}
scopes := assembleScopes(fnsym, fn, dwarfVars, varScopes)
var inlcalls dwarf.InlCalls
if base.Flag.GenDwarfInl > 0 {
inlcalls = assembleInlines(fnsym, dwarfVars)
}
return scopes, inlcalls
}
func declPos(decl *ir.Name) src.XPos {
return decl.Canonical().Pos()
}
// createDwarfVars process fn, returning a list of DWARF variables and the
// Nodes they represent.
func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var) {
// Collect a raw list of DWARF vars.
var vars []*dwarf.Var
var decls []*ir.Name
var selected ir.NameSet
if base.Ctxt.Flag_locationlists && base.Ctxt.Flag_optimize && fn.DebugInfo != nil && complexOK {
decls, vars, selected = createComplexVars(fnsym, fn)
} else {
decls, vars, selected = createSimpleVars(fnsym, apDecls)
}
dcl := apDecls
if fnsym.WasInlined() {
dcl = preInliningDcls(fnsym)
}
// If optimization is enabled, the list above will typically be
// missing some of the original pre-optimization variables in the
// function (they may have been promoted to registers, folded into
// constants, dead-coded away, etc). Input arguments not eligible
// for SSA optimization are also missing. Here we add back in entries
// for selected missing vars. Note that the recipe below creates a
// conservative location. The idea here is that we want to
// communicate to the user that "yes, there is a variable named X
// in this function, but no, I don't have enough information to
// reliably report its contents."
// For non-SSA-able arguments, however, the correct information
// is known -- they have a single home on the stack.
for _, n := range dcl {
if selected.Has(n) {
continue
}
c := n.Sym().Name[0]
if c == '.' || n.Type().IsUntyped() {
continue
}
if n.Class == ir.PPARAM && !ssagen.TypeOK(n.Type()) {
// SSA-able args get location lists, and may move in and
// out of registers, so those are handled elsewhere.
// Autos and named output params seem to get handled
// with VARDEF, which creates location lists.
// Args not of SSA-able type are treated here; they
// are homed on the stack in a single place for the
// entire call.
vars = append(vars, createSimpleVar(fnsym, n))
decls = append(decls, n)
continue
}
typename := dwarf.InfoPrefix + types.TypeSymName(n.Type())
decls = append(decls, n)
abbrev := dwarf.DW_ABRV_AUTO_LOCLIST
isReturnValue := (n.Class == ir.PPARAMOUT)
if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
}
if n.Esc() == ir.EscHeap {
// The variable in question has been promoted to the heap.
// Its address is in n.Heapaddr.
// TODO(thanm): generate a better location expression
}
inlIndex := 0
if base.Flag.GenDwarfInl > 1 {
if n.InlFormal() || n.InlLocal() {
inlIndex = posInlIndex(n.Pos()) + 1
if n.InlFormal() {
abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
}
}
}
declpos := base.Ctxt.InnermostPos(n.Pos())
vars = append(vars, &dwarf.Var{
Name: n.Sym().Name,
IsReturnValue: isReturnValue,
Abbrev: abbrev,
StackOffset: int32(n.FrameOffset()),
Type: base.Ctxt.Lookup(typename),
DeclFile: declpos.RelFilename(),
DeclLine: declpos.RelLine(),
DeclCol: declpos.Col(),
InlIndex: int32(inlIndex),
ChildIndex: -1,
})
// Record go type of to insure that it gets emitted by the linker.
fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
}
return decls, vars
}
// Given a function that was inlined at some point during the
// compilation, return a sorted list of nodes corresponding to the
// autos/locals in that function prior to inlining. If this is a
// function that is not local to the package being compiled, then the
// names of the variables may have been "versioned" to avoid conflicts
// with local vars; disregard this versioning when sorting.
func preInliningDcls(fnsym *obj.LSym) []*ir.Name {
fn := base.Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*ir.Func)
var rdcl []*ir.Name
for _, n := range fn.Inl.Dcl {
c := n.Sym().Name[0]
// Avoid reporting "_" parameters, since if there are more than
// one, it can result in a collision later on, as in #23179.
if unversion(n.Sym().Name) == "_" || c == '.' || n.Type().IsUntyped() {
continue
}
rdcl = append(rdcl, n)
}
return rdcl
}
// createSimpleVars creates a DWARF entry for every variable declared in the
// function, claiming that they are permanently on the stack.
func createSimpleVars(fnsym *obj.LSym, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var, ir.NameSet) {
var vars []*dwarf.Var
var decls []*ir.Name
var selected ir.NameSet
for _, n := range apDecls {
if ir.IsAutoTmp(n) {
continue
}
decls = append(decls, n)
vars = append(vars, createSimpleVar(fnsym, n))
selected.Add(n)
}
return decls, vars, selected
}
func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
var abbrev int
var offs int64
switch n.Class {
case ir.PAUTO:
offs = n.FrameOffset()
abbrev = dwarf.DW_ABRV_AUTO
if base.Ctxt.FixedFrameSize() == 0 {
offs -= int64(types.PtrSize)
}
if objabi.Framepointer_enabled || objabi.GOARCH == "arm64" {
// There is a word space for FP on ARM64 even if the frame pointer is disabled
offs -= int64(types.PtrSize)
}
case ir.PPARAM, ir.PPARAMOUT:
abbrev = dwarf.DW_ABRV_PARAM
offs = n.FrameOffset() + base.Ctxt.FixedFrameSize()
default:
base.Fatalf("createSimpleVar unexpected class %v for node %v", n.Class, n)
}
typename := dwarf.InfoPrefix + types.TypeSymName(n.Type())
delete(fnsym.Func().Autot, reflectdata.TypeLinksym(n.Type()))
inlIndex := 0
if base.Flag.GenDwarfInl > 1 {
if n.InlFormal() || n.InlLocal() {
inlIndex = posInlIndex(n.Pos()) + 1
if n.InlFormal() {
abbrev = dwarf.DW_ABRV_PARAM
}
}
}
declpos := base.Ctxt.InnermostPos(declPos(n))
return &dwarf.Var{
Name: n.Sym().Name,
IsReturnValue: n.Class == ir.PPARAMOUT,
IsInlFormal: n.InlFormal(),
Abbrev: abbrev,
StackOffset: int32(offs),
Type: base.Ctxt.Lookup(typename),
DeclFile: declpos.RelFilename(),
DeclLine: declpos.RelLine(),
DeclCol: declpos.Col(),
InlIndex: int32(inlIndex),
ChildIndex: -1,
}
}
// createComplexVars creates recomposed DWARF vars with location lists,
// suitable for describing optimized code.
func createComplexVars(fnsym *obj.LSym, fn *ir.Func) ([]*ir.Name, []*dwarf.Var, ir.NameSet) {
debugInfo := fn.DebugInfo.(*ssa.FuncDebug)
// Produce a DWARF variable entry for each user variable.
var decls []*ir.Name
var vars []*dwarf.Var
var ssaVars ir.NameSet
for varID, dvar := range debugInfo.Vars {
n := dvar
ssaVars.Add(n)
for _, slot := range debugInfo.VarSlots[varID] {
ssaVars.Add(debugInfo.Slots[slot].N)
}
if dvar := createComplexVar(fnsym, fn, ssa.VarID(varID)); dvar != nil {
decls = append(decls, n)
vars = append(vars, dvar)
}
}
return decls, vars, ssaVars
}
// createComplexVar builds a single DWARF variable entry and location list.
func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var {
debug := fn.DebugInfo.(*ssa.FuncDebug)
n := debug.Vars[varID]
var abbrev int
switch n.Class {
case ir.PAUTO:
abbrev = dwarf.DW_ABRV_AUTO_LOCLIST
case ir.PPARAM, ir.PPARAMOUT:
abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
default:
return nil
}
gotype := reflectdata.TypeLinksym(n.Type())
delete(fnsym.Func().Autot, gotype)
typename := dwarf.InfoPrefix + gotype.Name[len("type."):]
inlIndex := 0
if base.Flag.GenDwarfInl > 1 {
if n.InlFormal() || n.InlLocal() {
inlIndex = posInlIndex(n.Pos()) + 1
if n.InlFormal() {
abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
}
}
}
declpos := base.Ctxt.InnermostPos(n.Pos())
dvar := &dwarf.Var{
Name: n.Sym().Name,
IsReturnValue: n.Class == ir.PPARAMOUT,
IsInlFormal: n.InlFormal(),
Abbrev: abbrev,
Type: base.Ctxt.Lookup(typename),
// The stack offset is used as a sorting key, so for decomposed
// variables just give it the first one. It's not used otherwise.
// This won't work well if the first slot hasn't been assigned a stack
// location, but it's not obvious how to do better.
StackOffset: ssagen.StackOffset(debug.Slots[debug.VarSlots[varID][0]]),
DeclFile: declpos.RelFilename(),
DeclLine: declpos.RelLine(),
DeclCol: declpos.Col(),
InlIndex: int32(inlIndex),
ChildIndex: -1,
}
list := debug.LocationLists[varID]
if len(list) != 0 {
dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym))
}
}
return dvar
}
// RecordFlags records the specified command-line flags to be placed
// in the DWARF info.
func RecordFlags(flags ...string) {
if base.Ctxt.Pkgpath == "" {
// We can't record the flags if we don't know what the
// package name is.
return
}
type BoolFlag interface {
IsBoolFlag() bool
}
type CountFlag interface {
IsCountFlag() bool
}
var cmd bytes.Buffer
for _, name := range flags {
f := flag.Lookup(name)
if f == nil {
continue
}
getter := f.Value.(flag.Getter)
if getter.String() == f.DefValue {
// Flag has default value, so omit it.
continue
}
if bf, ok := f.Value.(BoolFlag); ok && bf.IsBoolFlag() {
val, ok := getter.Get().(bool)
if ok && val {
fmt.Fprintf(&cmd, " -%s", f.Name)
continue
}
}
if cf, ok := f.Value.(CountFlag); ok && cf.IsCountFlag() {
val, ok := getter.Get().(int)
if ok && val == 1 {
fmt.Fprintf(&cmd, " -%s", f.Name)
continue
}
}
fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get())
}
if cmd.Len() == 0 {
return
}
s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + base.Ctxt.Pkgpath)
s.Type = objabi.SDWARFCUINFO
// Sometimes (for example when building tests) we can link
// together two package main archives. So allow dups.
s.Set(obj.AttrDuplicateOK, true)
base.Ctxt.Data = append(base.Ctxt.Data, s)
s.P = cmd.Bytes()[1:]
}
// RecordPackageName records the name of the package being
// compiled, so that the linker can save it in the compile unit's DIE.
func RecordPackageName() {
s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + base.Ctxt.Pkgpath)
s.Type = objabi.SDWARFCUINFO
// Sometimes (for example when building tests) we can link
// together two package main archives. So allow dups.
s.Set(obj.AttrDuplicateOK, true)
base.Ctxt.Data = append(base.Ctxt.Data, s)
s.P = []byte(types.LocalPkg.Name)
}

View File

@@ -2,14 +2,17 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gc
package dwarfgen
import (
"fmt"
"strings"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/internal/dwarf"
"cmd/internal/obj"
"cmd/internal/src"
"fmt"
"strings"
)
// To identify variables by original source position.
@@ -26,8 +29,8 @@ type varPos struct {
func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls {
var inlcalls dwarf.InlCalls
if Debug_gendwarfinl != 0 {
Ctxt.Logf("assembling DWARF inlined routine info for %v\n", fnsym.Name)
if base.Debug.DwarfInl != 0 {
base.Ctxt.Logf("assembling DWARF inlined routine info for %v\n", fnsym.Name)
}
// This maps inline index (from Ctxt.InlTree) to index in inlcalls.Calls
@@ -106,7 +109,7 @@ func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls {
}
m = makePreinlineDclMap(fnsym)
} else {
ifnlsym := Ctxt.InlTree.InlinedFunction(int(ii - 1))
ifnlsym := base.Ctxt.InlTree.InlinedFunction(int(ii - 1))
m = makePreinlineDclMap(ifnlsym)
}
@@ -181,7 +184,7 @@ func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls {
}
// Debugging
if Debug_gendwarfinl != 0 {
if base.Debug.DwarfInl != 0 {
dumpInlCalls(inlcalls)
dumpInlVars(dwVars)
}
@@ -204,16 +207,17 @@ func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls {
// late in the compilation when it is determined that we need an
// abstract function DIE for an inlined routine imported from a
// previously compiled package.
func genAbstractFunc(fn *obj.LSym) {
ifn := Ctxt.DwFixups.GetPrecursorFunc(fn)
func AbstractFunc(fn *obj.LSym) {
ifn := base.Ctxt.DwFixups.GetPrecursorFunc(fn)
if ifn == nil {
Ctxt.Diag("failed to locate precursor fn for %v", fn)
base.Ctxt.Diag("failed to locate precursor fn for %v", fn)
return
}
if Debug_gendwarfinl != 0 {
Ctxt.Logf("DwarfAbstractFunc(%v)\n", fn.Name)
_ = ifn.(*ir.Func)
if base.Debug.DwarfInl != 0 {
base.Ctxt.Logf("DwarfAbstractFunc(%v)\n", fn.Name)
}
Ctxt.DwarfAbstractFunc(ifn, fn, myimportpath)
base.Ctxt.DwarfAbstractFunc(ifn, fn, base.Ctxt.Pkgpath)
}
// Undo any versioning performed when a name was written
@@ -235,15 +239,15 @@ func makePreinlineDclMap(fnsym *obj.LSym) map[varPos]int {
dcl := preInliningDcls(fnsym)
m := make(map[varPos]int)
for i, n := range dcl {
pos := Ctxt.InnermostPos(n.Pos)
pos := base.Ctxt.InnermostPos(n.Pos())
vp := varPos{
DeclName: unversion(n.Sym.Name),
DeclName: unversion(n.Sym().Name),
DeclFile: pos.RelFilename(),
DeclLine: pos.RelLine(),
DeclCol: pos.Col(),
}
if _, found := m[vp]; found {
Fatalf("child dcl collision on symbol %s within %v\n", n.Sym.Name, fnsym.Name)
base.Fatalf("child dcl collision on symbol %s within %v\n", n.Sym().Name, fnsym.Name)
}
m[vp] = i
}
@@ -260,17 +264,17 @@ func insertInlCall(dwcalls *dwarf.InlCalls, inlIdx int, imap map[int]int) int {
// is one. We do this first so that parents appear before their
// children in the resulting table.
parCallIdx := -1
parInlIdx := Ctxt.InlTree.Parent(inlIdx)
parInlIdx := base.Ctxt.InlTree.Parent(inlIdx)
if parInlIdx >= 0 {
parCallIdx = insertInlCall(dwcalls, parInlIdx, imap)
}
// Create new entry for this inline
inlinedFn := Ctxt.InlTree.InlinedFunction(inlIdx)
callXPos := Ctxt.InlTree.CallPos(inlIdx)
absFnSym := Ctxt.DwFixups.AbsFuncDwarfSym(inlinedFn)
pb := Ctxt.PosTable.Pos(callXPos).Base()
callFileSym := Ctxt.Lookup(pb.SymFilename())
inlinedFn := base.Ctxt.InlTree.InlinedFunction(inlIdx)
callXPos := base.Ctxt.InlTree.CallPos(inlIdx)
absFnSym := base.Ctxt.DwFixups.AbsFuncDwarfSym(inlinedFn)
pb := base.Ctxt.PosTable.Pos(callXPos).Base()
callFileSym := base.Ctxt.Lookup(pb.SymFilename())
ic := dwarf.InlCall{
InlIndex: inlIdx,
CallFile: callFileSym,
@@ -298,7 +302,7 @@ func insertInlCall(dwcalls *dwarf.InlCalls, inlIdx int, imap map[int]int) int {
// the index for a node from the inlined body of D will refer to the
// call to D from C. Whew.
func posInlIndex(xpos src.XPos) int {
pos := Ctxt.PosTable.Pos(xpos)
pos := base.Ctxt.PosTable.Pos(xpos)
if b := pos.Base(); b != nil {
ii := b.InliningIndex()
if ii >= 0 {
@@ -324,7 +328,7 @@ func addRange(calls []dwarf.InlCall, start, end int64, ii int, imap map[int]int)
// Append range to correct inlined call
callIdx, found := imap[ii]
if !found {
Fatalf("can't find inlIndex %d in imap for prog at %d\n", ii, start)
base.Fatalf("can't find inlIndex %d in imap for prog at %d\n", ii, start)
}
call := &calls[callIdx]
call.Ranges = append(call.Ranges, dwarf.Range{Start: start, End: end})
@@ -332,23 +336,23 @@ func addRange(calls []dwarf.InlCall, start, end int64, ii int, imap map[int]int)
func dumpInlCall(inlcalls dwarf.InlCalls, idx, ilevel int) {
for i := 0; i < ilevel; i++ {
Ctxt.Logf(" ")
base.Ctxt.Logf(" ")
}
ic := inlcalls.Calls[idx]
callee := Ctxt.InlTree.InlinedFunction(ic.InlIndex)
Ctxt.Logf(" %d: II:%d (%s) V: (", idx, ic.InlIndex, callee.Name)
callee := base.Ctxt.InlTree.InlinedFunction(ic.InlIndex)
base.Ctxt.Logf(" %d: II:%d (%s) V: (", idx, ic.InlIndex, callee.Name)
for _, f := range ic.InlVars {
Ctxt.Logf(" %v", f.Name)
base.Ctxt.Logf(" %v", f.Name)
}
Ctxt.Logf(" ) C: (")
base.Ctxt.Logf(" ) C: (")
for _, k := range ic.Children {
Ctxt.Logf(" %v", k)
base.Ctxt.Logf(" %v", k)
}
Ctxt.Logf(" ) R:")
base.Ctxt.Logf(" ) R:")
for _, r := range ic.Ranges {
Ctxt.Logf(" [%d,%d)", r.Start, r.End)
base.Ctxt.Logf(" [%d,%d)", r.Start, r.End)
}
Ctxt.Logf("\n")
base.Ctxt.Logf("\n")
for _, k := range ic.Children {
dumpInlCall(inlcalls, k, ilevel+1)
}
@@ -373,7 +377,7 @@ func dumpInlVars(dwvars []*dwarf.Var) {
if dwv.IsInAbstract {
ia = 1
}
Ctxt.Logf("V%d: %s CI:%d II:%d IA:%d %s\n", i, dwv.Name, dwv.ChildIndex, dwv.InlIndex-1, ia, typ)
base.Ctxt.Logf("V%d: %s CI:%d II:%d IA:%d %s\n", i, dwv.Name, dwv.ChildIndex, dwv.InlIndex-1, ia, typ)
}
}
@@ -410,7 +414,7 @@ func checkInlCall(funcName string, inlCalls dwarf.InlCalls, funcSize int64, idx,
// Callee
ic := inlCalls.Calls[idx]
callee := Ctxt.InlTree.InlinedFunction(ic.InlIndex).Name
callee := base.Ctxt.InlTree.InlinedFunction(ic.InlIndex).Name
calleeRanges := ic.Ranges
// Caller
@@ -418,14 +422,14 @@ func checkInlCall(funcName string, inlCalls dwarf.InlCalls, funcSize int64, idx,
parentRanges := []dwarf.Range{dwarf.Range{Start: int64(0), End: funcSize}}
if parentIdx != -1 {
pic := inlCalls.Calls[parentIdx]
caller = Ctxt.InlTree.InlinedFunction(pic.InlIndex).Name
caller = base.Ctxt.InlTree.InlinedFunction(pic.InlIndex).Name
parentRanges = pic.Ranges
}
// Callee ranges contained in caller ranges?
c, m := rangesContainsAll(parentRanges, calleeRanges)
if !c {
Fatalf("** malformed inlined routine range in %s: caller %s callee %s II=%d %s\n", funcName, caller, callee, idx, m)
base.Fatalf("** malformed inlined routine range in %s: caller %s callee %s II=%d %s\n", funcName, caller, callee, idx, m)
}
// Now visit kids

View File

@@ -0,0 +1,94 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package dwarfgen
import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/internal/src"
)
// A ScopeMarker tracks scope nesting and boundaries for later use
// during DWARF generation.
type ScopeMarker struct {
parents []ir.ScopeID
marks []ir.Mark
}
// checkPos validates the given position and returns the current scope.
func (m *ScopeMarker) checkPos(pos src.XPos) ir.ScopeID {
if !pos.IsKnown() {
base.Fatalf("unknown scope position")
}
if len(m.marks) == 0 {
return 0
}
last := &m.marks[len(m.marks)-1]
if xposBefore(pos, last.Pos) {
base.FatalfAt(pos, "non-monotonic scope positions\n\t%v: previous scope position", base.FmtPos(last.Pos))
}
return last.Scope
}
// Push records a transition to a new child scope of the current scope.
func (m *ScopeMarker) Push(pos src.XPos) {
current := m.checkPos(pos)
m.parents = append(m.parents, current)
child := ir.ScopeID(len(m.parents))
m.marks = append(m.marks, ir.Mark{Pos: pos, Scope: child})
}
// Pop records a transition back to the current scope's parent.
func (m *ScopeMarker) Pop(pos src.XPos) {
current := m.checkPos(pos)
parent := m.parents[current-1]
m.marks = append(m.marks, ir.Mark{Pos: pos, Scope: parent})
}
// Unpush removes the current scope, which must be empty.
func (m *ScopeMarker) Unpush() {
i := len(m.marks) - 1
current := m.marks[i].Scope
if current != ir.ScopeID(len(m.parents)) {
base.FatalfAt(m.marks[i].Pos, "current scope is not empty")
}
m.parents = m.parents[:current-1]
m.marks = m.marks[:i]
}
// WriteTo writes the recorded scope marks to the given function,
// and resets the marker for reuse.
func (m *ScopeMarker) WriteTo(fn *ir.Func) {
m.compactMarks()
fn.Parents = make([]ir.ScopeID, len(m.parents))
copy(fn.Parents, m.parents)
m.parents = m.parents[:0]
fn.Marks = make([]ir.Mark, len(m.marks))
copy(fn.Marks, m.marks)
m.marks = m.marks[:0]
}
func (m *ScopeMarker) compactMarks() {
n := 0
for _, next := range m.marks {
if n > 0 && next.Pos == m.marks[n-1].Pos {
m.marks[n-1].Scope = next.Scope
continue
}
m.marks[n] = next
n++
}
m.marks = m.marks[:n]
}

View File

@@ -2,21 +2,24 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gc
package dwarfgen
import (
"sort"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/internal/dwarf"
"cmd/internal/obj"
"cmd/internal/src"
"sort"
)
// See golang.org/issue/20390.
func xposBefore(p, q src.XPos) bool {
return Ctxt.PosTable.Pos(p).Before(Ctxt.PosTable.Pos(q))
return base.Ctxt.PosTable.Pos(p).Before(base.Ctxt.PosTable.Pos(q))
}
func findScope(marks []Mark, pos src.XPos) ScopeID {
func findScope(marks []ir.Mark, pos src.XPos) ir.ScopeID {
i := sort.Search(len(marks), func(i int) bool {
return xposBefore(pos, marks[i].Pos)
})
@@ -26,20 +29,20 @@ func findScope(marks []Mark, pos src.XPos) ScopeID {
return marks[i-1].Scope
}
func assembleScopes(fnsym *obj.LSym, fn *Node, dwarfVars []*dwarf.Var, varScopes []ScopeID) []dwarf.Scope {
func assembleScopes(fnsym *obj.LSym, fn *ir.Func, dwarfVars []*dwarf.Var, varScopes []ir.ScopeID) []dwarf.Scope {
// Initialize the DWARF scope tree based on lexical scopes.
dwarfScopes := make([]dwarf.Scope, 1+len(fn.Func.Parents))
for i, parent := range fn.Func.Parents {
dwarfScopes := make([]dwarf.Scope, 1+len(fn.Parents))
for i, parent := range fn.Parents {
dwarfScopes[i+1].Parent = int32(parent)
}
scopeVariables(dwarfVars, varScopes, dwarfScopes)
scopePCs(fnsym, fn.Func.Marks, dwarfScopes)
scopePCs(fnsym, fn.Marks, dwarfScopes)
return compactScopes(dwarfScopes)
}
// scopeVariables assigns DWARF variable records to their scopes.
func scopeVariables(dwarfVars []*dwarf.Var, varScopes []ScopeID, dwarfScopes []dwarf.Scope) {
func scopeVariables(dwarfVars []*dwarf.Var, varScopes []ir.ScopeID, dwarfScopes []dwarf.Scope) {
sort.Stable(varsByScopeAndOffset{dwarfVars, varScopes})
i0 := 0
@@ -56,7 +59,7 @@ func scopeVariables(dwarfVars []*dwarf.Var, varScopes []ScopeID, dwarfScopes []d
}
// scopePCs assigns PC ranges to their scopes.
func scopePCs(fnsym *obj.LSym, marks []Mark, dwarfScopes []dwarf.Scope) {
func scopePCs(fnsym *obj.LSym, marks []ir.Mark, dwarfScopes []dwarf.Scope) {
// If there aren't any child scopes (in particular, when scope
// tracking is disabled), we can skip a whole lot of work.
if len(marks) == 0 {
@@ -89,7 +92,7 @@ func compactScopes(dwarfScopes []dwarf.Scope) []dwarf.Scope {
type varsByScopeAndOffset struct {
vars []*dwarf.Var
scopes []ScopeID
scopes []ir.ScopeID
}
func (v varsByScopeAndOffset) Len() int {

View File

@@ -2,10 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gc_test
package dwarfgen
import (
"cmd/internal/objfile"
"debug/dwarf"
"fmt"
"internal/testenv"
@@ -18,6 +17,8 @@ import (
"strconv"
"strings"
"testing"
"cmd/internal/objfile"
)
type testline struct {

File diff suppressed because it is too large Load Diff

View File

@@ -1,959 +0,0 @@
// Copyright 2016 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 gc
import (
"cmd/compile/internal/types"
"cmd/internal/obj"
"fmt"
"sort"
)
// AlgKind describes the kind of algorithms used for comparing and
// hashing a Type.
type AlgKind int
//go:generate stringer -type AlgKind -trimprefix A
const (
// These values are known by runtime.
ANOEQ AlgKind = iota
AMEM0
AMEM8
AMEM16
AMEM32
AMEM64
AMEM128
ASTRING
AINTER
ANILINTER
AFLOAT32
AFLOAT64
ACPLX64
ACPLX128
// Type can be compared/hashed as regular memory.
AMEM AlgKind = 100
// Type needs special comparison/hashing functions.
ASPECIAL AlgKind = -1
)
// IsComparable reports whether t is a comparable type.
func IsComparable(t *types.Type) bool {
a, _ := algtype1(t)
return a != ANOEQ
}
// IsRegularMemory reports whether t can be compared/hashed as regular memory.
func IsRegularMemory(t *types.Type) bool {
a, _ := algtype1(t)
return a == AMEM
}
// IncomparableField returns an incomparable Field of struct Type t, if any.
func IncomparableField(t *types.Type) *types.Field {
for _, f := range t.FieldSlice() {
if !IsComparable(f.Type) {
return f
}
}
return nil
}
// EqCanPanic reports whether == on type t could panic (has an interface somewhere).
// t must be comparable.
func EqCanPanic(t *types.Type) bool {
switch t.Etype {
default:
return false
case TINTER:
return true
case TARRAY:
return EqCanPanic(t.Elem())
case TSTRUCT:
for _, f := range t.FieldSlice() {
if !f.Sym.IsBlank() && EqCanPanic(f.Type) {
return true
}
}
return false
}
}
// algtype is like algtype1, except it returns the fixed-width AMEMxx variants
// instead of the general AMEM kind when possible.
func algtype(t *types.Type) AlgKind {
a, _ := algtype1(t)
if a == AMEM {
switch t.Width {
case 0:
return AMEM0
case 1:
return AMEM8
case 2:
return AMEM16
case 4:
return AMEM32
case 8:
return AMEM64
case 16:
return AMEM128
}
}
return a
}
// algtype1 returns the AlgKind used for comparing and hashing Type t.
// If it returns ANOEQ, it also returns the component type of t that
// makes it incomparable.
func algtype1(t *types.Type) (AlgKind, *types.Type) {
if t.Broke() {
return AMEM, nil
}
if t.Noalg() {
return ANOEQ, t
}
switch t.Etype {
case TANY, TFORW:
// will be defined later.
return ANOEQ, t
case TINT8, TUINT8, TINT16, TUINT16,
TINT32, TUINT32, TINT64, TUINT64,
TINT, TUINT, TUINTPTR,
TBOOL, TPTR,
TCHAN, TUNSAFEPTR:
return AMEM, nil
case TFUNC, TMAP:
return ANOEQ, t
case TFLOAT32:
return AFLOAT32, nil
case TFLOAT64:
return AFLOAT64, nil
case TCOMPLEX64:
return ACPLX64, nil
case TCOMPLEX128:
return ACPLX128, nil
case TSTRING:
return ASTRING, nil
case TINTER:
if t.IsEmptyInterface() {
return ANILINTER, nil
}
return AINTER, nil
case TSLICE:
return ANOEQ, t
case TARRAY:
a, bad := algtype1(t.Elem())
switch a {
case AMEM:
return AMEM, nil
case ANOEQ:
return ANOEQ, bad
}
switch t.NumElem() {
case 0:
// We checked above that the element type is comparable.
return AMEM, nil
case 1:
// Single-element array is same as its lone element.
return a, nil
}
return ASPECIAL, nil
case TSTRUCT:
fields := t.FieldSlice()
// One-field struct is same as that one field alone.
if len(fields) == 1 && !fields[0].Sym.IsBlank() {
return algtype1(fields[0].Type)
}
ret := AMEM
for i, f := range fields {
// All fields must be comparable.
a, bad := algtype1(f.Type)
if a == ANOEQ {
return ANOEQ, bad
}
// Blank fields, padded fields, fields with non-memory
// equality need special compare.
if a != AMEM || f.Sym.IsBlank() || ispaddedfield(t, i) {
ret = ASPECIAL
}
}
return ret, nil
}
Fatalf("algtype1: unexpected type %v", t)
return 0, nil
}
// genhash returns a symbol which is the closure used to compute
// the hash of a value of type t.
// Note: the generated function must match runtime.typehash exactly.
func genhash(t *types.Type) *obj.LSym {
switch algtype(t) {
default:
// genhash is only called for types that have equality
Fatalf("genhash %v", t)
case AMEM0:
return sysClosure("memhash0")
case AMEM8:
return sysClosure("memhash8")
case AMEM16:
return sysClosure("memhash16")
case AMEM32:
return sysClosure("memhash32")
case AMEM64:
return sysClosure("memhash64")
case AMEM128:
return sysClosure("memhash128")
case ASTRING:
return sysClosure("strhash")
case AINTER:
return sysClosure("interhash")
case ANILINTER:
return sysClosure("nilinterhash")
case AFLOAT32:
return sysClosure("f32hash")
case AFLOAT64:
return sysClosure("f64hash")
case ACPLX64:
return sysClosure("c64hash")
case ACPLX128:
return sysClosure("c128hash")
case AMEM:
// For other sizes of plain memory, we build a closure
// that calls memhash_varlen. The size of the memory is
// encoded in the first slot of the closure.
closure := typeLookup(fmt.Sprintf(".hashfunc%d", t.Width)).Linksym()
if len(closure.P) > 0 { // already generated
return closure
}
if memhashvarlen == nil {
memhashvarlen = sysfunc("memhash_varlen")
}
ot := 0
ot = dsymptr(closure, ot, memhashvarlen, 0)
ot = duintptr(closure, ot, uint64(t.Width)) // size encoded in closure
ggloblsym(closure, int32(ot), obj.DUPOK|obj.RODATA)
return closure
case ASPECIAL:
break
}
closure := typesymprefix(".hashfunc", t).Linksym()
if len(closure.P) > 0 { // already generated
return closure
}
// Generate hash functions for subtypes.
// There are cases where we might not use these hashes,
// but in that case they will get dead-code eliminated.
// (And the closure generated by genhash will also get
// dead-code eliminated, as we call the subtype hashers
// directly.)
switch t.Etype {
case types.TARRAY:
genhash(t.Elem())
case types.TSTRUCT:
for _, f := range t.FieldSlice() {
genhash(f.Type)
}
}
sym := typesymprefix(".hash", t)
if Debug.r != 0 {
fmt.Printf("genhash %v %v %v\n", closure, sym, t)
}
lineno = autogeneratedPos // less confusing than end of input
dclcontext = PEXTERN
// func sym(p *T, h uintptr) uintptr
tfn := nod(OTFUNC, nil, nil)
tfn.List.Set2(
namedfield("p", types.NewPtr(t)),
namedfield("h", types.Types[TUINTPTR]),
)
tfn.Rlist.Set1(anonfield(types.Types[TUINTPTR]))
fn := dclfunc(sym, tfn)
np := asNode(tfn.Type.Params().Field(0).Nname)
nh := asNode(tfn.Type.Params().Field(1).Nname)
switch t.Etype {
case types.TARRAY:
// An array of pure memory would be handled by the
// standard algorithm, so the element type must not be
// pure memory.
hashel := hashfor(t.Elem())
n := nod(ORANGE, nil, nod(ODEREF, np, nil))
ni := newname(lookup("i"))
ni.Type = types.Types[TINT]
n.List.Set1(ni)
n.SetColas(true)
colasdefn(n.List.Slice(), n)
ni = n.List.First()
// h = hashel(&p[i], h)
call := nod(OCALL, hashel, nil)
nx := nod(OINDEX, np, ni)
nx.SetBounded(true)
na := nod(OADDR, nx, nil)
call.List.Append(na)
call.List.Append(nh)
n.Nbody.Append(nod(OAS, nh, call))
fn.Nbody.Append(n)
case types.TSTRUCT:
// Walk the struct using memhash for runs of AMEM
// and calling specific hash functions for the others.
for i, fields := 0, t.FieldSlice(); i < len(fields); {
f := fields[i]
// Skip blank fields.
if f.Sym.IsBlank() {
i++
continue
}
// Hash non-memory fields with appropriate hash function.
if !IsRegularMemory(f.Type) {
hashel := hashfor(f.Type)
call := nod(OCALL, hashel, nil)
nx := nodSym(OXDOT, np, f.Sym) // TODO: fields from other packages?
na := nod(OADDR, nx, nil)
call.List.Append(na)
call.List.Append(nh)
fn.Nbody.Append(nod(OAS, nh, call))
i++
continue
}
// Otherwise, hash a maximal length run of raw memory.
size, next := memrun(t, i)
// h = hashel(&p.first, size, h)
hashel := hashmem(f.Type)
call := nod(OCALL, hashel, nil)
nx := nodSym(OXDOT, np, f.Sym) // TODO: fields from other packages?
na := nod(OADDR, nx, nil)
call.List.Append(na)
call.List.Append(nh)
call.List.Append(nodintconst(size))
fn.Nbody.Append(nod(OAS, nh, call))
i = next
}
}
r := nod(ORETURN, nil, nil)
r.List.Append(nh)
fn.Nbody.Append(r)
if Debug.r != 0 {
dumplist("genhash body", fn.Nbody)
}
funcbody()
fn.Func.SetDupok(true)
fn = typecheck(fn, ctxStmt)
Curfn = fn
typecheckslice(fn.Nbody.Slice(), ctxStmt)
Curfn = nil
if debug_dclstack != 0 {
testdclstack()
}
fn.Func.SetNilCheckDisabled(true)
xtop = append(xtop, fn)
// Build closure. It doesn't close over any variables, so
// it contains just the function pointer.
dsymptr(closure, 0, sym.Linksym(), 0)
ggloblsym(closure, int32(Widthptr), obj.DUPOK|obj.RODATA)
return closure
}
func hashfor(t *types.Type) *Node {
var sym *types.Sym
switch a, _ := algtype1(t); a {
case AMEM:
Fatalf("hashfor with AMEM type")
case AINTER:
sym = Runtimepkg.Lookup("interhash")
case ANILINTER:
sym = Runtimepkg.Lookup("nilinterhash")
case ASTRING:
sym = Runtimepkg.Lookup("strhash")
case AFLOAT32:
sym = Runtimepkg.Lookup("f32hash")
case AFLOAT64:
sym = Runtimepkg.Lookup("f64hash")
case ACPLX64:
sym = Runtimepkg.Lookup("c64hash")
case ACPLX128:
sym = Runtimepkg.Lookup("c128hash")
default:
// Note: the caller of hashfor ensured that this symbol
// exists and has a body by calling genhash for t.
sym = typesymprefix(".hash", t)
}
n := newname(sym)
setNodeNameFunc(n)
n.Type = functype(nil, []*Node{
anonfield(types.NewPtr(t)),
anonfield(types.Types[TUINTPTR]),
}, []*Node{
anonfield(types.Types[TUINTPTR]),
})
return n
}
// sysClosure returns a closure which will call the
// given runtime function (with no closed-over variables).
func sysClosure(name string) *obj.LSym {
s := sysvar(name + "·f")
if len(s.P) == 0 {
f := sysfunc(name)
dsymptr(s, 0, f, 0)
ggloblsym(s, int32(Widthptr), obj.DUPOK|obj.RODATA)
}
return s
}
// geneq returns a symbol which is the closure used to compute
// equality for two objects of type t.
func geneq(t *types.Type) *obj.LSym {
switch algtype(t) {
case ANOEQ:
// The runtime will panic if it tries to compare
// a type with a nil equality function.
return nil
case AMEM0:
return sysClosure("memequal0")
case AMEM8:
return sysClosure("memequal8")
case AMEM16:
return sysClosure("memequal16")
case AMEM32:
return sysClosure("memequal32")
case AMEM64:
return sysClosure("memequal64")
case AMEM128:
return sysClosure("memequal128")
case ASTRING:
return sysClosure("strequal")
case AINTER:
return sysClosure("interequal")
case ANILINTER:
return sysClosure("nilinterequal")
case AFLOAT32:
return sysClosure("f32equal")
case AFLOAT64:
return sysClosure("f64equal")
case ACPLX64:
return sysClosure("c64equal")
case ACPLX128:
return sysClosure("c128equal")
case AMEM:
// make equality closure. The size of the type
// is encoded in the closure.
closure := typeLookup(fmt.Sprintf(".eqfunc%d", t.Width)).Linksym()
if len(closure.P) != 0 {
return closure
}
if memequalvarlen == nil {
memequalvarlen = sysvar("memequal_varlen") // asm func
}
ot := 0
ot = dsymptr(closure, ot, memequalvarlen, 0)
ot = duintptr(closure, ot, uint64(t.Width))
ggloblsym(closure, int32(ot), obj.DUPOK|obj.RODATA)
return closure
case ASPECIAL:
break
}
closure := typesymprefix(".eqfunc", t).Linksym()
if len(closure.P) > 0 { // already generated
return closure
}
sym := typesymprefix(".eq", t)
if Debug.r != 0 {
fmt.Printf("geneq %v\n", t)
}
// Autogenerate code for equality of structs and arrays.
lineno = autogeneratedPos // less confusing than end of input
dclcontext = PEXTERN
// func sym(p, q *T) bool
tfn := nod(OTFUNC, nil, nil)
tfn.List.Set2(
namedfield("p", types.NewPtr(t)),
namedfield("q", types.NewPtr(t)),
)
tfn.Rlist.Set1(namedfield("r", types.Types[TBOOL]))
fn := dclfunc(sym, tfn)
np := asNode(tfn.Type.Params().Field(0).Nname)
nq := asNode(tfn.Type.Params().Field(1).Nname)
nr := asNode(tfn.Type.Results().Field(0).Nname)
// Label to jump to if an equality test fails.
neq := autolabel(".neq")
// We reach here only for types that have equality but
// cannot be handled by the standard algorithms,
// so t must be either an array or a struct.
switch t.Etype {
default:
Fatalf("geneq %v", t)
case TARRAY:
nelem := t.NumElem()
// checkAll generates code to check the equality of all array elements.
// If unroll is greater than nelem, checkAll generates:
//
// if eq(p[0], q[0]) && eq(p[1], q[1]) && ... {
// } else {
// return
// }
//
// And so on.
//
// Otherwise it generates:
//
// for i := 0; i < nelem; i++ {
// if eq(p[i], q[i]) {
// } else {
// goto neq
// }
// }
//
// TODO(josharian): consider doing some loop unrolling
// for larger nelem as well, processing a few elements at a time in a loop.
checkAll := func(unroll int64, last bool, eq func(pi, qi *Node) *Node) {
// checkIdx generates a node to check for equality at index i.
checkIdx := func(i *Node) *Node {
// pi := p[i]
pi := nod(OINDEX, np, i)
pi.SetBounded(true)
pi.Type = t.Elem()
// qi := q[i]
qi := nod(OINDEX, nq, i)
qi.SetBounded(true)
qi.Type = t.Elem()
return eq(pi, qi)
}
if nelem <= unroll {
if last {
// Do last comparison in a different manner.
nelem--
}
// Generate a series of checks.
for i := int64(0); i < nelem; i++ {
// if check {} else { goto neq }
nif := nod(OIF, checkIdx(nodintconst(i)), nil)
nif.Rlist.Append(nodSym(OGOTO, nil, neq))
fn.Nbody.Append(nif)
}
if last {
fn.Nbody.Append(nod(OAS, nr, checkIdx(nodintconst(nelem))))
}
} else {
// Generate a for loop.
// for i := 0; i < nelem; i++
i := temp(types.Types[TINT])
init := nod(OAS, i, nodintconst(0))
cond := nod(OLT, i, nodintconst(nelem))
post := nod(OAS, i, nod(OADD, i, nodintconst(1)))
loop := nod(OFOR, cond, post)
loop.Ninit.Append(init)
// if eq(pi, qi) {} else { goto neq }
nif := nod(OIF, checkIdx(i), nil)
nif.Rlist.Append(nodSym(OGOTO, nil, neq))
loop.Nbody.Append(nif)
fn.Nbody.Append(loop)
if last {
fn.Nbody.Append(nod(OAS, nr, nodbool(true)))
}
}
}
switch t.Elem().Etype {
case TSTRING:
// Do two loops. First, check that all the lengths match (cheap).
// Second, check that all the contents match (expensive).
// TODO: when the array size is small, unroll the length match checks.
checkAll(3, false, func(pi, qi *Node) *Node {
// Compare lengths.
eqlen, _ := eqstring(pi, qi)
return eqlen
})
checkAll(1, true, func(pi, qi *Node) *Node {
// Compare contents.
_, eqmem := eqstring(pi, qi)
return eqmem
})
case TFLOAT32, TFLOAT64:
checkAll(2, true, func(pi, qi *Node) *Node {
// p[i] == q[i]
return nod(OEQ, pi, qi)
})
// TODO: pick apart structs, do them piecemeal too
default:
checkAll(1, true, func(pi, qi *Node) *Node {
// p[i] == q[i]
return nod(OEQ, pi, qi)
})
}
case TSTRUCT:
// Build a list of conditions to satisfy.
// The conditions are a list-of-lists. Conditions are reorderable
// within each inner list. The outer lists must be evaluated in order.
var conds [][]*Node
conds = append(conds, []*Node{})
and := func(n *Node) {
i := len(conds) - 1
conds[i] = append(conds[i], n)
}
// Walk the struct using memequal for runs of AMEM
// and calling specific equality tests for the others.
for i, fields := 0, t.FieldSlice(); i < len(fields); {
f := fields[i]
// Skip blank-named fields.
if f.Sym.IsBlank() {
i++
continue
}
// Compare non-memory fields with field equality.
if !IsRegularMemory(f.Type) {
if EqCanPanic(f.Type) {
// Enforce ordering by starting a new set of reorderable conditions.
conds = append(conds, []*Node{})
}
p := nodSym(OXDOT, np, f.Sym)
q := nodSym(OXDOT, nq, f.Sym)
switch {
case f.Type.IsString():
eqlen, eqmem := eqstring(p, q)
and(eqlen)
and(eqmem)
default:
and(nod(OEQ, p, q))
}
if EqCanPanic(f.Type) {
// Also enforce ordering after something that can panic.
conds = append(conds, []*Node{})
}
i++
continue
}
// Find maximal length run of memory-only fields.
size, next := memrun(t, i)
// TODO(rsc): All the calls to newname are wrong for
// cross-package unexported fields.
if s := fields[i:next]; len(s) <= 2 {
// Two or fewer fields: use plain field equality.
for _, f := range s {
and(eqfield(np, nq, f.Sym))
}
} else {
// More than two fields: use memequal.
and(eqmem(np, nq, f.Sym, size))
}
i = next
}
// Sort conditions to put runtime calls last.
// Preserve the rest of the ordering.
var flatConds []*Node
for _, c := range conds {
isCall := func(n *Node) bool {
return n.Op == OCALL || n.Op == OCALLFUNC
}
sort.SliceStable(c, func(i, j int) bool {
return !isCall(c[i]) && isCall(c[j])
})
flatConds = append(flatConds, c...)
}
if len(flatConds) == 0 {
fn.Nbody.Append(nod(OAS, nr, nodbool(true)))
} else {
for _, c := range flatConds[:len(flatConds)-1] {
// if cond {} else { goto neq }
n := nod(OIF, c, nil)
n.Rlist.Append(nodSym(OGOTO, nil, neq))
fn.Nbody.Append(n)
}
fn.Nbody.Append(nod(OAS, nr, flatConds[len(flatConds)-1]))
}
}
// ret:
// return
ret := autolabel(".ret")
fn.Nbody.Append(nodSym(OLABEL, nil, ret))
fn.Nbody.Append(nod(ORETURN, nil, nil))
// neq:
// r = false
// return (or goto ret)
fn.Nbody.Append(nodSym(OLABEL, nil, neq))
fn.Nbody.Append(nod(OAS, nr, nodbool(false)))
if EqCanPanic(t) || hasCall(fn) {
// Epilogue is large, so share it with the equal case.
fn.Nbody.Append(nodSym(OGOTO, nil, ret))
} else {
// Epilogue is small, so don't bother sharing.
fn.Nbody.Append(nod(ORETURN, nil, nil))
}
// TODO(khr): the epilogue size detection condition above isn't perfect.
// We should really do a generic CL that shares epilogues across
// the board. See #24936.
if Debug.r != 0 {
dumplist("geneq body", fn.Nbody)
}
funcbody()
fn.Func.SetDupok(true)
fn = typecheck(fn, ctxStmt)
Curfn = fn
typecheckslice(fn.Nbody.Slice(), ctxStmt)
Curfn = nil
if debug_dclstack != 0 {
testdclstack()
}
// Disable checknils while compiling this code.
// We are comparing a struct or an array,
// neither of which can be nil, and our comparisons
// are shallow.
fn.Func.SetNilCheckDisabled(true)
xtop = append(xtop, fn)
// Generate a closure which points at the function we just generated.
dsymptr(closure, 0, sym.Linksym(), 0)
ggloblsym(closure, int32(Widthptr), obj.DUPOK|obj.RODATA)
return closure
}
func hasCall(n *Node) bool {
if n.Op == OCALL || n.Op == OCALLFUNC {
return true
}
if n.Left != nil && hasCall(n.Left) {
return true
}
if n.Right != nil && hasCall(n.Right) {
return true
}
for _, x := range n.Ninit.Slice() {
if hasCall(x) {
return true
}
}
for _, x := range n.Nbody.Slice() {
if hasCall(x) {
return true
}
}
for _, x := range n.List.Slice() {
if hasCall(x) {
return true
}
}
for _, x := range n.Rlist.Slice() {
if hasCall(x) {
return true
}
}
return false
}
// eqfield returns the node
// p.field == q.field
func eqfield(p *Node, q *Node, field *types.Sym) *Node {
nx := nodSym(OXDOT, p, field)
ny := nodSym(OXDOT, q, field)
ne := nod(OEQ, nx, ny)
return ne
}
// eqstring returns the nodes
// len(s) == len(t)
// and
// memequal(s.ptr, t.ptr, len(s))
// which can be used to construct string equality comparison.
// eqlen must be evaluated before eqmem, and shortcircuiting is required.
func eqstring(s, t *Node) (eqlen, eqmem *Node) {
s = conv(s, types.Types[TSTRING])
t = conv(t, types.Types[TSTRING])
sptr := nod(OSPTR, s, nil)
tptr := nod(OSPTR, t, nil)
slen := conv(nod(OLEN, s, nil), types.Types[TUINTPTR])
tlen := conv(nod(OLEN, t, nil), types.Types[TUINTPTR])
fn := syslook("memequal")
fn = substArgTypes(fn, types.Types[TUINT8], types.Types[TUINT8])
call := nod(OCALL, fn, nil)
call.List.Append(sptr, tptr, slen.copy())
call = typecheck(call, ctxExpr|ctxMultiOK)
cmp := nod(OEQ, slen, tlen)
cmp = typecheck(cmp, ctxExpr)
cmp.Type = types.Types[TBOOL]
return cmp, call
}
// eqinterface returns the nodes
// s.tab == t.tab (or s.typ == t.typ, as appropriate)
// and
// ifaceeq(s.tab, s.data, t.data) (or efaceeq(s.typ, s.data, t.data), as appropriate)
// which can be used to construct interface equality comparison.
// eqtab must be evaluated before eqdata, and shortcircuiting is required.
func eqinterface(s, t *Node) (eqtab, eqdata *Node) {
if !types.Identical(s.Type, t.Type) {
Fatalf("eqinterface %v %v", s.Type, t.Type)
}
// func ifaceeq(tab *uintptr, x, y unsafe.Pointer) (ret bool)
// func efaceeq(typ *uintptr, x, y unsafe.Pointer) (ret bool)
var fn *Node
if s.Type.IsEmptyInterface() {
fn = syslook("efaceeq")
} else {
fn = syslook("ifaceeq")
}
stab := nod(OITAB, s, nil)
ttab := nod(OITAB, t, nil)
sdata := nod(OIDATA, s, nil)
tdata := nod(OIDATA, t, nil)
sdata.Type = types.Types[TUNSAFEPTR]
tdata.Type = types.Types[TUNSAFEPTR]
sdata.SetTypecheck(1)
tdata.SetTypecheck(1)
call := nod(OCALL, fn, nil)
call.List.Append(stab, sdata, tdata)
call = typecheck(call, ctxExpr|ctxMultiOK)
cmp := nod(OEQ, stab, ttab)
cmp = typecheck(cmp, ctxExpr)
cmp.Type = types.Types[TBOOL]
return cmp, call
}
// eqmem returns the node
// memequal(&p.field, &q.field [, size])
func eqmem(p *Node, q *Node, field *types.Sym, size int64) *Node {
nx := nod(OADDR, nodSym(OXDOT, p, field), nil)
ny := nod(OADDR, nodSym(OXDOT, q, field), nil)
nx = typecheck(nx, ctxExpr)
ny = typecheck(ny, ctxExpr)
fn, needsize := eqmemfunc(size, nx.Type.Elem())
call := nod(OCALL, fn, nil)
call.List.Append(nx)
call.List.Append(ny)
if needsize {
call.List.Append(nodintconst(size))
}
return call
}
func eqmemfunc(size int64, t *types.Type) (fn *Node, needsize bool) {
switch size {
default:
fn = syslook("memequal")
needsize = true
case 1, 2, 4, 8, 16:
buf := fmt.Sprintf("memequal%d", int(size)*8)
fn = syslook(buf)
}
fn = substArgTypes(fn, t, t)
return fn, needsize
}
// memrun finds runs of struct fields for which memory-only algs are appropriate.
// t is the parent struct type, and start is the field index at which to start the run.
// size is the length in bytes of the memory included in the run.
// next is the index just after the end of the memory run.
func memrun(t *types.Type, start int) (size int64, next int) {
next = start
for {
next++
if next == t.NumFields() {
break
}
// Stop run after a padded field.
if ispaddedfield(t, next-1) {
break
}
// Also, stop before a blank or non-memory field.
if f := t.Field(next); f.Sym.IsBlank() || !IsRegularMemory(f.Type) {
break
}
}
return t.Field(next-1).End() - t.Field(start).Offset, next
}
// ispaddedfield reports whether the i'th field of struct type t is followed
// by padding.
func ispaddedfield(t *types.Type, i int) bool {
if !t.IsStruct() {
Fatalf("ispaddedfield called non-struct %v", t)
}
end := t.Width
if i+1 < t.NumFields() {
end = t.Field(i + 1).Offset
}
return t.Field(i).End() != end
}

View File

@@ -1,177 +0,0 @@
// Copyright 2015 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 gc
import (
"cmd/compile/internal/types"
)
type exporter struct {
marked map[*types.Type]bool // types already seen by markType
}
// markType recursively visits types reachable from t to identify
// functions whose inline bodies may be needed.
func (p *exporter) markType(t *types.Type) {
if p.marked[t] {
return
}
p.marked[t] = true
// If this is a named type, mark all of its associated
// methods. Skip interface types because t.Methods contains
// only their unexpanded method set (i.e., exclusive of
// interface embeddings), and the switch statement below
// handles their full method set.
if t.Sym != nil && t.Etype != TINTER {
for _, m := range t.Methods().Slice() {
if types.IsExported(m.Sym.Name) {
p.markType(m.Type)
}
}
}
// Recursively mark any types that can be produced given a
// value of type t: dereferencing a pointer; indexing or
// iterating over an array, slice, or map; receiving from a
// channel; accessing a struct field or interface method; or
// calling a function.
//
// Notably, we don't mark function parameter types, because
// the user already needs some way to construct values of
// those types.
switch t.Etype {
case TPTR, TARRAY, TSLICE:
p.markType(t.Elem())
case TCHAN:
if t.ChanDir().CanRecv() {
p.markType(t.Elem())
}
case TMAP:
p.markType(t.Key())
p.markType(t.Elem())
case TSTRUCT:
for _, f := range t.FieldSlice() {
if types.IsExported(f.Sym.Name) || f.Embedded != 0 {
p.markType(f.Type)
}
}
case TFUNC:
// If t is the type of a function or method, then
// t.Nname() is its ONAME. Mark its inline body and
// any recursively called functions for export.
inlFlood(asNode(t.Nname()))
for _, f := range t.Results().FieldSlice() {
p.markType(f.Type)
}
case TINTER:
for _, f := range t.FieldSlice() {
if types.IsExported(f.Sym.Name) {
p.markType(f.Type)
}
}
}
}
// ----------------------------------------------------------------------------
// Export format
// Tags. Must be < 0.
const (
// Objects
packageTag = -(iota + 1)
constTag
typeTag
varTag
funcTag
endTag
// Types
namedTag
arrayTag
sliceTag
dddTag
structTag
pointerTag
signatureTag
interfaceTag
mapTag
chanTag
// Values
falseTag
trueTag
int64Tag
floatTag
fractionTag // not used by gc
complexTag
stringTag
nilTag
unknownTag // not used by gc (only appears in packages with errors)
// Type aliases
aliasTag
)
var predecl []*types.Type // initialized lazily
func predeclared() []*types.Type {
if predecl == nil {
// initialize lazily to be sure that all
// elements have been initialized before
predecl = []*types.Type{
// basic types
types.Types[TBOOL],
types.Types[TINT],
types.Types[TINT8],
types.Types[TINT16],
types.Types[TINT32],
types.Types[TINT64],
types.Types[TUINT],
types.Types[TUINT8],
types.Types[TUINT16],
types.Types[TUINT32],
types.Types[TUINT64],
types.Types[TUINTPTR],
types.Types[TFLOAT32],
types.Types[TFLOAT64],
types.Types[TCOMPLEX64],
types.Types[TCOMPLEX128],
types.Types[TSTRING],
// basic type aliases
types.Bytetype,
types.Runetype,
// error
types.Errortype,
// untyped types
types.UntypedBool,
types.UntypedInt,
types.UntypedRune,
types.UntypedFloat,
types.UntypedComplex,
types.UntypedString,
types.Types[TNIL],
// package unsafe
types.Types[TUNSAFEPTR],
// invalid type (package contains errors)
types.Types[Txxx],
// any type, for builtin export data
types.Types[TANY],
}
}
return predecl
}

View File

@@ -1,24 +0,0 @@
// Copyright 2015 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 gc
import (
"cmd/internal/src"
)
// numImport tracks how often a package with a given name is imported.
// It is used to provide a better error message (by using the package
// path to disambiguate) if a package that appears multiple times with
// the same name appears in an error message.
var numImport = make(map[string]int)
func npos(pos src.XPos, n *Node) *Node {
n.Pos = pos
return n
}
func builtinCall(op Op) *Node {
return nod(OCALL, mkname(builtinpkg.Lookup(goopnames[op])), nil)
}

View File

@@ -6,8 +6,11 @@
package gc
import "runtime"
import (
"cmd/compile/internal/base"
"runtime"
)
func startMutexProfiling() {
Fatalf("mutex profiling unavailable in version %v", runtime.Version())
base.Fatalf("mutex profiling unavailable in version %v", runtime.Version())
}

View File

@@ -1,340 +0,0 @@
// Code generated by mkbuiltin.go. DO NOT EDIT.
package gc
import "cmd/compile/internal/types"
var runtimeDecls = [...]struct {
name string
tag int
typ int
}{
{"newobject", funcTag, 4},
{"mallocgc", funcTag, 8},
{"panicdivide", funcTag, 9},
{"panicshift", funcTag, 9},
{"panicmakeslicelen", funcTag, 9},
{"panicmakeslicecap", funcTag, 9},
{"throwinit", funcTag, 9},
{"panicwrap", funcTag, 9},
{"gopanic", funcTag, 11},
{"gorecover", funcTag, 14},
{"goschedguarded", funcTag, 9},
{"goPanicIndex", funcTag, 16},
{"goPanicIndexU", funcTag, 18},
{"goPanicSliceAlen", funcTag, 16},
{"goPanicSliceAlenU", funcTag, 18},
{"goPanicSliceAcap", funcTag, 16},
{"goPanicSliceAcapU", funcTag, 18},
{"goPanicSliceB", funcTag, 16},
{"goPanicSliceBU", funcTag, 18},
{"goPanicSlice3Alen", funcTag, 16},
{"goPanicSlice3AlenU", funcTag, 18},
{"goPanicSlice3Acap", funcTag, 16},
{"goPanicSlice3AcapU", funcTag, 18},
{"goPanicSlice3B", funcTag, 16},
{"goPanicSlice3BU", funcTag, 18},
{"goPanicSlice3C", funcTag, 16},
{"goPanicSlice3CU", funcTag, 18},
{"printbool", funcTag, 19},
{"printfloat", funcTag, 21},
{"printint", funcTag, 23},
{"printhex", funcTag, 25},
{"printuint", funcTag, 25},
{"printcomplex", funcTag, 27},
{"printstring", funcTag, 29},
{"printpointer", funcTag, 30},
{"printuintptr", funcTag, 31},
{"printiface", funcTag, 30},
{"printeface", funcTag, 30},
{"printslice", funcTag, 30},
{"printnl", funcTag, 9},
{"printsp", funcTag, 9},
{"printlock", funcTag, 9},
{"printunlock", funcTag, 9},
{"concatstring2", funcTag, 34},
{"concatstring3", funcTag, 35},
{"concatstring4", funcTag, 36},
{"concatstring5", funcTag, 37},
{"concatstrings", funcTag, 39},
{"cmpstring", funcTag, 40},
{"intstring", funcTag, 43},
{"slicebytetostring", funcTag, 44},
{"slicebytetostringtmp", funcTag, 45},
{"slicerunetostring", funcTag, 48},
{"stringtoslicebyte", funcTag, 50},
{"stringtoslicerune", funcTag, 53},
{"slicecopy", funcTag, 54},
{"decoderune", funcTag, 55},
{"countrunes", funcTag, 56},
{"convI2I", funcTag, 57},
{"convT16", funcTag, 58},
{"convT32", funcTag, 58},
{"convT64", funcTag, 58},
{"convTstring", funcTag, 58},
{"convTslice", funcTag, 58},
{"convT2E", funcTag, 59},
{"convT2Enoptr", funcTag, 59},
{"convT2I", funcTag, 59},
{"convT2Inoptr", funcTag, 59},
{"assertE2I", funcTag, 57},
{"assertE2I2", funcTag, 60},
{"assertI2I", funcTag, 57},
{"assertI2I2", funcTag, 60},
{"panicdottypeE", funcTag, 61},
{"panicdottypeI", funcTag, 61},
{"panicnildottype", funcTag, 62},
{"ifaceeq", funcTag, 64},
{"efaceeq", funcTag, 64},
{"fastrand", funcTag, 66},
{"makemap64", funcTag, 68},
{"makemap", funcTag, 69},
{"makemap_small", funcTag, 70},
{"mapaccess1", funcTag, 71},
{"mapaccess1_fast32", funcTag, 72},
{"mapaccess1_fast64", funcTag, 72},
{"mapaccess1_faststr", funcTag, 72},
{"mapaccess1_fat", funcTag, 73},
{"mapaccess2", funcTag, 74},
{"mapaccess2_fast32", funcTag, 75},
{"mapaccess2_fast64", funcTag, 75},
{"mapaccess2_faststr", funcTag, 75},
{"mapaccess2_fat", funcTag, 76},
{"mapassign", funcTag, 71},
{"mapassign_fast32", funcTag, 72},
{"mapassign_fast32ptr", funcTag, 72},
{"mapassign_fast64", funcTag, 72},
{"mapassign_fast64ptr", funcTag, 72},
{"mapassign_faststr", funcTag, 72},
{"mapiterinit", funcTag, 77},
{"mapdelete", funcTag, 77},
{"mapdelete_fast32", funcTag, 78},
{"mapdelete_fast64", funcTag, 78},
{"mapdelete_faststr", funcTag, 78},
{"mapiternext", funcTag, 79},
{"mapclear", funcTag, 80},
{"makechan64", funcTag, 82},
{"makechan", funcTag, 83},
{"chanrecv1", funcTag, 85},
{"chanrecv2", funcTag, 86},
{"chansend1", funcTag, 88},
{"closechan", funcTag, 30},
{"writeBarrier", varTag, 90},
{"typedmemmove", funcTag, 91},
{"typedmemclr", funcTag, 92},
{"typedslicecopy", funcTag, 93},
{"selectnbsend", funcTag, 94},
{"selectnbrecv", funcTag, 95},
{"selectnbrecv2", funcTag, 97},
{"selectsetpc", funcTag, 98},
{"selectgo", funcTag, 99},
{"block", funcTag, 9},
{"makeslice", funcTag, 100},
{"makeslice64", funcTag, 101},
{"makeslicecopy", funcTag, 102},
{"growslice", funcTag, 104},
{"memmove", funcTag, 105},
{"memclrNoHeapPointers", funcTag, 106},
{"memclrHasPointers", funcTag, 106},
{"memequal", funcTag, 107},
{"memequal0", funcTag, 108},
{"memequal8", funcTag, 108},
{"memequal16", funcTag, 108},
{"memequal32", funcTag, 108},
{"memequal64", funcTag, 108},
{"memequal128", funcTag, 108},
{"f32equal", funcTag, 109},
{"f64equal", funcTag, 109},
{"c64equal", funcTag, 109},
{"c128equal", funcTag, 109},
{"strequal", funcTag, 109},
{"interequal", funcTag, 109},
{"nilinterequal", funcTag, 109},
{"memhash", funcTag, 110},
{"memhash0", funcTag, 111},
{"memhash8", funcTag, 111},
{"memhash16", funcTag, 111},
{"memhash32", funcTag, 111},
{"memhash64", funcTag, 111},
{"memhash128", funcTag, 111},
{"f32hash", funcTag, 111},
{"f64hash", funcTag, 111},
{"c64hash", funcTag, 111},
{"c128hash", funcTag, 111},
{"strhash", funcTag, 111},
{"interhash", funcTag, 111},
{"nilinterhash", funcTag, 111},
{"int64div", funcTag, 112},
{"uint64div", funcTag, 113},
{"int64mod", funcTag, 112},
{"uint64mod", funcTag, 113},
{"float64toint64", funcTag, 114},
{"float64touint64", funcTag, 115},
{"float64touint32", funcTag, 116},
{"int64tofloat64", funcTag, 117},
{"uint64tofloat64", funcTag, 118},
{"uint32tofloat64", funcTag, 119},
{"complex128div", funcTag, 120},
{"racefuncenter", funcTag, 31},
{"racefuncenterfp", funcTag, 9},
{"racefuncexit", funcTag, 9},
{"raceread", funcTag, 31},
{"racewrite", funcTag, 31},
{"racereadrange", funcTag, 121},
{"racewriterange", funcTag, 121},
{"msanread", funcTag, 121},
{"msanwrite", funcTag, 121},
{"msanmove", funcTag, 122},
{"checkptrAlignment", funcTag, 123},
{"checkptrArithmetic", funcTag, 125},
{"libfuzzerTraceCmp1", funcTag, 127},
{"libfuzzerTraceCmp2", funcTag, 129},
{"libfuzzerTraceCmp4", funcTag, 130},
{"libfuzzerTraceCmp8", funcTag, 131},
{"libfuzzerTraceConstCmp1", funcTag, 127},
{"libfuzzerTraceConstCmp2", funcTag, 129},
{"libfuzzerTraceConstCmp4", funcTag, 130},
{"libfuzzerTraceConstCmp8", funcTag, 131},
{"x86HasPOPCNT", varTag, 6},
{"x86HasSSE41", varTag, 6},
{"x86HasFMA", varTag, 6},
{"armHasVFPv4", varTag, 6},
{"arm64HasATOMICS", varTag, 6},
}
func runtimeTypes() []*types.Type {
var typs [132]*types.Type
typs[0] = types.Bytetype
typs[1] = types.NewPtr(typs[0])
typs[2] = types.Types[TANY]
typs[3] = types.NewPtr(typs[2])
typs[4] = functype(nil, []*Node{anonfield(typs[1])}, []*Node{anonfield(typs[3])})
typs[5] = types.Types[TUINTPTR]
typs[6] = types.Types[TBOOL]
typs[7] = types.Types[TUNSAFEPTR]
typs[8] = functype(nil, []*Node{anonfield(typs[5]), anonfield(typs[1]), anonfield(typs[6])}, []*Node{anonfield(typs[7])})
typs[9] = functype(nil, nil, nil)
typs[10] = types.Types[TINTER]
typs[11] = functype(nil, []*Node{anonfield(typs[10])}, nil)
typs[12] = types.Types[TINT32]
typs[13] = types.NewPtr(typs[12])
typs[14] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[10])})
typs[15] = types.Types[TINT]
typs[16] = functype(nil, []*Node{anonfield(typs[15]), anonfield(typs[15])}, nil)
typs[17] = types.Types[TUINT]
typs[18] = functype(nil, []*Node{anonfield(typs[17]), anonfield(typs[15])}, nil)
typs[19] = functype(nil, []*Node{anonfield(typs[6])}, nil)
typs[20] = types.Types[TFLOAT64]
typs[21] = functype(nil, []*Node{anonfield(typs[20])}, nil)
typs[22] = types.Types[TINT64]
typs[23] = functype(nil, []*Node{anonfield(typs[22])}, nil)
typs[24] = types.Types[TUINT64]
typs[25] = functype(nil, []*Node{anonfield(typs[24])}, nil)
typs[26] = types.Types[TCOMPLEX128]
typs[27] = functype(nil, []*Node{anonfield(typs[26])}, nil)
typs[28] = types.Types[TSTRING]
typs[29] = functype(nil, []*Node{anonfield(typs[28])}, nil)
typs[30] = functype(nil, []*Node{anonfield(typs[2])}, nil)
typs[31] = functype(nil, []*Node{anonfield(typs[5])}, nil)
typs[32] = types.NewArray(typs[0], 32)
typs[33] = types.NewPtr(typs[32])
typs[34] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])})
typs[35] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])})
typs[36] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])})
typs[37] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[28])})
typs[38] = types.NewSlice(typs[28])
typs[39] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[38])}, []*Node{anonfield(typs[28])})
typs[40] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[28])}, []*Node{anonfield(typs[15])})
typs[41] = types.NewArray(typs[0], 4)
typs[42] = types.NewPtr(typs[41])
typs[43] = functype(nil, []*Node{anonfield(typs[42]), anonfield(typs[22])}, []*Node{anonfield(typs[28])})
typs[44] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[28])})
typs[45] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[28])})
typs[46] = types.Runetype
typs[47] = types.NewSlice(typs[46])
typs[48] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[47])}, []*Node{anonfield(typs[28])})
typs[49] = types.NewSlice(typs[0])
typs[50] = functype(nil, []*Node{anonfield(typs[33]), anonfield(typs[28])}, []*Node{anonfield(typs[49])})
typs[51] = types.NewArray(typs[46], 32)
typs[52] = types.NewPtr(typs[51])
typs[53] = functype(nil, []*Node{anonfield(typs[52]), anonfield(typs[28])}, []*Node{anonfield(typs[47])})
typs[54] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[5])}, []*Node{anonfield(typs[15])})
typs[55] = functype(nil, []*Node{anonfield(typs[28]), anonfield(typs[15])}, []*Node{anonfield(typs[46]), anonfield(typs[15])})
typs[56] = functype(nil, []*Node{anonfield(typs[28])}, []*Node{anonfield(typs[15])})
typs[57] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2])})
typs[58] = functype(nil, []*Node{anonfield(typs[2])}, []*Node{anonfield(typs[7])})
typs[59] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, []*Node{anonfield(typs[2])})
typs[60] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2]), anonfield(typs[6])})
typs[61] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[1])}, nil)
typs[62] = functype(nil, []*Node{anonfield(typs[1])}, nil)
typs[63] = types.NewPtr(typs[5])
typs[64] = functype(nil, []*Node{anonfield(typs[63]), anonfield(typs[7]), anonfield(typs[7])}, []*Node{anonfield(typs[6])})
typs[65] = types.Types[TUINT32]
typs[66] = functype(nil, nil, []*Node{anonfield(typs[65])})
typs[67] = types.NewMap(typs[2], typs[2])
typs[68] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[3])}, []*Node{anonfield(typs[67])})
typs[69] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[3])}, []*Node{anonfield(typs[67])})
typs[70] = functype(nil, nil, []*Node{anonfield(typs[67])})
typs[71] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3])}, []*Node{anonfield(typs[3])})
typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[2])}, []*Node{anonfield(typs[3])})
typs[73] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])})
typs[74] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[6])})
typs[75] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[6])})
typs[76] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[6])})
typs[77] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[3])}, nil)
typs[78] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67]), anonfield(typs[2])}, nil)
typs[79] = functype(nil, []*Node{anonfield(typs[3])}, nil)
typs[80] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[67])}, nil)
typs[81] = types.NewChan(typs[2], types.Cboth)
typs[82] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22])}, []*Node{anonfield(typs[81])})
typs[83] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[81])})
typs[84] = types.NewChan(typs[2], types.Crecv)
typs[85] = functype(nil, []*Node{anonfield(typs[84]), anonfield(typs[3])}, nil)
typs[86] = functype(nil, []*Node{anonfield(typs[84]), anonfield(typs[3])}, []*Node{anonfield(typs[6])})
typs[87] = types.NewChan(typs[2], types.Csend)
typs[88] = functype(nil, []*Node{anonfield(typs[87]), anonfield(typs[3])}, nil)
typs[89] = types.NewArray(typs[0], 3)
typs[90] = tostruct([]*Node{namedfield("enabled", typs[6]), namedfield("pad", typs[89]), namedfield("needed", typs[6]), namedfield("cgo", typs[6]), namedfield("alignme", typs[24])})
typs[91] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil)
typs[92] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil)
typs[93] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[15]), anonfield(typs[3]), anonfield(typs[15])}, []*Node{anonfield(typs[15])})
typs[94] = functype(nil, []*Node{anonfield(typs[87]), anonfield(typs[3])}, []*Node{anonfield(typs[6])})
typs[95] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[84])}, []*Node{anonfield(typs[6])})
typs[96] = types.NewPtr(typs[6])
typs[97] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[96]), anonfield(typs[84])}, []*Node{anonfield(typs[6])})
typs[98] = functype(nil, []*Node{anonfield(typs[63])}, nil)
typs[99] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[63]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[6])}, []*Node{anonfield(typs[15]), anonfield(typs[6])})
typs[100] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[7])})
typs[101] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[7])})
typs[102] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15]), anonfield(typs[7])}, []*Node{anonfield(typs[7])})
typs[103] = types.NewSlice(typs[2])
typs[104] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[103]), anonfield(typs[15])}, []*Node{anonfield(typs[103])})
typs[105] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[5])}, nil)
typs[106] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5])}, nil)
typs[107] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[5])}, []*Node{anonfield(typs[6])})
typs[108] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3])}, []*Node{anonfield(typs[6])})
typs[109] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[7])}, []*Node{anonfield(typs[6])})
typs[110] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5]), anonfield(typs[5])}, []*Node{anonfield(typs[5])})
typs[111] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[5])}, []*Node{anonfield(typs[5])})
typs[112] = functype(nil, []*Node{anonfield(typs[22]), anonfield(typs[22])}, []*Node{anonfield(typs[22])})
typs[113] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, []*Node{anonfield(typs[24])})
typs[114] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[22])})
typs[115] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[24])})
typs[116] = functype(nil, []*Node{anonfield(typs[20])}, []*Node{anonfield(typs[65])})
typs[117] = functype(nil, []*Node{anonfield(typs[22])}, []*Node{anonfield(typs[20])})
typs[118] = functype(nil, []*Node{anonfield(typs[24])}, []*Node{anonfield(typs[20])})
typs[119] = functype(nil, []*Node{anonfield(typs[65])}, []*Node{anonfield(typs[20])})
typs[120] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[26])}, []*Node{anonfield(typs[26])})
typs[121] = functype(nil, []*Node{anonfield(typs[5]), anonfield(typs[5])}, nil)
typs[122] = functype(nil, []*Node{anonfield(typs[5]), anonfield(typs[5]), anonfield(typs[5])}, nil)
typs[123] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[1]), anonfield(typs[5])}, nil)
typs[124] = types.NewSlice(typs[7])
typs[125] = functype(nil, []*Node{anonfield(typs[7]), anonfield(typs[124])}, nil)
typs[126] = types.Types[TUINT8]
typs[127] = functype(nil, []*Node{anonfield(typs[126]), anonfield(typs[126])}, nil)
typs[128] = types.Types[TUINT16]
typs[129] = functype(nil, []*Node{anonfield(typs[128]), anonfield(typs[128])}, nil)
typs[130] = functype(nil, []*Node{anonfield(typs[65]), anonfield(typs[65])}, nil)
typs[131] = functype(nil, []*Node{anonfield(typs[24]), anonfield(typs[24])}, nil)
return typs[:]
}

View File

@@ -1,278 +0,0 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gc
import (
"math/bits"
)
const (
wordBits = 32
wordMask = wordBits - 1
wordShift = 5
)
// A bvec is a bit vector.
type bvec struct {
n int32 // number of bits in vector
b []uint32 // words holding bits
}
func bvalloc(n int32) bvec {
nword := (n + wordBits - 1) / wordBits
return bvec{n, make([]uint32, nword)}
}
type bulkBvec struct {
words []uint32
nbit int32
nword int32
}
func bvbulkalloc(nbit int32, count int32) bulkBvec {
nword := (nbit + wordBits - 1) / wordBits
size := int64(nword) * int64(count)
if int64(int32(size*4)) != size*4 {
Fatalf("bvbulkalloc too big: nbit=%d count=%d nword=%d size=%d", nbit, count, nword, size)
}
return bulkBvec{
words: make([]uint32, size),
nbit: nbit,
nword: nword,
}
}
func (b *bulkBvec) next() bvec {
out := bvec{b.nbit, b.words[:b.nword]}
b.words = b.words[b.nword:]
return out
}
func (bv1 bvec) Eq(bv2 bvec) bool {
if bv1.n != bv2.n {
Fatalf("bvequal: lengths %d and %d are not equal", bv1.n, bv2.n)
}
for i, x := range bv1.b {
if x != bv2.b[i] {
return false
}
}
return true
}
func (dst bvec) Copy(src bvec) {
copy(dst.b, src.b)
}
func (bv bvec) Get(i int32) bool {
if i < 0 || i >= bv.n {
Fatalf("bvget: index %d is out of bounds with length %d\n", i, bv.n)
}
mask := uint32(1 << uint(i%wordBits))
return bv.b[i>>wordShift]&mask != 0
}
func (bv bvec) Set(i int32) {
if i < 0 || i >= bv.n {
Fatalf("bvset: index %d is out of bounds with length %d\n", i, bv.n)
}
mask := uint32(1 << uint(i%wordBits))
bv.b[i/wordBits] |= mask
}
func (bv bvec) Unset(i int32) {
if i < 0 || i >= bv.n {
Fatalf("bvunset: index %d is out of bounds with length %d\n", i, bv.n)
}
mask := uint32(1 << uint(i%wordBits))
bv.b[i/wordBits] &^= mask
}
// bvnext returns the smallest index >= i for which bvget(bv, i) == 1.
// If there is no such index, bvnext returns -1.
func (bv bvec) Next(i int32) int32 {
if i >= bv.n {
return -1
}
// Jump i ahead to next word with bits.
if bv.b[i>>wordShift]>>uint(i&wordMask) == 0 {
i &^= wordMask
i += wordBits
for i < bv.n && bv.b[i>>wordShift] == 0 {
i += wordBits
}
}
if i >= bv.n {
return -1
}
// Find 1 bit.
w := bv.b[i>>wordShift] >> uint(i&wordMask)
i += int32(bits.TrailingZeros32(w))
return i
}
func (bv bvec) IsEmpty() bool {
for _, x := range bv.b {
if x != 0 {
return false
}
}
return true
}
func (bv bvec) Not() {
for i, x := range bv.b {
bv.b[i] = ^x
}
}
// union
func (dst bvec) Or(src1, src2 bvec) {
if len(src1.b) == 0 {
return
}
_, _ = dst.b[len(src1.b)-1], src2.b[len(src1.b)-1] // hoist bounds checks out of the loop
for i, x := range src1.b {
dst.b[i] = x | src2.b[i]
}
}
// intersection
func (dst bvec) And(src1, src2 bvec) {
if len(src1.b) == 0 {
return
}
_, _ = dst.b[len(src1.b)-1], src2.b[len(src1.b)-1] // hoist bounds checks out of the loop
for i, x := range src1.b {
dst.b[i] = x & src2.b[i]
}
}
// difference
func (dst bvec) AndNot(src1, src2 bvec) {
if len(src1.b) == 0 {
return
}
_, _ = dst.b[len(src1.b)-1], src2.b[len(src1.b)-1] // hoist bounds checks out of the loop
for i, x := range src1.b {
dst.b[i] = x &^ src2.b[i]
}
}
func (bv bvec) String() string {
s := make([]byte, 2+bv.n)
copy(s, "#*")
for i := int32(0); i < bv.n; i++ {
ch := byte('0')
if bv.Get(i) {
ch = '1'
}
s[2+i] = ch
}
return string(s)
}
func (bv bvec) Clear() {
for i := range bv.b {
bv.b[i] = 0
}
}
// FNV-1 hash function constants.
const (
H0 = 2166136261
Hp = 16777619
)
func hashbitmap(h uint32, bv bvec) uint32 {
n := int((bv.n + 31) / 32)
for i := 0; i < n; i++ {
w := bv.b[i]
h = (h * Hp) ^ (w & 0xff)
h = (h * Hp) ^ ((w >> 8) & 0xff)
h = (h * Hp) ^ ((w >> 16) & 0xff)
h = (h * Hp) ^ ((w >> 24) & 0xff)
}
return h
}
// bvecSet is a set of bvecs, in initial insertion order.
type bvecSet struct {
index []int // hash -> uniq index. -1 indicates empty slot.
uniq []bvec // unique bvecs, in insertion order
}
func (m *bvecSet) grow() {
// Allocate new index.
n := len(m.index) * 2
if n == 0 {
n = 32
}
newIndex := make([]int, n)
for i := range newIndex {
newIndex[i] = -1
}
// Rehash into newIndex.
for i, bv := range m.uniq {
h := hashbitmap(H0, bv) % uint32(len(newIndex))
for {
j := newIndex[h]
if j < 0 {
newIndex[h] = i
break
}
h++
if h == uint32(len(newIndex)) {
h = 0
}
}
}
m.index = newIndex
}
// add adds bv to the set and returns its index in m.extractUniqe.
// The caller must not modify bv after this.
func (m *bvecSet) add(bv bvec) int {
if len(m.uniq)*4 >= len(m.index) {
m.grow()
}
index := m.index
h := hashbitmap(H0, bv) % uint32(len(index))
for {
j := index[h]
if j < 0 {
// New bvec.
index[h] = len(m.uniq)
m.uniq = append(m.uniq, bv)
return len(m.uniq) - 1
}
jlive := m.uniq[j]
if bv.Eq(jlive) {
// Existing bvec.
return j
}
h++
if h == uint32(len(index)) {
h = 0
}
}
}
// extractUniqe returns this slice of unique bit vectors in m, as
// indexed by the result of bvecSet.add.
func (m *bvecSet) extractUniqe() []bvec {
return m.uniq
}

View File

@@ -1,594 +0,0 @@
// Copyright 2009 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 gc
import (
"cmd/compile/internal/syntax"
"cmd/compile/internal/types"
"fmt"
)
func (p *noder) funcLit(expr *syntax.FuncLit) *Node {
xtype := p.typeExpr(expr.Type)
ntype := p.typeExpr(expr.Type)
xfunc := p.nod(expr, ODCLFUNC, nil, nil)
xfunc.Func.SetIsHiddenClosure(Curfn != nil)
xfunc.Func.Nname = newfuncnamel(p.pos(expr), nblank.Sym) // filled in by typecheckclosure
xfunc.Func.Nname.Name.Param.Ntype = xtype
xfunc.Func.Nname.Name.Defn = xfunc
clo := p.nod(expr, OCLOSURE, nil, nil)
clo.Func.Ntype = ntype
xfunc.Func.Closure = clo
clo.Func.Closure = xfunc
p.funcBody(xfunc, expr.Body)
// closure-specific variables are hanging off the
// ordinary ones in the symbol table; see oldname.
// unhook them.
// make the list of pointers for the closure call.
for _, v := range xfunc.Func.Cvars.Slice() {
// Unlink from v1; see comment in syntax.go type Param for these fields.
v1 := v.Name.Defn
v1.Name.Param.Innermost = v.Name.Param.Outer
// If the closure usage of v is not dense,
// we need to make it dense; now that we're out
// of the function in which v appeared,
// look up v.Sym in the enclosing function
// and keep it around for use in the compiled code.
//
// That is, suppose we just finished parsing the innermost
// closure f4 in this code:
//
// func f() {
// v := 1
// func() { // f2
// use(v)
// func() { // f3
// func() { // f4
// use(v)
// }()
// }()
// }()
// }
//
// At this point v.Outer is f2's v; there is no f3's v.
// To construct the closure f4 from within f3,
// we need to use f3's v and in this case we need to create f3's v.
// We are now in the context of f3, so calling oldname(v.Sym)
// obtains f3's v, creating it if necessary (as it is in the example).
//
// capturevars will decide whether to use v directly or &v.
v.Name.Param.Outer = oldname(v.Sym)
}
return clo
}
// typecheckclosure typechecks an OCLOSURE node. It also creates the named
// function associated with the closure.
// TODO: This creation of the named function should probably really be done in a
// separate pass from type-checking.
func typecheckclosure(clo *Node, top int) {
xfunc := clo.Func.Closure
// Set current associated iota value, so iota can be used inside
// function in ConstSpec, see issue #22344
if x := getIotaValue(); x >= 0 {
xfunc.SetIota(x)
}
clo.Func.Ntype = typecheck(clo.Func.Ntype, ctxType)
clo.Type = clo.Func.Ntype.Type
clo.Func.Top = top
// Do not typecheck xfunc twice, otherwise, we will end up pushing
// xfunc to xtop multiple times, causing initLSym called twice.
// See #30709
if xfunc.Typecheck() == 1 {
return
}
for _, ln := range xfunc.Func.Cvars.Slice() {
n := ln.Name.Defn
if !n.Name.Captured() {
n.Name.SetCaptured(true)
if n.Name.Decldepth == 0 {
Fatalf("typecheckclosure: var %S does not have decldepth assigned", n)
}
// Ignore assignments to the variable in straightline code
// preceding the first capturing by a closure.
if n.Name.Decldepth == decldepth {
n.Name.SetAssigned(false)
}
}
}
xfunc.Func.Nname.Sym = closurename(Curfn)
setNodeNameFunc(xfunc.Func.Nname)
xfunc = typecheck(xfunc, ctxStmt)
// Type check the body now, but only if we're inside a function.
// At top level (in a variable initialization: curfn==nil) we're not
// ready to type check code yet; we'll check it later, because the
// underlying closure function we create is added to xtop.
if Curfn != nil && clo.Type != nil {
oldfn := Curfn
Curfn = xfunc
olddd := decldepth
decldepth = 1
typecheckslice(xfunc.Nbody.Slice(), ctxStmt)
decldepth = olddd
Curfn = oldfn
}
xtop = append(xtop, xfunc)
}
// globClosgen is like Func.Closgen, but for the global scope.
var globClosgen int
// closurename generates a new unique name for a closure within
// outerfunc.
func closurename(outerfunc *Node) *types.Sym {
outer := "glob."
prefix := "func"
gen := &globClosgen
if outerfunc != nil {
if outerfunc.Func.Closure != nil {
prefix = ""
}
outer = outerfunc.funcname()
// There may be multiple functions named "_". In those
// cases, we can't use their individual Closgens as it
// would lead to name clashes.
if !outerfunc.Func.Nname.isBlank() {
gen = &outerfunc.Func.Closgen
}
}
*gen++
return lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen))
}
// capturevarscomplete is set to true when the capturevars phase is done.
var capturevarscomplete bool
// capturevars is called in a separate phase after all typechecking is done.
// It decides whether each variable captured by a closure should be captured
// by value or by reference.
// We use value capturing for values <= 128 bytes that are never reassigned
// after capturing (effectively constant).
func capturevars(xfunc *Node) {
lno := lineno
lineno = xfunc.Pos
clo := xfunc.Func.Closure
cvars := xfunc.Func.Cvars.Slice()
out := cvars[:0]
for _, v := range cvars {
if v.Type == nil {
// If v.Type is nil, it means v looked like it
// was going to be used in the closure, but
// isn't. This happens in struct literals like
// s{f: x} where we can't distinguish whether
// f is a field identifier or expression until
// resolving s.
continue
}
out = append(out, v)
// type check the & of closed variables outside the closure,
// so that the outer frame also grabs them and knows they escape.
dowidth(v.Type)
outer := v.Name.Param.Outer
outermost := v.Name.Defn
// out parameters will be assigned to implicitly upon return.
if outermost.Class() != PPARAMOUT && !outermost.Name.Addrtaken() && !outermost.Name.Assigned() && v.Type.Width <= 128 {
v.Name.SetByval(true)
} else {
outermost.Name.SetAddrtaken(true)
outer = nod(OADDR, outer, nil)
}
if Debug.m > 1 {
var name *types.Sym
if v.Name.Curfn != nil && v.Name.Curfn.Func.Nname != nil {
name = v.Name.Curfn.Func.Nname.Sym
}
how := "ref"
if v.Name.Byval() {
how = "value"
}
Warnl(v.Pos, "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, outermost.Name.Addrtaken(), outermost.Name.Assigned(), int32(v.Type.Width))
}
outer = typecheck(outer, ctxExpr)
clo.Func.Enter.Append(outer)
}
xfunc.Func.Cvars.Set(out)
lineno = lno
}
// transformclosure is called in a separate phase after escape analysis.
// It transform closure bodies to properly reference captured variables.
func transformclosure(xfunc *Node) {
lno := lineno
lineno = xfunc.Pos
clo := xfunc.Func.Closure
if clo.Func.Top&ctxCallee != 0 {
// If the closure is directly called, we transform it to a plain function call
// with variables passed as args. This avoids allocation of a closure object.
// Here we do only a part of the transformation. Walk of OCALLFUNC(OCLOSURE)
// will complete the transformation later.
// For illustration, the following closure:
// func(a int) {
// println(byval)
// byref++
// }(42)
// becomes:
// func(byval int, &byref *int, a int) {
// println(byval)
// (*&byref)++
// }(byval, &byref, 42)
// f is ONAME of the actual function.
f := xfunc.Func.Nname
// We are going to insert captured variables before input args.
var params []*types.Field
var decls []*Node
for _, v := range xfunc.Func.Cvars.Slice() {
if !v.Name.Byval() {
// If v of type T is captured by reference,
// we introduce function param &v *T
// and v remains PAUTOHEAP with &v heapaddr
// (accesses will implicitly deref &v).
addr := newname(lookup("&" + v.Sym.Name))
addr.Type = types.NewPtr(v.Type)
v.Name.Param.Heapaddr = addr
v = addr
}
v.SetClass(PPARAM)
decls = append(decls, v)
fld := types.NewField()
fld.Nname = asTypesNode(v)
fld.Type = v.Type
fld.Sym = v.Sym
params = append(params, fld)
}
if len(params) > 0 {
// Prepend params and decls.
f.Type.Params().SetFields(append(params, f.Type.Params().FieldSlice()...))
xfunc.Func.Dcl = append(decls, xfunc.Func.Dcl...)
}
dowidth(f.Type)
xfunc.Type = f.Type // update type of ODCLFUNC
} else {
// The closure is not called, so it is going to stay as closure.
var body []*Node
offset := int64(Widthptr)
for _, v := range xfunc.Func.Cvars.Slice() {
// cv refers to the field inside of closure OSTRUCTLIT.
cv := nod(OCLOSUREVAR, nil, nil)
cv.Type = v.Type
if !v.Name.Byval() {
cv.Type = types.NewPtr(v.Type)
}
offset = Rnd(offset, int64(cv.Type.Align))
cv.Xoffset = offset
offset += cv.Type.Width
if v.Name.Byval() && v.Type.Width <= int64(2*Widthptr) {
// If it is a small variable captured by value, downgrade it to PAUTO.
v.SetClass(PAUTO)
xfunc.Func.Dcl = append(xfunc.Func.Dcl, v)
body = append(body, nod(OAS, v, cv))
} else {
// Declare variable holding addresses taken from closure
// and initialize in entry prologue.
addr := newname(lookup("&" + v.Sym.Name))
addr.Type = types.NewPtr(v.Type)
addr.SetClass(PAUTO)
addr.Name.SetUsed(true)
addr.Name.Curfn = xfunc
xfunc.Func.Dcl = append(xfunc.Func.Dcl, addr)
v.Name.Param.Heapaddr = addr
if v.Name.Byval() {
cv = nod(OADDR, cv, nil)
}
body = append(body, nod(OAS, addr, cv))
}
}
if len(body) > 0 {
typecheckslice(body, ctxStmt)
xfunc.Func.Enter.Set(body)
xfunc.Func.SetNeedctxt(true)
}
}
lineno = lno
}
// hasemptycvars reports whether closure clo has an
// empty list of captured vars.
func hasemptycvars(clo *Node) bool {
xfunc := clo.Func.Closure
return xfunc.Func.Cvars.Len() == 0
}
// closuredebugruntimecheck applies boilerplate checks for debug flags
// and compiling runtime
func closuredebugruntimecheck(clo *Node) {
if Debug_closure > 0 {
xfunc := clo.Func.Closure
if clo.Esc == EscHeap {
Warnl(clo.Pos, "heap closure, captured vars = %v", xfunc.Func.Cvars)
} else {
Warnl(clo.Pos, "stack closure, captured vars = %v", xfunc.Func.Cvars)
}
}
if compiling_runtime && clo.Esc == EscHeap {
yyerrorl(clo.Pos, "heap-allocated closure, not allowed in runtime")
}
}
// closureType returns the struct type used to hold all the information
// needed in the closure for clo (clo must be a OCLOSURE node).
// The address of a variable of the returned type can be cast to a func.
func closureType(clo *Node) *types.Type {
// Create closure in the form of a composite literal.
// supposing the closure captures an int i and a string s
// and has one float64 argument and no results,
// the generated code looks like:
//
// clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s}
//
// The use of the struct provides type information to the garbage
// collector so that it can walk the closure. We could use (in this case)
// [3]unsafe.Pointer instead, but that would leave the gc in the dark.
// The information appears in the binary in the form of type descriptors;
// the struct is unnamed so that closures in multiple packages with the
// same struct type can share the descriptor.
fields := []*Node{
namedfield(".F", types.Types[TUINTPTR]),
}
for _, v := range clo.Func.Closure.Func.Cvars.Slice() {
typ := v.Type
if !v.Name.Byval() {
typ = types.NewPtr(typ)
}
fields = append(fields, symfield(v.Sym, typ))
}
typ := tostruct(fields)
typ.SetNoalg(true)
return typ
}
func walkclosure(clo *Node, init *Nodes) *Node {
xfunc := clo.Func.Closure
// If no closure vars, don't bother wrapping.
if hasemptycvars(clo) {
if Debug_closure > 0 {
Warnl(clo.Pos, "closure converted to global")
}
return xfunc.Func.Nname
}
closuredebugruntimecheck(clo)
typ := closureType(clo)
clos := nod(OCOMPLIT, nil, typenod(typ))
clos.Esc = clo.Esc
clos.List.Set(append([]*Node{nod(OCFUNC, xfunc.Func.Nname, nil)}, clo.Func.Enter.Slice()...))
clos = nod(OADDR, clos, nil)
clos.Esc = clo.Esc
// Force type conversion from *struct to the func type.
clos = convnop(clos, clo.Type)
// non-escaping temp to use, if any.
if x := prealloc[clo]; x != nil {
if !types.Identical(typ, x.Type) {
panic("closure type does not match order's assigned type")
}
clos.Left.Right = x
delete(prealloc, clo)
}
return walkexpr(clos, init)
}
func typecheckpartialcall(fn *Node, sym *types.Sym) {
switch fn.Op {
case ODOTINTER, ODOTMETH:
break
default:
Fatalf("invalid typecheckpartialcall")
}
// Create top-level function.
xfunc := makepartialcall(fn, fn.Type, sym)
fn.Func = xfunc.Func
fn.Func.SetWrapper(true)
fn.Right = newname(sym)
fn.Op = OCALLPART
fn.Type = xfunc.Type
}
// makepartialcall returns a DCLFUNC node representing the wrapper function (*-fm) needed
// for partial calls.
func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node {
rcvrtype := fn.Left.Type
sym := methodSymSuffix(rcvrtype, meth, "-fm")
if sym.Uniq() {
return asNode(sym.Def)
}
sym.SetUniq(true)
savecurfn := Curfn
saveLineNo := lineno
Curfn = nil
// Set line number equal to the line number where the method is declared.
var m *types.Field
if lookdot0(meth, rcvrtype, &m, false) == 1 && m.Pos.IsKnown() {
lineno = m.Pos
}
// Note: !m.Pos.IsKnown() happens for method expressions where
// the method is implicitly declared. The Error method of the
// built-in error type is one such method. We leave the line
// number at the use of the method expression in this
// case. See issue 29389.
tfn := nod(OTFUNC, nil, nil)
tfn.List.Set(structargs(t0.Params(), true))
tfn.Rlist.Set(structargs(t0.Results(), false))
xfunc := dclfunc(sym, tfn)
xfunc.Func.SetDupok(true)
xfunc.Func.SetNeedctxt(true)
tfn.Type.SetPkg(t0.Pkg())
// Declare and initialize variable holding receiver.
cv := nod(OCLOSUREVAR, nil, nil)
cv.Type = rcvrtype
cv.Xoffset = Rnd(int64(Widthptr), int64(cv.Type.Align))
ptr := newname(lookup(".this"))
declare(ptr, PAUTO)
ptr.Name.SetUsed(true)
var body []*Node
if rcvrtype.IsPtr() || rcvrtype.IsInterface() {
ptr.Type = rcvrtype
body = append(body, nod(OAS, ptr, cv))
} else {
ptr.Type = types.NewPtr(rcvrtype)
body = append(body, nod(OAS, ptr, nod(OADDR, cv, nil)))
}
call := nod(OCALL, nodSym(OXDOT, ptr, meth), nil)
call.List.Set(paramNnames(tfn.Type))
call.SetIsDDD(tfn.Type.IsVariadic())
if t0.NumResults() != 0 {
n := nod(ORETURN, nil, nil)
n.List.Set1(call)
call = n
}
body = append(body, call)
xfunc.Nbody.Set(body)
funcbody()
xfunc = typecheck(xfunc, ctxStmt)
// Need to typecheck the body of the just-generated wrapper.
// typecheckslice() requires that Curfn is set when processing an ORETURN.
Curfn = xfunc
typecheckslice(xfunc.Nbody.Slice(), ctxStmt)
sym.Def = asTypesNode(xfunc)
xtop = append(xtop, xfunc)
Curfn = savecurfn
lineno = saveLineNo
return xfunc
}
// partialCallType returns the struct type used to hold all the information
// needed in the closure for n (n must be a OCALLPART node).
// The address of a variable of the returned type can be cast to a func.
func partialCallType(n *Node) *types.Type {
t := tostruct([]*Node{
namedfield("F", types.Types[TUINTPTR]),
namedfield("R", n.Left.Type),
})
t.SetNoalg(true)
return t
}
func walkpartialcall(n *Node, init *Nodes) *Node {
// Create closure in the form of a composite literal.
// For x.M with receiver (x) type T, the generated code looks like:
//
// clos = &struct{F uintptr; R T}{T.M·f, x}
//
// Like walkclosure above.
if n.Left.Type.IsInterface() {
// Trigger panic for method on nil interface now.
// Otherwise it happens in the wrapper and is confusing.
n.Left = cheapexpr(n.Left, init)
n.Left = walkexpr(n.Left, nil)
tab := nod(OITAB, n.Left, nil)
tab = typecheck(tab, ctxExpr)
c := nod(OCHECKNIL, tab, nil)
c.SetTypecheck(1)
init.Append(c)
}
typ := partialCallType(n)
clos := nod(OCOMPLIT, nil, typenod(typ))
clos.Esc = n.Esc
clos.List.Set2(nod(OCFUNC, n.Func.Nname, nil), n.Left)
clos = nod(OADDR, clos, nil)
clos.Esc = n.Esc
// Force type conversion from *struct to the func type.
clos = convnop(clos, n.Type)
// non-escaping temp to use, if any.
if x := prealloc[n]; x != nil {
if !types.Identical(typ, x.Type) {
panic("partial call type does not match order's assigned type")
}
clos.Left.Right = x
delete(prealloc, n)
}
return walkexpr(clos, init)
}
// callpartMethod returns the *types.Field representing the method
// referenced by method value n.
func callpartMethod(n *Node) *types.Field {
if n.Op != OCALLPART {
Fatalf("expected OCALLPART, got %v", n)
}
// TODO(mdempsky): Optimize this. If necessary,
// makepartialcall could save m for us somewhere.
var m *types.Field
if lookdot0(n.Right.Sym, n.Left.Type, &m, false) != 1 {
Fatalf("failed to find field for OCALLPART")
}
return m
}

View File

@@ -0,0 +1,147 @@
// Copyright 2011 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 gc
import (
"internal/race"
"math/rand"
"sort"
"sync"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/liveness"
"cmd/compile/internal/ssagen"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/compile/internal/walk"
)
// "Portable" code generation.
var (
compilequeue []*ir.Func // functions waiting to be compiled
)
func enqueueFunc(fn *ir.Func) {
if ir.CurFunc != nil {
base.FatalfAt(fn.Pos(), "enqueueFunc %v inside %v", fn, ir.CurFunc)
}
if ir.FuncName(fn) == "_" {
// Skip compiling blank functions.
// Frontend already reported any spec-mandated errors (#29870).
return
}
if clo := fn.OClosure; clo != nil && !ir.IsTrivialClosure(clo) {
return // we'll get this as part of its enclosing function
}
if len(fn.Body) == 0 {
// Initialize ABI wrappers if necessary.
ssagen.InitLSym(fn, false)
liveness.WriteFuncMap(fn)
return
}
errorsBefore := base.Errors()
todo := []*ir.Func{fn}
for len(todo) > 0 {
next := todo[len(todo)-1]
todo = todo[:len(todo)-1]
prepareFunc(next)
todo = append(todo, next.Closures...)
}
if base.Errors() > errorsBefore {
return
}
// Enqueue just fn itself. compileFunctions will handle
// scheduling compilation of its closures after it's done.
compilequeue = append(compilequeue, fn)
}
// prepareFunc handles any remaining frontend compilation tasks that
// aren't yet safe to perform concurrently.
func prepareFunc(fn *ir.Func) {
// Set up the function's LSym early to avoid data races with the assemblers.
// Do this before walk, as walk needs the LSym to set attributes/relocations
// (e.g. in MarkTypeUsedInInterface).
ssagen.InitLSym(fn, true)
// Calculate parameter offsets.
types.CalcSize(fn.Type())
typecheck.DeclContext = ir.PAUTO
ir.CurFunc = fn
walk.Walk(fn)
ir.CurFunc = nil // enforce no further uses of CurFunc
typecheck.DeclContext = ir.PEXTERN
}
// compileFunctions compiles all functions in compilequeue.
// It fans out nBackendWorkers to do the work
// and waits for them to complete.
func compileFunctions() {
if len(compilequeue) == 0 {
return
}
if race.Enabled {
// Randomize compilation order to try to shake out races.
tmp := make([]*ir.Func, len(compilequeue))
perm := rand.Perm(len(compilequeue))
for i, v := range perm {
tmp[v] = compilequeue[i]
}
copy(compilequeue, tmp)
} else {
// Compile the longest functions first,
// since they're most likely to be the slowest.
// This helps avoid stragglers.
sort.Slice(compilequeue, func(i, j int) bool {
return len(compilequeue[i].Body) > len(compilequeue[j].Body)
})
}
// We queue up a goroutine per function that needs to be
// compiled, but require them to grab an available worker ID
// before doing any substantial work to limit parallelism.
workerIDs := make(chan int, base.Flag.LowerC)
for i := 0; i < base.Flag.LowerC; i++ {
workerIDs <- i
}
var wg sync.WaitGroup
var asyncCompile func(*ir.Func)
asyncCompile = func(fn *ir.Func) {
wg.Add(1)
go func() {
worker := <-workerIDs
ssagen.Compile(fn, worker)
workerIDs <- worker
// Done compiling fn. Schedule it's closures for compilation.
for _, closure := range fn.Closures {
asyncCompile(closure)
}
wg.Done()
}()
}
types.CalcSizeDisabled = true // not safe to calculate sizes concurrently
base.Ctxt.InParallel = true
for _, fn := range compilequeue {
asyncCompile(fn)
}
compilequeue = nil
wg.Wait()
base.Ctxt.InParallel = false
types.CalcSizeDisabled = false
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,256 +0,0 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gc
import (
"cmd/compile/internal/syntax"
"cmd/compile/internal/types"
"cmd/internal/obj"
"encoding/json"
"io/ioutil"
"log"
"path"
"sort"
"strconv"
"strings"
)
var embedlist []*Node
var embedCfg struct {
Patterns map[string][]string
Files map[string]string
}
func readEmbedCfg(file string) {
data, err := ioutil.ReadFile(file)
if err != nil {
log.Fatalf("-embedcfg: %v", err)
}
if err := json.Unmarshal(data, &embedCfg); err != nil {
log.Fatalf("%s: %v", file, err)
}
if embedCfg.Patterns == nil {
log.Fatalf("%s: invalid embedcfg: missing Patterns", file)
}
if embedCfg.Files == nil {
log.Fatalf("%s: invalid embedcfg: missing Files", file)
}
}
const (
embedUnknown = iota
embedBytes
embedString
embedFiles
)
func varEmbed(p *noder, names []*Node, typ *Node, exprs []*Node, embeds []PragmaEmbed) {
haveEmbed := false
for _, decl := range p.file.DeclList {
imp, ok := decl.(*syntax.ImportDecl)
if !ok {
// imports always come first
break
}
path, _ := strconv.Unquote(imp.Path.Value)
if path == "embed" {
haveEmbed = true
break
}
}
pos := embeds[0].Pos
if !haveEmbed {
p.yyerrorpos(pos, "invalid go:embed: missing import \"embed\"")
return
}
if len(names) > 1 {
p.yyerrorpos(pos, "go:embed cannot apply to multiple vars")
return
}
if len(exprs) > 0 {
p.yyerrorpos(pos, "go:embed cannot apply to var with initializer")
return
}
if typ == nil {
// Should not happen, since len(exprs) == 0 now.
p.yyerrorpos(pos, "go:embed cannot apply to var without type")
return
}
if dclcontext != PEXTERN {
p.yyerrorpos(pos, "go:embed cannot apply to var inside func")
return
}
var list []irEmbed
for _, e := range embeds {
list = append(list, irEmbed{Pos: p.makeXPos(e.Pos), Patterns: e.Patterns})
}
v := names[0]
v.Name.Param.SetEmbedList(list)
embedlist = append(embedlist, v)
}
func embedFileList(v *Node, kind int) []string {
// Build list of files to store.
have := make(map[string]bool)
var list []string
for _, e := range v.Name.Param.EmbedList() {
for _, pattern := range e.Patterns {
files, ok := embedCfg.Patterns[pattern]
if !ok {
yyerrorl(e.Pos, "invalid go:embed: build system did not map pattern: %s", pattern)
}
for _, file := range files {
if embedCfg.Files[file] == "" {
yyerrorl(e.Pos, "invalid go:embed: build system did not map file: %s", file)
continue
}
if !have[file] {
have[file] = true
list = append(list, file)
}
if kind == embedFiles {
for dir := path.Dir(file); dir != "." && !have[dir]; dir = path.Dir(dir) {
have[dir] = true
list = append(list, dir+"/")
}
}
}
}
}
sort.Slice(list, func(i, j int) bool {
return embedFileLess(list[i], list[j])
})
if kind == embedString || kind == embedBytes {
if len(list) > 1 {
yyerrorl(v.Pos, "invalid go:embed: multiple files for type %v", v.Type)
return nil
}
}
return list
}
// embedKind determines the kind of embedding variable.
func embedKind(typ *types.Type) int {
if typ.Sym != nil && typ.Sym.Name == "FS" && (typ.Sym.Pkg.Path == "embed" || (typ.Sym.Pkg == localpkg && myimportpath == "embed")) {
return embedFiles
}
if typ.Etype == types.TSTRING {
return embedString
}
if typ.Etype == types.TSLICE && typ.Elem().Etype == types.TUINT8 {
return embedBytes
}
return embedUnknown
}
func embedFileNameSplit(name string) (dir, elem string, isDir bool) {
if name[len(name)-1] == '/' {
isDir = true
name = name[:len(name)-1]
}
i := len(name) - 1
for i >= 0 && name[i] != '/' {
i--
}
if i < 0 {
return ".", name, isDir
}
return name[:i], name[i+1:], isDir
}
// embedFileLess implements the sort order for a list of embedded files.
// See the comment inside ../../../../embed/embed.go's Files struct for rationale.
func embedFileLess(x, y string) bool {
xdir, xelem, _ := embedFileNameSplit(x)
ydir, yelem, _ := embedFileNameSplit(y)
return xdir < ydir || xdir == ydir && xelem < yelem
}
func dumpembeds() {
for _, v := range embedlist {
initEmbed(v)
}
}
// initEmbed emits the init data for a //go:embed variable,
// which is either a string, a []byte, or an embed.FS.
func initEmbed(v *Node) {
commentPos := v.Name.Param.EmbedList()[0].Pos
if !langSupported(1, 16, localpkg) {
lno := lineno
lineno = commentPos
yyerrorv("go1.16", "go:embed")
lineno = lno
return
}
if embedCfg.Patterns == nil {
yyerrorl(commentPos, "invalid go:embed: build system did not supply embed configuration")
return
}
kind := embedKind(v.Type)
if kind == embedUnknown {
yyerrorl(v.Pos, "go:embed cannot apply to var of type %v", v.Type)
return
}
files := embedFileList(v, kind)
switch kind {
case embedString, embedBytes:
file := files[0]
fsym, size, err := fileStringSym(v.Pos, embedCfg.Files[file], kind == embedString, nil)
if err != nil {
yyerrorl(v.Pos, "embed %s: %v", file, err)
}
sym := v.Sym.Linksym()
off := 0
off = dsymptr(sym, off, fsym, 0) // data string
off = duintptr(sym, off, uint64(size)) // len
if kind == embedBytes {
duintptr(sym, off, uint64(size)) // cap for slice
}
case embedFiles:
slicedata := Ctxt.Lookup(`"".` + v.Sym.Name + `.files`)
off := 0
// []files pointed at by Files
off = dsymptr(slicedata, off, slicedata, 3*Widthptr) // []file, pointing just past slice
off = duintptr(slicedata, off, uint64(len(files)))
off = duintptr(slicedata, off, uint64(len(files)))
// embed/embed.go type file is:
// name string
// data string
// hash [16]byte
// Emit one of these per file in the set.
const hashSize = 16
hash := make([]byte, hashSize)
for _, file := range files {
off = dsymptr(slicedata, off, stringsym(v.Pos, file), 0) // file string
off = duintptr(slicedata, off, uint64(len(file)))
if strings.HasSuffix(file, "/") {
// entry for directory - no data
off = duintptr(slicedata, off, 0)
off = duintptr(slicedata, off, 0)
off += hashSize
} else {
fsym, size, err := fileStringSym(v.Pos, embedCfg.Files[file], true, hash)
if err != nil {
yyerrorl(v.Pos, "embed %s: %v", file, err)
}
off = dsymptr(slicedata, off, fsym, 0) // data string
off = duintptr(slicedata, off, uint64(size))
off = int(slicedata.WriteBytes(Ctxt, int64(off), hash))
}
}
ggloblsym(slicedata, int32(off), obj.RODATA|obj.LOCAL)
sym := v.Sym.Linksym()
dsymptr(sym, 0, slicedata, 0)
}
}

View File

@@ -1,472 +0,0 @@
// Copyright 2011 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 gc
import (
"cmd/compile/internal/types"
"fmt"
)
func escapes(all []*Node) {
visitBottomUp(all, escapeFuncs)
}
const (
EscFuncUnknown = 0 + iota
EscFuncPlanned
EscFuncStarted
EscFuncTagged
)
func min8(a, b int8) int8 {
if a < b {
return a
}
return b
}
func max8(a, b int8) int8 {
if a > b {
return a
}
return b
}
const (
EscUnknown = iota
EscNone // Does not escape to heap, result, or parameters.
EscHeap // Reachable from the heap
EscNever // By construction will not escape.
)
// funcSym returns fn.Func.Nname.Sym if no nils are encountered along the way.
func funcSym(fn *Node) *types.Sym {
if fn == nil || fn.Func.Nname == nil {
return nil
}
return fn.Func.Nname.Sym
}
// Mark labels that have no backjumps to them as not increasing e.loopdepth.
// Walk hasn't generated (goto|label).Left.Sym.Label yet, so we'll cheat
// and set it to one of the following two. Then in esc we'll clear it again.
var (
looping Node
nonlooping Node
)
func isSliceSelfAssign(dst, src *Node) bool {
// Detect the following special case.
//
// func (b *Buffer) Foo() {
// n, m := ...
// b.buf = b.buf[n:m]
// }
//
// This assignment is a no-op for escape analysis,
// it does not store any new pointers into b that were not already there.
// However, without this special case b will escape, because we assign to OIND/ODOTPTR.
// Here we assume that the statement will not contain calls,
// that is, that order will move any calls to init.
// Otherwise base ONAME value could change between the moments
// when we evaluate it for dst and for src.
// dst is ONAME dereference.
if dst.Op != ODEREF && dst.Op != ODOTPTR || dst.Left.Op != ONAME {
return false
}
// src is a slice operation.
switch src.Op {
case OSLICE, OSLICE3, OSLICESTR:
// OK.
case OSLICEARR, OSLICE3ARR:
// Since arrays are embedded into containing object,
// slice of non-pointer array will introduce a new pointer into b that was not already there
// (pointer to b itself). After such assignment, if b contents escape,
// b escapes as well. If we ignore such OSLICEARR, we will conclude
// that b does not escape when b contents do.
//
// Pointer to an array is OK since it's not stored inside b directly.
// For slicing an array (not pointer to array), there is an implicit OADDR.
// We check that to determine non-pointer array slicing.
if src.Left.Op == OADDR {
return false
}
default:
return false
}
// slice is applied to ONAME dereference.
if src.Left.Op != ODEREF && src.Left.Op != ODOTPTR || src.Left.Left.Op != ONAME {
return false
}
// dst and src reference the same base ONAME.
return dst.Left == src.Left.Left
}
// isSelfAssign reports whether assignment from src to dst can
// be ignored by the escape analysis as it's effectively a self-assignment.
func isSelfAssign(dst, src *Node) bool {
if isSliceSelfAssign(dst, src) {
return true
}
// Detect trivial assignments that assign back to the same object.
//
// It covers these cases:
// val.x = val.y
// val.x[i] = val.y[j]
// val.x1.x2 = val.x1.y2
// ... etc
//
// These assignments do not change assigned object lifetime.
if dst == nil || src == nil || dst.Op != src.Op {
return false
}
switch dst.Op {
case ODOT, ODOTPTR:
// Safe trailing accessors that are permitted to differ.
case OINDEX:
if mayAffectMemory(dst.Right) || mayAffectMemory(src.Right) {
return false
}
default:
return false
}
// The expression prefix must be both "safe" and identical.
return samesafeexpr(dst.Left, src.Left)
}
// mayAffectMemory reports whether evaluation of n may affect the program's
// memory state. If the expression can't affect memory state, then it can be
// safely ignored by the escape analysis.
func mayAffectMemory(n *Node) bool {
// We may want to use a list of "memory safe" ops instead of generally
// "side-effect free", which would include all calls and other ops that can
// allocate or change global state. For now, it's safer to start with the latter.
//
// We're ignoring things like division by zero, index out of range,
// and nil pointer dereference here.
switch n.Op {
case ONAME, OCLOSUREVAR, OLITERAL:
return false
// Left+Right group.
case OINDEX, OADD, OSUB, OOR, OXOR, OMUL, OLSH, ORSH, OAND, OANDNOT, ODIV, OMOD:
return mayAffectMemory(n.Left) || mayAffectMemory(n.Right)
// Left group.
case ODOT, ODOTPTR, ODEREF, OCONVNOP, OCONV, OLEN, OCAP,
ONOT, OBITNOT, OPLUS, ONEG, OALIGNOF, OOFFSETOF, OSIZEOF:
return mayAffectMemory(n.Left)
default:
return true
}
}
// heapAllocReason returns the reason the given Node must be heap
// allocated, or the empty string if it doesn't.
func heapAllocReason(n *Node) string {
if n.Type == nil {
return ""
}
// Parameters are always passed via the stack.
if n.Op == ONAME && (n.Class() == PPARAM || n.Class() == PPARAMOUT) {
return ""
}
if n.Type.Width > maxStackVarSize {
return "too large for stack"
}
if (n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= maxImplicitStackVarSize {
return "too large for stack"
}
if n.Op == OCLOSURE && closureType(n).Size() >= maxImplicitStackVarSize {
return "too large for stack"
}
if n.Op == OCALLPART && partialCallType(n).Size() >= maxImplicitStackVarSize {
return "too large for stack"
}
if n.Op == OMAKESLICE {
r := n.Right
if r == nil {
r = n.Left
}
if !smallintconst(r) {
return "non-constant size"
}
if t := n.Type; t.Elem().Width != 0 && r.Int64Val() >= maxImplicitStackVarSize/t.Elem().Width {
return "too large for stack"
}
}
return ""
}
// addrescapes tags node n as having had its address taken
// by "increasing" the "value" of n.Esc to EscHeap.
// Storage is allocated as necessary to allow the address
// to be taken.
func addrescapes(n *Node) {
switch n.Op {
default:
// Unexpected Op, probably due to a previous type error. Ignore.
case ODEREF, ODOTPTR:
// Nothing to do.
case ONAME:
if n == nodfp {
break
}
// if this is a tmpname (PAUTO), it was tagged by tmpname as not escaping.
// on PPARAM it means something different.
if n.Class() == PAUTO && n.Esc == EscNever {
break
}
// If a closure reference escapes, mark the outer variable as escaping.
if n.Name.IsClosureVar() {
addrescapes(n.Name.Defn)
break
}
if n.Class() != PPARAM && n.Class() != PPARAMOUT && n.Class() != PAUTO {
break
}
// This is a plain parameter or local variable that needs to move to the heap,
// but possibly for the function outside the one we're compiling.
// That is, if we have:
//
// func f(x int) {
// func() {
// global = &x
// }
// }
//
// then we're analyzing the inner closure but we need to move x to the
// heap in f, not in the inner closure. Flip over to f before calling moveToHeap.
oldfn := Curfn
Curfn = n.Name.Curfn
if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE {
Curfn = Curfn.Func.Closure
}
ln := lineno
lineno = Curfn.Pos
moveToHeap(n)
Curfn = oldfn
lineno = ln
// ODOTPTR has already been introduced,
// so these are the non-pointer ODOT and OINDEX.
// In &x[0], if x is a slice, then x does not
// escape--the pointer inside x does, but that
// is always a heap pointer anyway.
case ODOT, OINDEX, OPAREN, OCONVNOP:
if !n.Left.Type.IsSlice() {
addrescapes(n.Left)
}
}
}
// moveToHeap records the parameter or local variable n as moved to the heap.
func moveToHeap(n *Node) {
if Debug.r != 0 {
Dump("MOVE", n)
}
if compiling_runtime {
yyerror("%v escapes to heap, not allowed in runtime", n)
}
if n.Class() == PAUTOHEAP {
Dump("n", n)
Fatalf("double move to heap")
}
// Allocate a local stack variable to hold the pointer to the heap copy.
// temp will add it to the function declaration list automatically.
heapaddr := temp(types.NewPtr(n.Type))
heapaddr.Sym = lookup("&" + n.Sym.Name)
heapaddr.Orig.Sym = heapaddr.Sym
heapaddr.Pos = n.Pos
// Unset AutoTemp to persist the &foo variable name through SSA to
// liveness analysis.
// TODO(mdempsky/drchase): Cleaner solution?
heapaddr.Name.SetAutoTemp(false)
// Parameters have a local stack copy used at function start/end
// in addition to the copy in the heap that may live longer than
// the function.
if n.Class() == PPARAM || n.Class() == PPARAMOUT {
if n.Xoffset == BADWIDTH {
Fatalf("addrescapes before param assignment")
}
// We rewrite n below to be a heap variable (indirection of heapaddr).
// Preserve a copy so we can still write code referring to the original,
// and substitute that copy into the function declaration list
// so that analyses of the local (on-stack) variables use it.
stackcopy := newname(n.Sym)
stackcopy.Type = n.Type
stackcopy.Xoffset = n.Xoffset
stackcopy.SetClass(n.Class())
stackcopy.Name.Param.Heapaddr = heapaddr
if n.Class() == PPARAMOUT {
// Make sure the pointer to the heap copy is kept live throughout the function.
// The function could panic at any point, and then a defer could recover.
// Thus, we need the pointer to the heap copy always available so the
// post-deferreturn code can copy the return value back to the stack.
// See issue 16095.
heapaddr.Name.SetIsOutputParamHeapAddr(true)
}
n.Name.Param.Stackcopy = stackcopy
// Substitute the stackcopy into the function variable list so that
// liveness and other analyses use the underlying stack slot
// and not the now-pseudo-variable n.
found := false
for i, d := range Curfn.Func.Dcl {
if d == n {
Curfn.Func.Dcl[i] = stackcopy
found = true
break
}
// Parameters are before locals, so can stop early.
// This limits the search even in functions with many local variables.
if d.Class() == PAUTO {
break
}
}
if !found {
Fatalf("cannot find %v in local variable list", n)
}
Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
}
// Modify n in place so that uses of n now mean indirection of the heapaddr.
n.SetClass(PAUTOHEAP)
n.Xoffset = 0
n.Name.Param.Heapaddr = heapaddr
n.Esc = EscHeap
if Debug.m != 0 {
Warnl(n.Pos, "moved to heap: %v", n)
}
}
// This special tag is applied to uintptr variables
// that we believe may hold unsafe.Pointers for
// calls into assembly functions.
const unsafeUintptrTag = "unsafe-uintptr"
// This special tag is applied to uintptr parameters of functions
// marked go:uintptrescapes.
const uintptrEscapesTag = "uintptr-escapes"
func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
name := func() string {
if f.Sym != nil {
return f.Sym.Name
}
return fmt.Sprintf("arg#%d", narg)
}
if fn.Nbody.Len() == 0 {
// Assume that uintptr arguments must be held live across the call.
// This is most important for syscall.Syscall.
// See golang.org/issue/13372.
// This really doesn't have much to do with escape analysis per se,
// but we are reusing the ability to annotate an individual function
// argument and pass those annotations along to importing code.
if f.Type.IsUintptr() {
if Debug.m != 0 {
Warnl(f.Pos, "assuming %v is unsafe uintptr", name())
}
return unsafeUintptrTag
}
if !f.Type.HasPointers() { // don't bother tagging for scalars
return ""
}
var esc EscLeaks
// External functions are assumed unsafe, unless
// //go:noescape is given before the declaration.
if fn.Func.Pragma&Noescape != 0 {
if Debug.m != 0 && f.Sym != nil {
Warnl(f.Pos, "%v does not escape", name())
}
} else {
if Debug.m != 0 && f.Sym != nil {
Warnl(f.Pos, "leaking param: %v", name())
}
esc.AddHeap(0)
}
return esc.Encode()
}
if fn.Func.Pragma&UintptrEscapes != 0 {
if f.Type.IsUintptr() {
if Debug.m != 0 {
Warnl(f.Pos, "marking %v as escaping uintptr", name())
}
return uintptrEscapesTag
}
if f.IsDDD() && f.Type.Elem().IsUintptr() {
// final argument is ...uintptr.
if Debug.m != 0 {
Warnl(f.Pos, "marking %v as escaping ...uintptr", name())
}
return uintptrEscapesTag
}
}
if !f.Type.HasPointers() { // don't bother tagging for scalars
return ""
}
// Unnamed parameters are unused and therefore do not escape.
if f.Sym == nil || f.Sym.IsBlank() {
var esc EscLeaks
return esc.Encode()
}
n := asNode(f.Nname)
loc := e.oldLoc(n)
esc := loc.paramEsc
esc.Optimize()
if Debug.m != 0 && !loc.escapes {
if esc.Empty() {
Warnl(f.Pos, "%v does not escape", name())
}
if x := esc.Heap(); x >= 0 {
if x == 0 {
Warnl(f.Pos, "leaking param: %v", name())
} else {
// TODO(mdempsky): Mention level=x like below?
Warnl(f.Pos, "leaking param content: %v", name())
}
}
for i := 0; i < numEscResults; i++ {
if x := esc.Result(i); x >= 0 {
res := fn.Type.Results().Field(i).Sym
Warnl(f.Pos, "leaking param: %v to result %v level=%d", name(), res, x)
}
}
}
return esc.Encode()
}

File diff suppressed because it is too large Load Diff

View File

@@ -5,225 +5,68 @@
package gc
import (
"cmd/compile/internal/base"
"cmd/compile/internal/inline"
"cmd/compile/internal/ir"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/bio"
"cmd/internal/src"
"fmt"
)
var (
Debug_export int // if set, print debugging information about export data
"go/constant"
)
func exportf(bout *bio.Writer, format string, args ...interface{}) {
fmt.Fprintf(bout, format, args...)
if Debug_export != 0 {
if base.Debug.Export != 0 {
fmt.Printf(format, args...)
}
}
var asmlist []*Node
// exportsym marks n for export (or reexport).
func exportsym(n *Node) {
if n.Sym.OnExportList() {
return
}
n.Sym.SetOnExportList(true)
if Debug.E != 0 {
fmt.Printf("export symbol %v\n", n.Sym)
}
exportlist = append(exportlist, n)
}
func initname(s string) bool {
return s == "init"
}
func autoexport(n *Node, ctxt Class) {
if n.Sym.Pkg != localpkg {
return
}
if (ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN {
return
}
if n.Type != nil && n.Type.IsKind(TFUNC) && n.IsMethod() {
return
}
if types.IsExported(n.Sym.Name) || initname(n.Sym.Name) {
exportsym(n)
}
if asmhdr != "" && !n.Sym.Asm() {
n.Sym.SetAsm(true)
asmlist = append(asmlist, n)
}
}
func dumpexport(bout *bio.Writer) {
p := &exporter{marked: make(map[*types.Type]bool)}
for _, n := range typecheck.Target.Exports {
p.markObject(n)
}
// The linker also looks for the $$ marker - use char after $$ to distinguish format.
exportf(bout, "\n$$B\n") // indicate binary export format
off := bout.Offset()
iexport(bout.Writer)
typecheck.WriteExports(bout.Writer)
size := bout.Offset() - off
exportf(bout, "\n$$\n")
if Debug_export != 0 {
fmt.Printf("BenchmarkExportSize:%s 1 %d bytes\n", myimportpath, size)
}
}
func importsym(ipkg *types.Pkg, s *types.Sym, op Op) *Node {
n := asNode(s.PkgDef())
if n == nil {
// iimport should have created a stub ONONAME
// declaration for all imported symbols. The exception
// is declarations for Runtimepkg, which are populated
// by loadsys instead.
if s.Pkg != Runtimepkg {
Fatalf("missing ONONAME for %v\n", s)
}
n = dclname(s)
s.SetPkgDef(asTypesNode(n))
s.Importdef = ipkg
}
if n.Op != ONONAME && n.Op != op {
redeclare(lineno, s, fmt.Sprintf("during import %q", ipkg.Path))
}
return n
}
// importtype returns the named type declared by symbol s.
// If no such type has been declared yet, a forward declaration is returned.
// ipkg is the package being imported
func importtype(ipkg *types.Pkg, pos src.XPos, s *types.Sym) *types.Type {
n := importsym(ipkg, s, OTYPE)
if n.Op != OTYPE {
t := types.New(TFORW)
t.Sym = s
t.Nod = asTypesNode(n)
n.Op = OTYPE
n.Pos = pos
n.Type = t
n.SetClass(PEXTERN)
}
t := n.Type
if t == nil {
Fatalf("importtype %v", s)
}
return t
}
// importobj declares symbol s as an imported object representable by op.
// ipkg is the package being imported
func importobj(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op Op, ctxt Class, t *types.Type) *Node {
n := importsym(ipkg, s, op)
if n.Op != ONONAME {
if n.Op == op && (n.Class() != ctxt || !types.Identical(n.Type, t)) {
redeclare(lineno, s, fmt.Sprintf("during import %q", ipkg.Path))
}
return nil
}
n.Op = op
n.Pos = pos
n.SetClass(ctxt)
if ctxt == PFUNC {
n.Sym.SetFunc(true)
}
n.Type = t
return n
}
// importconst declares symbol s as an imported constant with type t and value val.
// ipkg is the package being imported
func importconst(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type, val Val) {
n := importobj(ipkg, pos, s, OLITERAL, PEXTERN, t)
if n == nil { // TODO: Check that value matches.
return
}
n.SetVal(val)
if Debug.E != 0 {
fmt.Printf("import const %v %L = %v\n", s, t, val)
}
}
// importfunc declares symbol s as an imported function with type t.
// ipkg is the package being imported
func importfunc(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) {
n := importobj(ipkg, pos, s, ONAME, PFUNC, t)
if n == nil {
return
}
n.Func = new(Func)
t.SetNname(asTypesNode(n))
if Debug.E != 0 {
fmt.Printf("import func %v%S\n", s, t)
}
}
// importvar declares symbol s as an imported variable with type t.
// ipkg is the package being imported
func importvar(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) {
n := importobj(ipkg, pos, s, ONAME, PEXTERN, t)
if n == nil {
return
}
if Debug.E != 0 {
fmt.Printf("import var %v %L\n", s, t)
}
}
// importalias declares symbol s as an imported type alias with type t.
// ipkg is the package being imported
func importalias(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) {
n := importobj(ipkg, pos, s, OTYPE, PEXTERN, t)
if n == nil {
return
}
if Debug.E != 0 {
fmt.Printf("import type %v = %L\n", s, t)
if base.Debug.Export != 0 {
fmt.Printf("BenchmarkExportSize:%s 1 %d bytes\n", base.Ctxt.Pkgpath, size)
}
}
func dumpasmhdr() {
b, err := bio.Create(asmhdr)
b, err := bio.Create(base.Flag.AsmHdr)
if err != nil {
Fatalf("%v", err)
base.Fatalf("%v", err)
}
fmt.Fprintf(b, "// generated by compile -asmhdr from package %s\n\n", localpkg.Name)
for _, n := range asmlist {
if n.Sym.IsBlank() {
fmt.Fprintf(b, "// generated by compile -asmhdr from package %s\n\n", types.LocalPkg.Name)
for _, n := range typecheck.Target.Asms {
if n.Sym().IsBlank() {
continue
}
switch n.Op {
case OLITERAL:
t := n.Val().Ctype()
if t == CTFLT || t == CTCPLX {
switch n.Op() {
case ir.OLITERAL:
t := n.Val().Kind()
if t == constant.Float || t == constant.Complex {
break
}
fmt.Fprintf(b, "#define const_%s %#v\n", n.Sym.Name, n.Val())
fmt.Fprintf(b, "#define const_%s %#v\n", n.Sym().Name, n.Val())
case OTYPE:
t := n.Type
case ir.OTYPE:
t := n.Type()
if !t.IsStruct() || t.StructType().Map != nil || t.IsFuncArgStruct() {
break
}
fmt.Fprintf(b, "#define %s__size %d\n", n.Sym.Name, int(t.Width))
fmt.Fprintf(b, "#define %s__size %d\n", n.Sym().Name, int(t.Width))
for _, f := range t.Fields().Slice() {
if !f.Sym.IsBlank() {
fmt.Fprintf(b, "#define %s_%s %d\n", n.Sym.Name, f.Sym.Name, int(f.Offset))
fmt.Fprintf(b, "#define %s_%s %d\n", n.Sym().Name, f.Sym.Name, int(f.Offset))
}
}
}
@@ -231,3 +74,83 @@ func dumpasmhdr() {
b.Close()
}
type exporter struct {
marked map[*types.Type]bool // types already seen by markType
}
// markObject visits a reachable object.
func (p *exporter) markObject(n ir.Node) {
if n.Op() == ir.ONAME {
n := n.(*ir.Name)
if n.Class == ir.PFUNC {
inline.Inline_Flood(n, typecheck.Export)
}
}
p.markType(n.Type())
}
// markType recursively visits types reachable from t to identify
// functions whose inline bodies may be needed.
func (p *exporter) markType(t *types.Type) {
if p.marked[t] {
return
}
p.marked[t] = true
// If this is a named type, mark all of its associated
// methods. Skip interface types because t.Methods contains
// only their unexpanded method set (i.e., exclusive of
// interface embeddings), and the switch statement below
// handles their full method set.
if t.Sym() != nil && t.Kind() != types.TINTER {
for _, m := range t.Methods().Slice() {
if types.IsExported(m.Sym.Name) {
p.markObject(ir.AsNode(m.Nname))
}
}
}
// Recursively mark any types that can be produced given a
// value of type t: dereferencing a pointer; indexing or
// iterating over an array, slice, or map; receiving from a
// channel; accessing a struct field or interface method; or
// calling a function.
//
// Notably, we don't mark function parameter types, because
// the user already needs some way to construct values of
// those types.
switch t.Kind() {
case types.TPTR, types.TARRAY, types.TSLICE:
p.markType(t.Elem())
case types.TCHAN:
if t.ChanDir().CanRecv() {
p.markType(t.Elem())
}
case types.TMAP:
p.markType(t.Key())
p.markType(t.Elem())
case types.TSTRUCT:
for _, f := range t.FieldSlice() {
if types.IsExported(f.Sym.Name) || f.Embedded != 0 {
p.markType(f.Type)
}
}
case types.TFUNC:
for _, f := range t.Results().FieldSlice() {
p.markType(f.Type)
}
case types.TINTER:
for _, f := range t.FieldSlice() {
if types.IsExported(f.Sym.Name) {
p.markType(f.Type)
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,86 +0,0 @@
// Copyright 2009 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 gc
import (
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/src"
"strconv"
)
// sysfunc looks up Go function name in package runtime. This function
// must follow the internal calling convention.
func sysfunc(name string) *obj.LSym {
s := Runtimepkg.Lookup(name)
s.SetFunc(true)
return s.Linksym()
}
// sysvar looks up a variable (or assembly function) name in package
// runtime. If this is a function, it may have a special calling
// convention.
func sysvar(name string) *obj.LSym {
return Runtimepkg.Lookup(name).Linksym()
}
// isParamStackCopy reports whether this is the on-stack copy of a
// function parameter that moved to the heap.
func (n *Node) isParamStackCopy() bool {
return n.Op == ONAME && (n.Class() == PPARAM || n.Class() == PPARAMOUT) && n.Name.Param.Heapaddr != nil
}
// isParamHeapCopy reports whether this is the on-heap copy of
// a function parameter that moved to the heap.
func (n *Node) isParamHeapCopy() bool {
return n.Op == ONAME && n.Class() == PAUTOHEAP && n.Name.Param.Stackcopy != nil
}
// autotmpname returns the name for an autotmp variable numbered n.
func autotmpname(n int) string {
// Give each tmp a different name so that they can be registerized.
// Add a preceding . to avoid clashing with legal names.
const prefix = ".autotmp_"
// Start with a buffer big enough to hold a large n.
b := []byte(prefix + " ")[:len(prefix)]
b = strconv.AppendInt(b, int64(n), 10)
return types.InternString(b)
}
// make a new Node off the books
func tempAt(pos src.XPos, curfn *Node, t *types.Type) *Node {
if curfn == nil {
Fatalf("no curfn for tempAt")
}
if curfn.Func.Closure != nil && curfn.Op == OCLOSURE {
Dump("tempAt", curfn)
Fatalf("adding tempAt to wrong closure function")
}
if t == nil {
Fatalf("tempAt called with nil type")
}
s := &types.Sym{
Name: autotmpname(len(curfn.Func.Dcl)),
Pkg: localpkg,
}
n := newnamel(pos, s)
s.Def = asTypesNode(n)
n.Type = t
n.SetClass(PAUTO)
n.Esc = EscNever
n.Name.Curfn = curfn
n.Name.SetUsed(true)
n.Name.SetAutoTemp(true)
curfn.Func.Dcl = append(curfn.Func.Dcl, n)
dowidth(t)
return n.Orig
}
func temp(t *types.Type) *Node {
return tempAt(lineno, Curfn, t)
}

View File

@@ -1,349 +0,0 @@
// Copyright 2009 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 gc
import (
"cmd/compile/internal/ssa"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/src"
"sync"
)
const (
BADWIDTH = types.BADWIDTH
)
var (
// maximum size variable which we will allocate on the stack.
// This limit is for explicit variable declarations like "var x T" or "x := ...".
// Note: the flag smallframes can update this value.
maxStackVarSize = int64(10 * 1024 * 1024)
// maximum size of implicit variables that we will allocate on the stack.
// p := new(T) allocating T on the stack
// p := &T{} allocating T on the stack
// s := make([]T, n) allocating [n]T on the stack
// s := []byte("...") allocating [n]byte on the stack
// Note: the flag smallframes can update this value.
maxImplicitStackVarSize = int64(64 * 1024)
// smallArrayBytes is the maximum size of an array which is considered small.
// Small arrays will be initialized directly with a sequence of constant stores.
// Large arrays will be initialized by copying from a static temp.
// 256 bytes was chosen to minimize generated code + statictmp size.
smallArrayBytes = int64(256)
)
// isRuntimePkg reports whether p is package runtime.
func isRuntimePkg(p *types.Pkg) bool {
if compiling_runtime && p == localpkg {
return true
}
return p.Path == "runtime"
}
// isReflectPkg reports whether p is package reflect.
func isReflectPkg(p *types.Pkg) bool {
if p == localpkg {
return myimportpath == "reflect"
}
return p.Path == "reflect"
}
// The Class of a variable/function describes the "storage class"
// of a variable or function. During parsing, storage classes are
// called declaration contexts.
type Class uint8
//go:generate stringer -type=Class
const (
Pxxx Class = iota // no class; used during ssa conversion to indicate pseudo-variables
PEXTERN // global variables
PAUTO // local variables
PAUTOHEAP // local variables or parameters moved to heap
PPARAM // input arguments
PPARAMOUT // output results
PFUNC // global functions
// Careful: Class is stored in three bits in Node.flags.
_ = uint((1 << 3) - iota) // static assert for iota <= (1 << 3)
)
// Slices in the runtime are represented by three components:
//
// type slice struct {
// ptr unsafe.Pointer
// len int
// cap int
// }
//
// Strings in the runtime are represented by two components:
//
// type string struct {
// ptr unsafe.Pointer
// len int
// }
//
// These variables are the offsets of fields and sizes of these structs.
var (
slicePtrOffset int64
sliceLenOffset int64
sliceCapOffset int64
sizeofSlice int64
sizeofString int64
)
var pragcgobuf [][]string
var outfile string
var linkobj string
// nerrors is the number of compiler errors reported
// since the last call to saveerrors.
var nerrors int
// nsavederrors is the total number of compiler errors
// reported before the last call to saveerrors.
var nsavederrors int
var nsyntaxerrors int
var decldepth int32
var nolocalimports bool
// gc debug flags
type DebugFlags struct {
P, B, C, E,
K, L, N, S,
W, e, h, j,
l, m, r, w int
}
var Debug DebugFlags
var debugstr string
var Debug_checknil int
var Debug_typeassert int
var localpkg *types.Pkg // package being compiled
var inimport bool // set during import
var itabpkg *types.Pkg // fake pkg for itab entries
var itablinkpkg *types.Pkg // fake package for runtime itab entries
var Runtimepkg *types.Pkg // fake package runtime
var racepkg *types.Pkg // package runtime/race
var msanpkg *types.Pkg // package runtime/msan
var unsafepkg *types.Pkg // package unsafe
var trackpkg *types.Pkg // fake package for field tracking
var mappkg *types.Pkg // fake package for map zero value
var gopkg *types.Pkg // pseudo-package for method symbols on anonymous receiver types
var zerosize int64
var myimportpath string
var localimport string
var asmhdr string
var simtype [NTYPE]types.EType
var (
isInt [NTYPE]bool
isFloat [NTYPE]bool
isComplex [NTYPE]bool
issimple [NTYPE]bool
)
var (
okforeq [NTYPE]bool
okforadd [NTYPE]bool
okforand [NTYPE]bool
okfornone [NTYPE]bool
okforcmp [NTYPE]bool
okforbool [NTYPE]bool
okforcap [NTYPE]bool
okforlen [NTYPE]bool
okforarith [NTYPE]bool
okforconst [NTYPE]bool
)
var (
okfor [OEND][]bool
iscmp [OEND]bool
)
var minintval [NTYPE]*Mpint
var maxintval [NTYPE]*Mpint
var minfltval [NTYPE]*Mpflt
var maxfltval [NTYPE]*Mpflt
var xtop []*Node
var exportlist []*Node
var importlist []*Node // imported functions and methods with inlinable bodies
var (
funcsymsmu sync.Mutex // protects funcsyms and associated package lookups (see func funcsym)
funcsyms []*types.Sym
)
var dclcontext Class // PEXTERN/PAUTO
var Curfn *Node
var Widthptr int
var Widthreg int
var nblank *Node
var typecheckok bool
var compiling_runtime bool
// Compiling the standard library
var compiling_std bool
var use_writebarrier bool
var pure_go bool
var flag_installsuffix string
var flag_race bool
var flag_msan bool
var flagDWARF bool
// Whether we are adding any sort of code instrumentation, such as
// when the race detector is enabled.
var instrumenting bool
// Whether we are tracking lexical scopes for DWARF.
var trackScopes bool
// Controls generation of DWARF inlined instance records. Zero
// disables, 1 emits inlined routines but suppresses var info,
// and 2 emits inlined routines with tracking of formals/locals.
var genDwarfInline int
var debuglive int
var Ctxt *obj.Link
var writearchive bool
var nodfp *Node
var disable_checknil int
var autogeneratedPos src.XPos
// interface to back end
type Arch struct {
LinkArch *obj.LinkArch
REGSP int
MAXWIDTH int64
SoftFloat bool
PadFrame func(int64) int64
// ZeroRange zeroes a range of memory on stack. It is only inserted
// at function entry, and it is ok to clobber registers.
ZeroRange func(*Progs, *obj.Prog, int64, int64, *uint32) *obj.Prog
Ginsnop func(*Progs) *obj.Prog
Ginsnopdefer func(*Progs) *obj.Prog // special ginsnop for deferreturn
// SSAMarkMoves marks any MOVXconst ops that need to avoid clobbering flags.
SSAMarkMoves func(*SSAGenState, *ssa.Block)
// SSAGenValue emits Prog(s) for the Value.
SSAGenValue func(*SSAGenState, *ssa.Value)
// SSAGenBlock emits end-of-block Progs. SSAGenValue should be called
// for all values in the block before SSAGenBlock.
SSAGenBlock func(s *SSAGenState, b, next *ssa.Block)
}
var thearch Arch
var (
staticuint64s,
zerobase *Node
assertE2I,
assertE2I2,
assertI2I,
assertI2I2,
deferproc,
deferprocStack,
Deferreturn,
Duffcopy,
Duffzero,
gcWriteBarrier,
goschedguarded,
growslice,
msanread,
msanwrite,
msanmove,
newobject,
newproc,
panicdivide,
panicshift,
panicdottypeE,
panicdottypeI,
panicnildottype,
panicoverflow,
raceread,
racereadrange,
racewrite,
racewriterange,
x86HasPOPCNT,
x86HasSSE41,
x86HasFMA,
armHasVFPv4,
arm64HasATOMICS,
typedmemclr,
typedmemmove,
Udiv,
writeBarrier,
zerobaseSym *obj.LSym
BoundsCheckFunc [ssa.BoundsKindCount]*obj.LSym
ExtendCheckFunc [ssa.BoundsKindCount]*obj.LSym
// Wasm
WasmMove,
WasmZero,
WasmDiv,
WasmTruncS,
WasmTruncU,
SigPanic *obj.LSym
)
// GCWriteBarrierReg maps from registers to gcWriteBarrier implementation LSyms.
var GCWriteBarrierReg map[int16]*obj.LSym

View File

@@ -1,333 +0,0 @@
// Derived from Inferno utils/6c/txt.c
// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6c/txt.c
//
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
// Portions Copyright © 1997-1999 Vita Nuova Limited
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
// Portions Copyright © 2004,2006 Bruce Ellis
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
// Portions Copyright © 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package gc
import (
"cmd/compile/internal/ssa"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/src"
)
var sharedProgArray = new([10000]obj.Prog) // *T instead of T to work around issue 19839
// Progs accumulates Progs for a function and converts them into machine code.
type Progs struct {
Text *obj.Prog // ATEXT Prog for this function
next *obj.Prog // next Prog
pc int64 // virtual PC; count of Progs
pos src.XPos // position to use for new Progs
curfn *Node // fn these Progs are for
progcache []obj.Prog // local progcache
cacheidx int // first free element of progcache
nextLive LivenessIndex // liveness index for the next Prog
prevLive LivenessIndex // last emitted liveness index
}
// newProgs returns a new Progs for fn.
// worker indicates which of the backend workers will use the Progs.
func newProgs(fn *Node, worker int) *Progs {
pp := new(Progs)
if Ctxt.CanReuseProgs() {
sz := len(sharedProgArray) / nBackendWorkers
pp.progcache = sharedProgArray[sz*worker : sz*(worker+1)]
}
pp.curfn = fn
// prime the pump
pp.next = pp.NewProg()
pp.clearp(pp.next)
pp.pos = fn.Pos
pp.settext(fn)
// PCDATA tables implicitly start with index -1.
pp.prevLive = LivenessIndex{-1, false}
pp.nextLive = pp.prevLive
return pp
}
func (pp *Progs) NewProg() *obj.Prog {
var p *obj.Prog
if pp.cacheidx < len(pp.progcache) {
p = &pp.progcache[pp.cacheidx]
pp.cacheidx++
} else {
p = new(obj.Prog)
}
p.Ctxt = Ctxt
return p
}
// Flush converts from pp to machine code.
func (pp *Progs) Flush() {
plist := &obj.Plist{Firstpc: pp.Text, Curfn: pp.curfn}
obj.Flushplist(Ctxt, plist, pp.NewProg, myimportpath)
}
// Free clears pp and any associated resources.
func (pp *Progs) Free() {
if Ctxt.CanReuseProgs() {
// Clear progs to enable GC and avoid abuse.
s := pp.progcache[:pp.cacheidx]
for i := range s {
s[i] = obj.Prog{}
}
}
// Clear pp to avoid abuse.
*pp = Progs{}
}
// Prog adds a Prog with instruction As to pp.
func (pp *Progs) Prog(as obj.As) *obj.Prog {
if pp.nextLive.StackMapValid() && pp.nextLive.stackMapIndex != pp.prevLive.stackMapIndex {
// Emit stack map index change.
idx := pp.nextLive.stackMapIndex
pp.prevLive.stackMapIndex = idx
p := pp.Prog(obj.APCDATA)
Addrconst(&p.From, objabi.PCDATA_StackMapIndex)
Addrconst(&p.To, int64(idx))
}
if pp.nextLive.isUnsafePoint != pp.prevLive.isUnsafePoint {
// Emit unsafe-point marker.
pp.prevLive.isUnsafePoint = pp.nextLive.isUnsafePoint
p := pp.Prog(obj.APCDATA)
Addrconst(&p.From, objabi.PCDATA_UnsafePoint)
if pp.nextLive.isUnsafePoint {
Addrconst(&p.To, objabi.PCDATA_UnsafePointUnsafe)
} else {
Addrconst(&p.To, objabi.PCDATA_UnsafePointSafe)
}
}
p := pp.next
pp.next = pp.NewProg()
pp.clearp(pp.next)
p.Link = pp.next
if !pp.pos.IsKnown() && Debug.K != 0 {
Warn("prog: unknown position (line 0)")
}
p.As = as
p.Pos = pp.pos
if pp.pos.IsStmt() == src.PosIsStmt {
// Clear IsStmt for later Progs at this pos provided that as can be marked as a stmt
if ssa.LosesStmtMark(as) {
return p
}
pp.pos = pp.pos.WithNotStmt()
}
return p
}
func (pp *Progs) clearp(p *obj.Prog) {
obj.Nopout(p)
p.As = obj.AEND
p.Pc = pp.pc
pp.pc++
}
func (pp *Progs) Appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int16, foffset int64, ttype obj.AddrType, treg int16, toffset int64) *obj.Prog {
q := pp.NewProg()
pp.clearp(q)
q.As = as
q.Pos = p.Pos
q.From.Type = ftype
q.From.Reg = freg
q.From.Offset = foffset
q.To.Type = ttype
q.To.Reg = treg
q.To.Offset = toffset
q.Link = p.Link
p.Link = q
return q
}
func (pp *Progs) settext(fn *Node) {
if pp.Text != nil {
Fatalf("Progs.settext called twice")
}
ptxt := pp.Prog(obj.ATEXT)
pp.Text = ptxt
fn.Func.lsym.Func().Text = ptxt
ptxt.From.Type = obj.TYPE_MEM
ptxt.From.Name = obj.NAME_EXTERN
ptxt.From.Sym = fn.Func.lsym
}
// initLSym defines f's obj.LSym and initializes it based on the
// properties of f. This includes setting the symbol flags and ABI and
// creating and initializing related DWARF symbols.
//
// initLSym must be called exactly once per function and must be
// called for both functions with bodies and functions without bodies.
func (f *Func) initLSym(hasBody bool) {
if f.lsym != nil {
Fatalf("Func.initLSym called twice")
}
if nam := f.Nname; !nam.isBlank() {
f.lsym = nam.Sym.Linksym()
if f.Pragma&Systemstack != 0 {
f.lsym.Set(obj.AttrCFunc, true)
}
var aliasABI obj.ABI
needABIAlias := false
defABI, hasDefABI := symabiDefs[f.lsym.Name]
if hasDefABI && defABI == obj.ABI0 {
// Symbol is defined as ABI0. Create an
// Internal -> ABI0 wrapper.
f.lsym.SetABI(obj.ABI0)
needABIAlias, aliasABI = true, obj.ABIInternal
} else {
// No ABI override. Check that the symbol is
// using the expected ABI.
want := obj.ABIInternal
if f.lsym.ABI() != want {
Fatalf("function symbol %s has the wrong ABI %v, expected %v", f.lsym.Name, f.lsym.ABI(), want)
}
}
isLinknameExported := nam.Sym.Linkname != "" && (hasBody || hasDefABI)
if abi, ok := symabiRefs[f.lsym.Name]; (ok && abi == obj.ABI0) || isLinknameExported {
// Either 1) this symbol is definitely
// referenced as ABI0 from this package; or 2)
// this symbol is defined in this package but
// given a linkname, indicating that it may be
// referenced from another package. Create an
// ABI0 -> Internal wrapper so it can be
// called as ABI0. In case 2, it's important
// that we know it's defined in this package
// since other packages may "pull" symbols
// using linkname and we don't want to create
// duplicate ABI wrappers.
if f.lsym.ABI() != obj.ABI0 {
needABIAlias, aliasABI = true, obj.ABI0
}
}
if needABIAlias {
// These LSyms have the same name as the
// native function, so we create them directly
// rather than looking them up. The uniqueness
// of f.lsym ensures uniqueness of asym.
asym := &obj.LSym{
Name: f.lsym.Name,
Type: objabi.SABIALIAS,
R: []obj.Reloc{{Sym: f.lsym}}, // 0 size, so "informational"
}
asym.SetABI(aliasABI)
asym.Set(obj.AttrDuplicateOK, true)
Ctxt.ABIAliases = append(Ctxt.ABIAliases, asym)
}
}
if !hasBody {
// For body-less functions, we only create the LSym.
return
}
var flag int
if f.Dupok() {
flag |= obj.DUPOK
}
if f.Wrapper() {
flag |= obj.WRAPPER
}
if f.Needctxt() {
flag |= obj.NEEDCTXT
}
if f.Pragma&Nosplit != 0 {
flag |= obj.NOSPLIT
}
if f.ReflectMethod() {
flag |= obj.REFLECTMETHOD
}
// Clumsy but important.
// See test/recover.go for test cases and src/reflect/value.go
// for the actual functions being considered.
if myimportpath == "reflect" {
switch f.Nname.Sym.Name {
case "callReflect", "callMethod":
flag |= obj.WRAPPER
}
}
Ctxt.InitTextSym(f.lsym, flag)
}
func ggloblnod(nam *Node) {
s := nam.Sym.Linksym()
s.Gotype = ngotype(nam).Linksym()
flags := 0
if nam.Name.Readonly() {
flags = obj.RODATA
}
if nam.Type != nil && !nam.Type.HasPointers() {
flags |= obj.NOPTR
}
Ctxt.Globl(s, nam.Type.Width, flags)
if nam.Name.LibfuzzerExtraCounter() {
s.Type = objabi.SLIBFUZZER_EXTRA_COUNTER
}
if nam.Sym.Linkname != "" {
// Make sure linkname'd symbol is non-package. When a symbol is
// both imported and linkname'd, s.Pkg may not set to "_" in
// types.Sym.Linksym because LSym already exists. Set it here.
s.Pkg = "_"
}
}
func ggloblsym(s *obj.LSym, width int32, flags int16) {
if flags&obj.LOCAL != 0 {
s.Set(obj.AttrLocal, true)
flags &^= obj.LOCAL
}
Ctxt.Globl(s, int64(width), int(flags))
}
func Addrconst(a *obj.Addr, v int64) {
a.Sym = nil
a.Type = obj.TYPE_CONST
a.Offset = v
}
func Patch(p *obj.Prog, to *obj.Prog) {
if p.To.Type != obj.TYPE_BRANCH {
Fatalf("patch: not a branch")
}
p.To.SetTarget(to)
p.To.Offset = to.Pc
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,109 +0,0 @@
// Copyright 2009 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 gc
import (
"cmd/compile/internal/types"
"cmd/internal/obj"
)
// A function named init is a special case.
// It is called by the initialization before main is run.
// To make it unique within a package and also uncallable,
// the name, normally "pkg.init", is altered to "pkg.init.0".
var renameinitgen int
// Dummy function for autotmps generated during typechecking.
var dummyInitFn = nod(ODCLFUNC, nil, nil)
func renameinit() *types.Sym {
s := lookupN("init.", renameinitgen)
renameinitgen++
return s
}
// fninit makes an initialization record for the package.
// See runtime/proc.go:initTask for its layout.
// The 3 tasks for initialization are:
// 1) Initialize all of the packages the current package depends on.
// 2) Initialize all the variables that have initializers.
// 3) Run any init functions.
func fninit(n []*Node) {
nf := initOrder(n)
var deps []*obj.LSym // initTask records for packages the current package depends on
var fns []*obj.LSym // functions to call for package initialization
// Find imported packages with init tasks.
for _, s := range types.InitSyms {
deps = append(deps, s.Linksym())
}
// Make a function that contains all the initialization statements.
if len(nf) > 0 {
lineno = nf[0].Pos // prolog/epilog gets line number of first init stmt
initializers := lookup("init")
fn := dclfunc(initializers, nod(OTFUNC, nil, nil))
for _, dcl := range dummyInitFn.Func.Dcl {
dcl.Name.Curfn = fn
}
fn.Func.Dcl = append(fn.Func.Dcl, dummyInitFn.Func.Dcl...)
dummyInitFn.Func.Dcl = nil
fn.Nbody.Set(nf)
funcbody()
fn = typecheck(fn, ctxStmt)
Curfn = fn
typecheckslice(nf, ctxStmt)
Curfn = nil
xtop = append(xtop, fn)
fns = append(fns, initializers.Linksym())
}
if dummyInitFn.Func.Dcl != nil {
// We only generate temps using dummyInitFn if there
// are package-scope initialization statements, so
// something's weird if we get here.
Fatalf("dummyInitFn still has declarations")
}
dummyInitFn = nil
// Record user init functions.
for i := 0; i < renameinitgen; i++ {
s := lookupN("init.", i)
fn := asNode(s.Def).Name.Defn
// Skip init functions with empty bodies.
if fn.Nbody.Len() == 1 && fn.Nbody.First().Op == OEMPTY {
continue
}
fns = append(fns, s.Linksym())
}
if len(deps) == 0 && len(fns) == 0 && localpkg.Name != "main" && localpkg.Name != "runtime" {
return // nothing to initialize
}
// Make an .inittask structure.
sym := lookup(".inittask")
nn := newname(sym)
nn.Type = types.Types[TUINT8] // dummy type
nn.SetClass(PEXTERN)
sym.Def = asTypesNode(nn)
exportsym(nn)
lsym := sym.Linksym()
ot := 0
ot = duintptr(lsym, ot, 0) // state: not initialized yet
ot = duintptr(lsym, ot, uint64(len(deps)))
ot = duintptr(lsym, ot, uint64(len(fns)))
for _, d := range deps {
ot = dsymptr(lsym, ot, d, 0)
}
for _, f := range fns {
ot = dsymptr(lsym, ot, f, 0)
}
// An initTask has pointers, but none into the Go heap.
// It's not quite read only, the state field must be modifiable.
ggloblsym(lsym, int32(ot), obj.NOPTR)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,357 +0,0 @@
// Copyright 2009 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 gc
import (
"fmt"
"math"
"math/big"
)
// implements float arithmetic
const (
// Maximum size in bits for Mpints before signalling
// overflow and also mantissa precision for Mpflts.
Mpprec = 512
// Turn on for constant arithmetic debugging output.
Mpdebug = false
)
// Mpflt represents a floating-point constant.
type Mpflt struct {
Val big.Float
}
// Mpcplx represents a complex constant.
type Mpcplx struct {
Real Mpflt
Imag Mpflt
}
// Use newMpflt (not new(Mpflt)!) to get the correct default precision.
func newMpflt() *Mpflt {
var a Mpflt
a.Val.SetPrec(Mpprec)
return &a
}
// Use newMpcmplx (not new(Mpcplx)!) to get the correct default precision.
func newMpcmplx() *Mpcplx {
var a Mpcplx
a.Real = *newMpflt()
a.Imag = *newMpflt()
return &a
}
func (a *Mpflt) SetInt(b *Mpint) {
if b.checkOverflow(0) {
// sign doesn't really matter but copy anyway
a.Val.SetInf(b.Val.Sign() < 0)
return
}
a.Val.SetInt(&b.Val)
}
func (a *Mpflt) Set(b *Mpflt) {
a.Val.Set(&b.Val)
}
func (a *Mpflt) Add(b *Mpflt) {
if Mpdebug {
fmt.Printf("\n%v + %v", a, b)
}
a.Val.Add(&a.Val, &b.Val)
if Mpdebug {
fmt.Printf(" = %v\n\n", a)
}
}
func (a *Mpflt) AddFloat64(c float64) {
var b Mpflt
b.SetFloat64(c)
a.Add(&b)
}
func (a *Mpflt) Sub(b *Mpflt) {
if Mpdebug {
fmt.Printf("\n%v - %v", a, b)
}
a.Val.Sub(&a.Val, &b.Val)
if Mpdebug {
fmt.Printf(" = %v\n\n", a)
}
}
func (a *Mpflt) Mul(b *Mpflt) {
if Mpdebug {
fmt.Printf("%v\n * %v\n", a, b)
}
a.Val.Mul(&a.Val, &b.Val)
if Mpdebug {
fmt.Printf(" = %v\n\n", a)
}
}
func (a *Mpflt) MulFloat64(c float64) {
var b Mpflt
b.SetFloat64(c)
a.Mul(&b)
}
func (a *Mpflt) Quo(b *Mpflt) {
if Mpdebug {
fmt.Printf("%v\n / %v\n", a, b)
}
a.Val.Quo(&a.Val, &b.Val)
if Mpdebug {
fmt.Printf(" = %v\n\n", a)
}
}
func (a *Mpflt) Cmp(b *Mpflt) int {
return a.Val.Cmp(&b.Val)
}
func (a *Mpflt) CmpFloat64(c float64) int {
if c == 0 {
return a.Val.Sign() // common case shortcut
}
return a.Val.Cmp(big.NewFloat(c))
}
func (a *Mpflt) Float64() float64 {
x, _ := a.Val.Float64()
// check for overflow
if math.IsInf(x, 0) && nsavederrors+nerrors == 0 {
Fatalf("ovf in Mpflt Float64")
}
return x + 0 // avoid -0 (should not be needed, but be conservative)
}
func (a *Mpflt) Float32() float64 {
x32, _ := a.Val.Float32()
x := float64(x32)
// check for overflow
if math.IsInf(x, 0) && nsavederrors+nerrors == 0 {
Fatalf("ovf in Mpflt Float32")
}
return x + 0 // avoid -0 (should not be needed, but be conservative)
}
func (a *Mpflt) SetFloat64(c float64) {
if Mpdebug {
fmt.Printf("\nconst %g", c)
}
// convert -0 to 0
if c == 0 {
c = 0
}
a.Val.SetFloat64(c)
if Mpdebug {
fmt.Printf(" = %v\n", a)
}
}
func (a *Mpflt) Neg() {
// avoid -0
if a.Val.Sign() != 0 {
a.Val.Neg(&a.Val)
}
}
func (a *Mpflt) SetString(as string) {
f, _, err := a.Val.Parse(as, 0)
if err != nil {
yyerror("malformed constant: %s (%v)", as, err)
a.Val.SetFloat64(0)
return
}
if f.IsInf() {
yyerror("constant too large: %s", as)
a.Val.SetFloat64(0)
return
}
// -0 becomes 0
if f.Sign() == 0 && f.Signbit() {
a.Val.SetFloat64(0)
}
}
func (f *Mpflt) String() string {
return f.Val.Text('b', 0)
}
func (fvp *Mpflt) GoString() string {
// determine sign
sign := ""
f := &fvp.Val
if f.Sign() < 0 {
sign = "-"
f = new(big.Float).Abs(f)
}
// Don't try to convert infinities (will not terminate).
if f.IsInf() {
return sign + "Inf"
}
// Use exact fmt formatting if in float64 range (common case):
// proceed if f doesn't underflow to 0 or overflow to inf.
if x, _ := f.Float64(); f.Sign() == 0 == (x == 0) && !math.IsInf(x, 0) {
return fmt.Sprintf("%s%.6g", sign, x)
}
// Out of float64 range. Do approximate manual to decimal
// conversion to avoid precise but possibly slow Float
// formatting.
// f = mant * 2**exp
var mant big.Float
exp := f.MantExp(&mant) // 0.5 <= mant < 1.0
// approximate float64 mantissa m and decimal exponent d
// f ~ m * 10**d
m, _ := mant.Float64() // 0.5 <= m < 1.0
d := float64(exp) * (math.Ln2 / math.Ln10) // log_10(2)
// adjust m for truncated (integer) decimal exponent e
e := int64(d)
m *= math.Pow(10, d-float64(e))
// ensure 1 <= m < 10
switch {
case m < 1-0.5e-6:
// The %.6g format below rounds m to 5 digits after the
// decimal point. Make sure that m*10 < 10 even after
// rounding up: m*10 + 0.5e-5 < 10 => m < 1 - 0.5e6.
m *= 10
e--
case m >= 10:
m /= 10
e++
}
return fmt.Sprintf("%s%.6ge%+d", sign, m, e)
}
// complex multiply v *= rv
// (a, b) * (c, d) = (a*c - b*d, b*c + a*d)
func (v *Mpcplx) Mul(rv *Mpcplx) {
var ac, ad, bc, bd Mpflt
ac.Set(&v.Real)
ac.Mul(&rv.Real) // ac
bd.Set(&v.Imag)
bd.Mul(&rv.Imag) // bd
bc.Set(&v.Imag)
bc.Mul(&rv.Real) // bc
ad.Set(&v.Real)
ad.Mul(&rv.Imag) // ad
v.Real.Set(&ac)
v.Real.Sub(&bd) // ac-bd
v.Imag.Set(&bc)
v.Imag.Add(&ad) // bc+ad
}
// complex divide v /= rv
// (a, b) / (c, d) = ((a*c + b*d), (b*c - a*d))/(c*c + d*d)
func (v *Mpcplx) Div(rv *Mpcplx) bool {
if rv.Real.CmpFloat64(0) == 0 && rv.Imag.CmpFloat64(0) == 0 {
return false
}
var ac, ad, bc, bd, cc_plus_dd Mpflt
cc_plus_dd.Set(&rv.Real)
cc_plus_dd.Mul(&rv.Real) // cc
ac.Set(&rv.Imag)
ac.Mul(&rv.Imag) // dd
cc_plus_dd.Add(&ac) // cc+dd
// We already checked that c and d are not both zero, but we can't
// assume that c²+d² != 0 follows, because for tiny values of c
// and/or d c²+d² can underflow to zero. Check that c²+d² is
// nonzero, return if it's not.
if cc_plus_dd.CmpFloat64(0) == 0 {
return false
}
ac.Set(&v.Real)
ac.Mul(&rv.Real) // ac
bd.Set(&v.Imag)
bd.Mul(&rv.Imag) // bd
bc.Set(&v.Imag)
bc.Mul(&rv.Real) // bc
ad.Set(&v.Real)
ad.Mul(&rv.Imag) // ad
v.Real.Set(&ac)
v.Real.Add(&bd) // ac+bd
v.Real.Quo(&cc_plus_dd) // (ac+bd)/(cc+dd)
v.Imag.Set(&bc)
v.Imag.Sub(&ad) // bc-ad
v.Imag.Quo(&cc_plus_dd) // (bc+ad)/(cc+dd)
return true
}
func (v *Mpcplx) String() string {
return fmt.Sprintf("(%s+%si)", v.Real.String(), v.Imag.String())
}
func (v *Mpcplx) GoString() string {
var re string
sre := v.Real.CmpFloat64(0)
if sre != 0 {
re = v.Real.GoString()
}
var im string
sim := v.Imag.CmpFloat64(0)
if sim != 0 {
im = v.Imag.GoString()
}
switch {
case sre == 0 && sim == 0:
return "0"
case sre == 0:
return im + "i"
case sim == 0:
return re
case sim < 0:
return fmt.Sprintf("(%s%si)", re, im)
default:
return fmt.Sprintf("(%s+%si)", re, im)
}
}

View File

@@ -1,304 +0,0 @@
// Copyright 2009 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 gc
import (
"fmt"
"math/big"
)
// implements integer arithmetic
// Mpint represents an integer constant.
type Mpint struct {
Val big.Int
Ovf bool // set if Val overflowed compiler limit (sticky)
Rune bool // set if syntax indicates default type rune
}
func (a *Mpint) SetOverflow() {
a.Val.SetUint64(1) // avoid spurious div-zero errors
a.Ovf = true
}
func (a *Mpint) checkOverflow(extra int) bool {
// We don't need to be precise here, any reasonable upper limit would do.
// For now, use existing limit so we pass all the tests unchanged.
if a.Val.BitLen()+extra > Mpprec {
a.SetOverflow()
}
return a.Ovf
}
func (a *Mpint) Set(b *Mpint) {
a.Val.Set(&b.Val)
}
func (a *Mpint) SetFloat(b *Mpflt) bool {
// avoid converting huge floating-point numbers to integers
// (2*Mpprec is large enough to permit all tests to pass)
if b.Val.MantExp(nil) > 2*Mpprec {
a.SetOverflow()
return false
}
if _, acc := b.Val.Int(&a.Val); acc == big.Exact {
return true
}
const delta = 16 // a reasonably small number of bits > 0
var t big.Float
t.SetPrec(Mpprec - delta)
// try rounding down a little
t.SetMode(big.ToZero)
t.Set(&b.Val)
if _, acc := t.Int(&a.Val); acc == big.Exact {
return true
}
// try rounding up a little
t.SetMode(big.AwayFromZero)
t.Set(&b.Val)
if _, acc := t.Int(&a.Val); acc == big.Exact {
return true
}
a.Ovf = false
return false
}
func (a *Mpint) Add(b *Mpint) {
if a.Ovf || b.Ovf {
if nsavederrors+nerrors == 0 {
Fatalf("ovf in Mpint Add")
}
a.SetOverflow()
return
}
a.Val.Add(&a.Val, &b.Val)
if a.checkOverflow(0) {
yyerror("constant addition overflow")
}
}
func (a *Mpint) Sub(b *Mpint) {
if a.Ovf || b.Ovf {
if nsavederrors+nerrors == 0 {
Fatalf("ovf in Mpint Sub")
}
a.SetOverflow()
return
}
a.Val.Sub(&a.Val, &b.Val)
if a.checkOverflow(0) {
yyerror("constant subtraction overflow")
}
}
func (a *Mpint) Mul(b *Mpint) {
if a.Ovf || b.Ovf {
if nsavederrors+nerrors == 0 {
Fatalf("ovf in Mpint Mul")
}
a.SetOverflow()
return
}
a.Val.Mul(&a.Val, &b.Val)
if a.checkOverflow(0) {
yyerror("constant multiplication overflow")
}
}
func (a *Mpint) Quo(b *Mpint) {
if a.Ovf || b.Ovf {
if nsavederrors+nerrors == 0 {
Fatalf("ovf in Mpint Quo")
}
a.SetOverflow()
return
}
a.Val.Quo(&a.Val, &b.Val)
if a.checkOverflow(0) {
// can only happen for div-0 which should be checked elsewhere
yyerror("constant division overflow")
}
}
func (a *Mpint) Rem(b *Mpint) {
if a.Ovf || b.Ovf {
if nsavederrors+nerrors == 0 {
Fatalf("ovf in Mpint Rem")
}
a.SetOverflow()
return
}
a.Val.Rem(&a.Val, &b.Val)
if a.checkOverflow(0) {
// should never happen
yyerror("constant modulo overflow")
}
}
func (a *Mpint) Or(b *Mpint) {
if a.Ovf || b.Ovf {
if nsavederrors+nerrors == 0 {
Fatalf("ovf in Mpint Or")
}
a.SetOverflow()
return
}
a.Val.Or(&a.Val, &b.Val)
}
func (a *Mpint) And(b *Mpint) {
if a.Ovf || b.Ovf {
if nsavederrors+nerrors == 0 {
Fatalf("ovf in Mpint And")
}
a.SetOverflow()
return
}
a.Val.And(&a.Val, &b.Val)
}
func (a *Mpint) AndNot(b *Mpint) {
if a.Ovf || b.Ovf {
if nsavederrors+nerrors == 0 {
Fatalf("ovf in Mpint AndNot")
}
a.SetOverflow()
return
}
a.Val.AndNot(&a.Val, &b.Val)
}
func (a *Mpint) Xor(b *Mpint) {
if a.Ovf || b.Ovf {
if nsavederrors+nerrors == 0 {
Fatalf("ovf in Mpint Xor")
}
a.SetOverflow()
return
}
a.Val.Xor(&a.Val, &b.Val)
}
func (a *Mpint) Lsh(b *Mpint) {
if a.Ovf || b.Ovf {
if nsavederrors+nerrors == 0 {
Fatalf("ovf in Mpint Lsh")
}
a.SetOverflow()
return
}
s := b.Int64()
if s < 0 || s >= Mpprec {
msg := "shift count too large"
if s < 0 {
msg = "invalid negative shift count"
}
yyerror("%s: %d", msg, s)
a.SetInt64(0)
return
}
if a.checkOverflow(int(s)) {
yyerror("constant shift overflow")
return
}
a.Val.Lsh(&a.Val, uint(s))
}
func (a *Mpint) Rsh(b *Mpint) {
if a.Ovf || b.Ovf {
if nsavederrors+nerrors == 0 {
Fatalf("ovf in Mpint Rsh")
}
a.SetOverflow()
return
}
s := b.Int64()
if s < 0 {
yyerror("invalid negative shift count: %d", s)
if a.Val.Sign() < 0 {
a.SetInt64(-1)
} else {
a.SetInt64(0)
}
return
}
a.Val.Rsh(&a.Val, uint(s))
}
func (a *Mpint) Cmp(b *Mpint) int {
return a.Val.Cmp(&b.Val)
}
func (a *Mpint) CmpInt64(c int64) int {
if c == 0 {
return a.Val.Sign() // common case shortcut
}
return a.Val.Cmp(big.NewInt(c))
}
func (a *Mpint) Neg() {
a.Val.Neg(&a.Val)
}
func (a *Mpint) Int64() int64 {
if a.Ovf {
if nsavederrors+nerrors == 0 {
Fatalf("constant overflow")
}
return 0
}
return a.Val.Int64()
}
func (a *Mpint) SetInt64(c int64) {
a.Val.SetInt64(c)
}
func (a *Mpint) SetString(as string) {
_, ok := a.Val.SetString(as, 0)
if !ok {
// The lexer checks for correct syntax of the literal
// and reports detailed errors. Thus SetString should
// never fail (in theory it might run out of memory,
// but that wouldn't be reported as an error here).
Fatalf("malformed integer constant: %s", as)
return
}
if a.checkOverflow(0) {
yyerror("constant too large: %s", as)
}
}
func (a *Mpint) GoString() string {
return a.Val.String()
}
func (a *Mpint) String() string {
return fmt.Sprintf("%#x", &a.Val)
}

File diff suppressed because it is too large Load Diff

View File

@@ -5,28 +5,21 @@
package gc
import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/objw"
"cmd/compile/internal/reflectdata"
"cmd/compile/internal/staticdata"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/archive"
"cmd/internal/bio"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/src"
"crypto/sha256"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"sort"
"strconv"
)
// architecture-independent object file output
const ArhdrSize = 60
func formathdr(arhdr []byte, name string, size int64) {
copy(arhdr[:], fmt.Sprintf("%-16s%-12d%-6d%-6d%-8o%-10d`\n", name, 0, 0, 0, 0644, size))
}
// These modes say which kind of object file to generate.
// The default use of the toolchain is to set both bits,
// generating a combined compiler+linker object, one that
@@ -46,20 +39,20 @@ const (
)
func dumpobj() {
if linkobj == "" {
dumpobj1(outfile, modeCompilerObj|modeLinkerObj)
if base.Flag.LinkObj == "" {
dumpobj1(base.Flag.LowerO, modeCompilerObj|modeLinkerObj)
return
}
dumpobj1(outfile, modeCompilerObj)
dumpobj1(linkobj, modeLinkerObj)
dumpobj1(base.Flag.LowerO, modeCompilerObj)
dumpobj1(base.Flag.LinkObj, modeLinkerObj)
}
func dumpobj1(outfile string, mode int) {
bout, err := bio.Create(outfile)
if err != nil {
flusherrors()
base.FlushErrors()
fmt.Printf("can't create %s: %v\n", outfile, err)
errorexit()
base.ErrorExit()
}
defer bout.Close()
bout.WriteString("!<arch>\n")
@@ -78,17 +71,17 @@ func dumpobj1(outfile string, mode int) {
func printObjHeader(bout *bio.Writer) {
fmt.Fprintf(bout, "go object %s %s %s %s\n", objabi.GOOS, objabi.GOARCH, objabi.Version, objabi.Expstring())
if buildid != "" {
fmt.Fprintf(bout, "build id %q\n", buildid)
if base.Flag.BuildID != "" {
fmt.Fprintf(bout, "build id %q\n", base.Flag.BuildID)
}
if localpkg.Name == "main" {
if types.LocalPkg.Name == "main" {
fmt.Fprintf(bout, "main\n")
}
fmt.Fprintf(bout, "\n") // header ends with blank line
}
func startArchiveEntry(bout *bio.Writer) int64 {
var arhdr [ArhdrSize]byte
var arhdr [archive.HeaderSize]byte
bout.Write(arhdr[:])
return bout.Offset()
}
@@ -99,10 +92,10 @@ func finishArchiveEntry(bout *bio.Writer, start int64, name string) {
if size&1 != 0 {
bout.WriteByte(0)
}
bout.MustSeek(start-ArhdrSize, 0)
bout.MustSeek(start-archive.HeaderSize, 0)
var arhdr [ArhdrSize]byte
formathdr(arhdr[:], name, size)
var arhdr [archive.HeaderSize]byte
archive.FormatHeader(arhdr[:], name, size)
bout.Write(arhdr[:])
bout.Flush()
bout.MustSeek(start+size+(size&1), 0)
@@ -114,22 +107,21 @@ func dumpCompilerObj(bout *bio.Writer) {
}
func dumpdata() {
externs := len(externdcl)
xtops := len(xtop)
numExterns := len(typecheck.Target.Externs)
numDecls := len(typecheck.Target.Decls)
dumpglobls()
addptabs()
exportlistLen := len(exportlist)
addsignats(externdcl)
dumpsignats()
dumptabs()
ptabsLen := len(ptabs)
itabsLen := len(itabs)
dumpimportstrings()
dumpbasictypes()
dumpglobls(typecheck.Target.Externs)
reflectdata.CollectPTabs()
numExports := len(typecheck.Target.Exports)
addsignats(typecheck.Target.Externs)
reflectdata.WriteRuntimeTypes()
reflectdata.WriteTabs()
numPTabs, numITabs := reflectdata.CountTabs()
reflectdata.WriteImportStrings()
reflectdata.WriteBasicTypes()
dumpembeds()
// Calls to dumpsignats can generate functions,
// Calls to WriteRuntimeTypes can generate functions,
// like method wrappers and hash and equality routines.
// Compile any generated functions, process any new resulting types, repeat.
// This can't loop forever, because there is no way to generate an infinite
@@ -137,169 +129,108 @@ func dumpdata() {
// In the typical case, we loop 0 or 1 times.
// It was not until issue 24761 that we found any code that required a loop at all.
for {
for i := xtops; i < len(xtop); i++ {
n := xtop[i]
if n.Op == ODCLFUNC {
funccompile(n)
for i := numDecls; i < len(typecheck.Target.Decls); i++ {
if n, ok := typecheck.Target.Decls[i].(*ir.Func); ok {
enqueueFunc(n)
}
}
xtops = len(xtop)
numDecls = len(typecheck.Target.Decls)
compileFunctions()
dumpsignats()
if xtops == len(xtop) {
reflectdata.WriteRuntimeTypes()
if numDecls == len(typecheck.Target.Decls) {
break
}
}
// Dump extra globals.
tmp := externdcl
dumpglobls(typecheck.Target.Externs[numExterns:])
if externdcl != nil {
externdcl = externdcl[externs:]
}
dumpglobls()
externdcl = tmp
if zerosize > 0 {
zero := mappkg.Lookup("zero")
ggloblsym(zero.Linksym(), int32(zerosize), obj.DUPOK|obj.RODATA)
if reflectdata.ZeroSize > 0 {
zero := base.PkgLinksym("go.map", "zero", obj.ABI0)
objw.Global(zero, int32(reflectdata.ZeroSize), obj.DUPOK|obj.RODATA)
}
staticdata.WriteFuncSyms()
addGCLocals()
if exportlistLen != len(exportlist) {
Fatalf("exportlist changed after compile functions loop")
if numExports != len(typecheck.Target.Exports) {
base.Fatalf("Target.Exports changed after compile functions loop")
}
if ptabsLen != len(ptabs) {
Fatalf("ptabs changed after compile functions loop")
newNumPTabs, newNumITabs := reflectdata.CountTabs()
if newNumPTabs != numPTabs {
base.Fatalf("ptabs changed after compile functions loop")
}
if itabsLen != len(itabs) {
Fatalf("itabs changed after compile functions loop")
if newNumITabs != numITabs {
base.Fatalf("itabs changed after compile functions loop")
}
}
func dumpLinkerObj(bout *bio.Writer) {
printObjHeader(bout)
if len(pragcgobuf) != 0 {
if len(typecheck.Target.CgoPragmas) != 0 {
// write empty export section; must be before cgo section
fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
fmt.Fprintf(bout, "\n$$ // cgo\n")
if err := json.NewEncoder(bout).Encode(pragcgobuf); err != nil {
Fatalf("serializing pragcgobuf: %v", err)
if err := json.NewEncoder(bout).Encode(typecheck.Target.CgoPragmas); err != nil {
base.Fatalf("serializing pragcgobuf: %v", err)
}
fmt.Fprintf(bout, "\n$$\n\n")
}
fmt.Fprintf(bout, "\n!\n")
obj.WriteObjFile(Ctxt, bout)
obj.WriteObjFile(base.Ctxt, bout)
}
func addptabs() {
if !Ctxt.Flag_dynlink || localpkg.Name != "main" {
func dumpGlobal(n *ir.Name) {
if n.Type() == nil {
base.Fatalf("external %v nil type\n", n)
}
if n.Class == ir.PFUNC {
return
}
for _, exportn := range exportlist {
s := exportn.Sym
n := asNode(s.Def)
if n == nil {
continue
}
if n.Op != ONAME {
continue
}
if !types.IsExported(s.Name) {
continue
}
if s.Pkg.Name != "main" {
continue
}
if n.Type.Etype == TFUNC && n.Class() == PFUNC {
// function
ptabs = append(ptabs, ptabEntry{s: s, t: asNode(s.Def).Type})
} else {
// variable
ptabs = append(ptabs, ptabEntry{s: s, t: types.NewPtr(asNode(s.Def).Type)})
}
}
}
func dumpGlobal(n *Node) {
if n.Type == nil {
Fatalf("external %v nil type\n", n)
}
if n.Class() == PFUNC {
if n.Sym().Pkg != types.LocalPkg {
return
}
if n.Sym.Pkg != localpkg {
return
}
dowidth(n.Type)
types.CalcSize(n.Type())
ggloblnod(n)
}
func dumpGlobalConst(n *Node) {
func dumpGlobalConst(n ir.Node) {
// only export typed constants
t := n.Type
t := n.Type()
if t == nil {
return
}
if n.Sym.Pkg != localpkg {
if n.Sym().Pkg != types.LocalPkg {
return
}
// only export integer constants for now
switch t.Etype {
case TINT8:
case TINT16:
case TINT32:
case TINT64:
case TINT:
case TUINT8:
case TUINT16:
case TUINT32:
case TUINT64:
case TUINT:
case TUINTPTR:
// ok
case TIDEAL:
if !Isconst(n, CTINT) {
return
}
x := n.Val().U.(*Mpint)
if x.Cmp(minintval[TINT]) < 0 || x.Cmp(maxintval[TINT]) > 0 {
return
}
// Ideal integers we export as int (if they fit).
t = types.Types[TINT]
default:
if !t.IsInteger() {
return
}
Ctxt.DwarfIntConst(myimportpath, n.Sym.Name, typesymname(t), n.Int64Val())
v := n.Val()
if t.IsUntyped() {
// Export untyped integers as int (if they fit).
t = types.Types[types.TINT]
if ir.ConstOverflow(v, t) {
return
}
}
base.Ctxt.DwarfIntConst(base.Ctxt.Pkgpath, n.Sym().Name, types.TypeSymName(t), ir.IntVal(t, v))
}
func dumpglobls() {
func dumpglobls(externs []ir.Node) {
// add globals
for _, n := range externdcl {
switch n.Op {
case ONAME:
dumpGlobal(n)
case OLITERAL:
for _, n := range externs {
switch n.Op() {
case ir.ONAME:
dumpGlobal(n.(*ir.Name))
case ir.OLITERAL:
dumpGlobalConst(n)
}
}
sort.Slice(funcsyms, func(i, j int) bool {
return funcsyms[i].LinksymName() < funcsyms[j].LinksymName()
})
for _, s := range funcsyms {
sf := s.Pkg.Lookup(funcsymname(s)).Linksym()
dsymptr(sf, 0, s.Linksym(), 0)
ggloblsym(sf, int32(Widthptr), obj.DUPOK|obj.RODATA)
}
// Do not reprocess funcsyms on next dumpglobls call.
funcsyms = nil
}
// addGCLocals adds gcargs, gclocals, gcregs, and stack object symbols to Ctxt.Data.
@@ -307,332 +238,60 @@ func dumpglobls() {
// This is done during the sequential phase after compilation, since
// global symbols can't be declared during parallel compilation.
func addGCLocals() {
for _, s := range Ctxt.Text {
for _, s := range base.Ctxt.Text {
fn := s.Func()
if fn == nil {
continue
}
for _, gcsym := range []*obj.LSym{fn.GCArgs, fn.GCLocals} {
if gcsym != nil && !gcsym.OnList() {
ggloblsym(gcsym, int32(len(gcsym.P)), obj.RODATA|obj.DUPOK)
objw.Global(gcsym, int32(len(gcsym.P)), obj.RODATA|obj.DUPOK)
}
}
if x := fn.StackObjects; x != nil {
attr := int16(obj.RODATA)
ggloblsym(x, int32(len(x.P)), attr)
objw.Global(x, int32(len(x.P)), attr)
x.Set(obj.AttrStatic, true)
}
if x := fn.OpenCodedDeferInfo; x != nil {
ggloblsym(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
}
}
}
func duintxx(s *obj.LSym, off int, v uint64, wid int) int {
if off&(wid-1) != 0 {
Fatalf("duintxxLSym: misaligned: v=%d wid=%d off=%d", v, wid, off)
func ggloblnod(nam *ir.Name) {
s := nam.Linksym()
s.Gotype = reflectdata.TypeLinksym(nam.Type())
flags := 0
if nam.Readonly() {
flags = obj.RODATA
}
if nam.Type() != nil && !nam.Type().HasPointers() {
flags |= obj.NOPTR
}
base.Ctxt.Globl(s, nam.Type().Width, flags)
if nam.LibfuzzerExtraCounter() {
s.Type = objabi.SLIBFUZZER_EXTRA_COUNTER
}
if nam.Sym().Linkname != "" {
// Make sure linkname'd symbol is non-package. When a symbol is
// both imported and linkname'd, s.Pkg may not set to "_" in
// types.Sym.Linksym because LSym already exists. Set it here.
s.Pkg = "_"
}
s.WriteInt(Ctxt, int64(off), wid, int64(v))
return off + wid
}
func duint8(s *obj.LSym, off int, v uint8) int {
return duintxx(s, off, uint64(v), 1)
func dumpembeds() {
for _, v := range typecheck.Target.Embeds {
staticdata.WriteEmbed(v)
}
}
func duint16(s *obj.LSym, off int, v uint16) int {
return duintxx(s, off, uint64(v), 2)
}
func duint32(s *obj.LSym, off int, v uint32) int {
return duintxx(s, off, uint64(v), 4)
}
func duintptr(s *obj.LSym, off int, v uint64) int {
return duintxx(s, off, v, Widthptr)
}
func dbvec(s *obj.LSym, off int, bv bvec) int {
// Runtime reads the bitmaps as byte arrays. Oblige.
for j := 0; int32(j) < bv.n; j += 8 {
word := bv.b[j/32]
off = duint8(s, off, uint8(word>>(uint(j)%32)))
}
return off
}
const (
stringSymPrefix = "go.string."
stringSymPattern = ".gostring.%d.%x"
)
// stringsym returns a symbol containing the string s.
// The symbol contains the string data, not a string header.
func stringsym(pos src.XPos, s string) (data *obj.LSym) {
var symname string
if len(s) > 100 {
// Huge strings are hashed to avoid long names in object files.
// Indulge in some paranoia by writing the length of s, too,
// as protection against length extension attacks.
// Same pattern is known to fileStringSym below.
h := sha256.New()
io.WriteString(h, s)
symname = fmt.Sprintf(stringSymPattern, len(s), h.Sum(nil))
} else {
// Small strings get named directly by their contents.
symname = strconv.Quote(s)
}
symdata := Ctxt.Lookup(stringSymPrefix + symname)
if !symdata.OnList() {
off := dstringdata(symdata, 0, s, pos, "string")
ggloblsym(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL)
symdata.Set(obj.AttrContentAddressable, true)
}
return symdata
}
// fileStringSym returns a symbol for the contents and the size of file.
// If readonly is true, the symbol shares storage with any literal string
// or other file with the same content and is placed in a read-only section.
// If readonly is false, the symbol is a read-write copy separate from any other,
// for use as the backing store of a []byte.
// The content hash of file is copied into hash. (If hash is nil, nothing is copied.)
// The returned symbol contains the data itself, not a string header.
func fileStringSym(pos src.XPos, file string, readonly bool, hash []byte) (*obj.LSym, int64, error) {
f, err := os.Open(file)
if err != nil {
return nil, 0, err
}
defer f.Close()
info, err := f.Stat()
if err != nil {
return nil, 0, err
}
if !info.Mode().IsRegular() {
return nil, 0, fmt.Errorf("not a regular file")
}
size := info.Size()
if size <= 1*1024 {
data, err := ioutil.ReadAll(f)
if err != nil {
return nil, 0, err
func addsignats(dcls []ir.Node) {
// copy types from dcl list to signatset
for _, n := range dcls {
if n.Op() == ir.OTYPE {
reflectdata.NeedRuntimeType(n.Type())
}
if int64(len(data)) != size {
return nil, 0, fmt.Errorf("file changed between reads")
}
var sym *obj.LSym
if readonly {
sym = stringsym(pos, string(data))
} else {
sym = slicedata(pos, string(data)).Sym.Linksym()
}
if len(hash) > 0 {
sum := sha256.Sum256(data)
copy(hash, sum[:])
}
return sym, size, nil
}
if size > 2e9 {
// ggloblsym takes an int32,
// and probably the rest of the toolchain
// can't handle such big symbols either.
// See golang.org/issue/9862.
return nil, 0, fmt.Errorf("file too large")
}
// File is too big to read and keep in memory.
// Compute hash if needed for read-only content hashing or if the caller wants it.
var sum []byte
if readonly || len(hash) > 0 {
h := sha256.New()
n, err := io.Copy(h, f)
if err != nil {
return nil, 0, err
}
if n != size {
return nil, 0, fmt.Errorf("file changed between reads")
}
sum = h.Sum(nil)
copy(hash, sum)
}
var symdata *obj.LSym
if readonly {
symname := fmt.Sprintf(stringSymPattern, size, sum)
symdata = Ctxt.Lookup(stringSymPrefix + symname)
if !symdata.OnList() {
info := symdata.NewFileInfo()
info.Name = file
info.Size = size
ggloblsym(symdata, int32(size), obj.DUPOK|obj.RODATA|obj.LOCAL)
// Note: AttrContentAddressable cannot be set here,
// because the content-addressable-handling code
// does not know about file symbols.
}
} else {
// Emit a zero-length data symbol
// and then fix up length and content to use file.
symdata = slicedata(pos, "").Sym.Linksym()
symdata.Size = size
symdata.Type = objabi.SNOPTRDATA
info := symdata.NewFileInfo()
info.Name = file
info.Size = size
}
return symdata, size, nil
}
var slicedataGen int
func slicedata(pos src.XPos, s string) *Node {
slicedataGen++
symname := fmt.Sprintf(".gobytes.%d", slicedataGen)
sym := localpkg.Lookup(symname)
symnode := newname(sym)
sym.Def = asTypesNode(symnode)
lsym := sym.Linksym()
off := dstringdata(lsym, 0, s, pos, "slice")
ggloblsym(lsym, int32(off), obj.NOPTR|obj.LOCAL)
return symnode
}
func slicebytes(nam *Node, s string) {
if nam.Op != ONAME {
Fatalf("slicebytes %v", nam)
}
slicesym(nam, slicedata(nam.Pos, s), int64(len(s)))
}
func dstringdata(s *obj.LSym, off int, t string, pos src.XPos, what string) int {
// Objects that are too large will cause the data section to overflow right away,
// causing a cryptic error message by the linker. Check for oversize objects here
// and provide a useful error message instead.
if int64(len(t)) > 2e9 {
yyerrorl(pos, "%v with length %v is too big", what, len(t))
return 0
}
s.WriteString(Ctxt, int64(off), len(t), t)
return off + len(t)
}
func dsymptr(s *obj.LSym, off int, x *obj.LSym, xoff int) int {
off = int(Rnd(int64(off), int64(Widthptr)))
s.WriteAddr(Ctxt, int64(off), Widthptr, x, int64(xoff))
off += Widthptr
return off
}
func dsymptrOff(s *obj.LSym, off int, x *obj.LSym) int {
s.WriteOff(Ctxt, int64(off), x, 0)
off += 4
return off
}
func dsymptrWeakOff(s *obj.LSym, off int, x *obj.LSym) int {
s.WriteWeakOff(Ctxt, int64(off), x, 0)
off += 4
return off
}
// slicesym writes a static slice symbol {&arr, lencap, lencap} to n.
// arr must be an ONAME. slicesym does not modify n.
func slicesym(n, arr *Node, lencap int64) {
s := n.Sym.Linksym()
base := n.Xoffset
if arr.Op != ONAME {
Fatalf("slicesym non-name arr %v", arr)
}
s.WriteAddr(Ctxt, base, Widthptr, arr.Sym.Linksym(), arr.Xoffset)
s.WriteInt(Ctxt, base+sliceLenOffset, Widthptr, lencap)
s.WriteInt(Ctxt, base+sliceCapOffset, Widthptr, lencap)
}
// addrsym writes the static address of a to n. a must be an ONAME.
// Neither n nor a is modified.
func addrsym(n, a *Node) {
if n.Op != ONAME {
Fatalf("addrsym n op %v", n.Op)
}
if n.Sym == nil {
Fatalf("addrsym nil n sym")
}
if a.Op != ONAME {
Fatalf("addrsym a op %v", a.Op)
}
s := n.Sym.Linksym()
s.WriteAddr(Ctxt, n.Xoffset, Widthptr, a.Sym.Linksym(), a.Xoffset)
}
// pfuncsym writes the static address of f to n. f must be a global function.
// Neither n nor f is modified.
func pfuncsym(n, f *Node) {
if n.Op != ONAME {
Fatalf("pfuncsym n op %v", n.Op)
}
if n.Sym == nil {
Fatalf("pfuncsym nil n sym")
}
if f.Class() != PFUNC {
Fatalf("pfuncsym class not PFUNC %d", f.Class())
}
s := n.Sym.Linksym()
s.WriteAddr(Ctxt, n.Xoffset, Widthptr, funcsym(f.Sym).Linksym(), f.Xoffset)
}
// litsym writes the static literal c to n.
// Neither n nor c is modified.
func litsym(n, c *Node, wid int) {
if n.Op != ONAME {
Fatalf("litsym n op %v", n.Op)
}
if c.Op != OLITERAL {
Fatalf("litsym c op %v", c.Op)
}
if n.Sym == nil {
Fatalf("litsym nil n sym")
}
s := n.Sym.Linksym()
switch u := c.Val().U.(type) {
case bool:
i := int64(obj.Bool2int(u))
s.WriteInt(Ctxt, n.Xoffset, wid, i)
case *Mpint:
s.WriteInt(Ctxt, n.Xoffset, wid, u.Int64())
case *Mpflt:
f := u.Float64()
switch n.Type.Etype {
case TFLOAT32:
s.WriteFloat32(Ctxt, n.Xoffset, float32(f))
case TFLOAT64:
s.WriteFloat64(Ctxt, n.Xoffset, f)
}
case *Mpcplx:
r := u.Real.Float64()
i := u.Imag.Float64()
switch n.Type.Etype {
case TCOMPLEX64:
s.WriteFloat32(Ctxt, n.Xoffset, float32(r))
s.WriteFloat32(Ctxt, n.Xoffset+4, float32(i))
case TCOMPLEX128:
s.WriteFloat64(Ctxt, n.Xoffset, r)
s.WriteFloat64(Ctxt, n.Xoffset+8, i)
}
case string:
symdata := stringsym(n.Pos, u)
s.WriteAddr(Ctxt, n.Xoffset, Widthptr, symdata, 0)
s.WriteInt(Ctxt, n.Xoffset+int64(Widthptr), Widthptr, int64(len(u)))
default:
Fatalf("litsym unhandled OLITERAL %v", c)
}
}

View File

@@ -1,175 +0,0 @@
// Code generated by "stringer -type=Op -trimprefix=O"; DO NOT EDIT.
package gc
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[OXXX-0]
_ = x[ONAME-1]
_ = x[ONONAME-2]
_ = x[OTYPE-3]
_ = x[OPACK-4]
_ = x[OLITERAL-5]
_ = x[OADD-6]
_ = x[OSUB-7]
_ = x[OOR-8]
_ = x[OXOR-9]
_ = x[OADDSTR-10]
_ = x[OADDR-11]
_ = x[OANDAND-12]
_ = x[OAPPEND-13]
_ = x[OBYTES2STR-14]
_ = x[OBYTES2STRTMP-15]
_ = x[ORUNES2STR-16]
_ = x[OSTR2BYTES-17]
_ = x[OSTR2BYTESTMP-18]
_ = x[OSTR2RUNES-19]
_ = x[OAS-20]
_ = x[OAS2-21]
_ = x[OAS2DOTTYPE-22]
_ = x[OAS2FUNC-23]
_ = x[OAS2MAPR-24]
_ = x[OAS2RECV-25]
_ = x[OASOP-26]
_ = x[OCALL-27]
_ = x[OCALLFUNC-28]
_ = x[OCALLMETH-29]
_ = x[OCALLINTER-30]
_ = x[OCALLPART-31]
_ = x[OCAP-32]
_ = x[OCLOSE-33]
_ = x[OCLOSURE-34]
_ = x[OCOMPLIT-35]
_ = x[OMAPLIT-36]
_ = x[OSTRUCTLIT-37]
_ = x[OARRAYLIT-38]
_ = x[OSLICELIT-39]
_ = x[OPTRLIT-40]
_ = x[OCONV-41]
_ = x[OCONVIFACE-42]
_ = x[OCONVNOP-43]
_ = x[OCOPY-44]
_ = x[ODCL-45]
_ = x[ODCLFUNC-46]
_ = x[ODCLFIELD-47]
_ = x[ODCLCONST-48]
_ = x[ODCLTYPE-49]
_ = x[ODELETE-50]
_ = x[ODOT-51]
_ = x[ODOTPTR-52]
_ = x[ODOTMETH-53]
_ = x[ODOTINTER-54]
_ = x[OXDOT-55]
_ = x[ODOTTYPE-56]
_ = x[ODOTTYPE2-57]
_ = x[OEQ-58]
_ = x[ONE-59]
_ = x[OLT-60]
_ = x[OLE-61]
_ = x[OGE-62]
_ = x[OGT-63]
_ = x[ODEREF-64]
_ = x[OINDEX-65]
_ = x[OINDEXMAP-66]
_ = x[OKEY-67]
_ = x[OSTRUCTKEY-68]
_ = x[OLEN-69]
_ = x[OMAKE-70]
_ = x[OMAKECHAN-71]
_ = x[OMAKEMAP-72]
_ = x[OMAKESLICE-73]
_ = x[OMAKESLICECOPY-74]
_ = x[OMUL-75]
_ = x[ODIV-76]
_ = x[OMOD-77]
_ = x[OLSH-78]
_ = x[ORSH-79]
_ = x[OAND-80]
_ = x[OANDNOT-81]
_ = x[ONEW-82]
_ = x[ONEWOBJ-83]
_ = x[ONOT-84]
_ = x[OBITNOT-85]
_ = x[OPLUS-86]
_ = x[ONEG-87]
_ = x[OOROR-88]
_ = x[OPANIC-89]
_ = x[OPRINT-90]
_ = x[OPRINTN-91]
_ = x[OPAREN-92]
_ = x[OSEND-93]
_ = x[OSLICE-94]
_ = x[OSLICEARR-95]
_ = x[OSLICESTR-96]
_ = x[OSLICE3-97]
_ = x[OSLICE3ARR-98]
_ = x[OSLICEHEADER-99]
_ = x[ORECOVER-100]
_ = x[ORECV-101]
_ = x[ORUNESTR-102]
_ = x[OSELRECV-103]
_ = x[OSELRECV2-104]
_ = x[OIOTA-105]
_ = x[OREAL-106]
_ = x[OIMAG-107]
_ = x[OCOMPLEX-108]
_ = x[OALIGNOF-109]
_ = x[OOFFSETOF-110]
_ = x[OSIZEOF-111]
_ = x[OBLOCK-112]
_ = x[OBREAK-113]
_ = x[OCASE-114]
_ = x[OCONTINUE-115]
_ = x[ODEFER-116]
_ = x[OEMPTY-117]
_ = x[OFALL-118]
_ = x[OFOR-119]
_ = x[OFORUNTIL-120]
_ = x[OGOTO-121]
_ = x[OIF-122]
_ = x[OLABEL-123]
_ = x[OGO-124]
_ = x[ORANGE-125]
_ = x[ORETURN-126]
_ = x[OSELECT-127]
_ = x[OSWITCH-128]
_ = x[OTYPESW-129]
_ = x[OTCHAN-130]
_ = x[OTMAP-131]
_ = x[OTSTRUCT-132]
_ = x[OTINTER-133]
_ = x[OTFUNC-134]
_ = x[OTARRAY-135]
_ = x[ODDD-136]
_ = x[OINLCALL-137]
_ = x[OEFACE-138]
_ = x[OITAB-139]
_ = x[OIDATA-140]
_ = x[OSPTR-141]
_ = x[OCLOSUREVAR-142]
_ = x[OCFUNC-143]
_ = x[OCHECKNIL-144]
_ = x[OVARDEF-145]
_ = x[OVARKILL-146]
_ = x[OVARLIVE-147]
_ = x[ORESULT-148]
_ = x[OINLMARK-149]
_ = x[ORETJMP-150]
_ = x[OGETG-151]
_ = x[OEND-152]
}
const _Op_name = "XXXNAMENONAMETYPEPACKLITERALADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLFIELDDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNEWOBJNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECVSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFBLOCKBREAKCASECONTINUEDEFEREMPTYFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYDDDINLCALLEFACEITABIDATASPTRCLOSUREVARCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKRETJMPGETGEND"
var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 36, 39, 45, 49, 55, 61, 70, 82, 91, 100, 112, 121, 123, 126, 136, 143, 150, 157, 161, 165, 173, 181, 190, 198, 201, 206, 213, 220, 226, 235, 243, 251, 257, 261, 270, 277, 281, 284, 291, 299, 307, 314, 320, 323, 329, 336, 344, 348, 355, 363, 365, 367, 369, 371, 373, 375, 380, 385, 393, 396, 405, 408, 412, 420, 427, 436, 449, 452, 455, 458, 461, 464, 467, 473, 476, 482, 485, 491, 495, 498, 502, 507, 512, 518, 523, 527, 532, 540, 548, 554, 563, 574, 581, 585, 592, 599, 607, 611, 615, 619, 626, 633, 641, 647, 652, 657, 661, 669, 674, 679, 683, 686, 694, 698, 700, 705, 707, 712, 718, 724, 730, 736, 741, 745, 752, 758, 763, 769, 772, 779, 784, 788, 793, 797, 807, 812, 820, 826, 833, 840, 846, 853, 859, 863, 866}
func (i Op) String() string {
if i >= Op(len(_Op_index)-1) {
return "Op(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Op_name[_Op_index[i]:_Op_index[i+1]]
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,798 +0,0 @@
// Copyright 2011 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 gc
import (
"cmd/compile/internal/ssa"
"cmd/compile/internal/types"
"cmd/internal/dwarf"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/src"
"cmd/internal/sys"
"internal/race"
"math/rand"
"sort"
"sync"
"time"
)
// "Portable" code generation.
var (
nBackendWorkers int // number of concurrent backend workers, set by a compiler flag
compilequeue []*Node // functions waiting to be compiled
)
func emitptrargsmap(fn *Node) {
if fn.funcname() == "_" || fn.Func.Nname.Sym.Linkname != "" {
return
}
lsym := Ctxt.Lookup(fn.Func.lsym.Name + ".args_stackmap")
nptr := int(fn.Type.ArgWidth() / int64(Widthptr))
bv := bvalloc(int32(nptr) * 2)
nbitmap := 1
if fn.Type.NumResults() > 0 {
nbitmap = 2
}
off := duint32(lsym, 0, uint32(nbitmap))
off = duint32(lsym, off, uint32(bv.n))
if fn.IsMethod() {
onebitwalktype1(fn.Type.Recvs(), 0, bv)
}
if fn.Type.NumParams() > 0 {
onebitwalktype1(fn.Type.Params(), 0, bv)
}
off = dbvec(lsym, off, bv)
if fn.Type.NumResults() > 0 {
onebitwalktype1(fn.Type.Results(), 0, bv)
off = dbvec(lsym, off, bv)
}
ggloblsym(lsym, int32(off), obj.RODATA|obj.LOCAL)
}
// cmpstackvarlt reports whether the stack variable a sorts before b.
//
// Sort the list of stack variables. Autos after anything else,
// within autos, unused after used, within used, things with
// pointers first, zeroed things first, and then decreasing size.
// Because autos are laid out in decreasing addresses
// on the stack, pointers first, zeroed things first and decreasing size
// really means, in memory, things with pointers needing zeroing at
// the top of the stack and increasing in size.
// Non-autos sort on offset.
func cmpstackvarlt(a, b *Node) bool {
if (a.Class() == PAUTO) != (b.Class() == PAUTO) {
return b.Class() == PAUTO
}
if a.Class() != PAUTO {
return a.Xoffset < b.Xoffset
}
if a.Name.Used() != b.Name.Used() {
return a.Name.Used()
}
ap := a.Type.HasPointers()
bp := b.Type.HasPointers()
if ap != bp {
return ap
}
ap = a.Name.Needzero()
bp = b.Name.Needzero()
if ap != bp {
return ap
}
if a.Type.Width != b.Type.Width {
return a.Type.Width > b.Type.Width
}
return a.Sym.Name < b.Sym.Name
}
// byStackvar implements sort.Interface for []*Node using cmpstackvarlt.
type byStackVar []*Node
func (s byStackVar) Len() int { return len(s) }
func (s byStackVar) Less(i, j int) bool { return cmpstackvarlt(s[i], s[j]) }
func (s byStackVar) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s *ssafn) AllocFrame(f *ssa.Func) {
s.stksize = 0
s.stkptrsize = 0
fn := s.curfn.Func
// Mark the PAUTO's unused.
for _, ln := range fn.Dcl {
if ln.Class() == PAUTO {
ln.Name.SetUsed(false)
}
}
for _, l := range f.RegAlloc {
if ls, ok := l.(ssa.LocalSlot); ok {
ls.N.(*Node).Name.SetUsed(true)
}
}
scratchUsed := false
for _, b := range f.Blocks {
for _, v := range b.Values {
if n, ok := v.Aux.(*Node); ok {
switch n.Class() {
case PPARAM, PPARAMOUT:
// Don't modify nodfp; it is a global.
if n != nodfp {
n.Name.SetUsed(true)
}
case PAUTO:
n.Name.SetUsed(true)
}
}
if !scratchUsed {
scratchUsed = v.Op.UsesScratch()
}
}
}
if f.Config.NeedsFpScratch && scratchUsed {
s.scratchFpMem = tempAt(src.NoXPos, s.curfn, types.Types[TUINT64])
}
sort.Sort(byStackVar(fn.Dcl))
// Reassign stack offsets of the locals that are used.
lastHasPtr := false
for i, n := range fn.Dcl {
if n.Op != ONAME || n.Class() != PAUTO {
continue
}
if !n.Name.Used() {
fn.Dcl = fn.Dcl[:i]
break
}
dowidth(n.Type)
w := n.Type.Width
if w >= thearch.MAXWIDTH || w < 0 {
Fatalf("bad width")
}
if w == 0 && lastHasPtr {
// Pad between a pointer-containing object and a zero-sized object.
// This prevents a pointer to the zero-sized object from being interpreted
// as a pointer to the pointer-containing object (and causing it
// to be scanned when it shouldn't be). See issue 24993.
w = 1
}
s.stksize += w
s.stksize = Rnd(s.stksize, int64(n.Type.Align))
if n.Type.HasPointers() {
s.stkptrsize = s.stksize
lastHasPtr = true
} else {
lastHasPtr = false
}
if thearch.LinkArch.InFamily(sys.MIPS, sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) {
s.stksize = Rnd(s.stksize, int64(Widthptr))
}
n.Xoffset = -s.stksize
}
s.stksize = Rnd(s.stksize, int64(Widthreg))
s.stkptrsize = Rnd(s.stkptrsize, int64(Widthreg))
}
func funccompile(fn *Node) {
if Curfn != nil {
Fatalf("funccompile %v inside %v", fn.Func.Nname.Sym, Curfn.Func.Nname.Sym)
}
if fn.Type == nil {
if nerrors == 0 {
Fatalf("funccompile missing type")
}
return
}
// assign parameter offsets
dowidth(fn.Type)
if fn.Nbody.Len() == 0 {
// Initialize ABI wrappers if necessary.
fn.Func.initLSym(false)
emitptrargsmap(fn)
return
}
dclcontext = PAUTO
Curfn = fn
compile(fn)
Curfn = nil
dclcontext = PEXTERN
}
func compile(fn *Node) {
saveerrors()
order(fn)
if nerrors != 0 {
return
}
// Set up the function's LSym early to avoid data races with the assemblers.
// Do this before walk, as walk needs the LSym to set attributes/relocations
// (e.g. in markTypeUsedInInterface).
fn.Func.initLSym(true)
walk(fn)
if nerrors != 0 {
return
}
if instrumenting {
instrument(fn)
}
// From this point, there should be no uses of Curfn. Enforce that.
Curfn = nil
if fn.funcname() == "_" {
// We don't need to generate code for this function, just report errors in its body.
// At this point we've generated any errors needed.
// (Beyond here we generate only non-spec errors, like "stack frame too large".)
// See issue 29870.
return
}
// Make sure type syms are declared for all types that might
// be types of stack objects. We need to do this here
// because symbols must be allocated before the parallel
// phase of the compiler.
for _, n := range fn.Func.Dcl {
switch n.Class() {
case PPARAM, PPARAMOUT, PAUTO:
if livenessShouldTrack(n) && n.Name.Addrtaken() {
dtypesym(n.Type)
// Also make sure we allocate a linker symbol
// for the stack object data, for the same reason.
if fn.Func.lsym.Func().StackObjects == nil {
fn.Func.lsym.Func().StackObjects = Ctxt.Lookup(fn.Func.lsym.Name + ".stkobj")
}
}
}
}
if compilenow(fn) {
compileSSA(fn, 0)
} else {
compilequeue = append(compilequeue, fn)
}
}
// compilenow reports whether to compile immediately.
// If functions are not compiled immediately,
// they are enqueued in compilequeue,
// which is drained by compileFunctions.
func compilenow(fn *Node) bool {
// Issue 38068: if this function is a method AND an inline
// candidate AND was not inlined (yet), put it onto the compile
// queue instead of compiling it immediately. This is in case we
// wind up inlining it into a method wrapper that is generated by
// compiling a function later on in the xtop list.
if fn.IsMethod() && isInlinableButNotInlined(fn) {
return false
}
return nBackendWorkers == 1 && Debug_compilelater == 0
}
// isInlinableButNotInlined returns true if 'fn' was marked as an
// inline candidate but then never inlined (presumably because we
// found no call sites).
func isInlinableButNotInlined(fn *Node) bool {
if fn.Func.Nname.Func.Inl == nil {
return false
}
if fn.Sym == nil {
return true
}
return !fn.Sym.Linksym().WasInlined()
}
const maxStackSize = 1 << 30
// compileSSA builds an SSA backend function,
// uses it to generate a plist,
// and flushes that plist to machine code.
// worker indicates which of the backend workers is doing the processing.
func compileSSA(fn *Node, worker int) {
f := buildssa(fn, worker)
// Note: check arg size to fix issue 25507.
if f.Frontend().(*ssafn).stksize >= maxStackSize || fn.Type.ArgWidth() >= maxStackSize {
largeStackFramesMu.Lock()
largeStackFrames = append(largeStackFrames, largeStack{locals: f.Frontend().(*ssafn).stksize, args: fn.Type.ArgWidth(), pos: fn.Pos})
largeStackFramesMu.Unlock()
return
}
pp := newProgs(fn, worker)
defer pp.Free()
genssa(f, pp)
// Check frame size again.
// The check above included only the space needed for local variables.
// After genssa, the space needed includes local variables and the callee arg region.
// We must do this check prior to calling pp.Flush.
// If there are any oversized stack frames,
// the assembler may emit inscrutable complaints about invalid instructions.
if pp.Text.To.Offset >= maxStackSize {
largeStackFramesMu.Lock()
locals := f.Frontend().(*ssafn).stksize
largeStackFrames = append(largeStackFrames, largeStack{locals: locals, args: fn.Type.ArgWidth(), callee: pp.Text.To.Offset - locals, pos: fn.Pos})
largeStackFramesMu.Unlock()
return
}
pp.Flush() // assemble, fill in boilerplate, etc.
// fieldtrack must be called after pp.Flush. See issue 20014.
fieldtrack(pp.Text.From.Sym, fn.Func.FieldTrack)
}
func init() {
if race.Enabled {
rand.Seed(time.Now().UnixNano())
}
}
// compileFunctions compiles all functions in compilequeue.
// It fans out nBackendWorkers to do the work
// and waits for them to complete.
func compileFunctions() {
if len(compilequeue) != 0 {
sizeCalculationDisabled = true // not safe to calculate sizes concurrently
if race.Enabled {
// Randomize compilation order to try to shake out races.
tmp := make([]*Node, len(compilequeue))
perm := rand.Perm(len(compilequeue))
for i, v := range perm {
tmp[v] = compilequeue[i]
}
copy(compilequeue, tmp)
} else {
// Compile the longest functions first,
// since they're most likely to be the slowest.
// This helps avoid stragglers.
sort.Slice(compilequeue, func(i, j int) bool {
return compilequeue[i].Nbody.Len() > compilequeue[j].Nbody.Len()
})
}
var wg sync.WaitGroup
Ctxt.InParallel = true
c := make(chan *Node, nBackendWorkers)
for i := 0; i < nBackendWorkers; i++ {
wg.Add(1)
go func(worker int) {
for fn := range c {
compileSSA(fn, worker)
}
wg.Done()
}(i)
}
for _, fn := range compilequeue {
c <- fn
}
close(c)
compilequeue = nil
wg.Wait()
Ctxt.InParallel = false
sizeCalculationDisabled = false
}
}
func debuginfo(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls) {
fn := curfn.(*Node)
if fn.Func.Nname != nil {
if expect := fn.Func.Nname.Sym.Linksym(); fnsym != expect {
Fatalf("unexpected fnsym: %v != %v", fnsym, expect)
}
}
var apdecls []*Node
// Populate decls for fn.
for _, n := range fn.Func.Dcl {
if n.Op != ONAME { // might be OTYPE or OLITERAL
continue
}
switch n.Class() {
case PAUTO:
if !n.Name.Used() {
// Text == nil -> generating abstract function
if fnsym.Func().Text != nil {
Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)")
}
continue
}
case PPARAM, PPARAMOUT:
default:
continue
}
apdecls = append(apdecls, n)
fnsym.Func().RecordAutoType(ngotype(n).Linksym())
}
decls, dwarfVars := createDwarfVars(fnsym, fn.Func, apdecls)
// For each type referenced by the functions auto vars but not
// already referenced by a dwarf var, attach a dummy relocation to
// the function symbol to insure that the type included in DWARF
// processing during linking.
typesyms := []*obj.LSym{}
for t, _ := range fnsym.Func().Autot {
typesyms = append(typesyms, t)
}
sort.Sort(obj.BySymName(typesyms))
for _, sym := range typesyms {
r := obj.Addrel(infosym)
r.Sym = sym
r.Type = objabi.R_USETYPE
}
fnsym.Func().Autot = nil
var varScopes []ScopeID
for _, decl := range decls {
pos := declPos(decl)
varScopes = append(varScopes, findScope(fn.Func.Marks, pos))
}
scopes := assembleScopes(fnsym, fn, dwarfVars, varScopes)
var inlcalls dwarf.InlCalls
if genDwarfInline > 0 {
inlcalls = assembleInlines(fnsym, dwarfVars)
}
return scopes, inlcalls
}
func declPos(decl *Node) src.XPos {
if decl.Name.Defn != nil && (decl.Name.Captured() || decl.Name.Byval()) {
// It's not clear which position is correct for captured variables here:
// * decl.Pos is the wrong position for captured variables, in the inner
// function, but it is the right position in the outer function.
// * decl.Name.Defn is nil for captured variables that were arguments
// on the outer function, however the decl.Pos for those seems to be
// correct.
// * decl.Name.Defn is the "wrong" thing for variables declared in the
// header of a type switch, it's their position in the header, rather
// than the position of the case statement. In principle this is the
// right thing, but here we prefer the latter because it makes each
// instance of the header variable local to the lexical block of its
// case statement.
// This code is probably wrong for type switch variables that are also
// captured.
return decl.Name.Defn.Pos
}
return decl.Pos
}
// createSimpleVars creates a DWARF entry for every variable declared in the
// function, claiming that they are permanently on the stack.
func createSimpleVars(fnsym *obj.LSym, apDecls []*Node) ([]*Node, []*dwarf.Var, map[*Node]bool) {
var vars []*dwarf.Var
var decls []*Node
selected := make(map[*Node]bool)
for _, n := range apDecls {
if n.IsAutoTmp() {
continue
}
decls = append(decls, n)
vars = append(vars, createSimpleVar(fnsym, n))
selected[n] = true
}
return decls, vars, selected
}
func createSimpleVar(fnsym *obj.LSym, n *Node) *dwarf.Var {
var abbrev int
offs := n.Xoffset
switch n.Class() {
case PAUTO:
abbrev = dwarf.DW_ABRV_AUTO
if Ctxt.FixedFrameSize() == 0 {
offs -= int64(Widthptr)
}
if objabi.Framepointer_enabled || objabi.GOARCH == "arm64" {
// There is a word space for FP on ARM64 even if the frame pointer is disabled
offs -= int64(Widthptr)
}
case PPARAM, PPARAMOUT:
abbrev = dwarf.DW_ABRV_PARAM
offs += Ctxt.FixedFrameSize()
default:
Fatalf("createSimpleVar unexpected class %v for node %v", n.Class(), n)
}
typename := dwarf.InfoPrefix + typesymname(n.Type)
delete(fnsym.Func().Autot, ngotype(n).Linksym())
inlIndex := 0
if genDwarfInline > 1 {
if n.Name.InlFormal() || n.Name.InlLocal() {
inlIndex = posInlIndex(n.Pos) + 1
if n.Name.InlFormal() {
abbrev = dwarf.DW_ABRV_PARAM
}
}
}
declpos := Ctxt.InnermostPos(declPos(n))
return &dwarf.Var{
Name: n.Sym.Name,
IsReturnValue: n.Class() == PPARAMOUT,
IsInlFormal: n.Name.InlFormal(),
Abbrev: abbrev,
StackOffset: int32(offs),
Type: Ctxt.Lookup(typename),
DeclFile: declpos.RelFilename(),
DeclLine: declpos.RelLine(),
DeclCol: declpos.Col(),
InlIndex: int32(inlIndex),
ChildIndex: -1,
}
}
// createComplexVars creates recomposed DWARF vars with location lists,
// suitable for describing optimized code.
func createComplexVars(fnsym *obj.LSym, fn *Func) ([]*Node, []*dwarf.Var, map[*Node]bool) {
debugInfo := fn.DebugInfo
// Produce a DWARF variable entry for each user variable.
var decls []*Node
var vars []*dwarf.Var
ssaVars := make(map[*Node]bool)
for varID, dvar := range debugInfo.Vars {
n := dvar.(*Node)
ssaVars[n] = true
for _, slot := range debugInfo.VarSlots[varID] {
ssaVars[debugInfo.Slots[slot].N.(*Node)] = true
}
if dvar := createComplexVar(fnsym, fn, ssa.VarID(varID)); dvar != nil {
decls = append(decls, n)
vars = append(vars, dvar)
}
}
return decls, vars, ssaVars
}
// createDwarfVars process fn, returning a list of DWARF variables and the
// Nodes they represent.
func createDwarfVars(fnsym *obj.LSym, fn *Func, apDecls []*Node) ([]*Node, []*dwarf.Var) {
// Collect a raw list of DWARF vars.
var vars []*dwarf.Var
var decls []*Node
var selected map[*Node]bool
if Ctxt.Flag_locationlists && Ctxt.Flag_optimize && fn.DebugInfo != nil {
decls, vars, selected = createComplexVars(fnsym, fn)
} else {
decls, vars, selected = createSimpleVars(fnsym, apDecls)
}
dcl := apDecls
if fnsym.WasInlined() {
dcl = preInliningDcls(fnsym)
}
// If optimization is enabled, the list above will typically be
// missing some of the original pre-optimization variables in the
// function (they may have been promoted to registers, folded into
// constants, dead-coded away, etc). Input arguments not eligible
// for SSA optimization are also missing. Here we add back in entries
// for selected missing vars. Note that the recipe below creates a
// conservative location. The idea here is that we want to
// communicate to the user that "yes, there is a variable named X
// in this function, but no, I don't have enough information to
// reliably report its contents."
// For non-SSA-able arguments, however, the correct information
// is known -- they have a single home on the stack.
for _, n := range dcl {
if _, found := selected[n]; found {
continue
}
c := n.Sym.Name[0]
if c == '.' || n.Type.IsUntyped() {
continue
}
if n.Class() == PPARAM && !canSSAType(n.Type) {
// SSA-able args get location lists, and may move in and
// out of registers, so those are handled elsewhere.
// Autos and named output params seem to get handled
// with VARDEF, which creates location lists.
// Args not of SSA-able type are treated here; they
// are homed on the stack in a single place for the
// entire call.
vars = append(vars, createSimpleVar(fnsym, n))
decls = append(decls, n)
continue
}
typename := dwarf.InfoPrefix + typesymname(n.Type)
decls = append(decls, n)
abbrev := dwarf.DW_ABRV_AUTO_LOCLIST
isReturnValue := (n.Class() == PPARAMOUT)
if n.Class() == PPARAM || n.Class() == PPARAMOUT {
abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
} else if n.Class() == PAUTOHEAP {
// If dcl in question has been promoted to heap, do a bit
// of extra work to recover original class (auto or param);
// see issue 30908. This insures that we get the proper
// signature in the abstract function DIE, but leaves a
// misleading location for the param (we want pointer-to-heap
// and not stack).
// TODO(thanm): generate a better location expression
stackcopy := n.Name.Param.Stackcopy
if stackcopy != nil && (stackcopy.Class() == PPARAM || stackcopy.Class() == PPARAMOUT) {
abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
isReturnValue = (stackcopy.Class() == PPARAMOUT)
}
}
inlIndex := 0
if genDwarfInline > 1 {
if n.Name.InlFormal() || n.Name.InlLocal() {
inlIndex = posInlIndex(n.Pos) + 1
if n.Name.InlFormal() {
abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
}
}
}
declpos := Ctxt.InnermostPos(n.Pos)
vars = append(vars, &dwarf.Var{
Name: n.Sym.Name,
IsReturnValue: isReturnValue,
Abbrev: abbrev,
StackOffset: int32(n.Xoffset),
Type: Ctxt.Lookup(typename),
DeclFile: declpos.RelFilename(),
DeclLine: declpos.RelLine(),
DeclCol: declpos.Col(),
InlIndex: int32(inlIndex),
ChildIndex: -1,
})
// Record go type of to insure that it gets emitted by the linker.
fnsym.Func().RecordAutoType(ngotype(n).Linksym())
}
return decls, vars
}
// Given a function that was inlined at some point during the
// compilation, return a sorted list of nodes corresponding to the
// autos/locals in that function prior to inlining. If this is a
// function that is not local to the package being compiled, then the
// names of the variables may have been "versioned" to avoid conflicts
// with local vars; disregard this versioning when sorting.
func preInliningDcls(fnsym *obj.LSym) []*Node {
fn := Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*Node)
var rdcl []*Node
for _, n := range fn.Func.Inl.Dcl {
c := n.Sym.Name[0]
// Avoid reporting "_" parameters, since if there are more than
// one, it can result in a collision later on, as in #23179.
if unversion(n.Sym.Name) == "_" || c == '.' || n.Type.IsUntyped() {
continue
}
rdcl = append(rdcl, n)
}
return rdcl
}
// stackOffset returns the stack location of a LocalSlot relative to the
// stack pointer, suitable for use in a DWARF location entry. This has nothing
// to do with its offset in the user variable.
func stackOffset(slot ssa.LocalSlot) int32 {
n := slot.N.(*Node)
var base int64
switch n.Class() {
case PAUTO:
if Ctxt.FixedFrameSize() == 0 {
base -= int64(Widthptr)
}
if objabi.Framepointer_enabled || objabi.GOARCH == "arm64" {
// There is a word space for FP on ARM64 even if the frame pointer is disabled
base -= int64(Widthptr)
}
case PPARAM, PPARAMOUT:
base += Ctxt.FixedFrameSize()
}
return int32(base + n.Xoffset + slot.Off)
}
// createComplexVar builds a single DWARF variable entry and location list.
func createComplexVar(fnsym *obj.LSym, fn *Func, varID ssa.VarID) *dwarf.Var {
debug := fn.DebugInfo
n := debug.Vars[varID].(*Node)
var abbrev int
switch n.Class() {
case PAUTO:
abbrev = dwarf.DW_ABRV_AUTO_LOCLIST
case PPARAM, PPARAMOUT:
abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
default:
return nil
}
gotype := ngotype(n).Linksym()
delete(fnsym.Func().Autot, gotype)
typename := dwarf.InfoPrefix + gotype.Name[len("type."):]
inlIndex := 0
if genDwarfInline > 1 {
if n.Name.InlFormal() || n.Name.InlLocal() {
inlIndex = posInlIndex(n.Pos) + 1
if n.Name.InlFormal() {
abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
}
}
}
declpos := Ctxt.InnermostPos(n.Pos)
dvar := &dwarf.Var{
Name: n.Sym.Name,
IsReturnValue: n.Class() == PPARAMOUT,
IsInlFormal: n.Name.InlFormal(),
Abbrev: abbrev,
Type: Ctxt.Lookup(typename),
// The stack offset is used as a sorting key, so for decomposed
// variables just give it the first one. It's not used otherwise.
// This won't work well if the first slot hasn't been assigned a stack
// location, but it's not obvious how to do better.
StackOffset: stackOffset(debug.Slots[debug.VarSlots[varID][0]]),
DeclFile: declpos.RelFilename(),
DeclLine: declpos.RelLine(),
DeclCol: declpos.Col(),
InlIndex: int32(inlIndex),
ChildIndex: -1,
}
list := debug.LocationLists[varID]
if len(list) != 0 {
dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
debug.PutLocationList(list, Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym))
}
}
return dvar
}
// fieldtrack adds R_USEFIELD relocations to fnsym to record any
// struct fields that it used.
func fieldtrack(fnsym *obj.LSym, tracked map[*types.Sym]struct{}) {
if fnsym == nil {
return
}
if objabi.Fieldtrack_enabled == 0 || len(tracked) == 0 {
return
}
trackSyms := make([]*types.Sym, 0, len(tracked))
for sym := range tracked {
trackSyms = append(trackSyms, sym)
}
sort.Sort(symByName(trackSyms))
for _, sym := range trackSyms {
r := obj.Addrel(fnsym)
r.Sym = sym.Linksym()
r.Type = objabi.R_USEFIELD
}
}
type symByName []*types.Sym
func (a symByName) Len() int { return len(a) }
func (a symByName) Less(i, j int) bool { return a[i].Name < a[j].Name }
func (a symByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }

View File

@@ -1,196 +0,0 @@
// Copyright 2015 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 gc
import (
"cmd/compile/internal/types"
"reflect"
"sort"
"testing"
)
func typeWithoutPointers() *types.Type {
t := types.New(TSTRUCT)
f := &types.Field{Type: types.New(TINT)}
t.SetFields([]*types.Field{f})
return t
}
func typeWithPointers() *types.Type {
t := types.New(TSTRUCT)
f := &types.Field{Type: types.NewPtr(types.New(TINT))}
t.SetFields([]*types.Field{f})
return t
}
func markUsed(n *Node) *Node {
n.Name.SetUsed(true)
return n
}
func markNeedZero(n *Node) *Node {
n.Name.SetNeedzero(true)
return n
}
func nodeWithClass(n Node, c Class) *Node {
n.SetClass(c)
n.Name = new(Name)
return &n
}
// Test all code paths for cmpstackvarlt.
func TestCmpstackvar(t *testing.T) {
testdata := []struct {
a, b *Node
lt bool
}{
{
nodeWithClass(Node{}, PAUTO),
nodeWithClass(Node{}, PFUNC),
false,
},
{
nodeWithClass(Node{}, PFUNC),
nodeWithClass(Node{}, PAUTO),
true,
},
{
nodeWithClass(Node{Xoffset: 0}, PFUNC),
nodeWithClass(Node{Xoffset: 10}, PFUNC),
true,
},
{
nodeWithClass(Node{Xoffset: 20}, PFUNC),
nodeWithClass(Node{Xoffset: 10}, PFUNC),
false,
},
{
nodeWithClass(Node{Xoffset: 10}, PFUNC),
nodeWithClass(Node{Xoffset: 10}, PFUNC),
false,
},
{
nodeWithClass(Node{Xoffset: 10}, PPARAM),
nodeWithClass(Node{Xoffset: 20}, PPARAMOUT),
true,
},
{
nodeWithClass(Node{Xoffset: 10}, PPARAMOUT),
nodeWithClass(Node{Xoffset: 20}, PPARAM),
true,
},
{
markUsed(nodeWithClass(Node{}, PAUTO)),
nodeWithClass(Node{}, PAUTO),
true,
},
{
nodeWithClass(Node{}, PAUTO),
markUsed(nodeWithClass(Node{}, PAUTO)),
false,
},
{
nodeWithClass(Node{Type: typeWithoutPointers()}, PAUTO),
nodeWithClass(Node{Type: typeWithPointers()}, PAUTO),
false,
},
{
nodeWithClass(Node{Type: typeWithPointers()}, PAUTO),
nodeWithClass(Node{Type: typeWithoutPointers()}, PAUTO),
true,
},
{
markNeedZero(nodeWithClass(Node{Type: &types.Type{}}, PAUTO)),
nodeWithClass(Node{Type: &types.Type{}, Name: &Name{}}, PAUTO),
true,
},
{
nodeWithClass(Node{Type: &types.Type{}, Name: &Name{}}, PAUTO),
markNeedZero(nodeWithClass(Node{Type: &types.Type{}}, PAUTO)),
false,
},
{
nodeWithClass(Node{Type: &types.Type{Width: 1}, Name: &Name{}}, PAUTO),
nodeWithClass(Node{Type: &types.Type{Width: 2}, Name: &Name{}}, PAUTO),
false,
},
{
nodeWithClass(Node{Type: &types.Type{Width: 2}, Name: &Name{}}, PAUTO),
nodeWithClass(Node{Type: &types.Type{Width: 1}, Name: &Name{}}, PAUTO),
true,
},
{
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "abc"}}, PAUTO),
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "xyz"}}, PAUTO),
true,
},
{
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "abc"}}, PAUTO),
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "abc"}}, PAUTO),
false,
},
{
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "xyz"}}, PAUTO),
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "abc"}}, PAUTO),
false,
},
}
for _, d := range testdata {
got := cmpstackvarlt(d.a, d.b)
if got != d.lt {
t.Errorf("want %#v < %#v", d.a, d.b)
}
// If we expect a < b to be true, check that b < a is false.
if d.lt && cmpstackvarlt(d.b, d.a) {
t.Errorf("unexpected %#v < %#v", d.b, d.a)
}
}
}
func TestStackvarSort(t *testing.T) {
inp := []*Node{
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PFUNC),
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PAUTO),
nodeWithClass(Node{Xoffset: 0, Type: &types.Type{}, Sym: &types.Sym{}}, PFUNC),
nodeWithClass(Node{Xoffset: 10, Type: &types.Type{}, Sym: &types.Sym{}}, PFUNC),
nodeWithClass(Node{Xoffset: 20, Type: &types.Type{}, Sym: &types.Sym{}}, PFUNC),
markUsed(nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PAUTO)),
nodeWithClass(Node{Type: typeWithoutPointers(), Sym: &types.Sym{}}, PAUTO),
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PAUTO),
markNeedZero(nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PAUTO)),
nodeWithClass(Node{Type: &types.Type{Width: 1}, Sym: &types.Sym{}}, PAUTO),
nodeWithClass(Node{Type: &types.Type{Width: 2}, Sym: &types.Sym{}}, PAUTO),
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "abc"}}, PAUTO),
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "xyz"}}, PAUTO),
}
want := []*Node{
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PFUNC),
nodeWithClass(Node{Xoffset: 0, Type: &types.Type{}, Sym: &types.Sym{}}, PFUNC),
nodeWithClass(Node{Xoffset: 10, Type: &types.Type{}, Sym: &types.Sym{}}, PFUNC),
nodeWithClass(Node{Xoffset: 20, Type: &types.Type{}, Sym: &types.Sym{}}, PFUNC),
markUsed(nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PAUTO)),
markNeedZero(nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PAUTO)),
nodeWithClass(Node{Type: &types.Type{Width: 2}, Sym: &types.Sym{}}, PAUTO),
nodeWithClass(Node{Type: &types.Type{Width: 1}, Sym: &types.Sym{}}, PAUTO),
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PAUTO),
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{}}, PAUTO),
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "abc"}}, PAUTO),
nodeWithClass(Node{Type: &types.Type{}, Sym: &types.Sym{Name: "xyz"}}, PAUTO),
nodeWithClass(Node{Type: typeWithoutPointers(), Sym: &types.Sym{}}, PAUTO),
}
sort.Sort(byStackVar(inp))
if !reflect.DeepEqual(want, inp) {
t.Error("sort failed")
for i := range inp {
g := inp[i]
w := want[i]
eq := reflect.DeepEqual(w, g)
if !eq {
t.Log(i, w, g)
}
}
}
}

View File

@@ -1,628 +0,0 @@
// Copyright 2009 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 gc
import (
"cmd/compile/internal/types"
"cmd/internal/sys"
"unicode/utf8"
)
// range
func typecheckrange(n *Node) {
// Typechecking order is important here:
// 0. first typecheck range expression (slice/map/chan),
// it is evaluated only once and so logically it is not part of the loop.
// 1. typecheck produced values,
// this part can declare new vars and so it must be typechecked before body,
// because body can contain a closure that captures the vars.
// 2. decldepth++ to denote loop body.
// 3. typecheck body.
// 4. decldepth--.
typecheckrangeExpr(n)
// second half of dance, the first half being typecheckrangeExpr
n.SetTypecheck(1)
ls := n.List.Slice()
for i1, n1 := range ls {
if n1.Typecheck() == 0 {
ls[i1] = typecheck(ls[i1], ctxExpr|ctxAssign)
}
}
decldepth++
typecheckslice(n.Nbody.Slice(), ctxStmt)
decldepth--
}
func typecheckrangeExpr(n *Node) {
n.Right = typecheck(n.Right, ctxExpr)
t := n.Right.Type
if t == nil {
return
}
// delicate little dance. see typecheckas2
ls := n.List.Slice()
for i1, n1 := range ls {
if n1.Name == nil || n1.Name.Defn != n {
ls[i1] = typecheck(ls[i1], ctxExpr|ctxAssign)
}
}
if t.IsPtr() && t.Elem().IsArray() {
t = t.Elem()
}
n.Type = t
var t1, t2 *types.Type
toomany := false
switch t.Etype {
default:
yyerrorl(n.Pos, "cannot range over %L", n.Right)
return
case TARRAY, TSLICE:
t1 = types.Types[TINT]
t2 = t.Elem()
case TMAP:
t1 = t.Key()
t2 = t.Elem()
case TCHAN:
if !t.ChanDir().CanRecv() {
yyerrorl(n.Pos, "invalid operation: range %v (receive from send-only type %v)", n.Right, n.Right.Type)
return
}
t1 = t.Elem()
t2 = nil
if n.List.Len() == 2 {
toomany = true
}
case TSTRING:
t1 = types.Types[TINT]
t2 = types.Runetype
}
if n.List.Len() > 2 || toomany {
yyerrorl(n.Pos, "too many variables in range")
}
var v1, v2 *Node
if n.List.Len() != 0 {
v1 = n.List.First()
}
if n.List.Len() > 1 {
v2 = n.List.Second()
}
// this is not only an optimization but also a requirement in the spec.
// "if the second iteration variable is the blank identifier, the range
// clause is equivalent to the same clause with only the first variable
// present."
if v2.isBlank() {
if v1 != nil {
n.List.Set1(v1)
}
v2 = nil
}
if v1 != nil {
if v1.Name != nil && v1.Name.Defn == n {
v1.Type = t1
} else if v1.Type != nil {
if op, why := assignop(t1, v1.Type); op == OXXX {
yyerrorl(n.Pos, "cannot assign type %v to %L in range%s", t1, v1, why)
}
}
checkassign(n, v1)
}
if v2 != nil {
if v2.Name != nil && v2.Name.Defn == n {
v2.Type = t2
} else if v2.Type != nil {
if op, why := assignop(t2, v2.Type); op == OXXX {
yyerrorl(n.Pos, "cannot assign type %v to %L in range%s", t2, v2, why)
}
}
checkassign(n, v2)
}
}
func cheapComputableIndex(width int64) bool {
switch thearch.LinkArch.Family {
// MIPS does not have R+R addressing
// Arm64 may lack ability to generate this code in our assembler,
// but the architecture supports it.
case sys.PPC64, sys.S390X:
return width == 1
case sys.AMD64, sys.I386, sys.ARM64, sys.ARM:
switch width {
case 1, 2, 4, 8:
return true
}
}
return false
}
// walkrange transforms various forms of ORANGE into
// simpler forms. The result must be assigned back to n.
// Node n may also be modified in place, and may also be
// the returned node.
func walkrange(n *Node) *Node {
if isMapClear(n) {
m := n.Right
lno := setlineno(m)
n = mapClear(m)
lineno = lno
return n
}
// variable name conventions:
// ohv1, hv1, hv2: hidden (old) val 1, 2
// ha, hit: hidden aggregate, iterator
// hn, hp: hidden len, pointer
// hb: hidden bool
// a, v1, v2: not hidden aggregate, val 1, 2
t := n.Type
a := n.Right
lno := setlineno(a)
n.Right = nil
var v1, v2 *Node
l := n.List.Len()
if l > 0 {
v1 = n.List.First()
}
if l > 1 {
v2 = n.List.Second()
}
if v2.isBlank() {
v2 = nil
}
if v1.isBlank() && v2 == nil {
v1 = nil
}
if v1 == nil && v2 != nil {
Fatalf("walkrange: v2 != nil while v1 == nil")
}
// n.List has no meaning anymore, clear it
// to avoid erroneous processing by racewalk.
n.List.Set(nil)
var ifGuard *Node
translatedLoopOp := OFOR
var body []*Node
var init []*Node
switch t.Etype {
default:
Fatalf("walkrange")
case TARRAY, TSLICE:
if arrayClear(n, v1, v2, a) {
lineno = lno
return n
}
// order.stmt arranged for a copy of the array/slice variable if needed.
ha := a
hv1 := temp(types.Types[TINT])
hn := temp(types.Types[TINT])
init = append(init, nod(OAS, hv1, nil))
init = append(init, nod(OAS, hn, nod(OLEN, ha, nil)))
n.Left = nod(OLT, hv1, hn)
n.Right = nod(OAS, hv1, nod(OADD, hv1, nodintconst(1)))
// for range ha { body }
if v1 == nil {
break
}
// for v1 := range ha { body }
if v2 == nil {
body = []*Node{nod(OAS, v1, hv1)}
break
}
// for v1, v2 := range ha { body }
if cheapComputableIndex(n.Type.Elem().Width) {
// v1, v2 = hv1, ha[hv1]
tmp := nod(OINDEX, ha, hv1)
tmp.SetBounded(true)
// Use OAS2 to correctly handle assignments
// of the form "v1, a[v1] := range".
a := nod(OAS2, nil, nil)
a.List.Set2(v1, v2)
a.Rlist.Set2(hv1, tmp)
body = []*Node{a}
break
}
// TODO(austin): OFORUNTIL is a strange beast, but is
// necessary for expressing the control flow we need
// while also making "break" and "continue" work. It
// would be nice to just lower ORANGE during SSA, but
// racewalk needs to see many of the operations
// involved in ORANGE's implementation. If racewalk
// moves into SSA, consider moving ORANGE into SSA and
// eliminating OFORUNTIL.
// TODO(austin): OFORUNTIL inhibits bounds-check
// elimination on the index variable (see #20711).
// Enhance the prove pass to understand this.
ifGuard = nod(OIF, nil, nil)
ifGuard.Left = nod(OLT, hv1, hn)
translatedLoopOp = OFORUNTIL
hp := temp(types.NewPtr(n.Type.Elem()))
tmp := nod(OINDEX, ha, nodintconst(0))
tmp.SetBounded(true)
init = append(init, nod(OAS, hp, nod(OADDR, tmp, nil)))
// Use OAS2 to correctly handle assignments
// of the form "v1, a[v1] := range".
a := nod(OAS2, nil, nil)
a.List.Set2(v1, v2)
a.Rlist.Set2(hv1, nod(ODEREF, hp, nil))
body = append(body, a)
// Advance pointer as part of the late increment.
//
// This runs *after* the condition check, so we know
// advancing the pointer is safe and won't go past the
// end of the allocation.
a = nod(OAS, hp, addptr(hp, t.Elem().Width))
a = typecheck(a, ctxStmt)
n.List.Set1(a)
case TMAP:
// order.stmt allocated the iterator for us.
// we only use a once, so no copy needed.
ha := a
hit := prealloc[n]
th := hit.Type
n.Left = nil
keysym := th.Field(0).Sym // depends on layout of iterator struct. See reflect.go:hiter
elemsym := th.Field(1).Sym // ditto
fn := syslook("mapiterinit")
fn = substArgTypes(fn, t.Key(), t.Elem(), th)
init = append(init, mkcall1(fn, nil, nil, typename(t), ha, nod(OADDR, hit, nil)))
n.Left = nod(ONE, nodSym(ODOT, hit, keysym), nodnil())
fn = syslook("mapiternext")
fn = substArgTypes(fn, th)
n.Right = mkcall1(fn, nil, nil, nod(OADDR, hit, nil))
key := nodSym(ODOT, hit, keysym)
key = nod(ODEREF, key, nil)
if v1 == nil {
body = nil
} else if v2 == nil {
body = []*Node{nod(OAS, v1, key)}
} else {
elem := nodSym(ODOT, hit, elemsym)
elem = nod(ODEREF, elem, nil)
a := nod(OAS2, nil, nil)
a.List.Set2(v1, v2)
a.Rlist.Set2(key, elem)
body = []*Node{a}
}
case TCHAN:
// order.stmt arranged for a copy of the channel variable.
ha := a
n.Left = nil
hv1 := temp(t.Elem())
hv1.SetTypecheck(1)
if t.Elem().HasPointers() {
init = append(init, nod(OAS, hv1, nil))
}
hb := temp(types.Types[TBOOL])
n.Left = nod(ONE, hb, nodbool(false))
a := nod(OAS2RECV, nil, nil)
a.SetTypecheck(1)
a.List.Set2(hv1, hb)
a.Right = nod(ORECV, ha, nil)
n.Left.Ninit.Set1(a)
if v1 == nil {
body = nil
} else {
body = []*Node{nod(OAS, v1, hv1)}
}
// Zero hv1. This prevents hv1 from being the sole, inaccessible
// reference to an otherwise GC-able value during the next channel receive.
// See issue 15281.
body = append(body, nod(OAS, hv1, nil))
case TSTRING:
// Transform string range statements like "for v1, v2 = range a" into
//
// ha := a
// for hv1 := 0; hv1 < len(ha); {
// hv1t := hv1
// hv2 := rune(ha[hv1])
// if hv2 < utf8.RuneSelf {
// hv1++
// } else {
// hv2, hv1 = decoderune(ha, hv1)
// }
// v1, v2 = hv1t, hv2
// // original body
// }
// order.stmt arranged for a copy of the string variable.
ha := a
hv1 := temp(types.Types[TINT])
hv1t := temp(types.Types[TINT])
hv2 := temp(types.Runetype)
// hv1 := 0
init = append(init, nod(OAS, hv1, nil))
// hv1 < len(ha)
n.Left = nod(OLT, hv1, nod(OLEN, ha, nil))
if v1 != nil {
// hv1t = hv1
body = append(body, nod(OAS, hv1t, hv1))
}
// hv2 := rune(ha[hv1])
nind := nod(OINDEX, ha, hv1)
nind.SetBounded(true)
body = append(body, nod(OAS, hv2, conv(nind, types.Runetype)))
// if hv2 < utf8.RuneSelf
nif := nod(OIF, nil, nil)
nif.Left = nod(OLT, hv2, nodintconst(utf8.RuneSelf))
// hv1++
nif.Nbody.Set1(nod(OAS, hv1, nod(OADD, hv1, nodintconst(1))))
// } else {
eif := nod(OAS2, nil, nil)
nif.Rlist.Set1(eif)
// hv2, hv1 = decoderune(ha, hv1)
eif.List.Set2(hv2, hv1)
fn := syslook("decoderune")
eif.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, ha, hv1))
body = append(body, nif)
if v1 != nil {
if v2 != nil {
// v1, v2 = hv1t, hv2
a := nod(OAS2, nil, nil)
a.List.Set2(v1, v2)
a.Rlist.Set2(hv1t, hv2)
body = append(body, a)
} else {
// v1 = hv1t
body = append(body, nod(OAS, v1, hv1t))
}
}
}
n.Op = translatedLoopOp
typecheckslice(init, ctxStmt)
if ifGuard != nil {
ifGuard.Ninit.Append(init...)
ifGuard = typecheck(ifGuard, ctxStmt)
} else {
n.Ninit.Append(init...)
}
typecheckslice(n.Left.Ninit.Slice(), ctxStmt)
n.Left = typecheck(n.Left, ctxExpr)
n.Left = defaultlit(n.Left, nil)
n.Right = typecheck(n.Right, ctxStmt)
typecheckslice(body, ctxStmt)
n.Nbody.Prepend(body...)
if ifGuard != nil {
ifGuard.Nbody.Set1(n)
n = ifGuard
}
n = walkstmt(n)
lineno = lno
return n
}
// isMapClear checks if n is of the form:
//
// for k := range m {
// delete(m, k)
// }
//
// where == for keys of map m is reflexive.
func isMapClear(n *Node) bool {
if Debug.N != 0 || instrumenting {
return false
}
if n.Op != ORANGE || n.Type.Etype != TMAP || n.List.Len() != 1 {
return false
}
k := n.List.First()
if k == nil || k.isBlank() {
return false
}
// Require k to be a new variable name.
if k.Name == nil || k.Name.Defn != n {
return false
}
if n.Nbody.Len() != 1 {
return false
}
stmt := n.Nbody.First() // only stmt in body
if stmt == nil || stmt.Op != ODELETE {
return false
}
m := n.Right
if !samesafeexpr(stmt.List.First(), m) || !samesafeexpr(stmt.List.Second(), k) {
return false
}
// Keys where equality is not reflexive can not be deleted from maps.
if !isreflexive(m.Type.Key()) {
return false
}
return true
}
// mapClear constructs a call to runtime.mapclear for the map m.
func mapClear(m *Node) *Node {
t := m.Type
// instantiate mapclear(typ *type, hmap map[any]any)
fn := syslook("mapclear")
fn = substArgTypes(fn, t.Key(), t.Elem())
n := mkcall1(fn, nil, nil, typename(t), m)
n = typecheck(n, ctxStmt)
n = walkstmt(n)
return n
}
// Lower n into runtime·memclr if possible, for
// fast zeroing of slices and arrays (issue 5373).
// Look for instances of
//
// for i := range a {
// a[i] = zero
// }
//
// in which the evaluation of a is side-effect-free.
//
// Parameters are as in walkrange: "for v1, v2 = range a".
func arrayClear(n, v1, v2, a *Node) bool {
if Debug.N != 0 || instrumenting {
return false
}
if v1 == nil || v2 != nil {
return false
}
if n.Nbody.Len() != 1 || n.Nbody.First() == nil {
return false
}
stmt := n.Nbody.First() // only stmt in body
if stmt.Op != OAS || stmt.Left.Op != OINDEX {
return false
}
if !samesafeexpr(stmt.Left.Left, a) || !samesafeexpr(stmt.Left.Right, v1) {
return false
}
elemsize := n.Type.Elem().Width
if elemsize <= 0 || !isZero(stmt.Right) {
return false
}
// Convert to
// if len(a) != 0 {
// hp = &a[0]
// hn = len(a)*sizeof(elem(a))
// memclr{NoHeap,Has}Pointers(hp, hn)
// i = len(a) - 1
// }
n.Op = OIF
n.Nbody.Set(nil)
n.Left = nod(ONE, nod(OLEN, a, nil), nodintconst(0))
// hp = &a[0]
hp := temp(types.Types[TUNSAFEPTR])
tmp := nod(OINDEX, a, nodintconst(0))
tmp.SetBounded(true)
tmp = nod(OADDR, tmp, nil)
tmp = convnop(tmp, types.Types[TUNSAFEPTR])
n.Nbody.Append(nod(OAS, hp, tmp))
// hn = len(a) * sizeof(elem(a))
hn := temp(types.Types[TUINTPTR])
tmp = nod(OLEN, a, nil)
tmp = nod(OMUL, tmp, nodintconst(elemsize))
tmp = conv(tmp, types.Types[TUINTPTR])
n.Nbody.Append(nod(OAS, hn, tmp))
var fn *Node
if a.Type.Elem().HasPointers() {
// memclrHasPointers(hp, hn)
Curfn.Func.setWBPos(stmt.Pos)
fn = mkcall("memclrHasPointers", nil, nil, hp, hn)
} else {
// memclrNoHeapPointers(hp, hn)
fn = mkcall("memclrNoHeapPointers", nil, nil, hp, hn)
}
n.Nbody.Append(fn)
// i = len(a) - 1
v1 = nod(OAS, v1, nod(OSUB, nod(OLEN, a, nil), nodintconst(1)))
n.Nbody.Append(v1)
n.Left = typecheck(n.Left, ctxExpr)
n.Left = defaultlit(n.Left, nil)
typecheckslice(n.Nbody.Slice(), ctxStmt)
n = walkstmt(n)
return true
}
// addptr returns (*T)(uintptr(p) + n).
func addptr(p *Node, n int64) *Node {
t := p.Type
p = nod(OCONVNOP, p, nil)
p.Type = types.Types[TUINTPTR]
p = nod(OADD, p, nodintconst(n))
p = nod(OCONVNOP, p, nil)
p.Type = t
return p
}

View File

@@ -1,387 +0,0 @@
// Copyright 2009 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 gc
import "cmd/compile/internal/types"
// select
func typecheckselect(sel *Node) {
var def *Node
lno := setlineno(sel)
typecheckslice(sel.Ninit.Slice(), ctxStmt)
for _, ncase := range sel.List.Slice() {
if ncase.Op != OCASE {
setlineno(ncase)
Fatalf("typecheckselect %v", ncase.Op)
}
if ncase.List.Len() == 0 {
// default
if def != nil {
yyerrorl(ncase.Pos, "multiple defaults in select (first at %v)", def.Line())
} else {
def = ncase
}
} else if ncase.List.Len() > 1 {
yyerrorl(ncase.Pos, "select cases cannot be lists")
} else {
ncase.List.SetFirst(typecheck(ncase.List.First(), ctxStmt))
n := ncase.List.First()
ncase.Left = n
ncase.List.Set(nil)
switch n.Op {
default:
pos := n.Pos
if n.Op == ONAME {
// We don't have the right position for ONAME nodes (see #15459 and
// others). Using ncase.Pos for now as it will provide the correct
// line number (assuming the expression follows the "case" keyword
// on the same line). This matches the approach before 1.10.
pos = ncase.Pos
}
yyerrorl(pos, "select case must be receive, send or assign recv")
// convert x = <-c into OSELRECV(x, <-c).
// remove implicit conversions; the eventual assignment
// will reintroduce them.
case OAS:
if (n.Right.Op == OCONVNOP || n.Right.Op == OCONVIFACE) && n.Right.Implicit() {
n.Right = n.Right.Left
}
if n.Right.Op != ORECV {
yyerrorl(n.Pos, "select assignment must have receive on right hand side")
break
}
n.Op = OSELRECV
// convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
case OAS2RECV:
if n.Right.Op != ORECV {
yyerrorl(n.Pos, "select assignment must have receive on right hand side")
break
}
n.Op = OSELRECV2
n.Left = n.List.First()
n.List.Set1(n.List.Second())
// convert <-c into OSELRECV(N, <-c)
case ORECV:
n = nodl(n.Pos, OSELRECV, nil, n)
n.SetTypecheck(1)
ncase.Left = n
case OSEND:
break
}
}
typecheckslice(ncase.Nbody.Slice(), ctxStmt)
}
lineno = lno
}
func walkselect(sel *Node) {
lno := setlineno(sel)
if sel.Nbody.Len() != 0 {
Fatalf("double walkselect")
}
init := sel.Ninit.Slice()
sel.Ninit.Set(nil)
init = append(init, walkselectcases(&sel.List)...)
sel.List.Set(nil)
sel.Nbody.Set(init)
walkstmtlist(sel.Nbody.Slice())
lineno = lno
}
func walkselectcases(cases *Nodes) []*Node {
ncas := cases.Len()
sellineno := lineno
// optimization: zero-case select
if ncas == 0 {
return []*Node{mkcall("block", nil, nil)}
}
// optimization: one-case select: single op.
if ncas == 1 {
cas := cases.First()
setlineno(cas)
l := cas.Ninit.Slice()
if cas.Left != nil { // not default:
n := cas.Left
l = append(l, n.Ninit.Slice()...)
n.Ninit.Set(nil)
switch n.Op {
default:
Fatalf("select %v", n.Op)
case OSEND:
// already ok
case OSELRECV, OSELRECV2:
if n.Op == OSELRECV || n.List.Len() == 0 {
if n.Left == nil {
n = n.Right
} else {
n.Op = OAS
}
break
}
if n.Left == nil {
nblank = typecheck(nblank, ctxExpr|ctxAssign)
n.Left = nblank
}
n.Op = OAS2
n.List.Prepend(n.Left)
n.Rlist.Set1(n.Right)
n.Right = nil
n.Left = nil
n.SetTypecheck(0)
n = typecheck(n, ctxStmt)
}
l = append(l, n)
}
l = append(l, cas.Nbody.Slice()...)
l = append(l, nod(OBREAK, nil, nil))
return l
}
// convert case value arguments to addresses.
// this rewrite is used by both the general code and the next optimization.
var dflt *Node
for _, cas := range cases.Slice() {
setlineno(cas)
n := cas.Left
if n == nil {
dflt = cas
continue
}
switch n.Op {
case OSEND:
n.Right = nod(OADDR, n.Right, nil)
n.Right = typecheck(n.Right, ctxExpr)
case OSELRECV, OSELRECV2:
if n.Op == OSELRECV2 && n.List.Len() == 0 {
n.Op = OSELRECV
}
if n.Left != nil {
n.Left = nod(OADDR, n.Left, nil)
n.Left = typecheck(n.Left, ctxExpr)
}
}
}
// optimization: two-case select but one is default: single non-blocking op.
if ncas == 2 && dflt != nil {
cas := cases.First()
if cas == dflt {
cas = cases.Second()
}
n := cas.Left
setlineno(n)
r := nod(OIF, nil, nil)
r.Ninit.Set(cas.Ninit.Slice())
switch n.Op {
default:
Fatalf("select %v", n.Op)
case OSEND:
// if selectnbsend(c, v) { body } else { default body }
ch := n.Left
r.Left = mkcall1(chanfn("selectnbsend", 2, ch.Type), types.Types[TBOOL], &r.Ninit, ch, n.Right)
case OSELRECV:
// if selectnbrecv(&v, c) { body } else { default body }
ch := n.Right.Left
elem := n.Left
if elem == nil {
elem = nodnil()
}
r.Left = mkcall1(chanfn("selectnbrecv", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, ch)
case OSELRECV2:
// if selectnbrecv2(&v, &received, c) { body } else { default body }
ch := n.Right.Left
elem := n.Left
if elem == nil {
elem = nodnil()
}
receivedp := nod(OADDR, n.List.First(), nil)
receivedp = typecheck(receivedp, ctxExpr)
r.Left = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), types.Types[TBOOL], &r.Ninit, elem, receivedp, ch)
}
r.Left = typecheck(r.Left, ctxExpr)
r.Nbody.Set(cas.Nbody.Slice())
r.Rlist.Set(append(dflt.Ninit.Slice(), dflt.Nbody.Slice()...))
return []*Node{r, nod(OBREAK, nil, nil)}
}
if dflt != nil {
ncas--
}
casorder := make([]*Node, ncas)
nsends, nrecvs := 0, 0
var init []*Node
// generate sel-struct
lineno = sellineno
selv := temp(types.NewArray(scasetype(), int64(ncas)))
r := nod(OAS, selv, nil)
r = typecheck(r, ctxStmt)
init = append(init, r)
// No initialization for order; runtime.selectgo is responsible for that.
order := temp(types.NewArray(types.Types[TUINT16], 2*int64(ncas)))
var pc0, pcs *Node
if flag_race {
pcs = temp(types.NewArray(types.Types[TUINTPTR], int64(ncas)))
pc0 = typecheck(nod(OADDR, nod(OINDEX, pcs, nodintconst(0)), nil), ctxExpr)
} else {
pc0 = nodnil()
}
// register cases
for _, cas := range cases.Slice() {
setlineno(cas)
init = append(init, cas.Ninit.Slice()...)
cas.Ninit.Set(nil)
n := cas.Left
if n == nil { // default:
continue
}
var i int
var c, elem *Node
switch n.Op {
default:
Fatalf("select %v", n.Op)
case OSEND:
i = nsends
nsends++
c = n.Left
elem = n.Right
case OSELRECV, OSELRECV2:
nrecvs++
i = ncas - nrecvs
c = n.Right.Left
elem = n.Left
}
casorder[i] = cas
setField := func(f string, val *Node) {
r := nod(OAS, nodSym(ODOT, nod(OINDEX, selv, nodintconst(int64(i))), lookup(f)), val)
r = typecheck(r, ctxStmt)
init = append(init, r)
}
c = convnop(c, types.Types[TUNSAFEPTR])
setField("c", c)
if elem != nil {
elem = convnop(elem, types.Types[TUNSAFEPTR])
setField("elem", elem)
}
// TODO(mdempsky): There should be a cleaner way to
// handle this.
if flag_race {
r = mkcall("selectsetpc", nil, nil, nod(OADDR, nod(OINDEX, pcs, nodintconst(int64(i))), nil))
init = append(init, r)
}
}
if nsends+nrecvs != ncas {
Fatalf("walkselectcases: miscount: %v + %v != %v", nsends, nrecvs, ncas)
}
// run the select
lineno = sellineno
chosen := temp(types.Types[TINT])
recvOK := temp(types.Types[TBOOL])
r = nod(OAS2, nil, nil)
r.List.Set2(chosen, recvOK)
fn := syslook("selectgo")
r.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, bytePtrToIndex(selv, 0), bytePtrToIndex(order, 0), pc0, nodintconst(int64(nsends)), nodintconst(int64(nrecvs)), nodbool(dflt == nil)))
r = typecheck(r, ctxStmt)
init = append(init, r)
// selv and order are no longer alive after selectgo.
init = append(init, nod(OVARKILL, selv, nil))
init = append(init, nod(OVARKILL, order, nil))
if flag_race {
init = append(init, nod(OVARKILL, pcs, nil))
}
// dispatch cases
dispatch := func(cond, cas *Node) {
cond = typecheck(cond, ctxExpr)
cond = defaultlit(cond, nil)
r := nod(OIF, cond, nil)
if n := cas.Left; n != nil && n.Op == OSELRECV2 {
x := nod(OAS, n.List.First(), recvOK)
x = typecheck(x, ctxStmt)
r.Nbody.Append(x)
}
r.Nbody.AppendNodes(&cas.Nbody)
r.Nbody.Append(nod(OBREAK, nil, nil))
init = append(init, r)
}
if dflt != nil {
setlineno(dflt)
dispatch(nod(OLT, chosen, nodintconst(0)), dflt)
}
for i, cas := range casorder {
setlineno(cas)
dispatch(nod(OEQ, chosen, nodintconst(int64(i))), cas)
}
return init
}
// bytePtrToIndex returns a Node representing "(*byte)(&n[i])".
func bytePtrToIndex(n *Node, i int64) *Node {
s := nod(OADDR, nod(OINDEX, n, nodintconst(i)), nil)
t := types.NewPtr(types.Types[TUINT8])
return convnop(s, t)
}
var scase *types.Type
// Keep in sync with src/runtime/select.go.
func scasetype() *types.Type {
if scase == nil {
scase = tostruct([]*Node{
namedfield("c", types.Types[TUNSAFEPTR]),
namedfield("elem", types.Types[TUNSAFEPTR]),
})
scase.SetNoalg(true)
}
return scase
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,756 +0,0 @@
// Copyright 2009 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 gc
import (
"cmd/compile/internal/types"
"cmd/internal/src"
"sort"
)
// typecheckswitch typechecks a switch statement.
func typecheckswitch(n *Node) {
typecheckslice(n.Ninit.Slice(), ctxStmt)
if n.Left != nil && n.Left.Op == OTYPESW {
typecheckTypeSwitch(n)
} else {
typecheckExprSwitch(n)
}
}
func typecheckTypeSwitch(n *Node) {
n.Left.Right = typecheck(n.Left.Right, ctxExpr)
t := n.Left.Right.Type
if t != nil && !t.IsInterface() {
yyerrorl(n.Pos, "cannot type switch on non-interface value %L", n.Left.Right)
t = nil
}
// We don't actually declare the type switch's guarded
// declaration itself. So if there are no cases, we won't
// notice that it went unused.
if v := n.Left.Left; v != nil && !v.isBlank() && n.List.Len() == 0 {
yyerrorl(v.Pos, "%v declared but not used", v.Sym)
}
var defCase, nilCase *Node
var ts typeSet
for _, ncase := range n.List.Slice() {
ls := ncase.List.Slice()
if len(ls) == 0 { // default:
if defCase != nil {
yyerrorl(ncase.Pos, "multiple defaults in switch (first at %v)", defCase.Line())
} else {
defCase = ncase
}
}
for i := range ls {
ls[i] = typecheck(ls[i], ctxExpr|ctxType)
n1 := ls[i]
if t == nil || n1.Type == nil {
continue
}
var missing, have *types.Field
var ptr int
switch {
case n1.isNil(): // case nil:
if nilCase != nil {
yyerrorl(ncase.Pos, "multiple nil cases in type switch (first at %v)", nilCase.Line())
} else {
nilCase = ncase
}
case n1.Op != OTYPE:
yyerrorl(ncase.Pos, "%L is not a type", n1)
case !n1.Type.IsInterface() && !implements(n1.Type, t, &missing, &have, &ptr) && !missing.Broke():
if have != nil && !have.Broke() {
yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+
" (wrong type for %v method)\n\thave %v%S\n\twant %v%S", n.Left.Right, n1.Type, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
} else if ptr != 0 {
yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+
" (%v method has pointer receiver)", n.Left.Right, n1.Type, missing.Sym)
} else {
yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+
" (missing %v method)", n.Left.Right, n1.Type, missing.Sym)
}
}
if n1.Op == OTYPE {
ts.add(ncase.Pos, n1.Type)
}
}
if ncase.Rlist.Len() != 0 {
// Assign the clause variable's type.
vt := t
if len(ls) == 1 {
if ls[0].Op == OTYPE {
vt = ls[0].Type
} else if ls[0].Op != OLITERAL { // TODO(mdempsky): Should be !ls[0].isNil()
// Invalid single-type case;
// mark variable as broken.
vt = nil
}
}
// TODO(mdempsky): It should be possible to
// still typecheck the case body.
if vt == nil {
continue
}
nvar := ncase.Rlist.First()
nvar.Type = vt
nvar = typecheck(nvar, ctxExpr|ctxAssign)
ncase.Rlist.SetFirst(nvar)
}
typecheckslice(ncase.Nbody.Slice(), ctxStmt)
}
}
type typeSet struct {
m map[string][]typeSetEntry
}
type typeSetEntry struct {
pos src.XPos
typ *types.Type
}
func (s *typeSet) add(pos src.XPos, typ *types.Type) {
if s.m == nil {
s.m = make(map[string][]typeSetEntry)
}
// LongString does not uniquely identify types, so we need to
// disambiguate collisions with types.Identical.
// TODO(mdempsky): Add a method that *is* unique.
ls := typ.LongString()
prevs := s.m[ls]
for _, prev := range prevs {
if types.Identical(typ, prev.typ) {
yyerrorl(pos, "duplicate case %v in type switch\n\tprevious case at %s", typ, linestr(prev.pos))
return
}
}
s.m[ls] = append(prevs, typeSetEntry{pos, typ})
}
func typecheckExprSwitch(n *Node) {
t := types.Types[TBOOL]
if n.Left != nil {
n.Left = typecheck(n.Left, ctxExpr)
n.Left = defaultlit(n.Left, nil)
t = n.Left.Type
}
var nilonly string
if t != nil {
switch {
case t.IsMap():
nilonly = "map"
case t.Etype == TFUNC:
nilonly = "func"
case t.IsSlice():
nilonly = "slice"
case !IsComparable(t):
if t.IsStruct() {
yyerrorl(n.Pos, "cannot switch on %L (struct containing %v cannot be compared)", n.Left, IncomparableField(t).Type)
} else {
yyerrorl(n.Pos, "cannot switch on %L", n.Left)
}
t = nil
}
}
var defCase *Node
var cs constSet
for _, ncase := range n.List.Slice() {
ls := ncase.List.Slice()
if len(ls) == 0 { // default:
if defCase != nil {
yyerrorl(ncase.Pos, "multiple defaults in switch (first at %v)", defCase.Line())
} else {
defCase = ncase
}
}
for i := range ls {
setlineno(ncase)
ls[i] = typecheck(ls[i], ctxExpr)
ls[i] = defaultlit(ls[i], t)
n1 := ls[i]
if t == nil || n1.Type == nil {
continue
}
if nilonly != "" && !n1.isNil() {
yyerrorl(ncase.Pos, "invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Left)
} else if t.IsInterface() && !n1.Type.IsInterface() && !IsComparable(n1.Type) {
yyerrorl(ncase.Pos, "invalid case %L in switch (incomparable type)", n1)
} else {
op1, _ := assignop(n1.Type, t)
op2, _ := assignop(t, n1.Type)
if op1 == OXXX && op2 == OXXX {
if n.Left != nil {
yyerrorl(ncase.Pos, "invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Left, n1.Type, t)
} else {
yyerrorl(ncase.Pos, "invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type)
}
}
}
// Don't check for duplicate bools. Although the spec allows it,
// (1) the compiler hasn't checked it in the past, so compatibility mandates it, and
// (2) it would disallow useful things like
// case GOARCH == "arm" && GOARM == "5":
// case GOARCH == "arm":
// which would both evaluate to false for non-ARM compiles.
if !n1.Type.IsBoolean() {
cs.add(ncase.Pos, n1, "case", "switch")
}
}
typecheckslice(ncase.Nbody.Slice(), ctxStmt)
}
}
// walkswitch walks a switch statement.
func walkswitch(sw *Node) {
// Guard against double walk, see #25776.
if sw.List.Len() == 0 && sw.Nbody.Len() > 0 {
return // Was fatal, but eliminating every possible source of double-walking is hard
}
if sw.Left != nil && sw.Left.Op == OTYPESW {
walkTypeSwitch(sw)
} else {
walkExprSwitch(sw)
}
}
// walkExprSwitch generates an AST implementing sw. sw is an
// expression switch.
func walkExprSwitch(sw *Node) {
lno := setlineno(sw)
cond := sw.Left
sw.Left = nil
// convert switch {...} to switch true {...}
if cond == nil {
cond = nodbool(true)
cond = typecheck(cond, ctxExpr)
cond = defaultlit(cond, nil)
}
// Given "switch string(byteslice)",
// with all cases being side-effect free,
// use a zero-cost alias of the byte slice.
// Do this before calling walkexpr on cond,
// because walkexpr will lower the string
// conversion into a runtime call.
// See issue 24937 for more discussion.
if cond.Op == OBYTES2STR && allCaseExprsAreSideEffectFree(sw) {
cond.Op = OBYTES2STRTMP
}
cond = walkexpr(cond, &sw.Ninit)
if cond.Op != OLITERAL {
cond = copyexpr(cond, cond.Type, &sw.Nbody)
}
lineno = lno
s := exprSwitch{
exprname: cond,
}
var defaultGoto *Node
var body Nodes
for _, ncase := range sw.List.Slice() {
label := autolabel(".s")
jmp := npos(ncase.Pos, nodSym(OGOTO, nil, label))
// Process case dispatch.
if ncase.List.Len() == 0 {
if defaultGoto != nil {
Fatalf("duplicate default case not detected during typechecking")
}
defaultGoto = jmp
}
for _, n1 := range ncase.List.Slice() {
s.Add(ncase.Pos, n1, jmp)
}
// Process body.
body.Append(npos(ncase.Pos, nodSym(OLABEL, nil, label)))
body.Append(ncase.Nbody.Slice()...)
if fall, pos := hasFall(ncase.Nbody.Slice()); !fall {
br := nod(OBREAK, nil, nil)
br.Pos = pos
body.Append(br)
}
}
sw.List.Set(nil)
if defaultGoto == nil {
br := nod(OBREAK, nil, nil)
br.Pos = br.Pos.WithNotStmt()
defaultGoto = br
}
s.Emit(&sw.Nbody)
sw.Nbody.Append(defaultGoto)
sw.Nbody.AppendNodes(&body)
walkstmtlist(sw.Nbody.Slice())
}
// An exprSwitch walks an expression switch.
type exprSwitch struct {
exprname *Node // value being switched on
done Nodes
clauses []exprClause
}
type exprClause struct {
pos src.XPos
lo, hi *Node
jmp *Node
}
func (s *exprSwitch) Add(pos src.XPos, expr, jmp *Node) {
c := exprClause{pos: pos, lo: expr, hi: expr, jmp: jmp}
if okforcmp[s.exprname.Type.Etype] && expr.Op == OLITERAL {
s.clauses = append(s.clauses, c)
return
}
s.flush()
s.clauses = append(s.clauses, c)
s.flush()
}
func (s *exprSwitch) Emit(out *Nodes) {
s.flush()
out.AppendNodes(&s.done)
}
func (s *exprSwitch) flush() {
cc := s.clauses
s.clauses = nil
if len(cc) == 0 {
return
}
// Caution: If len(cc) == 1, then cc[0] might not an OLITERAL.
// The code below is structured to implicitly handle this case
// (e.g., sort.Slice doesn't need to invoke the less function
// when there's only a single slice element).
if s.exprname.Type.IsString() && len(cc) >= 2 {
// Sort strings by length and then by value. It is
// much cheaper to compare lengths than values, and
// all we need here is consistency. We respect this
// sorting below.
sort.Slice(cc, func(i, j int) bool {
si := cc[i].lo.StringVal()
sj := cc[j].lo.StringVal()
if len(si) != len(sj) {
return len(si) < len(sj)
}
return si < sj
})
// runLen returns the string length associated with a
// particular run of exprClauses.
runLen := func(run []exprClause) int64 { return int64(len(run[0].lo.StringVal())) }
// Collapse runs of consecutive strings with the same length.
var runs [][]exprClause
start := 0
for i := 1; i < len(cc); i++ {
if runLen(cc[start:]) != runLen(cc[i:]) {
runs = append(runs, cc[start:i])
start = i
}
}
runs = append(runs, cc[start:])
// Perform two-level binary search.
nlen := nod(OLEN, s.exprname, nil)
binarySearch(len(runs), &s.done,
func(i int) *Node {
return nod(OLE, nlen, nodintconst(runLen(runs[i-1])))
},
func(i int, nif *Node) {
run := runs[i]
nif.Left = nod(OEQ, nlen, nodintconst(runLen(run)))
s.search(run, &nif.Nbody)
},
)
return
}
sort.Slice(cc, func(i, j int) bool {
return compareOp(cc[i].lo.Val(), OLT, cc[j].lo.Val())
})
// Merge consecutive integer cases.
if s.exprname.Type.IsInteger() {
merged := cc[:1]
for _, c := range cc[1:] {
last := &merged[len(merged)-1]
if last.jmp == c.jmp && last.hi.Int64Val()+1 == c.lo.Int64Val() {
last.hi = c.lo
} else {
merged = append(merged, c)
}
}
cc = merged
}
s.search(cc, &s.done)
}
func (s *exprSwitch) search(cc []exprClause, out *Nodes) {
binarySearch(len(cc), out,
func(i int) *Node {
return nod(OLE, s.exprname, cc[i-1].hi)
},
func(i int, nif *Node) {
c := &cc[i]
nif.Left = c.test(s.exprname)
nif.Nbody.Set1(c.jmp)
},
)
}
func (c *exprClause) test(exprname *Node) *Node {
// Integer range.
if c.hi != c.lo {
low := nodl(c.pos, OGE, exprname, c.lo)
high := nodl(c.pos, OLE, exprname, c.hi)
return nodl(c.pos, OANDAND, low, high)
}
// Optimize "switch true { ...}" and "switch false { ... }".
if Isconst(exprname, CTBOOL) && !c.lo.Type.IsInterface() {
if exprname.BoolVal() {
return c.lo
} else {
return nodl(c.pos, ONOT, c.lo, nil)
}
}
return nodl(c.pos, OEQ, exprname, c.lo)
}
func allCaseExprsAreSideEffectFree(sw *Node) bool {
// In theory, we could be more aggressive, allowing any
// side-effect-free expressions in cases, but it's a bit
// tricky because some of that information is unavailable due
// to the introduction of temporaries during order.
// Restricting to constants is simple and probably powerful
// enough.
for _, ncase := range sw.List.Slice() {
if ncase.Op != OCASE {
Fatalf("switch string(byteslice) bad op: %v", ncase.Op)
}
for _, v := range ncase.List.Slice() {
if v.Op != OLITERAL {
return false
}
}
}
return true
}
// hasFall reports whether stmts ends with a "fallthrough" statement.
func hasFall(stmts []*Node) (bool, src.XPos) {
// Search backwards for the index of the fallthrough
// statement. Do not assume it'll be in the last
// position, since in some cases (e.g. when the statement
// list contains autotmp_ variables), one or more OVARKILL
// nodes will be at the end of the list.
i := len(stmts) - 1
for i >= 0 && stmts[i].Op == OVARKILL {
i--
}
if i < 0 {
return false, src.NoXPos
}
return stmts[i].Op == OFALL, stmts[i].Pos
}
// walkTypeSwitch generates an AST that implements sw, where sw is a
// type switch.
func walkTypeSwitch(sw *Node) {
var s typeSwitch
s.facename = sw.Left.Right
sw.Left = nil
s.facename = walkexpr(s.facename, &sw.Ninit)
s.facename = copyexpr(s.facename, s.facename.Type, &sw.Nbody)
s.okname = temp(types.Types[TBOOL])
// Get interface descriptor word.
// For empty interfaces this will be the type.
// For non-empty interfaces this will be the itab.
itab := nod(OITAB, s.facename, nil)
// For empty interfaces, do:
// if e._type == nil {
// do nil case if it exists, otherwise default
// }
// h := e._type.hash
// Use a similar strategy for non-empty interfaces.
ifNil := nod(OIF, nil, nil)
ifNil.Left = nod(OEQ, itab, nodnil())
lineno = lineno.WithNotStmt() // disable statement marks after the first check.
ifNil.Left = typecheck(ifNil.Left, ctxExpr)
ifNil.Left = defaultlit(ifNil.Left, nil)
// ifNil.Nbody assigned at end.
sw.Nbody.Append(ifNil)
// Load hash from type or itab.
dotHash := nodSym(ODOTPTR, itab, nil)
dotHash.Type = types.Types[TUINT32]
dotHash.SetTypecheck(1)
if s.facename.Type.IsEmptyInterface() {
dotHash.Xoffset = int64(2 * Widthptr) // offset of hash in runtime._type
} else {
dotHash.Xoffset = int64(2 * Widthptr) // offset of hash in runtime.itab
}
dotHash.SetBounded(true) // guaranteed not to fault
s.hashname = copyexpr(dotHash, dotHash.Type, &sw.Nbody)
br := nod(OBREAK, nil, nil)
var defaultGoto, nilGoto *Node
var body Nodes
for _, ncase := range sw.List.Slice() {
var caseVar *Node
if ncase.Rlist.Len() != 0 {
caseVar = ncase.Rlist.First()
}
// For single-type cases with an interface type,
// we initialize the case variable as part of the type assertion.
// In other cases, we initialize it in the body.
var singleType *types.Type
if ncase.List.Len() == 1 && ncase.List.First().Op == OTYPE {
singleType = ncase.List.First().Type
}
caseVarInitialized := false
label := autolabel(".s")
jmp := npos(ncase.Pos, nodSym(OGOTO, nil, label))
if ncase.List.Len() == 0 { // default:
if defaultGoto != nil {
Fatalf("duplicate default case not detected during typechecking")
}
defaultGoto = jmp
}
for _, n1 := range ncase.List.Slice() {
if n1.isNil() { // case nil:
if nilGoto != nil {
Fatalf("duplicate nil case not detected during typechecking")
}
nilGoto = jmp
continue
}
if singleType != nil && singleType.IsInterface() {
s.Add(ncase.Pos, n1.Type, caseVar, jmp)
caseVarInitialized = true
} else {
s.Add(ncase.Pos, n1.Type, nil, jmp)
}
}
body.Append(npos(ncase.Pos, nodSym(OLABEL, nil, label)))
if caseVar != nil && !caseVarInitialized {
val := s.facename
if singleType != nil {
// We have a single concrete type. Extract the data.
if singleType.IsInterface() {
Fatalf("singleType interface should have been handled in Add")
}
val = ifaceData(ncase.Pos, s.facename, singleType)
}
l := []*Node{
nodl(ncase.Pos, ODCL, caseVar, nil),
nodl(ncase.Pos, OAS, caseVar, val),
}
typecheckslice(l, ctxStmt)
body.Append(l...)
}
body.Append(ncase.Nbody.Slice()...)
body.Append(br)
}
sw.List.Set(nil)
if defaultGoto == nil {
defaultGoto = br
}
if nilGoto == nil {
nilGoto = defaultGoto
}
ifNil.Nbody.Set1(nilGoto)
s.Emit(&sw.Nbody)
sw.Nbody.Append(defaultGoto)
sw.Nbody.AppendNodes(&body)
walkstmtlist(sw.Nbody.Slice())
}
// A typeSwitch walks a type switch.
type typeSwitch struct {
// Temporary variables (i.e., ONAMEs) used by type switch dispatch logic:
facename *Node // value being type-switched on
hashname *Node // type hash of the value being type-switched on
okname *Node // boolean used for comma-ok type assertions
done Nodes
clauses []typeClause
}
type typeClause struct {
hash uint32
body Nodes
}
func (s *typeSwitch) Add(pos src.XPos, typ *types.Type, caseVar, jmp *Node) {
var body Nodes
if caseVar != nil {
l := []*Node{
nodl(pos, ODCL, caseVar, nil),
nodl(pos, OAS, caseVar, nil),
}
typecheckslice(l, ctxStmt)
body.Append(l...)
} else {
caseVar = nblank
}
// cv, ok = iface.(type)
as := nodl(pos, OAS2, nil, nil)
as.List.Set2(caseVar, s.okname) // cv, ok =
dot := nodl(pos, ODOTTYPE, s.facename, nil)
dot.Type = typ // iface.(type)
as.Rlist.Set1(dot)
as = typecheck(as, ctxStmt)
as = walkexpr(as, &body)
body.Append(as)
// if ok { goto label }
nif := nodl(pos, OIF, nil, nil)
nif.Left = s.okname
nif.Nbody.Set1(jmp)
body.Append(nif)
if !typ.IsInterface() {
s.clauses = append(s.clauses, typeClause{
hash: typehash(typ),
body: body,
})
return
}
s.flush()
s.done.AppendNodes(&body)
}
func (s *typeSwitch) Emit(out *Nodes) {
s.flush()
out.AppendNodes(&s.done)
}
func (s *typeSwitch) flush() {
cc := s.clauses
s.clauses = nil
if len(cc) == 0 {
return
}
sort.Slice(cc, func(i, j int) bool { return cc[i].hash < cc[j].hash })
// Combine adjacent cases with the same hash.
merged := cc[:1]
for _, c := range cc[1:] {
last := &merged[len(merged)-1]
if last.hash == c.hash {
last.body.AppendNodes(&c.body)
} else {
merged = append(merged, c)
}
}
cc = merged
binarySearch(len(cc), &s.done,
func(i int) *Node {
return nod(OLE, s.hashname, nodintconst(int64(cc[i-1].hash)))
},
func(i int, nif *Node) {
// TODO(mdempsky): Omit hash equality check if
// there's only one type.
c := cc[i]
nif.Left = nod(OEQ, s.hashname, nodintconst(int64(c.hash)))
nif.Nbody.AppendNodes(&c.body)
},
)
}
// binarySearch constructs a binary search tree for handling n cases,
// and appends it to out. It's used for efficiently implementing
// switch statements.
//
// less(i) should return a boolean expression. If it evaluates true,
// then cases before i will be tested; otherwise, cases i and later.
//
// base(i, nif) should setup nif (an OIF node) to test case i. In
// particular, it should set nif.Left and nif.Nbody.
func binarySearch(n int, out *Nodes, less func(i int) *Node, base func(i int, nif *Node)) {
const binarySearchMin = 4 // minimum number of cases for binary search
var do func(lo, hi int, out *Nodes)
do = func(lo, hi int, out *Nodes) {
n := hi - lo
if n < binarySearchMin {
for i := lo; i < hi; i++ {
nif := nod(OIF, nil, nil)
base(i, nif)
lineno = lineno.WithNotStmt()
nif.Left = typecheck(nif.Left, ctxExpr)
nif.Left = defaultlit(nif.Left, nil)
out.Append(nif)
out = &nif.Rlist
}
return
}
half := lo + n/2
nif := nod(OIF, nil, nil)
nif.Left = less(half)
lineno = lineno.WithNotStmt()
nif.Left = typecheck(nif.Left, ctxExpr)
nif.Left = defaultlit(nif.Left, nil)
do(lo, half, &nif.Nbody)
do(half, hi, &nif.Rlist)
out.Append(nif)
}
do(0, n, out)
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,8 @@ package gc
import (
"os"
tracepkg "runtime/trace"
"cmd/compile/internal/base"
)
func init() {
@@ -18,10 +20,10 @@ func init() {
func traceHandlerGo17(traceprofile string) {
f, err := os.Create(traceprofile)
if err != nil {
Fatalf("%v", err)
base.Fatalf("%v", err)
}
if err := tracepkg.Start(f); err != nil {
Fatalf("%v", err)
base.Fatalf("%v", err)
}
atExit(tracepkg.Stop)
base.AtExit(tracepkg.Stop)
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,58 +0,0 @@
// Copyright 2009 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 gc
import (
"cmd/compile/internal/types"
)
// convenience constants
const (
Txxx = types.Txxx
TINT8 = types.TINT8
TUINT8 = types.TUINT8
TINT16 = types.TINT16
TUINT16 = types.TUINT16
TINT32 = types.TINT32
TUINT32 = types.TUINT32
TINT64 = types.TINT64
TUINT64 = types.TUINT64
TINT = types.TINT
TUINT = types.TUINT
TUINTPTR = types.TUINTPTR
TCOMPLEX64 = types.TCOMPLEX64
TCOMPLEX128 = types.TCOMPLEX128
TFLOAT32 = types.TFLOAT32
TFLOAT64 = types.TFLOAT64
TBOOL = types.TBOOL
TPTR = types.TPTR
TFUNC = types.TFUNC
TSLICE = types.TSLICE
TARRAY = types.TARRAY
TSTRUCT = types.TSTRUCT
TCHAN = types.TCHAN
TMAP = types.TMAP
TINTER = types.TINTER
TFORW = types.TFORW
TANY = types.TANY
TSTRING = types.TSTRING
TUNSAFEPTR = types.TUNSAFEPTR
// pseudo-types for literals
TIDEAL = types.TIDEAL
TNIL = types.TNIL
TBLANK = types.TBLANK
// pseudo-types for frame layout
TFUNCARGS = types.TFUNCARGS
TCHANARGS = types.TCHANARGS
NTYPE = types.NTYPE
)

View File

@@ -1,16 +0,0 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements convertions between *types.Node and *Node.
// TODO(gri) try to eliminate these soon
package gc
import (
"cmd/compile/internal/types"
"unsafe"
)
func asNode(n *types.Node) *Node { return (*Node)(unsafe.Pointer(n)) }
func asTypesNode(n *Node) *types.Node { return (*types.Node)(unsafe.Pointer(n)) }

View File

@@ -1,453 +0,0 @@
// Copyright 2009 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(gri) This file should probably become part of package types.
package gc
import "cmd/compile/internal/types"
// builtinpkg is a fake package that declares the universe block.
var builtinpkg *types.Pkg
var basicTypes = [...]struct {
name string
etype types.EType
}{
{"int8", TINT8},
{"int16", TINT16},
{"int32", TINT32},
{"int64", TINT64},
{"uint8", TUINT8},
{"uint16", TUINT16},
{"uint32", TUINT32},
{"uint64", TUINT64},
{"float32", TFLOAT32},
{"float64", TFLOAT64},
{"complex64", TCOMPLEX64},
{"complex128", TCOMPLEX128},
{"bool", TBOOL},
{"string", TSTRING},
}
var typedefs = [...]struct {
name string
etype types.EType
sameas32 types.EType
sameas64 types.EType
}{
{"int", TINT, TINT32, TINT64},
{"uint", TUINT, TUINT32, TUINT64},
{"uintptr", TUINTPTR, TUINT32, TUINT64},
}
var builtinFuncs = [...]struct {
name string
op Op
}{
{"append", OAPPEND},
{"cap", OCAP},
{"close", OCLOSE},
{"complex", OCOMPLEX},
{"copy", OCOPY},
{"delete", ODELETE},
{"imag", OIMAG},
{"len", OLEN},
{"make", OMAKE},
{"new", ONEW},
{"panic", OPANIC},
{"print", OPRINT},
{"println", OPRINTN},
{"real", OREAL},
{"recover", ORECOVER},
}
// isBuiltinFuncName reports whether name matches a builtin function
// name.
func isBuiltinFuncName(name string) bool {
for _, fn := range &builtinFuncs {
if fn.name == name {
return true
}
}
return false
}
var unsafeFuncs = [...]struct {
name string
op Op
}{
{"Alignof", OALIGNOF},
{"Offsetof", OOFFSETOF},
{"Sizeof", OSIZEOF},
}
// initUniverse initializes the universe block.
func initUniverse() {
lexinit()
typeinit()
lexinit1()
}
// lexinit initializes known symbols and the basic types.
func lexinit() {
for _, s := range &basicTypes {
etype := s.etype
if int(etype) >= len(types.Types) {
Fatalf("lexinit: %s bad etype", s.name)
}
s2 := builtinpkg.Lookup(s.name)
t := types.Types[etype]
if t == nil {
t = types.New(etype)
t.Sym = s2
if etype != TANY && etype != TSTRING {
dowidth(t)
}
types.Types[etype] = t
}
s2.Def = asTypesNode(typenod(t))
asNode(s2.Def).Name = new(Name)
}
for _, s := range &builtinFuncs {
s2 := builtinpkg.Lookup(s.name)
s2.Def = asTypesNode(newname(s2))
asNode(s2.Def).SetSubOp(s.op)
}
for _, s := range &unsafeFuncs {
s2 := unsafepkg.Lookup(s.name)
s2.Def = asTypesNode(newname(s2))
asNode(s2.Def).SetSubOp(s.op)
}
types.UntypedString = types.New(TSTRING)
types.UntypedBool = types.New(TBOOL)
types.Types[TANY] = types.New(TANY)
s := builtinpkg.Lookup("true")
s.Def = asTypesNode(nodbool(true))
asNode(s.Def).Sym = lookup("true")
asNode(s.Def).Name = new(Name)
asNode(s.Def).Type = types.UntypedBool
s = builtinpkg.Lookup("false")
s.Def = asTypesNode(nodbool(false))
asNode(s.Def).Sym = lookup("false")
asNode(s.Def).Name = new(Name)
asNode(s.Def).Type = types.UntypedBool
s = lookup("_")
s.Block = -100
s.Def = asTypesNode(newname(s))
types.Types[TBLANK] = types.New(TBLANK)
asNode(s.Def).Type = types.Types[TBLANK]
nblank = asNode(s.Def)
s = builtinpkg.Lookup("_")
s.Block = -100
s.Def = asTypesNode(newname(s))
types.Types[TBLANK] = types.New(TBLANK)
asNode(s.Def).Type = types.Types[TBLANK]
types.Types[TNIL] = types.New(TNIL)
s = builtinpkg.Lookup("nil")
var v Val
v.U = new(NilVal)
s.Def = asTypesNode(nodlit(v))
asNode(s.Def).Sym = s
asNode(s.Def).Name = new(Name)
s = builtinpkg.Lookup("iota")
s.Def = asTypesNode(nod(OIOTA, nil, nil))
asNode(s.Def).Sym = s
asNode(s.Def).Name = new(Name)
}
func typeinit() {
if Widthptr == 0 {
Fatalf("typeinit before betypeinit")
}
for et := types.EType(0); et < NTYPE; et++ {
simtype[et] = et
}
types.Types[TPTR] = types.New(TPTR)
dowidth(types.Types[TPTR])
t := types.New(TUNSAFEPTR)
types.Types[TUNSAFEPTR] = t
t.Sym = unsafepkg.Lookup("Pointer")
t.Sym.Def = asTypesNode(typenod(t))
asNode(t.Sym.Def).Name = new(Name)
dowidth(types.Types[TUNSAFEPTR])
for et := TINT8; et <= TUINT64; et++ {
isInt[et] = true
}
isInt[TINT] = true
isInt[TUINT] = true
isInt[TUINTPTR] = true
isFloat[TFLOAT32] = true
isFloat[TFLOAT64] = true
isComplex[TCOMPLEX64] = true
isComplex[TCOMPLEX128] = true
// initialize okfor
for et := types.EType(0); et < NTYPE; et++ {
if isInt[et] || et == TIDEAL {
okforeq[et] = true
okforcmp[et] = true
okforarith[et] = true
okforadd[et] = true
okforand[et] = true
okforconst[et] = true
issimple[et] = true
minintval[et] = new(Mpint)
maxintval[et] = new(Mpint)
}
if isFloat[et] {
okforeq[et] = true
okforcmp[et] = true
okforadd[et] = true
okforarith[et] = true
okforconst[et] = true
issimple[et] = true
minfltval[et] = newMpflt()
maxfltval[et] = newMpflt()
}
if isComplex[et] {
okforeq[et] = true
okforadd[et] = true
okforarith[et] = true
okforconst[et] = true
issimple[et] = true
}
}
issimple[TBOOL] = true
okforadd[TSTRING] = true
okforbool[TBOOL] = true
okforcap[TARRAY] = true
okforcap[TCHAN] = true
okforcap[TSLICE] = true
okforconst[TBOOL] = true
okforconst[TSTRING] = true
okforlen[TARRAY] = true
okforlen[TCHAN] = true
okforlen[TMAP] = true
okforlen[TSLICE] = true
okforlen[TSTRING] = true
okforeq[TPTR] = true
okforeq[TUNSAFEPTR] = true
okforeq[TINTER] = true
okforeq[TCHAN] = true
okforeq[TSTRING] = true
okforeq[TBOOL] = true
okforeq[TMAP] = true // nil only; refined in typecheck
okforeq[TFUNC] = true // nil only; refined in typecheck
okforeq[TSLICE] = true // nil only; refined in typecheck
okforeq[TARRAY] = true // only if element type is comparable; refined in typecheck
okforeq[TSTRUCT] = true // only if all struct fields are comparable; refined in typecheck
okforcmp[TSTRING] = true
var i int
for i = 0; i < len(okfor); i++ {
okfor[i] = okfornone[:]
}
// binary
okfor[OADD] = okforadd[:]
okfor[OAND] = okforand[:]
okfor[OANDAND] = okforbool[:]
okfor[OANDNOT] = okforand[:]
okfor[ODIV] = okforarith[:]
okfor[OEQ] = okforeq[:]
okfor[OGE] = okforcmp[:]
okfor[OGT] = okforcmp[:]
okfor[OLE] = okforcmp[:]
okfor[OLT] = okforcmp[:]
okfor[OMOD] = okforand[:]
okfor[OMUL] = okforarith[:]
okfor[ONE] = okforeq[:]
okfor[OOR] = okforand[:]
okfor[OOROR] = okforbool[:]
okfor[OSUB] = okforarith[:]
okfor[OXOR] = okforand[:]
okfor[OLSH] = okforand[:]
okfor[ORSH] = okforand[:]
// unary
okfor[OBITNOT] = okforand[:]
okfor[ONEG] = okforarith[:]
okfor[ONOT] = okforbool[:]
okfor[OPLUS] = okforarith[:]
// special
okfor[OCAP] = okforcap[:]
okfor[OLEN] = okforlen[:]
// comparison
iscmp[OLT] = true
iscmp[OGT] = true
iscmp[OGE] = true
iscmp[OLE] = true
iscmp[OEQ] = true
iscmp[ONE] = true
maxintval[TINT8].SetString("0x7f")
minintval[TINT8].SetString("-0x80")
maxintval[TINT16].SetString("0x7fff")
minintval[TINT16].SetString("-0x8000")
maxintval[TINT32].SetString("0x7fffffff")
minintval[TINT32].SetString("-0x80000000")
maxintval[TINT64].SetString("0x7fffffffffffffff")
minintval[TINT64].SetString("-0x8000000000000000")
maxintval[TUINT8].SetString("0xff")
maxintval[TUINT16].SetString("0xffff")
maxintval[TUINT32].SetString("0xffffffff")
maxintval[TUINT64].SetString("0xffffffffffffffff")
// f is valid float if min < f < max. (min and max are not themselves valid.)
maxfltval[TFLOAT32].SetString("33554431p103") // 2^24-1 p (127-23) + 1/2 ulp
minfltval[TFLOAT32].SetString("-33554431p103")
maxfltval[TFLOAT64].SetString("18014398509481983p970") // 2^53-1 p (1023-52) + 1/2 ulp
minfltval[TFLOAT64].SetString("-18014398509481983p970")
maxfltval[TCOMPLEX64] = maxfltval[TFLOAT32]
minfltval[TCOMPLEX64] = minfltval[TFLOAT32]
maxfltval[TCOMPLEX128] = maxfltval[TFLOAT64]
minfltval[TCOMPLEX128] = minfltval[TFLOAT64]
types.Types[TINTER] = types.New(TINTER) // empty interface
// simple aliases
simtype[TMAP] = TPTR
simtype[TCHAN] = TPTR
simtype[TFUNC] = TPTR
simtype[TUNSAFEPTR] = TPTR
slicePtrOffset = 0
sliceLenOffset = Rnd(slicePtrOffset+int64(Widthptr), int64(Widthptr))
sliceCapOffset = Rnd(sliceLenOffset+int64(Widthptr), int64(Widthptr))
sizeofSlice = Rnd(sliceCapOffset+int64(Widthptr), int64(Widthptr))
// string is same as slice wo the cap
sizeofString = Rnd(sliceLenOffset+int64(Widthptr), int64(Widthptr))
dowidth(types.Types[TSTRING])
dowidth(types.UntypedString)
}
func makeErrorInterface() *types.Type {
field := types.NewField()
field.Type = types.Types[TSTRING]
f := functypefield(fakeRecvField(), nil, []*types.Field{field})
field = types.NewField()
field.Sym = lookup("Error")
field.Type = f
t := types.New(TINTER)
t.SetInterface([]*types.Field{field})
return t
}
func lexinit1() {
// error type
s := builtinpkg.Lookup("error")
types.Errortype = makeErrorInterface()
types.Errortype.Sym = s
types.Errortype.Orig = makeErrorInterface()
s.Def = asTypesNode(typenod(types.Errortype))
dowidth(types.Errortype)
// We create separate byte and rune types for better error messages
// rather than just creating type alias *types.Sym's for the uint8 and
// int32 types. Hence, (bytetype|runtype).Sym.isAlias() is false.
// TODO(gri) Should we get rid of this special case (at the cost
// of less informative error messages involving bytes and runes)?
// (Alternatively, we could introduce an OTALIAS node representing
// type aliases, albeit at the cost of having to deal with it everywhere).
// byte alias
s = builtinpkg.Lookup("byte")
types.Bytetype = types.New(TUINT8)
types.Bytetype.Sym = s
s.Def = asTypesNode(typenod(types.Bytetype))
asNode(s.Def).Name = new(Name)
dowidth(types.Bytetype)
// rune alias
s = builtinpkg.Lookup("rune")
types.Runetype = types.New(TINT32)
types.Runetype.Sym = s
s.Def = asTypesNode(typenod(types.Runetype))
asNode(s.Def).Name = new(Name)
dowidth(types.Runetype)
// backend-dependent builtin types (e.g. int).
for _, s := range &typedefs {
s1 := builtinpkg.Lookup(s.name)
sameas := s.sameas32
if Widthptr == 8 {
sameas = s.sameas64
}
simtype[s.etype] = sameas
minfltval[s.etype] = minfltval[sameas]
maxfltval[s.etype] = maxfltval[sameas]
minintval[s.etype] = minintval[sameas]
maxintval[s.etype] = maxintval[sameas]
t := types.New(s.etype)
t.Sym = s1
types.Types[s.etype] = t
s1.Def = asTypesNode(typenod(t))
asNode(s1.Def).Name = new(Name)
s1.Origpkg = builtinpkg
dowidth(t)
}
}
// finishUniverse makes the universe block visible within the current package.
func finishUniverse() {
// Operationally, this is similar to a dot import of builtinpkg, except
// that we silently skip symbols that are already declared in the
// package block rather than emitting a redeclared symbol error.
for _, s := range builtinpkg.Syms {
if s.Def == nil {
continue
}
s1 := lookup(s.Name)
if s1.Def != nil {
continue
}
s1.Def = s.Def
s1.Block = s.Block
}
nodfp = newname(lookup(".fp"))
nodfp.Type = types.Types[TINT32]
nodfp.SetClass(PPARAM)
nodfp.Name.SetUsed(true)
}

View File

@@ -1,76 +0,0 @@
// Copyright 2009 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 gc
// evalunsafe evaluates a package unsafe operation and returns the result.
func evalunsafe(n *Node) int64 {
switch n.Op {
case OALIGNOF, OSIZEOF:
n.Left = typecheck(n.Left, ctxExpr)
n.Left = defaultlit(n.Left, nil)
tr := n.Left.Type
if tr == nil {
return 0
}
dowidth(tr)
if n.Op == OALIGNOF {
return int64(tr.Align)
}
return tr.Width
case OOFFSETOF:
// must be a selector.
if n.Left.Op != OXDOT {
yyerror("invalid expression %v", n)
return 0
}
// Remember base of selector to find it back after dot insertion.
// Since r->left may be mutated by typechecking, check it explicitly
// first to track it correctly.
n.Left.Left = typecheck(n.Left.Left, ctxExpr)
base := n.Left.Left
n.Left = typecheck(n.Left, ctxExpr)
if n.Left.Type == nil {
return 0
}
switch n.Left.Op {
case ODOT, ODOTPTR:
break
case OCALLPART:
yyerror("invalid expression %v: argument is a method value", n)
return 0
default:
yyerror("invalid expression %v", n)
return 0
}
// Sum offsets for dots until we reach base.
var v int64
for r := n.Left; r != base; r = r.Left {
switch r.Op {
case ODOTPTR:
// For Offsetof(s.f), s may itself be a pointer,
// but accessing f must not otherwise involve
// indirection via embedded pointer types.
if r.Left != base {
yyerror("invalid expression %v: selector implies indirection of embedded %v", n, r.Left)
return 0
}
fallthrough
case ODOT:
v += r.Xoffset
default:
Dump("unsafenmagic", n.Left)
Fatalf("impossible %#v node after dot insertion", r.Op)
}
}
return v
}
Fatalf("unexpected op %v", n.Op)
return 0
}

View File

@@ -8,59 +8,35 @@ import (
"os"
"runtime"
"runtime/pprof"
"cmd/compile/internal/base"
)
// Line returns n's position as a string. If n has been inlined,
// it uses the outermost position where n has been inlined.
func (n *Node) Line() string {
return linestr(n.Pos)
}
var atExitFuncs []func()
func atExit(f func()) {
atExitFuncs = append(atExitFuncs, f)
}
func Exit(code int) {
for i := len(atExitFuncs) - 1; i >= 0; i-- {
f := atExitFuncs[i]
atExitFuncs = atExitFuncs[:i]
f()
}
os.Exit(code)
}
var (
blockprofile string
cpuprofile string
memprofile string
memprofilerate int64
traceprofile string
traceHandler func(string)
mutexprofile string
)
func startProfile() {
if cpuprofile != "" {
f, err := os.Create(cpuprofile)
if base.Flag.CPUProfile != "" {
f, err := os.Create(base.Flag.CPUProfile)
if err != nil {
Fatalf("%v", err)
base.Fatalf("%v", err)
}
if err := pprof.StartCPUProfile(f); err != nil {
Fatalf("%v", err)
base.Fatalf("%v", err)
}
atExit(pprof.StopCPUProfile)
base.AtExit(pprof.StopCPUProfile)
}
if memprofile != "" {
if base.Flag.MemProfile != "" {
if memprofilerate != 0 {
runtime.MemProfileRate = int(memprofilerate)
}
f, err := os.Create(memprofile)
f, err := os.Create(base.Flag.MemProfile)
if err != nil {
Fatalf("%v", err)
base.Fatalf("%v", err)
}
atExit(func() {
base.AtExit(func() {
// Profile all outstanding allocations.
runtime.GC()
// compilebench parses the memory profile to extract memstats,
@@ -68,36 +44,36 @@ func startProfile() {
// See golang.org/issue/18641 and runtime/pprof/pprof.go:writeHeap.
const writeLegacyFormat = 1
if err := pprof.Lookup("heap").WriteTo(f, writeLegacyFormat); err != nil {
Fatalf("%v", err)
base.Fatalf("%v", err)
}
})
} else {
// Not doing memory profiling; disable it entirely.
runtime.MemProfileRate = 0
}
if blockprofile != "" {
f, err := os.Create(blockprofile)
if base.Flag.BlockProfile != "" {
f, err := os.Create(base.Flag.BlockProfile)
if err != nil {
Fatalf("%v", err)
base.Fatalf("%v", err)
}
runtime.SetBlockProfileRate(1)
atExit(func() {
base.AtExit(func() {
pprof.Lookup("block").WriteTo(f, 0)
f.Close()
})
}
if mutexprofile != "" {
f, err := os.Create(mutexprofile)
if base.Flag.MutexProfile != "" {
f, err := os.Create(base.Flag.MutexProfile)
if err != nil {
Fatalf("%v", err)
base.Fatalf("%v", err)
}
startMutexProfiling()
atExit(func() {
base.AtExit(func() {
pprof.Lookup("mutex").WriteTo(f, 0)
f.Close()
})
}
if traceprofile != "" && traceHandler != nil {
traceHandler(traceprofile)
if base.Flag.TraceProfile != "" && traceHandler != nil {
traceHandler(base.Flag.TraceProfile)
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gc
package ir
type bitset8 uint8
@@ -14,6 +14,18 @@ func (f *bitset8) set(mask uint8, b bool) {
}
}
func (f bitset8) get2(shift uint8) uint8 {
return uint8(f>>shift) & 3
}
// set2 sets two bits in f using the bottom two bits of b.
func (f *bitset8) set2(shift uint8, b uint8) {
// Clear old bits.
*(*uint8)(f) &^= 3 << shift
// Set new bits.
*(*uint8)(f) |= uint8(b&3) << shift
}
type bitset16 uint16
func (f *bitset16) set(mask uint16, b bool) {

View File

@@ -0,0 +1,26 @@
// Copyright 2009 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 ir
var (
// maximum size variable which we will allocate on the stack.
// This limit is for explicit variable declarations like "var x T" or "x := ...".
// Note: the flag smallframes can update this value.
MaxStackVarSize = int64(10 * 1024 * 1024)
// maximum size of implicit variables that we will allocate on the stack.
// p := new(T) allocating T on the stack
// p := &T{} allocating T on the stack
// s := make([]T, n) allocating [n]T on the stack
// s := []byte("...") allocating [n]byte on the stack
// Note: the flag smallframes can update this value.
MaxImplicitStackVarSize = int64(64 * 1024)
// MaxSmallArraySize is the maximum size of an array which is considered small.
// Small arrays will be initialized directly with a sequence of constant stores.
// Large arrays will be initialized by copying from a static temp.
// 256 bytes was chosen to minimize generated code + statictmp size.
MaxSmallArraySize = int64(256)
)

View File

@@ -1,6 +1,6 @@
// Code generated by "stringer -type=Class"; DO NOT EDIT.
// Code generated by "stringer -type=Class name.go"; DO NOT EDIT.
package gc
package ir
import "strconv"

View File

@@ -0,0 +1,99 @@
// Copyright 2009 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 ir
import (
"go/constant"
"math"
"math/big"
"cmd/compile/internal/base"
"cmd/compile/internal/types"
)
func NewBool(b bool) Node {
return NewLiteral(constant.MakeBool(b))
}
func NewInt(v int64) Node {
return NewLiteral(constant.MakeInt64(v))
}
func NewString(s string) Node {
return NewLiteral(constant.MakeString(s))
}
const (
// Maximum size in bits for big.Ints before signalling
// overflow and also mantissa precision for big.Floats.
ConstPrec = 512
)
func BigFloat(v constant.Value) *big.Float {
f := new(big.Float)
f.SetPrec(ConstPrec)
switch u := constant.Val(v).(type) {
case int64:
f.SetInt64(u)
case *big.Int:
f.SetInt(u)
case *big.Float:
f.Set(u)
case *big.Rat:
f.SetRat(u)
default:
base.Fatalf("unexpected: %v", u)
}
return f
}
// ConstOverflow reports whether constant value v is too large
// to represent with type t.
func ConstOverflow(v constant.Value, t *types.Type) bool {
switch {
case t.IsInteger():
bits := uint(8 * t.Size())
if t.IsUnsigned() {
x, ok := constant.Uint64Val(v)
return !ok || x>>bits != 0
}
x, ok := constant.Int64Val(v)
if x < 0 {
x = ^x
}
return !ok || x>>(bits-1) != 0
case t.IsFloat():
switch t.Size() {
case 4:
f, _ := constant.Float32Val(v)
return math.IsInf(float64(f), 0)
case 8:
f, _ := constant.Float64Val(v)
return math.IsInf(f, 0)
}
case t.IsComplex():
ft := types.FloatForComplex(t)
return ConstOverflow(constant.Real(v), ft) || ConstOverflow(constant.Imag(v), ft)
}
base.Fatalf("ConstOverflow: %v, %v", v, t)
panic("unreachable")
}
// IsConstNode reports whether n is a Go language constant (as opposed to a
// compile-time constant).
//
// Expressions derived from nil, like string([]byte(nil)), while they
// may be known at compile time, are not Go language constants.
func IsConstNode(n Node) bool {
return n.Op() == OLITERAL
}
func IsSmallIntConst(n Node) bool {
if n.Op() == OLITERAL {
v, ok := constant.Int64Val(n.Val())
return ok && int64(int32(v)) == v
}
return false
}

View File

@@ -0,0 +1,102 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ir
import (
"cmd/compile/internal/base"
"cmd/internal/src"
)
// A Node may implement the Orig and SetOrig method to
// maintain a pointer to the "unrewritten" form of a Node.
// If a Node does not implement OrigNode, it is its own Orig.
//
// Note that both SepCopy and Copy have definitions compatible
// with a Node that does not implement OrigNode: such a Node
// is its own Orig, and in that case, that's what both want to return
// anyway (SepCopy unconditionally, and Copy only when the input
// is its own Orig as well, but if the output does not implement
// OrigNode, then neither does the input, making the condition true).
type OrigNode interface {
Node
Orig() Node
SetOrig(Node)
}
// origNode may be embedded into a Node to make it implement OrigNode.
type origNode struct {
orig Node `mknode:"-"`
}
func (n *origNode) Orig() Node { return n.orig }
func (n *origNode) SetOrig(o Node) { n.orig = o }
// Orig returns the “original” node for n.
// If n implements OrigNode, Orig returns n.Orig().
// Otherwise Orig returns n itself.
func Orig(n Node) Node {
if n, ok := n.(OrigNode); ok {
o := n.Orig()
if o == nil {
Dump("Orig nil", n)
base.Fatalf("Orig returned nil")
}
return o
}
return n
}
// SepCopy returns a separate shallow copy of n,
// breaking any Orig link to any other nodes.
func SepCopy(n Node) Node {
n = n.copy()
if n, ok := n.(OrigNode); ok {
n.SetOrig(n)
}
return n
}
// Copy returns a shallow copy of n.
// If Orig(n) == n, then Orig(Copy(n)) == the copy.
// Otherwise the Orig link is preserved as well.
//
// The specific semantics surrounding Orig are subtle but right for most uses.
// See issues #26855 and #27765 for pitfalls.
func Copy(n Node) Node {
c := n.copy()
if n, ok := n.(OrigNode); ok && n.Orig() == n {
c.(OrigNode).SetOrig(c)
}
return c
}
// DeepCopy returns a “deep” copy of n, with its entire structure copied
// (except for shared nodes like ONAME, ONONAME, OLITERAL, and OTYPE).
// If pos.IsKnown(), it sets the source position of newly allocated Nodes to pos.
func DeepCopy(pos src.XPos, n Node) Node {
var edit func(Node) Node
edit = func(x Node) Node {
switch x.Op() {
case OPACK, ONAME, ONONAME, OLITERAL, ONIL, OTYPE:
return x
}
x = Copy(x)
if pos.IsKnown() {
x.SetPos(pos)
}
EditChildren(x, edit)
return x
}
return edit(n)
}
// DeepCopyList returns a list of deep copies (using DeepCopy) of the nodes in list.
func DeepCopyList(pos src.XPos, list []Node) []Node {
var out []Node
for _, n := range list {
out = append(out, DeepCopy(pos, n))
}
return out
}

View File

@@ -6,21 +6,23 @@
// for debugging purposes. The code is customized for Node graphs
// and may be used for an alternative view of the node structure.
package gc
package ir
import (
"cmd/compile/internal/types"
"cmd/internal/src"
"fmt"
"io"
"os"
"reflect"
"regexp"
"cmd/compile/internal/base"
"cmd/compile/internal/types"
"cmd/internal/src"
)
// dump is like fdump but prints to stderr.
func dump(root interface{}, filter string, depth int) {
fdump(os.Stderr, root, filter, depth)
func DumpAny(root interface{}, filter string, depth int) {
FDumpAny(os.Stderr, root, filter, depth)
}
// fdump prints the structure of a rooted data structure
@@ -40,7 +42,7 @@ func dump(root interface{}, filter string, depth int) {
// rather than their type; struct fields with zero values or
// non-matching field names are omitted, and "…" means recursion
// depth has been reached or struct fields have been omitted.
func fdump(w io.Writer, root interface{}, filter string, depth int) {
func FDumpAny(w io.Writer, root interface{}, filter string, depth int) {
if root == nil {
fmt.Fprintln(w, "nil")
return
@@ -138,19 +140,9 @@ func (p *dumper) dump(x reflect.Value, depth int) {
return
}
// special cases
switch v := x.Interface().(type) {
case Nodes:
// unpack Nodes since reflect cannot look inside
// due to the unexported field in its struct
x = reflect.ValueOf(v.Slice())
case src.XPos:
p.printf("%s", linestr(v))
if pos, ok := x.Interface().(src.XPos); ok {
p.printf("%s", base.FmtPos(pos))
return
case *types.Node:
x = reflect.ValueOf(asNode(v))
}
switch x.Kind() {
@@ -203,7 +195,7 @@ func (p *dumper) dump(x reflect.Value, depth int) {
isNode := false
if n, ok := x.Interface().(Node); ok {
isNode = true
p.printf("%s %s {", n.Op.String(), p.addr(x))
p.printf("%s %s {", n.Op().String(), p.addr(x))
} else {
p.printf("%s {", typ)
}
@@ -230,7 +222,7 @@ func (p *dumper) dump(x reflect.Value, depth int) {
omitted = true
continue // exclude zero-valued fields
}
if n, ok := x.Interface().(Nodes); ok && n.Len() == 0 {
if n, ok := x.Interface().(Nodes); ok && len(n) == 0 {
omitted = true
continue // exclude empty Nodes slices
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,284 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ir
import (
"cmd/compile/internal/base"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/src"
)
// A Func corresponds to a single function in a Go program
// (and vice versa: each function is denoted by exactly one *Func).
//
// There are multiple nodes that represent a Func in the IR.
//
// The ONAME node (Func.Nname) is used for plain references to it.
// The ODCLFUNC node (the Func itself) is used for its declaration code.
// The OCLOSURE node (Func.OClosure) is used for a reference to a
// function literal.
//
// An imported function will have an ONAME node which points to a Func
// with an empty body.
// A declared function or method has an ODCLFUNC (the Func itself) and an ONAME.
// A function literal is represented directly by an OCLOSURE, but it also
// has an ODCLFUNC (and a matching ONAME) representing the compiled
// underlying form of the closure, which accesses the captured variables
// using a special data structure passed in a register.
//
// A method declaration is represented like functions, except f.Sym
// will be the qualified method name (e.g., "T.m") and
// f.Func.Shortname is the bare method name (e.g., "m").
//
// A method expression (T.M) is represented as an OMETHEXPR node,
// in which n.Left and n.Right point to the type and method, respectively.
// Each distinct mention of a method expression in the source code
// constructs a fresh node.
//
// A method value (t.M) is represented by ODOTMETH/ODOTINTER
// when it is called directly and by OCALLPART otherwise.
// These are like method expressions, except that for ODOTMETH/ODOTINTER,
// the method name is stored in Sym instead of Right.
// Each OCALLPART ends up being implemented as a new
// function, a bit like a closure, with its own ODCLFUNC.
// The OCALLPART uses n.Func to record the linkage to
// the generated ODCLFUNC, but there is no
// pointer from the Func back to the OCALLPART.
type Func struct {
miniNode
Body Nodes
Iota int64
Nname *Name // ONAME node
OClosure *ClosureExpr // OCLOSURE node
Shortname *types.Sym
// Extra entry code for the function. For example, allocate and initialize
// memory for escaping parameters.
Enter Nodes
Exit Nodes
// ONAME nodes for all params/locals for this func/closure, does NOT
// include closurevars until transforming closures during walk.
// Names must be listed PPARAMs, PPARAMOUTs, then PAUTOs,
// with PPARAMs and PPARAMOUTs in order corresponding to the function signature.
// However, as anonymous or blank PPARAMs are not actually declared,
// they are omitted from Dcl.
// Anonymous and blank PPARAMOUTs are declared as ~rNN and ~bNN Names, respectively.
Dcl []*Name
// ClosureVars lists the free variables that are used within a
// function literal, but formally declared in an enclosing
// function. The variables in this slice are the closure function's
// own copy of the variables, which are used within its function
// body. They will also each have IsClosureVar set, and will have
// Byval set if they're captured by value.
ClosureVars []*Name
// Enclosed functions that need to be compiled.
// Populated during walk.
Closures []*Func
// Parents records the parent scope of each scope within a
// function. The root scope (0) has no parent, so the i'th
// scope's parent is stored at Parents[i-1].
Parents []ScopeID
// Marks records scope boundary changes.
Marks []Mark
FieldTrack map[*obj.LSym]struct{}
DebugInfo interface{}
LSym *obj.LSym
Inl *Inline
// Closgen tracks how many closures have been generated within
// this function. Used by closurename for creating unique
// function names.
Closgen int32
Label int32 // largest auto-generated label in this function
Endlineno src.XPos
WBPos src.XPos // position of first write barrier; see SetWBPos
Pragma PragmaFlag // go:xxx function annotations
flags bitset16
NumDefers int32 // number of defer calls in the function
NumReturns int32 // number of explicit returns in the function
// nwbrCalls records the LSyms of functions called by this
// function for go:nowritebarrierrec analysis. Only filled in
// if nowritebarrierrecCheck != nil.
NWBRCalls *[]SymAndPos
}
func NewFunc(pos src.XPos) *Func {
f := new(Func)
f.pos = pos
f.op = ODCLFUNC
f.Iota = -1
return f
}
func (f *Func) isStmt() {}
func (n *Func) copy() Node { panic(n.no("copy")) }
func (n *Func) doChildren(do func(Node) bool) bool { return doNodes(n.Body, do) }
func (n *Func) editChildren(edit func(Node) Node) { editNodes(n.Body, edit) }
func (f *Func) Type() *types.Type { return f.Nname.Type() }
func (f *Func) Sym() *types.Sym { return f.Nname.Sym() }
func (f *Func) Linksym() *obj.LSym { return f.Nname.Linksym() }
func (f *Func) LinksymABI(abi obj.ABI) *obj.LSym { return f.Nname.LinksymABI(abi) }
// An Inline holds fields used for function bodies that can be inlined.
type Inline struct {
Cost int32 // heuristic cost of inlining this function
// Copies of Func.Dcl and Nbody for use during inlining.
Dcl []*Name
Body []Node
}
// A Mark represents a scope boundary.
type Mark struct {
// Pos is the position of the token that marks the scope
// change.
Pos src.XPos
// Scope identifies the innermost scope to the right of Pos.
Scope ScopeID
}
// A ScopeID represents a lexical scope within a function.
type ScopeID int32
const (
funcDupok = 1 << iota // duplicate definitions ok
funcWrapper // is method wrapper
funcNeedctxt // function uses context register (has closure variables)
funcReflectMethod // function calls reflect.Type.Method or MethodByName
// true if closure inside a function; false if a simple function or a
// closure in a global variable initialization
funcIsHiddenClosure
funcHasDefer // contains a defer statement
funcNilCheckDisabled // disable nil checks when compiling this function
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
funcClosureCalled // closure is only immediately called
)
type SymAndPos struct {
Sym *obj.LSym // LSym of callee
Pos src.XPos // line of call
}
func (f *Func) Dupok() bool { return f.flags&funcDupok != 0 }
func (f *Func) Wrapper() bool { return f.flags&funcWrapper != 0 }
func (f *Func) Needctxt() bool { return f.flags&funcNeedctxt != 0 }
func (f *Func) ReflectMethod() bool { return f.flags&funcReflectMethod != 0 }
func (f *Func) IsHiddenClosure() bool { return f.flags&funcIsHiddenClosure != 0 }
func (f *Func) HasDefer() bool { return f.flags&funcHasDefer != 0 }
func (f *Func) NilCheckDisabled() bool { return f.flags&funcNilCheckDisabled != 0 }
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) ClosureCalled() bool { return f.flags&funcClosureCalled != 0 }
func (f *Func) SetDupok(b bool) { f.flags.set(funcDupok, b) }
func (f *Func) SetWrapper(b bool) { f.flags.set(funcWrapper, b) }
func (f *Func) SetNeedctxt(b bool) { f.flags.set(funcNeedctxt, b) }
func (f *Func) SetReflectMethod(b bool) { f.flags.set(funcReflectMethod, b) }
func (f *Func) SetIsHiddenClosure(b bool) { f.flags.set(funcIsHiddenClosure, b) }
func (f *Func) SetHasDefer(b bool) { f.flags.set(funcHasDefer, b) }
func (f *Func) SetNilCheckDisabled(b bool) { f.flags.set(funcNilCheckDisabled, b) }
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) SetClosureCalled(b bool) { f.flags.set(funcClosureCalled, b) }
func (f *Func) SetWBPos(pos src.XPos) {
if base.Debug.WB != 0 {
base.WarnfAt(pos, "write barrier")
}
if !f.WBPos.IsKnown() {
f.WBPos = pos
}
}
// funcname returns the name (without the package) of the function n.
func FuncName(f *Func) string {
if f == nil || f.Nname == nil {
return "<nil>"
}
return f.Sym().Name
}
// pkgFuncName returns the name of the function referenced by n, with package prepended.
// This differs from the compiler's internal convention where local functions lack a package
// because the ultimate consumer of this is a human looking at an IDE; package is only empty
// if the compilation package is actually the empty string.
func PkgFuncName(f *Func) string {
if f == nil || f.Nname == nil {
return "<nil>"
}
s := f.Sym()
pkg := s.Pkg
p := base.Ctxt.Pkgpath
if pkg != nil && pkg.Path != "" {
p = pkg.Path
}
if p == "" {
return s.Name
}
return p + "." + s.Name
}
var CurFunc *Func
func FuncSymName(s *types.Sym) string {
return s.Name + "·f"
}
// MarkFunc marks a node as a function.
func MarkFunc(n *Name) {
if n.Op() != ONAME || n.Class != Pxxx {
base.Fatalf("expected ONAME/Pxxx node, got %v", n)
}
n.Class = PFUNC
n.Sym().SetFunc(true)
}
// ClosureDebugRuntimeCheck applies boilerplate checks for debug flags
// and compiling runtime
func ClosureDebugRuntimeCheck(clo *ClosureExpr) {
if base.Debug.Closure > 0 {
if clo.Esc() == EscHeap {
base.WarnfAt(clo.Pos(), "heap closure, captured vars = %v", clo.Func.ClosureVars)
} else {
base.WarnfAt(clo.Pos(), "stack closure, captured vars = %v", clo.Func.ClosureVars)
}
}
if base.Flag.CompilingRuntime && clo.Esc() == EscHeap {
base.ErrorfAt(clo.Pos(), "heap-allocated closure, not allowed in runtime")
}
}
// IsTrivialClosure reports whether closure clo has an
// empty list of captured vars.
func IsTrivialClosure(clo *ClosureExpr) bool {
return len(clo.Func.ClosureVars) == 0
}

View File

@@ -0,0 +1,5 @@
// Copyright 2009 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 ir

View File

@@ -0,0 +1,92 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate go run -mod=mod mknode.go
package ir
import (
"cmd/compile/internal/types"
"cmd/internal/src"
"fmt"
"go/constant"
)
// A miniNode is a minimal node implementation,
// meant to be embedded as the first field in a larger node implementation,
// at a cost of 8 bytes.
//
// A miniNode is NOT a valid Node by itself: the embedding struct
// must at the least provide:
//
// func (n *MyNode) String() string { return fmt.Sprint(n) }
// func (n *MyNode) rawCopy() Node { c := *n; return &c }
// func (n *MyNode) Format(s fmt.State, verb rune) { FmtNode(n, s, verb) }
//
// The embedding struct should also fill in n.op in its constructor,
// for more useful panic messages when invalid methods are called,
// instead of implementing Op itself.
//
type miniNode struct {
pos src.XPos // uint32
op Op // uint8
bits bitset8
esc uint16
}
// posOr returns pos if known, or else n.pos.
// For use in DeepCopy.
func (n *miniNode) posOr(pos src.XPos) src.XPos {
if pos.IsKnown() {
return pos
}
return n.pos
}
// op can be read, but not written.
// An embedding implementation can provide a SetOp if desired.
// (The panicking SetOp is with the other panics below.)
func (n *miniNode) Op() Op { return n.op }
func (n *miniNode) Pos() src.XPos { return n.pos }
func (n *miniNode) SetPos(x src.XPos) { n.pos = x }
func (n *miniNode) Esc() uint16 { return n.esc }
func (n *miniNode) SetEsc(x uint16) { n.esc = x }
const (
miniWalkdefShift = 0 // TODO(mdempsky): Move to Name.flags.
miniTypecheckShift = 2
miniDiag = 1 << 4
miniWalked = 1 << 5 // to prevent/catch re-walking
)
func (n *miniNode) Typecheck() uint8 { return n.bits.get2(miniTypecheckShift) }
func (n *miniNode) SetTypecheck(x uint8) {
if x > 3 {
panic(fmt.Sprintf("cannot SetTypecheck %d", x))
}
n.bits.set2(miniTypecheckShift, x)
}
func (n *miniNode) Diag() bool { return n.bits&miniDiag != 0 }
func (n *miniNode) SetDiag(x bool) { n.bits.set(miniDiag, x) }
func (n *miniNode) Walked() bool { return n.bits&miniWalked != 0 }
func (n *miniNode) SetWalked(x bool) { n.bits.set(miniWalked, x) }
// Empty, immutable graph structure.
func (n *miniNode) Init() Nodes { return Nodes{} }
// Additional functionality unavailable.
func (n *miniNode) no(name string) string { return "cannot " + name + " on " + n.op.String() }
func (n *miniNode) Type() *types.Type { return nil }
func (n *miniNode) SetType(*types.Type) { panic(n.no("SetType")) }
func (n *miniNode) Name() *Name { return nil }
func (n *miniNode) Sym() *types.Sym { return nil }
func (n *miniNode) Val() constant.Value { panic(n.no("Val")) }
func (n *miniNode) SetVal(v constant.Value) { panic(n.no("SetVal")) }
func (n *miniNode) NonNil() bool { return false }
func (n *miniNode) MarkNonNil() { panic(n.no("MarkNonNil")) }

View File

@@ -0,0 +1,228 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
package main
import (
"bytes"
"fmt"
"go/format"
"go/types"
"io/ioutil"
"log"
"reflect"
"sort"
"strings"
"golang.org/x/tools/go/packages"
)
var irPkg *types.Package
var buf bytes.Buffer
func main() {
cfg := &packages.Config{
Mode: packages.NeedSyntax | packages.NeedTypes,
}
pkgs, err := packages.Load(cfg, "cmd/compile/internal/ir")
if err != nil {
log.Fatal(err)
}
irPkg = pkgs[0].Types
fmt.Fprintln(&buf, "// Code generated by mknode.go. DO NOT EDIT.")
fmt.Fprintln(&buf)
fmt.Fprintln(&buf, "package ir")
fmt.Fprintln(&buf)
fmt.Fprintln(&buf, `import "fmt"`)
scope := irPkg.Scope()
for _, name := range scope.Names() {
if strings.HasPrefix(name, "mini") {
continue
}
obj, ok := scope.Lookup(name).(*types.TypeName)
if !ok {
continue
}
typ := obj.Type().(*types.Named)
if !implementsNode(types.NewPointer(typ)) {
continue
}
fmt.Fprintf(&buf, "\n")
fmt.Fprintf(&buf, "func (n *%s) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }\n", name)
switch name {
case "Name", "Func":
// Too specialized to automate.
continue
}
forNodeFields(typ,
"func (n *%[1]s) copy() Node { c := *n\n",
"",
"c.%[1]s = copy%[2]s(c.%[1]s)",
"return &c }\n")
forNodeFields(typ,
"func (n *%[1]s) doChildren(do func(Node) bool) bool {\n",
"if n.%[1]s != nil && do(n.%[1]s) { return true }",
"if do%[2]s(n.%[1]s, do) { return true }",
"return false }\n")
forNodeFields(typ,
"func (n *%[1]s) editChildren(edit func(Node) Node) {\n",
"if n.%[1]s != nil { n.%[1]s = edit(n.%[1]s).(%[2]s) }",
"edit%[2]s(n.%[1]s, edit)",
"}\n")
}
makeHelpers()
out, err := format.Source(buf.Bytes())
if err != nil {
// write out mangled source so we can see the bug.
out = buf.Bytes()
}
err = ioutil.WriteFile("node_gen.go", out, 0666)
if err != nil {
log.Fatal(err)
}
}
// needHelper maps needed slice helpers from their base name to their
// respective slice-element type.
var needHelper = map[string]string{}
func makeHelpers() {
var names []string
for name := range needHelper {
names = append(names, name)
}
sort.Strings(names)
for _, name := range names {
fmt.Fprintf(&buf, sliceHelperTmpl, name, needHelper[name])
}
}
const sliceHelperTmpl = `
func copy%[1]s(list []%[2]s) []%[2]s {
if list == nil {
return nil
}
c := make([]%[2]s, len(list))
copy(c, list)
return c
}
func do%[1]s(list []%[2]s, do func(Node) bool) bool {
for _, x := range list {
if x != nil && do(x) {
return true
}
}
return false
}
func edit%[1]s(list []%[2]s, edit func(Node) Node) {
for i, x := range list {
if x != nil {
list[i] = edit(x).(%[2]s)
}
}
}
`
func forNodeFields(named *types.Named, prologue, singleTmpl, sliceTmpl, epilogue string) {
fmt.Fprintf(&buf, prologue, named.Obj().Name())
anyField(named.Underlying().(*types.Struct), func(f *types.Var) bool {
if f.Embedded() {
return false
}
name, typ := f.Name(), f.Type()
slice, _ := typ.Underlying().(*types.Slice)
if slice != nil {
typ = slice.Elem()
}
tmpl, what := singleTmpl, types.TypeString(typ, types.RelativeTo(irPkg))
if implementsNode(typ) {
if slice != nil {
helper := strings.TrimPrefix(what, "*") + "s"
needHelper[helper] = what
tmpl, what = sliceTmpl, helper
}
} else if what == "*Field" {
// Special case for *Field.
tmpl = sliceTmpl
if slice != nil {
what = "Fields"
} else {
what = "Field"
}
} else {
return false
}
if tmpl == "" {
return false
}
// Allow template to not use all arguments without
// upsetting fmt.Printf.
s := fmt.Sprintf(tmpl+"\x00 %[1]s %[2]s", name, what)
fmt.Fprintln(&buf, s[:strings.LastIndex(s, "\x00")])
return false
})
fmt.Fprintf(&buf, epilogue)
}
func implementsNode(typ types.Type) bool {
if _, ok := typ.Underlying().(*types.Interface); ok {
// TODO(mdempsky): Check the interface implements Node.
// Worst case, node_gen.go will fail to compile if we're wrong.
return true
}
if ptr, ok := typ.(*types.Pointer); ok {
if str, ok := ptr.Elem().Underlying().(*types.Struct); ok {
return anyField(str, func(f *types.Var) bool {
return f.Embedded() && f.Name() == "miniNode"
})
}
}
return false
}
func anyField(typ *types.Struct, pred func(f *types.Var) bool) bool {
for i, n := 0, typ.NumFields(); i < n; i++ {
if value, ok := reflect.StructTag(typ.Tag(i)).Lookup("mknode"); ok {
if value != "-" {
panic(fmt.Sprintf("unexpected tag value: %q", value))
}
continue
}
f := typ.Field(i)
if pred(f) {
return true
}
if f.Embedded() {
if typ, ok := f.Type().Underlying().(*types.Struct); ok {
if anyField(typ, pred) {
return true
}
}
}
}
return false
}

View File

@@ -0,0 +1,512 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ir
import (
"cmd/compile/internal/base"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/src"
"fmt"
"go/constant"
)
// An Ident is an identifier, possibly qualified.
type Ident struct {
miniExpr
sym *types.Sym
}
func NewIdent(pos src.XPos, sym *types.Sym) *Ident {
n := new(Ident)
n.op = ONONAME
n.pos = pos
n.sym = sym
return n
}
func (n *Ident) Sym() *types.Sym { return n.sym }
func (*Ident) CanBeNtype() {}
// Name holds Node fields used only by named nodes (ONAME, OTYPE, some OLITERAL).
type Name struct {
miniExpr
BuiltinOp Op // uint8
Class Class // uint8
pragma PragmaFlag // int16
flags bitset16
sym *types.Sym
Func *Func
Offset_ int64
val constant.Value
Opt interface{} // for use by escape analysis
Embed *[]Embed // list of embedded files, for ONAME var
PkgName *PkgName // real package for import . names
// For a local variable (not param) or extern, the initializing assignment (OAS or OAS2).
// For a closure var, the ONAME node of the outer captured variable
Defn Node
// The function, method, or closure in which local variable or param is declared.
Curfn *Func
Ntype Ntype
Heapaddr *Name // temp holding heap address of param
// ONAME closure linkage
// Consider:
//
// func f() {
// x := 1 // x1
// func() {
// use(x) // x2
// func() {
// use(x) // x3
// --- parser is here ---
// }()
// }()
// }
//
// There is an original declaration of x and then a chain of mentions of x
// leading into the current function. Each time x is mentioned in a new closure,
// we create a variable representing x for use in that specific closure,
// since the way you get to x is different in each closure.
//
// Let's number the specific variables as shown in the code:
// x1 is the original x, x2 is when mentioned in the closure,
// and x3 is when mentioned in the closure in the closure.
//
// We keep these linked (assume N > 1):
//
// - x1.Defn = original declaration statement for x (like most variables)
// - x1.Innermost = current innermost closure x (in this case x3), or nil for none
// - x1.IsClosureVar() = false
//
// - xN.Defn = x1, N > 1
// - xN.IsClosureVar() = true, N > 1
// - x2.Outer = nil
// - xN.Outer = x(N-1), N > 2
//
//
// When we look up x in the symbol table, we always get x1.
// Then we can use x1.Innermost (if not nil) to get the x
// for the innermost known closure function,
// but the first reference in a closure will find either no x1.Innermost
// or an x1.Innermost with .Funcdepth < Funcdepth.
// In that case, a new xN must be created, linked in with:
//
// xN.Defn = x1
// xN.Outer = x1.Innermost
// x1.Innermost = xN
//
// When we finish the function, we'll process its closure variables
// and find xN and pop it off the list using:
//
// x1 := xN.Defn
// x1.Innermost = xN.Outer
//
// We leave x1.Innermost set so that we can still get to the original
// variable quickly. Not shown here, but once we're
// done parsing a function and no longer need xN.Outer for the
// lexical x reference links as described above, funcLit
// recomputes xN.Outer as the semantic x reference link tree,
// even filling in x in intermediate closures that might not
// have mentioned it along the way to inner closures that did.
// See funcLit for details.
//
// During the eventual compilation, then, for closure variables we have:
//
// xN.Defn = original variable
// xN.Outer = variable captured in next outward scope
// to make closure where xN appears
//
// Because of the sharding of pieces of the node, x.Defn means x.Name.Defn
// and x.Innermost/Outer means x.Name.Param.Innermost/Outer.
Innermost *Name
Outer *Name
}
func (n *Name) isExpr() {}
func (n *Name) copy() Node { panic(n.no("copy")) }
func (n *Name) doChildren(do func(Node) bool) bool { return false }
func (n *Name) editChildren(edit func(Node) Node) {}
// TypeDefn returns the type definition for a named OTYPE.
// That is, given "type T Defn", it returns Defn.
// It is used by package types.
func (n *Name) TypeDefn() *types.Type {
return n.Ntype.Type()
}
// RecordFrameOffset records the frame offset for the name.
// It is used by package types when laying out function arguments.
func (n *Name) RecordFrameOffset(offset int64) {
n.SetFrameOffset(offset)
}
// NewNameAt returns a new ONAME Node associated with symbol s at position pos.
// The caller is responsible for setting Curfn.
func NewNameAt(pos src.XPos, sym *types.Sym) *Name {
if sym == nil {
base.Fatalf("NewNameAt nil")
}
return newNameAt(pos, ONAME, sym)
}
// NewIota returns a new OIOTA Node.
func NewIota(pos src.XPos, sym *types.Sym) *Name {
if sym == nil {
base.Fatalf("NewIota nil")
}
return newNameAt(pos, OIOTA, sym)
}
// NewDeclNameAt returns a new Name associated with symbol s at position pos.
// The caller is responsible for setting Curfn.
func NewDeclNameAt(pos src.XPos, op Op, sym *types.Sym) *Name {
if sym == nil {
base.Fatalf("NewDeclNameAt nil")
}
switch op {
case ONAME, OTYPE, OLITERAL:
// ok
default:
base.Fatalf("NewDeclNameAt op %v", op)
}
return newNameAt(pos, op, sym)
}
// NewConstAt returns a new OLITERAL Node associated with symbol s at position pos.
func NewConstAt(pos src.XPos, sym *types.Sym, typ *types.Type, val constant.Value) *Name {
if sym == nil {
base.Fatalf("NewConstAt nil")
}
n := newNameAt(pos, OLITERAL, sym)
n.SetType(typ)
n.SetVal(val)
return n
}
// newNameAt is like NewNameAt but allows sym == nil.
func newNameAt(pos src.XPos, op Op, sym *types.Sym) *Name {
n := new(Name)
n.op = op
n.pos = pos
n.sym = sym
return n
}
func (n *Name) Name() *Name { return n }
func (n *Name) Sym() *types.Sym { return n.sym }
func (n *Name) SetSym(x *types.Sym) { n.sym = x }
func (n *Name) SubOp() Op { return n.BuiltinOp }
func (n *Name) SetSubOp(x Op) { n.BuiltinOp = x }
func (n *Name) SetFunc(x *Func) { n.Func = x }
func (n *Name) Offset() int64 { panic("Name.Offset") }
func (n *Name) SetOffset(x int64) {
if x != 0 {
panic("Name.SetOffset")
}
}
func (n *Name) FrameOffset() int64 { return n.Offset_ }
func (n *Name) SetFrameOffset(x int64) { n.Offset_ = x }
func (n *Name) Iota() int64 { return n.Offset_ }
func (n *Name) SetIota(x int64) { n.Offset_ = x }
func (n *Name) Walkdef() uint8 { return n.bits.get2(miniWalkdefShift) }
func (n *Name) SetWalkdef(x uint8) {
if x > 3 {
panic(fmt.Sprintf("cannot SetWalkdef %d", x))
}
n.bits.set2(miniWalkdefShift, x)
}
func (n *Name) Linksym() *obj.LSym { return n.sym.Linksym() }
func (n *Name) LinksymABI(abi obj.ABI) *obj.LSym { return n.sym.LinksymABI(abi) }
func (*Name) CanBeNtype() {}
func (*Name) CanBeAnSSASym() {}
func (*Name) CanBeAnSSAAux() {}
// Pragma returns the PragmaFlag for p, which must be for an OTYPE.
func (n *Name) Pragma() PragmaFlag { return n.pragma }
// SetPragma sets the PragmaFlag for p, which must be for an OTYPE.
func (n *Name) SetPragma(flag PragmaFlag) { n.pragma = flag }
// Alias reports whether p, which must be for an OTYPE, is a type alias.
func (n *Name) Alias() bool { return n.flags&nameAlias != 0 }
// SetAlias sets whether p, which must be for an OTYPE, is a type alias.
func (n *Name) SetAlias(alias bool) { n.flags.set(nameAlias, alias) }
const (
nameReadonly = 1 << iota
nameByval // is the variable captured by value or by reference
nameNeedzero // if it contains pointers, needs to be zeroed on function entry
nameAutoTemp // is the variable a temporary (implies no dwarf info. reset if escapes to heap)
nameUsed // for variable declared and not used error
nameIsClosureVar // PAUTOHEAP closure pseudo-variable; original (if any) at n.Defn
nameIsOutputParamHeapAddr // pointer to a result parameter's heap copy
nameAddrtaken // address taken, even if not moved to heap
nameInlFormal // PAUTO created by inliner, derived from callee formal
nameInlLocal // PAUTO created by inliner, derived from callee local
nameOpenDeferSlot // if temporary var storing info for open-coded defers
nameLibfuzzerExtraCounter // if PEXTERN should be assigned to __libfuzzer_extra_counters section
nameAlias // is type name an alias
)
func (n *Name) Readonly() bool { return n.flags&nameReadonly != 0 }
func (n *Name) Needzero() bool { return n.flags&nameNeedzero != 0 }
func (n *Name) AutoTemp() bool { return n.flags&nameAutoTemp != 0 }
func (n *Name) Used() bool { return n.flags&nameUsed != 0 }
func (n *Name) IsClosureVar() bool { return n.flags&nameIsClosureVar != 0 }
func (n *Name) IsOutputParamHeapAddr() bool { return n.flags&nameIsOutputParamHeapAddr != 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) LibfuzzerExtraCounter() bool { return n.flags&nameLibfuzzerExtraCounter != 0 }
func (n *Name) setReadonly(b bool) { n.flags.set(nameReadonly, b) }
func (n *Name) SetNeedzero(b bool) { n.flags.set(nameNeedzero, b) }
func (n *Name) SetAutoTemp(b bool) { n.flags.set(nameAutoTemp, b) }
func (n *Name) SetUsed(b bool) { n.flags.set(nameUsed, b) }
func (n *Name) SetIsClosureVar(b bool) { n.flags.set(nameIsClosureVar, b) }
func (n *Name) SetIsOutputParamHeapAddr(b bool) { n.flags.set(nameIsOutputParamHeapAddr, 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) }
func (n *Name) SetLibfuzzerExtraCounter(b bool) { n.flags.set(nameLibfuzzerExtraCounter, b) }
// OnStack reports whether variable n may reside on the stack.
func (n *Name) OnStack() bool {
if n.Op() == ONAME {
switch n.Class {
case PPARAM, PPARAMOUT, PAUTO:
return n.Esc() != EscHeap
case PEXTERN, PAUTOHEAP:
return false
}
}
// Note: fmt.go:dumpNodeHeader calls all "func() bool"-typed
// methods, but it can only recover from panics, not Fatalf.
panic(fmt.Sprintf("%v: not a variable: %v", base.FmtPos(n.Pos()), n))
}
// MarkReadonly indicates that n is an ONAME with readonly contents.
func (n *Name) MarkReadonly() {
if n.Op() != ONAME {
base.Fatalf("Node.MarkReadonly %v", n.Op())
}
n.setReadonly(true)
// Mark the linksym as readonly immediately
// so that the SSA backend can use this information.
// It will be overridden later during dumpglobls.
n.Linksym().Type = objabi.SRODATA
}
// Val returns the constant.Value for the node.
func (n *Name) Val() constant.Value {
if n.val == nil {
return constant.MakeUnknown()
}
return n.val
}
// SetVal sets the constant.Value for the node.
func (n *Name) SetVal(v constant.Value) {
if n.op != OLITERAL {
panic(n.no("SetVal"))
}
AssertValidTypeForConst(n.Type(), v)
n.val = v
}
// Canonical returns the logical declaration that n represents. If n
// is a closure variable, then Canonical returns the original Name as
// it appears in the function that immediately contains the
// declaration. Otherwise, Canonical simply returns n itself.
func (n *Name) Canonical() *Name {
if n.IsClosureVar() && n.Defn != nil {
n = n.Defn.(*Name)
}
return n
}
func (n *Name) SetByval(b bool) {
if n.Canonical() != n {
base.Fatalf("SetByval called on non-canonical variable: %v", n)
}
n.flags.set(nameByval, b)
}
func (n *Name) Byval() bool {
// We require byval to be set on the canonical variable, but we
// allow it to be accessed from any instance.
return n.Canonical().flags&nameByval != 0
}
// CaptureName returns a Name suitable for referring to n from within function
// fn or from the package block if fn is nil. If n is a free variable declared
// within a function that encloses fn, then CaptureName returns a closure
// variable that refers to n and adds it to fn.ClosureVars. Otherwise, it simply
// returns n.
func CaptureName(pos src.XPos, fn *Func, n *Name) *Name {
if n.IsClosureVar() {
base.FatalfAt(pos, "misuse of CaptureName on closure variable: %v", n)
}
if n.Op() != ONAME || n.Curfn == nil || n.Curfn == fn {
return n // okay to use directly
}
if fn == nil {
base.FatalfAt(pos, "package-block reference to %v, declared in %v", n, n.Curfn)
}
c := n.Innermost
if c != nil && c.Curfn == fn {
return c
}
// Do not have a closure var for the active closure yet; make one.
c = NewNameAt(pos, n.Sym())
c.Curfn = fn
c.Class = PAUTOHEAP
c.SetIsClosureVar(true)
c.Defn = n
// Link into list of active closure variables.
// Popped from list in FinishCaptureNames.
c.Outer = n.Innermost
n.Innermost = c
fn.ClosureVars = append(fn.ClosureVars, c)
return c
}
// FinishCaptureNames handles any work leftover from calling CaptureName
// earlier. outerfn should be the function that immediately encloses fn.
func FinishCaptureNames(pos src.XPos, outerfn, fn *Func) {
// closure-specific variables are hanging off the
// ordinary ones; see CaptureName above.
// unhook them.
// make the list of pointers for the closure call.
for _, cv := range fn.ClosureVars {
// Unlink from n; see comment in syntax.go type Param for these fields.
n := cv.Defn.(*Name)
n.Innermost = cv.Outer
// If the closure usage of n is not dense, we need to make it
// dense by recapturing n within the enclosing function.
//
// That is, suppose we just finished parsing the innermost
// closure f4 in this code:
//
// func f() {
// n := 1
// func() { // f2
// use(n)
// func() { // f3
// func() { // f4
// use(n)
// }()
// }()
// }()
// }
//
// At this point cv.Outer is f2's n; there is no n for f3. To
// construct the closure f4 from within f3, we need to use f3's
// n and in this case we need to create f3's n with CaptureName.
//
// We'll decide later in walk whether to use v directly or &v.
cv.Outer = CaptureName(pos, outerfn, n)
}
}
// SameSource reports whether two nodes refer to the same source
// element.
//
// It exists to help incrementally migrate the compiler towards
// allowing the introduction of IdentExpr (#42990). Once we have
// IdentExpr, it will no longer be safe to directly compare Node
// values to tell if they refer to the same Name. Instead, code will
// need to explicitly get references to the underlying Name object(s),
// and compare those instead.
//
// It will still be safe to compare Nodes directly for checking if two
// nodes are syntactically the same. The SameSource function exists to
// indicate code that intentionally compares Nodes for syntactic
// equality as opposed to code that has yet to be updated in
// preparation for IdentExpr.
func SameSource(n1, n2 Node) bool {
return n1 == n2
}
// Uses reports whether expression x is a (direct) use of the given
// variable.
func Uses(x Node, v *Name) bool {
if v == nil || v.Op() != ONAME {
base.Fatalf("RefersTo bad Name: %v", v)
}
return x.Op() == ONAME && x.Name() == v
}
// DeclaredBy reports whether expression x refers (directly) to a
// variable that was declared by the given statement.
func DeclaredBy(x, stmt Node) bool {
if stmt == nil {
base.Fatalf("DeclaredBy nil")
}
return x.Op() == ONAME && SameSource(x.Name().Defn, stmt)
}
// The Class of a variable/function describes the "storage class"
// of a variable or function. During parsing, storage classes are
// called declaration contexts.
type Class uint8
//go:generate stringer -type=Class name.go
const (
Pxxx Class = iota // no class; used during ssa conversion to indicate pseudo-variables
PEXTERN // global variables
PAUTO // local variables
PAUTOHEAP // local variables or parameters moved to heap
PPARAM // input arguments
PPARAMOUT // output results
PFUNC // global functions
// Careful: Class is stored in three bits in Node.flags.
_ = uint((1 << 3) - iota) // static assert for iota <= (1 << 3)
)
type Embed struct {
Pos src.XPos
Patterns []string
}
// A Pack is an identifier referring to an imported package.
type PkgName struct {
miniNode
sym *types.Sym
Pkg *types.Pkg
Used bool
}
func (p *PkgName) Sym() *types.Sym { return p.sym }
func (*PkgName) CanBeNtype() {}
func NewPkgName(pos src.XPos, sym *types.Sym, pkg *types.Pkg) *PkgName {
p := &PkgName{sym: sym, Pkg: pkg}
p.op = OPACK
p.pos = pos
return p
}
var RegFP *Name

View File

@@ -0,0 +1,591 @@
// Copyright 2009 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.
// “Abstract” syntax representation.
package ir
import (
"fmt"
"go/constant"
"sort"
"cmd/compile/internal/base"
"cmd/compile/internal/types"
"cmd/internal/src"
)
// A Node is the abstract interface to an IR node.
type Node interface {
// Formatting
Format(s fmt.State, verb rune)
// Source position.
Pos() src.XPos
SetPos(x src.XPos)
// For making copies. For Copy and SepCopy.
copy() Node
doChildren(func(Node) bool) bool
editChildren(func(Node) Node)
// Abstract graph structure, for generic traversals.
Op() Op
Init() Nodes
// Fields specific to certain Ops only.
Type() *types.Type
SetType(t *types.Type)
Name() *Name
Sym() *types.Sym
Val() constant.Value
SetVal(v constant.Value)
// Storage for analysis passes.
Esc() uint16
SetEsc(x uint16)
Diag() bool
SetDiag(x bool)
Typecheck() uint8
SetTypecheck(x uint8)
NonNil() bool
MarkNonNil()
}
// Line returns n's position as a string. If n has been inlined,
// it uses the outermost position where n has been inlined.
func Line(n Node) string {
return base.FmtPos(n.Pos())
}
func IsSynthetic(n Node) bool {
name := n.Sym().Name
return name[0] == '.' || name[0] == '~'
}
// IsAutoTmp indicates if n was created by the compiler as a temporary,
// based on the setting of the .AutoTemp flag in n's Name.
func IsAutoTmp(n Node) bool {
if n == nil || n.Op() != ONAME {
return false
}
return n.Name().AutoTemp()
}
// mayBeShared reports whether n may occur in multiple places in the AST.
// Extra care must be taken when mutating such a node.
func MayBeShared(n Node) bool {
switch n.Op() {
case ONAME, OLITERAL, ONIL, OTYPE:
return true
}
return false
}
type InitNode interface {
Node
PtrInit() *Nodes
SetInit(x Nodes)
}
func TakeInit(n Node) Nodes {
init := n.Init()
if len(init) != 0 {
n.(InitNode).SetInit(nil)
}
return init
}
//go:generate stringer -type=Op -trimprefix=O node.go
type Op uint8
// Node ops.
const (
OXXX Op = iota
// names
ONAME // var or func name
// Unnamed arg or return value: f(int, string) (int, error) { etc }
// Also used for a qualified package identifier that hasn't been resolved yet.
ONONAME
OTYPE // type name
OPACK // import
OLITERAL // literal
ONIL // nil
// expressions
OADD // Left + Right
OSUB // Left - Right
OOR // Left | Right
OXOR // Left ^ Right
OADDSTR // +{List} (string addition, list elements are strings)
OADDR // &Left
OANDAND // Left && Right
OAPPEND // append(List); after walk, Left may contain elem type descriptor
OBYTES2STR // Type(Left) (Type is string, Left is a []byte)
OBYTES2STRTMP // Type(Left) (Type is string, Left is a []byte, ephemeral)
ORUNES2STR // Type(Left) (Type is string, Left is a []rune)
OSTR2BYTES // Type(Left) (Type is []byte, Left is a string)
OSTR2BYTESTMP // Type(Left) (Type is []byte, Left is a string, ephemeral)
OSTR2RUNES // Type(Left) (Type is []rune, Left is a string)
// Left = Right or (if Colas=true) Left := Right
// If Colas, then Ninit includes a DCL node for Left.
OAS
// List = Rlist (x, y, z = a, b, c) or (if Colas=true) List := Rlist
// If Colas, then Ninit includes DCL nodes for List
OAS2
OAS2DOTTYPE // List = Right (x, ok = I.(int))
OAS2FUNC // List = Right (x, y = f())
OAS2MAPR // List = Right (x, ok = m["foo"])
OAS2RECV // List = Right (x, ok = <-c)
OASOP // Left Etype= Right (x += y)
OCALL // Left(List) (function call, method call or type conversion)
// OCALLFUNC, OCALLMETH, and OCALLINTER have the same structure.
// Prior to walk, they are: Left(List), where List is all regular arguments.
// After walk, List is a series of assignments to temporaries,
// and Rlist is an updated set of arguments.
// Nbody is all OVARLIVE nodes that are attached to OCALLxxx.
// TODO(josharian/khr): Use Ninit instead of List for the assignments to temporaries. See CL 114797.
OCALLFUNC // Left(List/Rlist) (function call f(args))
OCALLMETH // Left(List/Rlist) (direct method call x.Method(args))
OCALLINTER // Left(List/Rlist) (interface method call x.Method(args))
OCALLPART // Left.Right (method expression x.Method, not called)
OCAP // cap(Left)
OCLOSE // close(Left)
OCLOSURE // func Type { Func.Closure.Nbody } (func literal)
OCOMPLIT // Right{List} (composite literal, not yet lowered to specific form)
OMAPLIT // Type{List} (composite literal, Type is map)
OSTRUCTLIT // Type{List} (composite literal, Type is struct)
OARRAYLIT // Type{List} (composite literal, Type is array)
OSLICELIT // Type{List} (composite literal, Type is slice) Right.Int64() = slice length.
OPTRLIT // &Left (left is composite literal)
OCONV // Type(Left) (type conversion)
OCONVIFACE // Type(Left) (type conversion, to interface)
OCONVNOP // Type(Left) (type conversion, no effect)
OCOPY // copy(Left, Right)
ODCL // var Left (declares Left of type Left.Type)
// Used during parsing but don't last.
ODCLFUNC // func f() or func (r) f()
ODCLCONST // const pi = 3.14
ODCLTYPE // type Int int or type Int = int
ODELETE // delete(List)
ODOT // Left.Sym (Left is of struct type)
ODOTPTR // Left.Sym (Left is of pointer to struct type)
ODOTMETH // Left.Sym (Left is non-interface, Right is method name)
ODOTINTER // Left.Sym (Left is interface, Right is method name)
OXDOT // Left.Sym (before rewrite to one of the preceding)
ODOTTYPE // Left.Right or Left.Type (.Right during parsing, .Type once resolved); after walk, .Right contains address of interface type descriptor and .Right.Right contains address of concrete type descriptor
ODOTTYPE2 // Left.Right or Left.Type (.Right during parsing, .Type once resolved; on rhs of OAS2DOTTYPE); after walk, .Right contains address of interface type descriptor
OEQ // Left == Right
ONE // Left != Right
OLT // Left < Right
OLE // Left <= Right
OGE // Left >= Right
OGT // Left > Right
ODEREF // *Left
OINDEX // Left[Right] (index of array or slice)
OINDEXMAP // Left[Right] (index of map)
OKEY // Left:Right (key:value in struct/array/map literal)
OSTRUCTKEY // Sym:Left (key:value in struct literal, after type checking)
OLEN // len(Left)
OMAKE // make(List) (before type checking converts to one of the following)
OMAKECHAN // make(Type, Left) (type is chan)
OMAKEMAP // make(Type, Left) (type is map)
OMAKESLICE // make(Type, Left, Right) (type is slice)
OMAKESLICECOPY // makeslicecopy(Type, Left, Right) (type is slice; Left is length and Right is the copied from slice)
// OMAKESLICECOPY is created by the order pass and corresponds to:
// s = make(Type, Left); copy(s, Right)
//
// Bounded can be set on the node when Left == len(Right) is known at compile time.
//
// This node is created so the walk pass can optimize this pattern which would
// otherwise be hard to detect after the order pass.
OMUL // Left * Right
ODIV // Left / Right
OMOD // Left % Right
OLSH // Left << Right
ORSH // Left >> Right
OAND // Left & Right
OANDNOT // Left &^ Right
ONEW // new(Left); corresponds to calls to new in source code
ONOT // !Left
OBITNOT // ^Left
OPLUS // +Left
ONEG // -Left
OOROR // Left || Right
OPANIC // panic(Left)
OPRINT // print(List)
OPRINTN // println(List)
OPAREN // (Left)
OSEND // Left <- Right
OSLICE // Left[List[0] : List[1]] (Left is untypechecked or slice)
OSLICEARR // Left[List[0] : List[1]] (Left is pointer to array)
OSLICESTR // Left[List[0] : List[1]] (Left is string)
OSLICE3 // Left[List[0] : List[1] : List[2]] (Left is untypedchecked or slice)
OSLICE3ARR // Left[List[0] : List[1] : List[2]] (Left is pointer to array)
OSLICEHEADER // sliceheader{Left, List[0], List[1]} (Left is unsafe.Pointer, List[0] is length, List[1] is capacity)
ORECOVER // recover()
ORECV // <-Left
ORUNESTR // Type(Left) (Type is string, Left is rune)
OSELRECV2 // like OAS2: List = Rlist where len(List)=2, len(Rlist)=1, Rlist[0].Op = ORECV (appears as .Left of OCASE)
OIOTA // iota
OREAL // real(Left)
OIMAG // imag(Left)
OCOMPLEX // complex(Left, Right) or complex(List[0]) where List[0] is a 2-result function call
OALIGNOF // unsafe.Alignof(Left)
OOFFSETOF // unsafe.Offsetof(Left)
OSIZEOF // unsafe.Sizeof(Left)
OMETHEXPR // method expression
OSTMTEXPR // statement expression (Init; Left)
// statements
OBLOCK // { List } (block of code)
OBREAK // break [Sym]
// OCASE: case List: Nbody (List==nil means default)
// For OTYPESW, List is a OTYPE node for the specified type (or OLITERAL
// for nil), and, if a type-switch variable is specified, Rlist is an
// ONAME for the version of the type-switch variable with the specified
// type.
OCASE
OCONTINUE // continue [Sym]
ODEFER // defer Left (Left must be call)
OFALL // fallthrough
OFOR // for Ninit; Left; Right { Nbody }
// OFORUNTIL is like OFOR, but the test (Left) is applied after the body:
// Ninit
// top: { Nbody } // Execute the body at least once
// cont: Right
// if Left { // And then test the loop condition
// List // Before looping to top, execute List
// goto top
// }
// OFORUNTIL is created by walk. There's no way to write this in Go code.
OFORUNTIL
OGOTO // goto Sym
OIF // if Ninit; Left { Nbody } else { Rlist }
OLABEL // Sym:
OGO // go Left (Left must be call)
ORANGE // for List = range Right { Nbody }
ORETURN // return List
OSELECT // select { List } (List is list of OCASE)
OSWITCH // switch Ninit; Left { List } (List is a list of OCASE)
// OTYPESW: Left := Right.(type) (appears as .Left of OSWITCH)
// Left is nil if there is no type-switch variable
OTYPESW
// types
OTCHAN // chan int
OTMAP // map[string]int
OTSTRUCT // struct{}
OTINTER // interface{}
// OTFUNC: func() - Left is receiver field, List is list of param fields, Rlist is
// list of result fields.
OTFUNC
OTARRAY // [8]int or [...]int
OTSLICE // []int
// misc
// intermediate representation of an inlined call. Uses Init (assignments
// for the captured variables, parameters, retvars, & INLMARK op),
// Body (body of the inlined function), and ReturnVars (list of
// return values)
OINLCALL // intermediary representation of an inlined call.
OEFACE // itable and data words of an empty-interface value.
OITAB // itable word of an interface value.
OIDATA // data word of an interface value in Left
OSPTR // base pointer of a slice or string.
OCFUNC // reference to c function pointer (not go func value)
OCHECKNIL // emit code to ensure pointer/interface not nil
OVARDEF // variable is about to be fully initialized
OVARKILL // variable is dead
OVARLIVE // variable is alive
ORESULT // result of a function call; Xoffset is stack offset
OINLMARK // start of an inlined body, with file/line of caller. Xoffset is an index into the inline tree.
OLINKSYMOFFSET // offset within a name
// arch-specific opcodes
OTAILCALL // tail call to another function
OGETG // runtime.getg() (read g pointer)
OEND
)
// Nodes is a pointer to a slice of *Node.
// For fields that are not used in most nodes, this is used instead of
// a slice to save space.
type Nodes []Node
// Append appends entries to Nodes.
func (n *Nodes) Append(a ...Node) {
if len(a) == 0 {
return
}
*n = append(*n, a...)
}
// Prepend prepends entries to Nodes.
// If a slice is passed in, this will take ownership of it.
func (n *Nodes) Prepend(a ...Node) {
if len(a) == 0 {
return
}
*n = append(a, *n...)
}
// Take clears n, returning its former contents.
func (n *Nodes) Take() []Node {
ret := *n
*n = nil
return ret
}
// Copy returns a copy of the content of the slice.
func (n Nodes) Copy() Nodes {
if n == nil {
return nil
}
c := make(Nodes, len(n))
copy(c, n)
return c
}
// NameQueue is a FIFO queue of *Name. The zero value of NameQueue is
// a ready-to-use empty queue.
type NameQueue struct {
ring []*Name
head, tail int
}
// Empty reports whether q contains no Names.
func (q *NameQueue) Empty() bool {
return q.head == q.tail
}
// PushRight appends n to the right of the queue.
func (q *NameQueue) PushRight(n *Name) {
if len(q.ring) == 0 {
q.ring = make([]*Name, 16)
} else if q.head+len(q.ring) == q.tail {
// Grow the ring.
nring := make([]*Name, len(q.ring)*2)
// Copy the old elements.
part := q.ring[q.head%len(q.ring):]
if q.tail-q.head <= len(part) {
part = part[:q.tail-q.head]
copy(nring, part)
} else {
pos := copy(nring, part)
copy(nring[pos:], q.ring[:q.tail%len(q.ring)])
}
q.ring, q.head, q.tail = nring, 0, q.tail-q.head
}
q.ring[q.tail%len(q.ring)] = n
q.tail++
}
// PopLeft pops a Name from the left of the queue. It panics if q is
// empty.
func (q *NameQueue) PopLeft() *Name {
if q.Empty() {
panic("dequeue empty")
}
n := q.ring[q.head%len(q.ring)]
q.head++
return n
}
// NameSet is a set of Names.
type NameSet map[*Name]struct{}
// Has reports whether s contains n.
func (s NameSet) Has(n *Name) bool {
_, isPresent := s[n]
return isPresent
}
// Add adds n to s.
func (s *NameSet) Add(n *Name) {
if *s == nil {
*s = make(map[*Name]struct{})
}
(*s)[n] = struct{}{}
}
// Sorted returns s sorted according to less.
func (s NameSet) Sorted(less func(*Name, *Name) bool) []*Name {
var res []*Name
for n := range s {
res = append(res, n)
}
sort.Slice(res, func(i, j int) bool { return less(res[i], res[j]) })
return res
}
type PragmaFlag int16
const (
// Func pragmas.
Nointerface PragmaFlag = 1 << iota
Noescape // func parameters don't escape
Norace // func must not have race detector annotations
Nosplit // func should not execute on separate stack
Noinline // func should not be inlined
NoCheckPtr // func should not be instrumented by checkptr
CgoUnsafeArgs // treat a pointer to one arg as a pointer to them all
UintptrEscapes // pointers converted to uintptr escape
// Runtime-only func pragmas.
// See ../../../../runtime/README.md for detailed descriptions.
Systemstack // func must run on system stack
Nowritebarrier // emit compiler error instead of write barrier
Nowritebarrierrec // error on write barrier in this or recursive callees
Yeswritebarrierrec // cancels Nowritebarrierrec in this function and callees
// Runtime and cgo type pragmas
NotInHeap // values of this type must not be heap allocated
// Go command pragmas
GoBuildPragma
RegisterParams // TODO remove after register abi is working
)
func AsNode(n types.Object) Node {
if n == nil {
return nil
}
return n.(Node)
}
var BlankNode Node
func IsConst(n Node, ct constant.Kind) bool {
return ConstType(n) == ct
}
// isNil reports whether n represents the universal untyped zero value "nil".
func IsNil(n Node) bool {
// Check n.Orig because constant propagation may produce typed nil constants,
// which don't exist in the Go spec.
return n != nil && Orig(n).Op() == ONIL
}
func IsBlank(n Node) bool {
if n == nil {
return false
}
return n.Sym().IsBlank()
}
// IsMethod reports whether n is a method.
// n must be a function or a method.
func IsMethod(n Node) bool {
return n.Type().Recv() != nil
}
func HasNamedResults(fn *Func) bool {
typ := fn.Type()
return typ.NumResults() > 0 && types.OrigSym(typ.Results().Field(0).Sym) != nil
}
// HasUniquePos reports whether n has a unique position that can be
// used for reporting error messages.
//
// It's primarily used to distinguish references to named objects,
// whose Pos will point back to their declaration position rather than
// their usage position.
func HasUniquePos(n Node) bool {
switch n.Op() {
case ONAME, OPACK:
return false
case OLITERAL, ONIL, OTYPE:
if n.Sym() != nil {
return false
}
}
if !n.Pos().IsKnown() {
if base.Flag.K != 0 {
base.Warn("setlineno: unknown position (line 0)")
}
return false
}
return true
}
func SetPos(n Node) src.XPos {
lno := base.Pos
if n != nil && HasUniquePos(n) {
base.Pos = n.Pos()
}
return lno
}
// The result of InitExpr MUST be assigned back to n, e.g.
// n.Left = InitExpr(init, n.Left)
func InitExpr(init []Node, expr Node) Node {
if len(init) == 0 {
return expr
}
n, ok := expr.(InitNode)
if !ok || MayBeShared(n) {
// Introduce OCONVNOP to hold init list.
n = NewConvExpr(base.Pos, OCONVNOP, nil, expr)
n.SetType(expr.Type())
n.SetTypecheck(1)
}
n.PtrInit().Prepend(init...)
return n
}
// what's the outer value that a write to n affects?
// outer value means containing struct or array.
func OuterValue(n Node) Node {
for {
switch nn := n; nn.Op() {
case OXDOT:
base.Fatalf("OXDOT in walk")
case ODOT:
nn := nn.(*SelectorExpr)
n = nn.X
continue
case OPAREN:
nn := nn.(*ParenExpr)
n = nn.X
continue
case OCONVNOP:
nn := nn.(*ConvExpr)
n = nn.X
continue
case OINDEX:
nn := nn.(*IndexExpr)
if nn.X.Type() == nil {
base.Fatalf("OuterValue needs type for %v", nn.X)
}
if nn.X.Type().IsArray() {
n = nn.X
continue
}
}
return n
}
}
const (
EscUnknown = iota
EscNone // Does not escape to heap, result, or parameters.
EscHeap // Reachable from the heap
EscNever // By construction will not escape.
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,174 @@
// Code generated by "stringer -type=Op -trimprefix=O node.go"; DO NOT EDIT.
package ir
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[OXXX-0]
_ = x[ONAME-1]
_ = x[ONONAME-2]
_ = x[OTYPE-3]
_ = x[OPACK-4]
_ = x[OLITERAL-5]
_ = x[ONIL-6]
_ = x[OADD-7]
_ = x[OSUB-8]
_ = x[OOR-9]
_ = x[OXOR-10]
_ = x[OADDSTR-11]
_ = x[OADDR-12]
_ = x[OANDAND-13]
_ = x[OAPPEND-14]
_ = x[OBYTES2STR-15]
_ = x[OBYTES2STRTMP-16]
_ = x[ORUNES2STR-17]
_ = x[OSTR2BYTES-18]
_ = x[OSTR2BYTESTMP-19]
_ = x[OSTR2RUNES-20]
_ = x[OAS-21]
_ = x[OAS2-22]
_ = x[OAS2DOTTYPE-23]
_ = x[OAS2FUNC-24]
_ = x[OAS2MAPR-25]
_ = x[OAS2RECV-26]
_ = x[OASOP-27]
_ = x[OCALL-28]
_ = x[OCALLFUNC-29]
_ = x[OCALLMETH-30]
_ = x[OCALLINTER-31]
_ = x[OCALLPART-32]
_ = x[OCAP-33]
_ = x[OCLOSE-34]
_ = x[OCLOSURE-35]
_ = x[OCOMPLIT-36]
_ = x[OMAPLIT-37]
_ = x[OSTRUCTLIT-38]
_ = x[OARRAYLIT-39]
_ = x[OSLICELIT-40]
_ = x[OPTRLIT-41]
_ = x[OCONV-42]
_ = x[OCONVIFACE-43]
_ = x[OCONVNOP-44]
_ = x[OCOPY-45]
_ = x[ODCL-46]
_ = x[ODCLFUNC-47]
_ = x[ODCLCONST-48]
_ = x[ODCLTYPE-49]
_ = x[ODELETE-50]
_ = x[ODOT-51]
_ = x[ODOTPTR-52]
_ = x[ODOTMETH-53]
_ = x[ODOTINTER-54]
_ = x[OXDOT-55]
_ = x[ODOTTYPE-56]
_ = x[ODOTTYPE2-57]
_ = x[OEQ-58]
_ = x[ONE-59]
_ = x[OLT-60]
_ = x[OLE-61]
_ = x[OGE-62]
_ = x[OGT-63]
_ = x[ODEREF-64]
_ = x[OINDEX-65]
_ = x[OINDEXMAP-66]
_ = x[OKEY-67]
_ = x[OSTRUCTKEY-68]
_ = x[OLEN-69]
_ = x[OMAKE-70]
_ = x[OMAKECHAN-71]
_ = x[OMAKEMAP-72]
_ = x[OMAKESLICE-73]
_ = x[OMAKESLICECOPY-74]
_ = x[OMUL-75]
_ = x[ODIV-76]
_ = x[OMOD-77]
_ = x[OLSH-78]
_ = x[ORSH-79]
_ = x[OAND-80]
_ = x[OANDNOT-81]
_ = x[ONEW-82]
_ = x[ONOT-83]
_ = x[OBITNOT-84]
_ = x[OPLUS-85]
_ = x[ONEG-86]
_ = x[OOROR-87]
_ = x[OPANIC-88]
_ = x[OPRINT-89]
_ = x[OPRINTN-90]
_ = x[OPAREN-91]
_ = x[OSEND-92]
_ = x[OSLICE-93]
_ = x[OSLICEARR-94]
_ = x[OSLICESTR-95]
_ = x[OSLICE3-96]
_ = x[OSLICE3ARR-97]
_ = x[OSLICEHEADER-98]
_ = x[ORECOVER-99]
_ = x[ORECV-100]
_ = x[ORUNESTR-101]
_ = x[OSELRECV2-102]
_ = x[OIOTA-103]
_ = x[OREAL-104]
_ = x[OIMAG-105]
_ = x[OCOMPLEX-106]
_ = x[OALIGNOF-107]
_ = x[OOFFSETOF-108]
_ = x[OSIZEOF-109]
_ = x[OMETHEXPR-110]
_ = x[OSTMTEXPR-111]
_ = x[OBLOCK-112]
_ = x[OBREAK-113]
_ = x[OCASE-114]
_ = x[OCONTINUE-115]
_ = x[ODEFER-116]
_ = x[OFALL-117]
_ = x[OFOR-118]
_ = x[OFORUNTIL-119]
_ = x[OGOTO-120]
_ = x[OIF-121]
_ = x[OLABEL-122]
_ = x[OGO-123]
_ = x[ORANGE-124]
_ = x[ORETURN-125]
_ = x[OSELECT-126]
_ = x[OSWITCH-127]
_ = x[OTYPESW-128]
_ = x[OTCHAN-129]
_ = x[OTMAP-130]
_ = x[OTSTRUCT-131]
_ = x[OTINTER-132]
_ = x[OTFUNC-133]
_ = x[OTARRAY-134]
_ = x[OTSLICE-135]
_ = x[OINLCALL-136]
_ = x[OEFACE-137]
_ = x[OITAB-138]
_ = x[OIDATA-139]
_ = x[OSPTR-140]
_ = x[OCFUNC-141]
_ = x[OCHECKNIL-142]
_ = x[OVARDEF-143]
_ = x[OVARKILL-144]
_ = x[OVARLIVE-145]
_ = x[ORESULT-146]
_ = x[OINLMARK-147]
_ = x[OLINKSYMOFFSET-148]
_ = x[OTAILCALL-149]
_ = x[OGETG-150]
_ = x[OEND-151]
}
const _Op_name = "XXXNAMENONAMETYPEPACKLITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFMETHEXPRSTMTEXPRBLOCKBREAKCASECONTINUEDEFERFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYTSLICEINLCALLEFACEITABIDATASPTRCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKLINKSYMOFFSETTAILCALLGETGEND"
var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 37, 39, 42, 48, 52, 58, 64, 73, 85, 94, 103, 115, 124, 126, 129, 139, 146, 153, 160, 164, 168, 176, 184, 193, 201, 204, 209, 216, 223, 229, 238, 246, 254, 260, 264, 273, 280, 284, 287, 294, 302, 309, 315, 318, 324, 331, 339, 343, 350, 358, 360, 362, 364, 366, 368, 370, 375, 380, 388, 391, 400, 403, 407, 415, 422, 431, 444, 447, 450, 453, 456, 459, 462, 468, 471, 474, 480, 484, 487, 491, 496, 501, 507, 512, 516, 521, 529, 537, 543, 552, 563, 570, 574, 581, 589, 593, 597, 601, 608, 615, 623, 629, 637, 645, 650, 655, 659, 667, 672, 676, 679, 687, 691, 693, 698, 700, 705, 711, 717, 723, 729, 734, 738, 745, 751, 756, 762, 768, 775, 780, 784, 789, 793, 798, 806, 812, 819, 826, 832, 839, 852, 860, 864, 867}
func (i Op) String() string {
if i >= Op(len(_Op_index)-1) {
return "Op(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Op_name[_Op_index[i]:_Op_index[i+1]]
}

View File

@@ -0,0 +1,35 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ir
import "cmd/compile/internal/types"
// A Package holds information about the package being compiled.
type Package struct {
// Imports, listed in source order.
// See golang.org/issue/31636.
Imports []*types.Pkg
// Init functions, listed in source order.
Inits []*Func
// Top-level declarations.
Decls []Node
// Extern (package global) declarations.
Externs []Node
// Assembly function declarations.
Asms []*Name
// Cgo directives.
CgoPragmas [][]string
// Variables with //go:embed lines.
Embeds []*Name
// Exported (or re-exported) symbols.
Exports []*Name
}

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gc
package ir
// Strongly connected components.
//
@@ -30,13 +30,13 @@ package gc
// when analyzing a set of mutually recursive functions.
type bottomUpVisitor struct {
analyze func([]*Node, bool)
analyze func([]*Func, bool)
visitgen uint32
nodeID map[*Node]uint32
stack []*Node
nodeID map[*Func]uint32
stack []*Func
}
// visitBottomUp invokes analyze on the ODCLFUNC nodes listed in list.
// VisitFuncsBottomUp invokes analyze on the ODCLFUNC nodes listed in list.
// It calls analyze with successive groups of functions, working from
// the bottom of the call graph upward. Each time analyze is called with
// a list of functions, every function on that list only calls other functions
@@ -49,18 +49,21 @@ type bottomUpVisitor struct {
// If recursive is false, the list consists of only a single function and its closures.
// If recursive is true, the list may still contain only a single function,
// if that function is itself recursive.
func visitBottomUp(list []*Node, analyze func(list []*Node, recursive bool)) {
func VisitFuncsBottomUp(list []Node, analyze func(list []*Func, recursive bool)) {
var v bottomUpVisitor
v.analyze = analyze
v.nodeID = make(map[*Node]uint32)
v.nodeID = make(map[*Func]uint32)
for _, n := range list {
if n.Op == ODCLFUNC && !n.Func.IsHiddenClosure() {
v.visit(n)
if n.Op() == ODCLFUNC {
n := n.(*Func)
if !n.IsHiddenClosure() {
v.visit(n)
}
}
}
}
func (v *bottomUpVisitor) visit(n *Node) uint32 {
func (v *bottomUpVisitor) visit(n *Func) uint32 {
if id := v.nodeID[n]; id > 0 {
// already visited
return id
@@ -73,42 +76,31 @@ func (v *bottomUpVisitor) visit(n *Node) uint32 {
min := v.visitgen
v.stack = append(v.stack, n)
inspectList(n.Nbody, func(n *Node) bool {
switch n.Op {
case ONAME:
if n.Class() == PFUNC {
if n.isMethodExpression() {
n = asNode(n.Type.Nname())
}
if n != nil && n.Name.Defn != nil {
if m := v.visit(n.Name.Defn); m < min {
min = m
}
}
}
case ODOTMETH:
fn := asNode(n.Type.Nname())
if fn != nil && fn.Op == ONAME && fn.Class() == PFUNC && fn.Name.Defn != nil {
if m := v.visit(fn.Name.Defn); m < min {
min = m
}
}
case OCALLPART:
fn := asNode(callpartMethod(n).Type.Nname())
if fn != nil && fn.Op == ONAME && fn.Class() == PFUNC && fn.Name.Defn != nil {
if m := v.visit(fn.Name.Defn); m < min {
min = m
}
}
case OCLOSURE:
if m := v.visit(n.Func.Closure); m < min {
do := func(defn Node) {
if defn != nil {
if m := v.visit(defn.(*Func)); m < min {
min = m
}
}
return true
}
Visit(n, func(n Node) {
switch n.Op() {
case ONAME:
if n := n.(*Name); n.Class == PFUNC {
do(n.Defn)
}
case ODOTMETH, OCALLPART, OMETHEXPR:
if fn := MethodExprName(n); fn != nil {
do(fn.Defn)
}
case OCLOSURE:
n := n.(*ClosureExpr)
do(n.Func)
}
})
if (min == id || min == id+1) && !n.Func.IsHiddenClosure() {
if (min == id || min == id+1) && !n.IsHiddenClosure() {
// This node is the root of a strongly connected component.
// The original min passed to visitcodelist was v.nodeID[n]+1.

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gc
package ir
import (
"reflect"
@@ -20,10 +20,8 @@ func TestSizeof(t *testing.T) {
_32bit uintptr // size on 32bit platforms
_64bit uintptr // size on 64bit platforms
}{
{Func{}, 124, 224},
{Name{}, 32, 56},
{Param{}, 24, 48},
{Node{}, 76, 128},
{Func{}, 188, 328},
{Name{}, 112, 200},
}
for _, tt := range tests {

View File

@@ -0,0 +1,414 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ir
import (
"cmd/compile/internal/base"
"cmd/compile/internal/types"
"cmd/internal/src"
)
// A Decl is a declaration of a const, type, or var. (A declared func is a Func.)
type Decl struct {
miniNode
X *Name // the thing being declared
}
func NewDecl(pos src.XPos, op Op, x *Name) *Decl {
n := &Decl{X: x}
n.pos = pos
switch op {
default:
panic("invalid Decl op " + op.String())
case ODCL, ODCLCONST, ODCLTYPE:
n.op = op
}
return n
}
func (*Decl) isStmt() {}
// A Stmt is a Node that can appear as a statement.
// This includes statement-like expressions such as f().
//
// (It's possible it should include <-c, but that would require
// splitting ORECV out of UnaryExpr, which hasn't yet been
// necessary. Maybe instead we will introduce ExprStmt at
// some point.)
type Stmt interface {
Node
isStmt()
}
// A miniStmt is a miniNode with extra fields common to statements.
type miniStmt struct {
miniNode
init Nodes
}
func (*miniStmt) isStmt() {}
func (n *miniStmt) Init() Nodes { return n.init }
func (n *miniStmt) SetInit(x Nodes) { n.init = x }
func (n *miniStmt) PtrInit() *Nodes { return &n.init }
// An AssignListStmt is an assignment statement with
// more than one item on at least one side: Lhs = Rhs.
// If Def is true, the assignment is a :=.
type AssignListStmt struct {
miniStmt
Lhs Nodes
Def bool
Rhs Nodes
}
func NewAssignListStmt(pos src.XPos, op Op, lhs, rhs []Node) *AssignListStmt {
n := &AssignListStmt{}
n.pos = pos
n.SetOp(op)
n.Lhs = lhs
n.Rhs = rhs
return n
}
func (n *AssignListStmt) SetOp(op Op) {
switch op {
default:
panic(n.no("SetOp " + op.String()))
case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV, OSELRECV2:
n.op = op
}
}
// An AssignStmt is a simple assignment statement: X = Y.
// If Def is true, the assignment is a :=.
type AssignStmt struct {
miniStmt
X Node
Def bool
Y Node
}
func NewAssignStmt(pos src.XPos, x, y Node) *AssignStmt {
n := &AssignStmt{X: x, Y: y}
n.pos = pos
n.op = OAS
return n
}
func (n *AssignStmt) SetOp(op Op) {
switch op {
default:
panic(n.no("SetOp " + op.String()))
case OAS:
n.op = op
}
}
// An AssignOpStmt is an AsOp= assignment statement: X AsOp= Y.
type AssignOpStmt struct {
miniStmt
X Node
AsOp Op // OADD etc
Y Node
IncDec bool // actually ++ or --
}
func NewAssignOpStmt(pos src.XPos, asOp Op, x, y Node) *AssignOpStmt {
n := &AssignOpStmt{AsOp: asOp, X: x, Y: y}
n.pos = pos
n.op = OASOP
return n
}
// A BlockStmt is a block: { List }.
type BlockStmt struct {
miniStmt
List Nodes
}
func NewBlockStmt(pos src.XPos, list []Node) *BlockStmt {
n := &BlockStmt{}
n.pos = pos
if !pos.IsKnown() {
n.pos = base.Pos
if len(list) > 0 {
n.pos = list[0].Pos()
}
}
n.op = OBLOCK
n.List = list
return n
}
// A BranchStmt is a break, continue, fallthrough, or goto statement.
type BranchStmt struct {
miniStmt
Label *types.Sym // label if present
}
func NewBranchStmt(pos src.XPos, op Op, label *types.Sym) *BranchStmt {
switch op {
case OBREAK, OCONTINUE, OFALL, OGOTO:
// ok
default:
panic("NewBranch " + op.String())
}
n := &BranchStmt{Label: label}
n.pos = pos
n.op = op
return n
}
func (n *BranchStmt) Sym() *types.Sym { return n.Label }
// A CaseClause is a case statement in a switch or select: case List: Body.
type CaseClause struct {
miniStmt
Var *Name // declared variable for this case in type switch
List Nodes // list of expressions for switch, early select
Body Nodes
}
func NewCaseStmt(pos src.XPos, list, body []Node) *CaseClause {
n := &CaseClause{List: list, Body: body}
n.pos = pos
n.op = OCASE
return n
}
type CommClause struct {
miniStmt
Comm Node // communication case
Body Nodes
}
func NewCommStmt(pos src.XPos, comm Node, body []Node) *CommClause {
n := &CommClause{Comm: comm, Body: body}
n.pos = pos
n.op = OCASE
return n
}
// A ForStmt is a non-range for loop: for Init; Cond; Post { Body }
// Op can be OFOR or OFORUNTIL (!Cond).
type ForStmt struct {
miniStmt
Label *types.Sym
Cond Node
Late Nodes
Post Node
Body Nodes
HasBreak bool
}
func NewForStmt(pos src.XPos, init Node, cond, post Node, body []Node) *ForStmt {
n := &ForStmt{Cond: cond, Post: post}
n.pos = pos
n.op = OFOR
if init != nil {
n.init = []Node{init}
}
n.Body = body
return n
}
func (n *ForStmt) SetOp(op Op) {
if op != OFOR && op != OFORUNTIL {
panic(n.no("SetOp " + op.String()))
}
n.op = op
}
// A GoDeferStmt is a go or defer statement: go Call / defer Call.
//
// The two opcodes use a signle syntax because the implementations
// are very similar: both are concerned with saving Call and running it
// in a different context (a separate goroutine or a later time).
type GoDeferStmt struct {
miniStmt
Call Node
}
func NewGoDeferStmt(pos src.XPos, op Op, call Node) *GoDeferStmt {
n := &GoDeferStmt{Call: call}
n.pos = pos
switch op {
case ODEFER, OGO:
n.op = op
default:
panic("NewGoDeferStmt " + op.String())
}
return n
}
// A IfStmt is a return statement: if Init; Cond { Then } else { Else }.
type IfStmt struct {
miniStmt
Cond Node
Body Nodes
Else Nodes
Likely bool // code layout hint
}
func NewIfStmt(pos src.XPos, cond Node, body, els []Node) *IfStmt {
n := &IfStmt{Cond: cond}
n.pos = pos
n.op = OIF
n.Body = body
n.Else = els
return n
}
// An InlineMarkStmt is a marker placed just before an inlined body.
type InlineMarkStmt struct {
miniStmt
Index int64
}
func NewInlineMarkStmt(pos src.XPos, index int64) *InlineMarkStmt {
n := &InlineMarkStmt{Index: index}
n.pos = pos
n.op = OINLMARK
return n
}
func (n *InlineMarkStmt) Offset() int64 { return n.Index }
func (n *InlineMarkStmt) SetOffset(x int64) { n.Index = x }
// A LabelStmt is a label statement (just the label, not including the statement it labels).
type LabelStmt struct {
miniStmt
Label *types.Sym // "Label:"
}
func NewLabelStmt(pos src.XPos, label *types.Sym) *LabelStmt {
n := &LabelStmt{Label: label}
n.pos = pos
n.op = OLABEL
return n
}
func (n *LabelStmt) Sym() *types.Sym { return n.Label }
// A RangeStmt is a range loop: for Key, Value = range X { Body }
type RangeStmt struct {
miniStmt
Label *types.Sym
Def bool
X Node
Key Node
Value Node
Body Nodes
HasBreak bool
Prealloc *Name
}
func NewRangeStmt(pos src.XPos, key, value, x Node, body []Node) *RangeStmt {
n := &RangeStmt{X: x, Key: key, Value: value}
n.pos = pos
n.op = ORANGE
n.Body = body
return n
}
// A ReturnStmt is a return statement.
type ReturnStmt struct {
miniStmt
origNode // for typecheckargs rewrite
Results Nodes // return list
}
func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt {
n := &ReturnStmt{}
n.pos = pos
n.op = ORETURN
n.orig = n
n.Results = results
return n
}
// A SelectStmt is a block: { Cases }.
type SelectStmt struct {
miniStmt
Label *types.Sym
Cases []*CommClause
HasBreak bool
// TODO(rsc): Instead of recording here, replace with a block?
Compiled Nodes // compiled form, after walkSwitch
}
func NewSelectStmt(pos src.XPos, cases []*CommClause) *SelectStmt {
n := &SelectStmt{Cases: cases}
n.pos = pos
n.op = OSELECT
return n
}
// A SendStmt is a send statement: X <- Y.
type SendStmt struct {
miniStmt
Chan Node
Value Node
}
func NewSendStmt(pos src.XPos, ch, value Node) *SendStmt {
n := &SendStmt{Chan: ch, Value: value}
n.pos = pos
n.op = OSEND
return n
}
// A SwitchStmt is a switch statement: switch Init; Expr { Cases }.
type SwitchStmt struct {
miniStmt
Tag Node
Cases []*CaseClause
Label *types.Sym
HasBreak bool
// TODO(rsc): Instead of recording here, replace with a block?
Compiled Nodes // compiled form, after walkSwitch
}
func NewSwitchStmt(pos src.XPos, tag Node, cases []*CaseClause) *SwitchStmt {
n := &SwitchStmt{Tag: tag, Cases: cases}
n.pos = pos
n.op = OSWITCH
return n
}
// A TailCallStmt is a tail call statement, which is used for back-end
// code generation to jump directly to another function entirely.
type TailCallStmt struct {
miniStmt
Target *Name
}
func NewTailCallStmt(pos src.XPos, target *Name) *TailCallStmt {
if target.Op() != ONAME || target.Class != PFUNC {
base.FatalfAt(pos, "tail call to non-func %v", target)
}
n := &TailCallStmt{Target: target}
n.pos = pos
n.op = OTAILCALL
return n
}
// A TypeSwitchGuard is the [Name :=] X.(type) in a type switch.
type TypeSwitchGuard struct {
miniNode
Tag *Ident
X Node
Used bool
}
func NewTypeSwitchGuard(pos src.XPos, tag *Ident, x Node) *TypeSwitchGuard {
n := &TypeSwitchGuard{Tag: tag, X: x}
n.pos = pos
n.op = OTYPESW
return n
}

View File

@@ -0,0 +1,72 @@
// Copyright 2009 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 ir
import (
"cmd/compile/internal/types"
"cmd/internal/obj"
)
// Syms holds known symbols.
var Syms struct {
AssertE2I *obj.LSym
AssertE2I2 *obj.LSym
AssertI2I *obj.LSym
AssertI2I2 *obj.LSym
Deferproc *obj.LSym
DeferprocStack *obj.LSym
Deferreturn *obj.LSym
Duffcopy *obj.LSym
Duffzero *obj.LSym
GCWriteBarrier *obj.LSym
Goschedguarded *obj.LSym
Growslice *obj.LSym
Msanread *obj.LSym
Msanwrite *obj.LSym
Msanmove *obj.LSym
Newobject *obj.LSym
Newproc *obj.LSym
Panicdivide *obj.LSym
Panicshift *obj.LSym
PanicdottypeE *obj.LSym
PanicdottypeI *obj.LSym
Panicnildottype *obj.LSym
Panicoverflow *obj.LSym
Raceread *obj.LSym
Racereadrange *obj.LSym
Racewrite *obj.LSym
Racewriterange *obj.LSym
// Wasm
SigPanic *obj.LSym
Staticuint64s *obj.LSym
Typedmemclr *obj.LSym
Typedmemmove *obj.LSym
Udiv *obj.LSym
WriteBarrier *obj.LSym
Zerobase *obj.LSym
ARM64HasATOMICS *obj.LSym
ARMHasVFPv4 *obj.LSym
X86HasFMA *obj.LSym
X86HasPOPCNT *obj.LSym
X86HasSSE41 *obj.LSym
// Wasm
WasmDiv *obj.LSym
// Wasm
WasmMove *obj.LSym
// Wasm
WasmZero *obj.LSym
// Wasm
WasmTruncS *obj.LSym
// Wasm
WasmTruncU *obj.LSym
}
// Pkgs holds known packages.
var Pkgs struct {
Go *types.Pkg
Itab *types.Pkg
Runtime *types.Pkg
Unsafe *types.Pkg
}

View File

@@ -0,0 +1,310 @@
// Copyright 2009 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 ir
import (
"cmd/compile/internal/base"
"cmd/compile/internal/types"
"cmd/internal/src"
"fmt"
)
// Nodes that represent the syntax of a type before type-checking.
// After type-checking, they serve only as shells around a *types.Type.
// Calling TypeNode converts a *types.Type to a Node shell.
// An Ntype is a Node that syntactically looks like a type.
// It can be the raw syntax for a type before typechecking,
// or it can be an OTYPE with Type() set to a *types.Type.
// Note that syntax doesn't guarantee it's a type: an expression
// like *fmt is an Ntype (we don't know whether names are types yet),
// but at least 1+1 is not an Ntype.
type Ntype interface {
Node
CanBeNtype()
}
// A miniType is a minimal type syntax Node implementation,
// to be embedded as the first field in a larger node implementation.
type miniType struct {
miniNode
typ *types.Type
}
func (*miniType) CanBeNtype() {}
func (n *miniType) Type() *types.Type { return n.typ }
// setOTYPE changes n to be an OTYPE node returning t.
// Rewriting the node in place this way should not be strictly
// necessary (we should be able to update the uses with
// proper OTYPE nodes), but it's mostly harmless and easy
// to keep doing for now.
//
// setOTYPE also records t.Nod = self if t.Nod is not already set.
// (Some types are shared by multiple OTYPE nodes, so only
// the first such node is used as t.Nod.)
func (n *miniType) setOTYPE(t *types.Type, self Ntype) {
if n.typ != nil {
panic(n.op.String() + " SetType: type already set")
}
n.op = OTYPE
n.typ = t
t.SetNod(self)
}
func (n *miniType) Sym() *types.Sym { return nil } // for Format OTYPE
func (n *miniType) Implicit() bool { return false } // for Format OTYPE
// A ChanType represents a chan Elem syntax with the direction Dir.
type ChanType struct {
miniType
Elem Ntype
Dir types.ChanDir
}
func NewChanType(pos src.XPos, elem Ntype, dir types.ChanDir) *ChanType {
n := &ChanType{Elem: elem, Dir: dir}
n.op = OTCHAN
n.pos = pos
return n
}
func (n *ChanType) SetOTYPE(t *types.Type) {
n.setOTYPE(t, n)
n.Elem = nil
}
// A MapType represents a map[Key]Value type syntax.
type MapType struct {
miniType
Key Ntype
Elem Ntype
}
func NewMapType(pos src.XPos, key, elem Ntype) *MapType {
n := &MapType{Key: key, Elem: elem}
n.op = OTMAP
n.pos = pos
return n
}
func (n *MapType) SetOTYPE(t *types.Type) {
n.setOTYPE(t, n)
n.Key = nil
n.Elem = nil
}
// A StructType represents a struct { ... } type syntax.
type StructType struct {
miniType
Fields []*Field
}
func NewStructType(pos src.XPos, fields []*Field) *StructType {
n := &StructType{Fields: fields}
n.op = OTSTRUCT
n.pos = pos
return n
}
func (n *StructType) SetOTYPE(t *types.Type) {
n.setOTYPE(t, n)
n.Fields = nil
}
// An InterfaceType represents a struct { ... } type syntax.
type InterfaceType struct {
miniType
Methods []*Field
}
func NewInterfaceType(pos src.XPos, methods []*Field) *InterfaceType {
n := &InterfaceType{Methods: methods}
n.op = OTINTER
n.pos = pos
return n
}
func (n *InterfaceType) SetOTYPE(t *types.Type) {
n.setOTYPE(t, n)
n.Methods = nil
}
// A FuncType represents a func(Args) Results type syntax.
type FuncType struct {
miniType
Recv *Field
Params []*Field
Results []*Field
}
func NewFuncType(pos src.XPos, rcvr *Field, args, results []*Field) *FuncType {
n := &FuncType{Recv: rcvr, Params: args, Results: results}
n.op = OTFUNC
n.pos = pos
return n
}
func (n *FuncType) SetOTYPE(t *types.Type) {
n.setOTYPE(t, n)
n.Recv = nil
n.Params = nil
n.Results = nil
}
// A Field is a declared struct field, interface method, or function argument.
// It is not a Node.
type Field struct {
Pos src.XPos
Sym *types.Sym
Ntype Ntype
Type *types.Type
Embedded bool
IsDDD bool
Note string
Decl *Name
}
func NewField(pos src.XPos, sym *types.Sym, ntyp Ntype, typ *types.Type) *Field {
return &Field{Pos: pos, Sym: sym, Ntype: ntyp, Type: typ}
}
func (f *Field) String() string {
var typ string
if f.Type != nil {
typ = fmt.Sprint(f.Type)
} else {
typ = fmt.Sprint(f.Ntype)
}
if f.Sym != nil {
return fmt.Sprintf("%v %v", f.Sym, typ)
}
return typ
}
// TODO(mdempsky): Make Field a Node again so these can be generated?
// Fields are Nodes in go/ast and cmd/compile/internal/syntax.
func copyField(f *Field) *Field {
if f == nil {
return nil
}
c := *f
return &c
}
func doField(f *Field, do func(Node) bool) bool {
if f == nil {
return false
}
if f.Decl != nil && do(f.Decl) {
return true
}
if f.Ntype != nil && do(f.Ntype) {
return true
}
return false
}
func editField(f *Field, edit func(Node) Node) {
if f == nil {
return
}
if f.Decl != nil {
f.Decl = edit(f.Decl).(*Name)
}
if f.Ntype != nil {
f.Ntype = edit(f.Ntype).(Ntype)
}
}
func copyFields(list []*Field) []*Field {
out := make([]*Field, len(list))
for i, f := range list {
out[i] = copyField(f)
}
return out
}
func doFields(list []*Field, do func(Node) bool) bool {
for _, x := range list {
if doField(x, do) {
return true
}
}
return false
}
func editFields(list []*Field, edit func(Node) Node) {
for _, f := range list {
editField(f, edit)
}
}
// A SliceType represents a []Elem type syntax.
// If DDD is true, it's the ...Elem at the end of a function list.
type SliceType struct {
miniType
Elem Ntype
DDD bool
}
func NewSliceType(pos src.XPos, elem Ntype) *SliceType {
n := &SliceType{Elem: elem}
n.op = OTSLICE
n.pos = pos
return n
}
func (n *SliceType) SetOTYPE(t *types.Type) {
n.setOTYPE(t, n)
n.Elem = nil
}
// An ArrayType represents a [Len]Elem type syntax.
// If Len is nil, the type is a [...]Elem in an array literal.
type ArrayType struct {
miniType
Len Node
Elem Ntype
}
func NewArrayType(pos src.XPos, len Node, elem Ntype) *ArrayType {
n := &ArrayType{Len: len, Elem: elem}
n.op = OTARRAY
n.pos = pos
return n
}
func (n *ArrayType) SetOTYPE(t *types.Type) {
n.setOTYPE(t, n)
n.Len = nil
n.Elem = nil
}
// A typeNode is a Node wrapper for type t.
type typeNode struct {
miniNode
typ *types.Type
}
func newTypeNode(pos src.XPos, typ *types.Type) *typeNode {
n := &typeNode{typ: typ}
n.pos = pos
n.op = OTYPE
return n
}
func (n *typeNode) Type() *types.Type { return n.typ }
func (n *typeNode) Sym() *types.Sym { return n.typ.Sym() }
func (n *typeNode) CanBeNtype() {}
// TypeNode returns the Node representing the type t.
func TypeNode(t *types.Type) Ntype {
if n := t.Obj(); n != nil {
if n.Type() != t {
base.Fatalf("type skew: %v has type %v, but expected %v", n, n.Type(), t)
}
return n.(Ntype)
}
return newTypeNode(src.NoXPos, t)
}

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