mirror of
https://github.com/golang/go.git
synced 2026-01-31 16:12:04 +03:00
Compare commits
87 Commits
go1.8
...
dev.typeal
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a5ef1501d | ||
|
|
352996a381 | ||
|
|
bb5055d6f1 | ||
|
|
439c0c8be8 | ||
|
|
e396667ba3 | ||
|
|
daf6706f37 | ||
|
|
958c64bbab | ||
|
|
195e20a976 | ||
|
|
f55bc1c4eb | ||
|
|
51f508bb4a | ||
|
|
243dee1737 | ||
|
|
a43c0d2dc8 | ||
|
|
1054085dcf | ||
|
|
18a13d373a | ||
|
|
6efa2f22ac | ||
|
|
fb9770f09b | ||
|
|
b6a8fc8d8c | ||
|
|
59870f9e19 | ||
|
|
c9688ddb6b | ||
|
|
38d35f49e7 | ||
|
|
1ba29926f3 | ||
|
|
a4c18f063b | ||
|
|
8babce23e3 | ||
|
|
853d533ed6 | ||
|
|
166f2159d8 | ||
|
|
95a5b80e6d | ||
|
|
fe79c75268 | ||
|
|
d7989b784e | ||
|
|
056be9f79c | ||
|
|
02240408a1 | ||
|
|
04017ffadf | ||
|
|
2d0043014f | ||
|
|
3ca0d34fa1 | ||
|
|
84192f2734 | ||
|
|
752b8b773d | ||
|
|
ff5695d0fd | ||
|
|
517a38c630 | ||
|
|
dc70a5efd1 | ||
|
|
77476e81d9 | ||
|
|
bf71119d54 | ||
|
|
11a224bc56 | ||
|
|
3a8841bcaf | ||
|
|
6c5abcf21a | ||
|
|
43fa04c23c | ||
|
|
e35c01b404 | ||
|
|
c955eb1935 | ||
|
|
f8ed4539eb | ||
|
|
d43130743c | ||
|
|
0a5cec792f | ||
|
|
8890527476 | ||
|
|
ea6781bcd0 | ||
|
|
2327d696c1 | ||
|
|
ba48d2002e | ||
|
|
b43fabfb30 | ||
|
|
6a712dfac1 | ||
|
|
865536b197 | ||
|
|
bae53daa72 | ||
|
|
0954fdd51e | ||
|
|
d4ee1f4a40 | ||
|
|
991ee8f4ac | ||
|
|
3b4fc5d1c6 | ||
|
|
f8b4123613 | ||
|
|
9ecc3ee252 | ||
|
|
49b7af8a30 | ||
|
|
9bbb07ddec | ||
|
|
43c7094386 | ||
|
|
9657e0b077 | ||
|
|
de2e5459ae | ||
|
|
9259f3073a | ||
|
|
5d92916770 | ||
|
|
a7c884efc1 | ||
|
|
5802cfd900 | ||
|
|
d7cabd40dd | ||
|
|
cc2dcce3d7 | ||
|
|
5c160b28ba | ||
|
|
b2386dffa1 | ||
|
|
ac8421f9a5 | ||
|
|
f011e0c6c3 | ||
|
|
49de5f0351 | ||
|
|
5ceec42dc0 | ||
|
|
aa1f0681bc | ||
|
|
c80748e389 | ||
|
|
80d8b69e95 | ||
|
|
a917097b5e | ||
|
|
3e11940437 | ||
|
|
e0a05c274a | ||
|
|
2e5116bd99 |
@@ -22,8 +22,6 @@ using the go <code>tool</code> subcommand, such as <code>go tool vet</code>.
|
||||
This style of invocation allows, for instance, checking a single source file
|
||||
rather than an entire package: <code>go tool vet myprogram.go</code> as
|
||||
compared to <code>go vet mypackage</code>.
|
||||
Some of the commands, such as <code>yacc</code>, are accessible only through
|
||||
the go <code>tool</code> subcommand.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -95,12 +93,6 @@ gofmt</a> command with more general options.</td>
|
||||
calls whose arguments do not align with the format string.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><a href="/cmd/yacc/">yacc</a></td>
|
||||
<td> </td>
|
||||
<td>Yacc is a version of yacc that generates parsers implemented in Go.</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -34,6 +34,7 @@ We encourage all Go users to subscribe to
|
||||
<p>A <a href="/doc/devel/release.html">summary</a> of the changes between Go releases. Notes for the major releases:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="/doc/go1.8">Go 1.8</a> <small>(February 2017)</small></li>
|
||||
<li><a href="/doc/go1.7">Go 1.7</a> <small>(August 2016)</small></li>
|
||||
<li><a href="/doc/go1.6">Go 1.6</a> <small>(February 2016)</small></li>
|
||||
<li><a href="/doc/go1.5">Go 1.5</a> <small>(August 2015)</small></li>
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
<!--{
|
||||
"Title": "Contribution Guidelines"
|
||||
"Title": "Contribution Guide"
|
||||
}-->
|
||||
|
||||
<h2 id="Introduction">Introduction</h2>
|
||||
|
||||
<p>
|
||||
This document explains how to contribute changes to the Go project.
|
||||
It assumes you have followed the
|
||||
<a href="/doc/install/source">installation instructions</a> and
|
||||
have <a href="code.html">written and tested your code</a>.
|
||||
The Go project welcomes all contributors. The process of contributing
|
||||
to the Go project may be different than many projects you are used to.
|
||||
This document is intended as a guide to help you through the contribution
|
||||
process. This guide assumes you have a basic understanding of Git and Go.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -20,103 +18,54 @@ see <a href="gccgo_contribute.html">Contributing to gccgo</a>.)
|
||||
Sensitive security-related issues should be reported to <a href="mailto:security@golang.org">security@golang.org</a>.
|
||||
</p>
|
||||
|
||||
<h2 id="Design">Discuss your design</h2>
|
||||
<h1 id="contributor">Becoming a contributor</h1>
|
||||
|
||||
<p>
|
||||
The project welcomes submissions but please let everyone know what
|
||||
you're working on if you want to change or add to the Go repositories.
|
||||
Before you can contribute to the Go project you need to setup a few prerequisites.
|
||||
The Go project uses <a href="https://www.gerritcodereview.com/">Gerrit</a>, an open
|
||||
source online tool, to perform all code reviews.
|
||||
Gerrit uses your email address as a unique identifier.
|
||||
The Go project contributing flow is currently configured to work only with Google Accounts.
|
||||
You must go through the following process <em>prior to contributing</em>.
|
||||
You only need to do this once per Google Account.
|
||||
</p>
|
||||
|
||||
<h2 id="auth">Configure Git to use Gerrit</h2>
|
||||
<p>
|
||||
You'll need a web browser and a command line terminal.
|
||||
You should already have Git installed.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Before undertaking to write something new for the Go project,
|
||||
please <a href="https://golang.org/issue/new">file an issue</a>
|
||||
(or claim an <a href="https://golang.org/issues">existing issue</a>).
|
||||
Significant changes must go through the
|
||||
<a href="https://golang.org/s/proposal-process">change proposal process</a>
|
||||
before they can be accepted.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This process gives everyone a chance to validate the design,
|
||||
helps prevent duplication of effort,
|
||||
and ensures that the idea fits inside the goals for the language and tools.
|
||||
It also checks that the design is sound before code is written;
|
||||
the code review tool is not the place for high-level discussions.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
When planning work, please note that the Go project follows a
|
||||
<a href="https://golang.org/wiki/Go-Release-Cycle">six-month
|
||||
development cycle</a>. The latter half of each cycle is a three-month
|
||||
feature freeze during which only bug fixes and doc updates are accepted.
|
||||
New work cannot be submitted during a feature freeze.
|
||||
</p>
|
||||
|
||||
<h2 id="Testing">Testing redux</h2>
|
||||
|
||||
<p>
|
||||
You've <a href="code.html">written and tested your code</a>, but
|
||||
before sending code out for review, run all the tests for the whole
|
||||
tree to make sure the changes don't break other packages or programs:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ cd go/src
|
||||
$ ./all.bash
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
(To build under Windows use <code>all.bat</code>.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
After running for a while, the command should print
|
||||
"<code>ALL</code> <code>TESTS</code> <code>PASSED</code>".
|
||||
</p>
|
||||
|
||||
<h2 id="Code_review">Code review</h2>
|
||||
|
||||
<p>
|
||||
Changes to Go must be reviewed before they are accepted,
|
||||
no matter who makes the change.
|
||||
A custom git command called <code>git-codereview</code>,
|
||||
discussed below, helps manage the code review process through a Google-hosted
|
||||
<a href="https://go-review.googlesource.com/">instance</a> of the code review
|
||||
system called <a href="https://www.gerritcodereview.com/">Gerrit</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="auth">Set up authentication for code review</h3>
|
||||
|
||||
<p>
|
||||
Gerrit uses Google Accounts for authentication. If you don't have
|
||||
a Google Account, you can create an account which
|
||||
Gerrit uses Google Accounts for authentication.
|
||||
If you don't have a Google Account, you can create an account which
|
||||
<a href="https://www.google.com/accounts/NewAccount">includes
|
||||
a new Gmail email account</a> or create an account associated
|
||||
<a href="https://accounts.google.com/SignUpWithoutGmail">with your existing
|
||||
email address</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The email address associated with the Google Account you use will be recorded in
|
||||
the <a href="https://go.googlesource.com/go/+log/">change log</a>
|
||||
and in the <a href="/CONTRIBUTORS">contributors file</a>.
|
||||
</p>
|
||||
<h3>Step 1: Sign in to googlesource and generate a password</h3>
|
||||
|
||||
<p>
|
||||
To set up your account in Gerrit, visit
|
||||
<a href="https://go.googlesource.com">go.googlesource.com</a>
|
||||
Visit <a href="https://go.googlesource.com">go.googlesource.com</a>
|
||||
and click on "Generate Password" in the page's top right menu bar.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You will be redirected to accounts.google.com to sign in.
|
||||
</p>
|
||||
|
||||
<h3>Step 2: Run the provided script</h3>
|
||||
<p>
|
||||
Once signed in, you are returned back to go.googlesource.com to "Configure Git".
|
||||
Follow the instructions on the page.
|
||||
(If you are on a Windows computer, you should instead follow the instructions
|
||||
in the yellow box to run the command.)
|
||||
After signing in, you are taken to a page on go.googlesource.com with the title "Configure Git".
|
||||
This page contains a personalized script which when run locally will configure git
|
||||
to have your unique authentication key.
|
||||
This key is paired with one generated server side similar to how ssh keys work.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Copy and run this script locally in your command line terminal.
|
||||
(On a Windows computer using cmd you should instead follow the instructions
|
||||
in the yellow box to run the command. If you are using git-bash use the same
|
||||
script as *nix.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -124,23 +73,25 @@ Your secret authentication token is now in a <code>.gitcookie</code> file
|
||||
and Git is configured to use this file.
|
||||
</p>
|
||||
|
||||
<h3 id="gerrit">Register with Gerrit</h3>
|
||||
<h3 id="gerrit">Step 3: Register with Gerrit</h3>
|
||||
|
||||
<p>
|
||||
Now that you have your authentication token,
|
||||
you need to register your account with Gerrit.
|
||||
To do this, visit
|
||||
<a href="https://go-review.googlesource.com/login/">
|
||||
go-review.googlesource.com/login/</a>. You will immediately be redirected
|
||||
to Google Accounts. Sign in using the same Google Account you used above.
|
||||
That is all that is required.
|
||||
Now that you have your authentication token, you need to register your
|
||||
account with Gerrit.
|
||||
To do this, visit <a href="https://go-review.googlesource.com/login/">
|
||||
go-review.googlesource.com/login/</a>.
|
||||
Sign in using the same Google Account you used above.
|
||||
</p>
|
||||
|
||||
<h3 id="cla">Contributor License Agreement</h3>
|
||||
<h2 id="cla">Contributor License Agreement</h2>
|
||||
|
||||
<h3 id="which_cla">Which CLA</h3>
|
||||
<p>
|
||||
Before sending your first change to the Go project
|
||||
you must have completed one of the following two CLAs.
|
||||
Which CLA you should sign depends on who owns the copyright to your work.
|
||||
</p>
|
||||
|
||||
<p>Gerrit serves as the gatekeeper and uses your e-mail address as the key.
|
||||
To send your first change to the Go project from a given address,
|
||||
you must have completed one of the contributor license agreements:
|
||||
<ul>
|
||||
<li>
|
||||
If you are the copyright holder, you will need to agree to the
|
||||
@@ -151,37 +102,49 @@ contributor license agreement</a>, which can be completed online.
|
||||
If your organization is the copyright holder, the organization
|
||||
will need to agree to the
|
||||
<a href="https://developers.google.com/open-source/cla/corporate">corporate
|
||||
contributor license agreement</a>.
|
||||
(If the copyright holder for your code has already completed the
|
||||
agreement in connection with another Google open source project,
|
||||
it does not need to be completed again.)
|
||||
contributor license agreement</a>.<br>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
You can use the links above to create and sign the contributor license agreement
|
||||
or you can show your current agreements and create new ones through the Gerrit
|
||||
interface. <a href="https://go-review.googlesource.com/login/">Log into Gerrit</a>,
|
||||
<i>If the copyright holder for your contribution has already completed the
|
||||
agreement in connection with another Google open source project,
|
||||
it does not need to be completed again.</i>
|
||||
</p>
|
||||
|
||||
<h3 id="signing_cla">Completing the CLA</h3>
|
||||
|
||||
<p>
|
||||
You can see your currently signed agreements and sign new ones through the Gerrit
|
||||
interface.
|
||||
To do this, <a href="https://go-review.googlesource.com/login/">Log into Gerrit</a>,
|
||||
click your name in the upper-right, choose "Settings", then select "Agreements"
|
||||
from the topics on the left. If you do not have a signed agreement listed here,
|
||||
from the topics on the left.
|
||||
If you do not have a signed agreement listed here,
|
||||
you can create one by clicking "New Contributor Agreement" and following the steps.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This rigmarole only needs to be done for your first submission for each email address.
|
||||
If the copyright holder for the code you are submitting changes — for example,
|
||||
if you start contributing code on behalf of a new company — please send email
|
||||
to golang-dev and let us know, so that we can make sure an appropriate agreement is
|
||||
completed and update the <code>AUTHORS</code> file.
|
||||
</p>
|
||||
|
||||
<span id="Code_review"></span>
|
||||
<h1 id="prepare_dev_env">Preparing a Development Environment for Contributing</h1>
|
||||
|
||||
<h2 id="git-codereview">Setting up Git for submission to Gerrit</h2>
|
||||
<p>
|
||||
If the copyright holder for the code you are submitting changes—for example,
|
||||
if you start contributing code on behalf of a new company—please send email
|
||||
to let us know, so that we can make sure an appropriate agreement is completed
|
||||
and update the <code>AUTHORS</code> file.
|
||||
Changes to Go must be reviewed before they are accepted, no matter who makes the change.
|
||||
A custom git command called <code>git-codereview</code>, discussed below,
|
||||
helps manage the code review process through a Google-hosted
|
||||
<a href="https://go-review.googlesource.com/">instance</a> Gerrit.
|
||||
</p>
|
||||
|
||||
<h3 id="git-codereview">Install the git-codereview command</h3>
|
||||
|
||||
<h3 id="git-codereview_install">Install the git-codereview command</h3>
|
||||
<p>
|
||||
Now install the <code>git-codereview</code> command by running,
|
||||
Install the <code>git-codereview</code> command by running,
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
@@ -202,18 +165,28 @@ prints help text, not an error.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Note to Git aficionados:</b>
|
||||
The <code>git-codereview</code> command is not required to
|
||||
upload and manage Gerrit code reviews. For those who prefer plain Git, the text
|
||||
below gives the Git equivalent of each git-codereview command.
|
||||
On Windows, when using git-bash you must make sure that
|
||||
<code>git-codereview.exe</code> is in your git exec-path.
|
||||
Run <code>git --exec-path</code> to discover the right location then create a
|
||||
symbolic link or simply copy the executible from $GOPATH/bin to this directory.
|
||||
</p>
|
||||
|
||||
<p>If you do use plain
|
||||
Git, note that you still need the commit hooks that the git-codereview command
|
||||
configures; those hooks add a Gerrit <code>Change-Id</code> line to the commit
|
||||
message and check that all Go source files have been formatted with gofmt. Even
|
||||
if you intend to use plain Git for daily work, install the hooks in a new Git
|
||||
checkout by running <code>git-codereview</code> <code>hooks</code>.
|
||||
<p>
|
||||
<b>Note to Git aficionados:</b>
|
||||
The <code>git-codereview</code> command is not required to
|
||||
upload and manage Gerrit code reviews.
|
||||
For those who prefer plain Git, the text below gives the Git equivalent of
|
||||
each git-codereview command.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you do use plain Git, note that you still need the commit hooks that the
|
||||
git-codereview command configures; those hooks add a Gerrit
|
||||
<code>Change-Id</code> line to the commit message and check that all Go source
|
||||
files have been formatted with gofmt.
|
||||
Even if you intend to use plain Git for
|
||||
daily work, install the hooks in a new Git checkout by running
|
||||
<code>git-codereview</code> <code>hooks</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -264,7 +237,8 @@ To install them, copy this text into your Git configuration file
|
||||
sync = codereview sync
|
||||
</pre>
|
||||
|
||||
<h3 id="help">Understanding the git-codereview command</h3>
|
||||
<span id="help"></span>
|
||||
<h3 id="understanding_git-codereview">Understanding the git-codereview command</h3>
|
||||
|
||||
<p>After installing the <code>git-codereview</code> command, you can run</p>
|
||||
|
||||
@@ -277,11 +251,70 @@ to learn more about its commands.
|
||||
You can also read the <a href="https://godoc.org/golang.org/x/review/git-codereview">command documentation</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="master">Switch to the master branch</h3>
|
||||
|
||||
<h1 id="making_a_contribution">Making a Contribution</h1>
|
||||
|
||||
<h2 id="Design">Discuss your design</h2>
|
||||
|
||||
<p>
|
||||
The project welcomes submissions but please let everyone know what
|
||||
you're working on if you want to change or add to the Go repositories.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Before undertaking to write something new for the Go project,
|
||||
please <a href="https://golang.org/issue/new">file an issue</a>
|
||||
(or claim an <a href="https://golang.org/issues">existing issue</a>).
|
||||
Significant changes must go through the
|
||||
<a href="https://golang.org/s/proposal-process">change proposal process</a>
|
||||
before they can be accepted.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This process gives everyone a chance to validate the design,
|
||||
helps prevent duplication of effort,
|
||||
and ensures that the idea fits inside the goals for the language and tools.
|
||||
It also checks that the design is sound before code is written;
|
||||
the code review tool is not the place for high-level discussions.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
When planning work, please note that the Go project follows a <a
|
||||
href="https://golang.org/wiki/Go-Release-Cycle">six-month development cycle</a>.
|
||||
The latter half of each cycle is a three-month feature freeze during
|
||||
which only bug fixes and doc updates are accepted. New contributions can be
|
||||
sent during a feature freeze but will not be accepted until the freeze thaws.
|
||||
</p>
|
||||
|
||||
<h2 id="making_a_change">Making a change</h2>
|
||||
|
||||
<h3 id="checkout_go">Getting Go Source</h3>
|
||||
<p>
|
||||
First you need to have a local copy of the source checked out from the correct
|
||||
repository.
|
||||
As Go builds Go you will also likely need to have a working version
|
||||
of Go installed (some documentation changes may not need this).
|
||||
This should be a recent version of Go and can be obtained via any package or
|
||||
binary distribution or you can build it from source.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You should checkout the Go source repo anywhere you want as long as it's
|
||||
outside of your $GOPATH.
|
||||
Go to a directory where you want the source to appear and run the following
|
||||
command in a terminal.
|
||||
</p>
|
||||
|
||||
<pre><code>
|
||||
$ git clone https://go.googlesource.com/go
|
||||
$ cd go
|
||||
</code></pre>
|
||||
|
||||
<h3 id="master">Contributing to the main Go tree</h3>
|
||||
|
||||
<p>
|
||||
Most Go installations use a release branch, but new changes should
|
||||
only be made based on the master branch.
|
||||
only be made based on the master branch. <br>
|
||||
(They may be applied later to a release branch as part of the release process,
|
||||
but most contributors won't do this themselves.)
|
||||
Before making a change, make sure you start on the master branch:
|
||||
@@ -297,10 +330,61 @@ $ git sync
|
||||
<code>git</code> <code>pull</code> <code>-r</code>.)
|
||||
</p>
|
||||
|
||||
<h3 id="change">Make a change</h3>
|
||||
<h3 id="subrepos">Contributing to subrepositories (golang.org/x/...)</h3>
|
||||
|
||||
<p>
|
||||
If you are contributing a change to a subrepository, obtain the
|
||||
Go package using <code>go get</code>. For example, to contribute
|
||||
to <code>golang.org/x/oauth2</code>, check out the code by running:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ go get -d golang.org/x/oauth2/...
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Then, change your directory to the package's source directory
|
||||
(<code>$GOPATH/src/golang.org/x/oauth2</code>).
|
||||
</p>
|
||||
|
||||
<h3 id="change">Make your changes</h3>
|
||||
|
||||
<p>
|
||||
The entire checked-out tree is editable.
|
||||
Make your changes as you see fit ensuring that you create appropriate
|
||||
tests along with your changes. Test your changes as you go.
|
||||
</p>
|
||||
|
||||
<h3 id="copyright">Copyright</h3>
|
||||
|
||||
<p>
|
||||
Files in the Go repository don't list author names, both to avoid clutter
|
||||
and to avoid having to keep the lists up to date.
|
||||
Instead, your name will appear in the
|
||||
<a href="https://golang.org/change">change log</a> and in the <a
|
||||
href="/CONTRIBUTORS"><code>CONTRIBUTORS</code></a> file and perhaps the <a
|
||||
href="/AUTHORS"><code>AUTHORS</code></a> file.
|
||||
These files are automatically generated from the commit logs perodically.
|
||||
The <a href="/AUTHORS"><code>AUTHORS</code></a> file defines who “The Go
|
||||
Authors”—the copyright holders—are.
|
||||
</p>
|
||||
|
||||
<p>New files that you contribute should use the standard copyright header:</p>
|
||||
|
||||
<pre>
|
||||
// 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.
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Files in the repository are copyright the year they are added.
|
||||
Do not update the copyright year on files that you change.
|
||||
</p>
|
||||
|
||||
<h3 id="commit_changes">Commit your changes</h3>
|
||||
|
||||
<p>
|
||||
The entire checked-out tree is writable.
|
||||
Once you have edited files, you must tell Git that they have been modified.
|
||||
You must also tell Git about any files that are added, removed, or renamed files.
|
||||
These operations are done with the usual Git commands,
|
||||
@@ -311,16 +395,19 @@ and
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you wish to checkpoint your work, or are ready to send the code out for review, run</p>
|
||||
Once you have the changes queued up, you will want to commit them.
|
||||
In the Go contribution workflow this is done with a `git change` command,
|
||||
which creates a local branch and commits the changes directly to that local branch.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ git change <i><branch></i>
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
from any directory in your Go repository to commit the changes so far.
|
||||
The name <i><branch></i> is an arbitrary one you choose to identify the
|
||||
local branch containing your changes.
|
||||
local branch containing your changes and will not be used elsewhere.
|
||||
This is an offline operation and nothing will be sent to the server yet.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -331,9 +418,11 @@ then <code>git</code> <code>commit</code>.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Git will open a change description file in your editor.
|
||||
As the `git commit` is the final step, Git will open an editor to ask for a
|
||||
commit message.
|
||||
(It uses the editor named by the <code>$EDITOR</code> environment variable,
|
||||
<code>vi</code> by default.)
|
||||
|
||||
The file will look like:
|
||||
</p>
|
||||
|
||||
@@ -352,7 +441,7 @@ At the beginning of this file is a blank line; replace it
|
||||
with a thorough description of your change.
|
||||
The first line of the change description is conventionally a one-line
|
||||
summary of the change, prefixed by the primary affected package,
|
||||
and is used as the subject for code review mail.
|
||||
and is used as the subject for code review email.
|
||||
It should complete the sentence "This change modifies Go to _____."
|
||||
The rest of the description elaborates and should provide context for the
|
||||
change and explain what it does.
|
||||
@@ -395,7 +484,7 @@ the command and move that file to a different branch.
|
||||
<p>
|
||||
The special notation "Fixes #159" associates the change with issue 159 in the
|
||||
<a href="https://golang.org/issue/159">Go issue tracker</a>.
|
||||
When this change is eventually submitted, the issue
|
||||
When this change is eventually applied, the issue
|
||||
tracker will automatically mark the issue as fixed.
|
||||
(There are several such conventions, described in detail in the
|
||||
<a href="https://help.github.com/articles/closing-issues-via-commit-messages/">GitHub Issue Tracker documentation</a>.)
|
||||
@@ -406,6 +495,13 @@ Once you have finished writing the commit message,
|
||||
save the file and exit the editor.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You must have the $EDITOR environment variable set properly and working properly (exiting cleanly)
|
||||
for this operation to succeed.
|
||||
If you run into any issues at this step, it's likely your editor isn't exiting cleanly.
|
||||
Try setting a different editor in your $EDITOR environment variable.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you wish to do more editing, re-stage your changes using
|
||||
<code>git</code> <code>add</code>, and then run
|
||||
@@ -416,8 +512,8 @@ $ git change
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
to update the change description and incorporate the staged changes. The
|
||||
change description contains a <code>Change-Id</code> line near the bottom,
|
||||
to update the change description and incorporate the staged changes.
|
||||
The change description contains a <code>Change-Id</code> line near the bottom,
|
||||
added by a Git commit hook during the initial
|
||||
<code>git</code> <code>change</code>.
|
||||
That line is used by Gerrit to match successive uploads of the same change.
|
||||
@@ -429,35 +525,44 @@ Do not edit or delete it.
|
||||
runs <code>git</code> <code>commit</code> <code>--amend</code>.)
|
||||
</p>
|
||||
|
||||
<h3 id="mail">Mail the change for review</h3>
|
||||
<h3 id="Testing">Testing</h3>
|
||||
|
||||
<p>
|
||||
Once the change is ready, mail it out for review:
|
||||
You've <a href="code.html">written and tested your code</a>, but
|
||||
before sending code out for review, run all the tests for the whole
|
||||
tree to make sure the changes don't break other packages or programs:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ cd go/src
|
||||
$ ./all.bash
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
(To build under Windows use <code>all.bat</code>.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
After running for a while, the command should print
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
"ALL TESTS PASSED".
|
||||
</pre>
|
||||
|
||||
<h3 id="mail">Send the change for review</h3>
|
||||
|
||||
<p>
|
||||
Once the change is ready, send it for review.
|
||||
This is similar to a <code>git push</code> in a GitHub style workflow.
|
||||
This is done via the mail alias setup earlier which despite its name, doesn't
|
||||
directly mail anything, it simply sends the change to Gerrit via git push.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ git mail
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
You can specify a reviewer or CC interested parties
|
||||
using the <code>-r</code> or <code>-cc</code> options.
|
||||
Both accept a comma-separated list of email addresses:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ git mail -r joe@golang.org -cc mabel@example.com,math-nuts@swtch.com
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Unless explicitly told otherwise, such as in the discussion leading
|
||||
up to sending in the change list, it's better not to specify a reviewer.
|
||||
All changes are automatically CC'ed to the
|
||||
<a href="https://groups.google.com/group/golang-codereviews">golang-codereviews@googlegroups.com</a>
|
||||
mailing list. If this is your first ever change, there may be a moderation
|
||||
delay before it appears on the mailing list, to prevent spam.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
(In Git terms, <code>git</code> <code>mail</code> pushes the local committed
|
||||
changes to Gerrit using <code>git</code> <code>push</code> <code>origin</code>
|
||||
@@ -479,7 +584,76 @@ remote: New Changes:
|
||||
remote: https://go-review.googlesource.com/99999 math: improved Sin, Cos and Tan precision for very large arguments
|
||||
</pre>
|
||||
|
||||
<h3 id="review">Reviewing code</h3>
|
||||
<h3>Troubleshooting</h3>
|
||||
|
||||
<p>
|
||||
The most common way that the <code>git mail</code> command fails is because the
|
||||
email address used has not gone through the setup above.
|
||||
<br>
|
||||
If you see something like...
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
remote: Processing changes: refs: 1, done
|
||||
remote:
|
||||
remote: ERROR: In commit ab13517fa29487dcf8b0d48916c51639426c5ee9
|
||||
remote: ERROR: author email address XXXXXXXXXXXXXXXXXXX
|
||||
remote: ERROR: does not match your user account.
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
You need to either add the email address listed to the CLA or set this repo to use
|
||||
another email address already approved.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
First let's change the email address for this repo so this doesn't happen again.
|
||||
You can change your email address for this repo with the following command:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ git config user.email email@address.com
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Then change the previous commit to use this alternative email address.
|
||||
You can do that with:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ git commit --amend --author="Author Name <email@address.com>"
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Finally try to resend with:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ git mail
|
||||
</pre>
|
||||
|
||||
<h3 id="cc">Specifying a reviewer / CCing others</h3>
|
||||
|
||||
<p>
|
||||
Unless explicitly told otherwise, such as in the discussion leading
|
||||
up to sending in the change list, it's better not to specify a reviewer.
|
||||
All changes are automatically CC'ed to the
|
||||
<a href="https://groups.google.com/group/golang-codereviews">golang-codereviews@googlegroups.com</a>
|
||||
mailing list. If this is your first ever change, there may be a moderation
|
||||
delay before it appears on the mailing list, to prevent spam.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can specify a reviewer or CC interested parties
|
||||
using the <code>-r</code> or <code>-cc</code> options.
|
||||
Both accept a comma-separated list of email addresses:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ git mail -r joe@golang.org -cc mabel@example.com,math-nuts@swtch.com
|
||||
</pre>
|
||||
|
||||
<h2 id="review">Going through the review process</h2>
|
||||
|
||||
<p>
|
||||
Running <code>git</code> <code>mail</code> will send an email to you and the
|
||||
@@ -491,7 +665,15 @@ You must reply through the web interface.
|
||||
(Unlike with the old Rietveld review system, replying by mail has no effect.)
|
||||
</p>
|
||||
|
||||
<h3 id="revise">Revise and upload</h3>
|
||||
<h3 id="revise">Revise and resend</h3>
|
||||
|
||||
<p>
|
||||
The Go contribution workflow is optimized for iterative revisions based on
|
||||
feedback.
|
||||
It is rare that an initial contribution will be ready to be applied as is.
|
||||
As you revise your contribution and resend Gerrit will retain a history of
|
||||
all the changes and comments made in the single URL.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You must respond to review comments through the web interface.
|
||||
@@ -534,6 +716,8 @@ $ git sync
|
||||
<code>git</code> <code>pull</code> <code>-r</code>.)
|
||||
</p>
|
||||
|
||||
<h3 id="resolving_conflicts">Resolving Conflicts</h3>
|
||||
|
||||
<p>
|
||||
If files you were editing have changed, Git does its best to merge the
|
||||
remote changes into your local changes.
|
||||
@@ -609,8 +793,8 @@ might turn up:
|
||||
<p>
|
||||
Git doesn't show it, but suppose the original text that both edits
|
||||
started with was 1e8; you changed it to 1e10 and the other change to 1e9,
|
||||
so the correct answer might now be 1e10. First, edit the section
|
||||
to remove the markers and leave the correct code:
|
||||
so the correct answer might now be 1e10.
|
||||
First, edit the section to remove the markers and leave the correct code:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
@@ -639,10 +823,13 @@ restore the change commit.
|
||||
<h3 id="download">Reviewing code by others</h3>
|
||||
|
||||
<p>
|
||||
You can import a change proposed by someone else into your local Git repository.
|
||||
As part of the review process reviewers can propose changes directly (in the
|
||||
GitHub workflow this would be someone else attaching commits to a pull request).
|
||||
|
||||
You can import these changes proposed by someone else into your local Git repository.
|
||||
On the Gerrit review page, click the "Download ▼" link in the upper right
|
||||
corner, copy the "Checkout" command and run it from your local Git repo.
|
||||
It should look something like this:
|
||||
corner, copy the "Checkout" command and run it from your local Git repo. It
|
||||
should look something like this:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
@@ -653,11 +840,11 @@ $ git fetch https://go.googlesource.com/review refs/changes/21/1221/1 &&
|
||||
To revert, change back to the branch you were working in.
|
||||
</p>
|
||||
|
||||
<h3 id="submit">Submit the change after the review</h3>
|
||||
<h2 id="submit">Apply the change to the master branch</h2>
|
||||
|
||||
<p>
|
||||
After the code has been <code>LGTM</code>'ed, an approver may
|
||||
submit it to the master branch using the Gerrit UI.
|
||||
apply it to the master branch using the Gerrit UI.
|
||||
There is a "Submit" button on the web page for the change
|
||||
that appears once the change is approved (marked +2).
|
||||
</p>
|
||||
@@ -669,41 +856,13 @@ and the code review will be updated with a link to the change
|
||||
in the repository.
|
||||
Since the method used to integrate the changes is "Cherry Pick",
|
||||
the commit hashes in the repository will be changed by
|
||||
the submit operation.
|
||||
the "Submit" operation.
|
||||
</p>
|
||||
|
||||
<h3 id="more">More information</h3>
|
||||
<h2 id="more">More information</h2>
|
||||
|
||||
<p>
|
||||
In addition to the information here, the Go community maintains a <a href="https://golang.org/wiki/CodeReview">CodeReview</a> wiki page.
|
||||
In addition to the information here, the Go community maintains a <a
|
||||
href="https://golang.org/wiki/CodeReview">CodeReview</a> wiki page.
|
||||
Feel free to contribute to this page as you learn the review process.
|
||||
</p>
|
||||
|
||||
<h2 id="copyright">Copyright</h2>
|
||||
|
||||
<p>Files in the Go repository don't list author names,
|
||||
both to avoid clutter and to avoid having to keep the lists up to date.
|
||||
Instead, your name will appear in the
|
||||
<a href="https://golang.org/change">change log</a>
|
||||
and in the <a href="/CONTRIBUTORS"><code>CONTRIBUTORS</code></a> file
|
||||
and perhaps the <a href="/AUTHORS"><code>AUTHORS</code></a> file.
|
||||
</p>
|
||||
|
||||
<p>The <a href="/CONTRIBUTORS"><code>CONTRIBUTORS</code></a> file
|
||||
defines who the Go contributors—the people—are;
|
||||
the <a href="/AUTHORS"><code>AUTHORS</code></a> file defines
|
||||
who “The Go Authors”—the copyright holders—are.
|
||||
These files will be periodically updated based on the commit logs.
|
||||
|
||||
<p>Code that you contribute should use the standard copyright header:</p>
|
||||
|
||||
<pre>
|
||||
// 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.
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Files in the repository are copyright the year they are added. It is not
|
||||
necessary to update the copyright year on files that you change.
|
||||
</p>
|
||||
|
||||
@@ -37,6 +37,32 @@ Go 1.8 is a major release of Go.
|
||||
Read the <a href="/doc/go1.8">Go 1.8 Release Notes</a> for more information.
|
||||
</p>
|
||||
|
||||
<h3 id="go1.8.minor">Minor revisions</h3>
|
||||
|
||||
<p>
|
||||
go1.8.1 (released 2017/04/07) includes fixes to the compiler, linker, runtime,
|
||||
documentation, <code>go</code> command and the <code>crypto/tls</code>,
|
||||
<code>encoding/xml</code>, <code>image/png</code>, <code>net</code>,
|
||||
<code>net/http</code>, <code>reflect</code>, <code>text/template</code>,
|
||||
and <code>time</code> packages.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.8.1">Go
|
||||
1.8.1 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.8.2 (released 2017/05/23) includes a security fix to the
|
||||
<code>crypto/elliptic</code> package.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.8.2">Go
|
||||
1.8.2 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.8.3 (released 2017/05/24) includes fixes to the compiler, runtime,
|
||||
documentation, and the <code>database/sql</code> package.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.8.3">Go
|
||||
1.8.3 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.7">go1.7 (released 2016/08/15)</h2>
|
||||
|
||||
<p>
|
||||
@@ -83,6 +109,13 @@ See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.5">Go
|
||||
1.7.5 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.7.6 (released 2017/05/23) includes the same security fix as Go 1.8.2 and
|
||||
was released at the same time.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.8.2">Go
|
||||
1.8.2 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.6">go1.6 (released 2016/02/17)</h2>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -1304,7 +1304,7 @@ crypto/x509: return error for missing SerialNumber (CL 27238)
|
||||
request must have the new
|
||||
<a href="/pkg/net/http/#Request"><code>Request.GetBody</code></a>
|
||||
field defined.
|
||||
<a href="pkg/net/http/#NewRequest"><code>NewRequest</code></a>
|
||||
<a href="/pkg/net/http/#NewRequest"><code>NewRequest</code></a>
|
||||
sets <code>Request.GetBody</code> automatically for common
|
||||
body types.
|
||||
</li>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--{
|
||||
"Title": "The Go Programming Language Specification",
|
||||
"Subtitle": "Version of November 18, 2016",
|
||||
"Subtitle": "Version of January 31, 2017",
|
||||
"Path": "/ref/spec"
|
||||
}-->
|
||||
|
||||
@@ -738,7 +738,7 @@ The method set of any other type <code>T</code> consists of all
|
||||
The method set of the corresponding <a href="#Pointer_types">pointer type</a> <code>*T</code>
|
||||
is the set of all methods declared with receiver <code>*T</code> or <code>T</code>
|
||||
(that is, it also contains the method set of <code>T</code>).
|
||||
Further rules apply to structs containing anonymous fields, as described
|
||||
Further rules apply to structs containing embedded fields, as described
|
||||
in the section on <a href="#Struct_types">struct types</a>.
|
||||
Any other type has an empty method set.
|
||||
In a method set, each method must have a
|
||||
@@ -947,16 +947,16 @@ Moreover, the inner slices must be initialized individually.
|
||||
<p>
|
||||
A struct is a sequence of named elements, called fields, each of which has a
|
||||
name and a type. Field names may be specified explicitly (IdentifierList) or
|
||||
implicitly (AnonymousField).
|
||||
implicitly (EmbeddedField).
|
||||
Within a struct, non-<a href="#Blank_identifier">blank</a> field names must
|
||||
be <a href="#Uniqueness_of_identifiers">unique</a>.
|
||||
</p>
|
||||
|
||||
<pre class="ebnf">
|
||||
StructType = "struct" "{" { FieldDecl ";" } "}" .
|
||||
FieldDecl = (IdentifierList Type | AnonymousField) [ Tag ] .
|
||||
AnonymousField = [ "*" ] TypeName .
|
||||
Tag = string_lit .
|
||||
StructType = "struct" "{" { FieldDecl ";" } "}" .
|
||||
FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] .
|
||||
EmbeddedField = [ "*" ] TypeName .
|
||||
Tag = string_lit .
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
@@ -974,16 +974,15 @@ struct {
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
A field declared with a type but no explicit field name is an <i>anonymous field</i>,
|
||||
also called an <i>embedded</i> field or an embedding of the type in the struct.
|
||||
An embedded type must be specified as
|
||||
A field declared with a type but no explicit field name is called an <i>embedded field</i>.
|
||||
An embedded field must be specified as
|
||||
a type name <code>T</code> or as a pointer to a non-interface type name <code>*T</code>,
|
||||
and <code>T</code> itself may not be
|
||||
a pointer type. The unqualified type name acts as the field name.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
// A struct with four anonymous fields of type T1, *T2, P.T3 and *P.T4
|
||||
// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4
|
||||
struct {
|
||||
T1 // field name is T1
|
||||
*T2 // field name is T2
|
||||
@@ -1000,15 +999,15 @@ in a struct type:
|
||||
|
||||
<pre>
|
||||
struct {
|
||||
T // conflicts with anonymous field *T and *P.T
|
||||
*T // conflicts with anonymous field T and *P.T
|
||||
*P.T // conflicts with anonymous field T and *T
|
||||
T // conflicts with embedded field *T and *P.T
|
||||
*T // conflicts with embedded field T and *P.T
|
||||
*P.T // conflicts with embedded field T and *T
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
A field or <a href="#Method_declarations">method</a> <code>f</code> of an
|
||||
anonymous field in a struct <code>x</code> is called <i>promoted</i> if
|
||||
embedded field in a struct <code>x</code> is called <i>promoted</i> if
|
||||
<code>x.f</code> is a legal <a href="#Selectors">selector</a> that denotes
|
||||
that field or method <code>f</code>.
|
||||
</p>
|
||||
@@ -1025,7 +1024,7 @@ promoted methods are included in the method set of the struct as follows:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
If <code>S</code> contains an anonymous field <code>T</code>,
|
||||
If <code>S</code> contains an embedded field <code>T</code>,
|
||||
the <a href="#Method_sets">method sets</a> of <code>S</code>
|
||||
and <code>*S</code> both include promoted methods with receiver
|
||||
<code>T</code>. The method set of <code>*S</code> also
|
||||
@@ -1033,7 +1032,7 @@ promoted methods are included in the method set of the struct as follows:
|
||||
</li>
|
||||
|
||||
<li>
|
||||
If <code>S</code> contains an anonymous field <code>*T</code>,
|
||||
If <code>S</code> contains an embedded field <code>*T</code>,
|
||||
the method sets of <code>S</code> and <code>*S</code> both
|
||||
include promoted methods with receiver <code>T</code> or
|
||||
<code>*T</code>.
|
||||
@@ -1434,8 +1433,8 @@ literal structure and corresponding components have identical types. In detail:
|
||||
<li>Two struct types are identical if they have the same sequence of fields,
|
||||
and if corresponding fields have the same names, and identical types,
|
||||
and identical tags.
|
||||
Two anonymous fields are considered to have the same name. Lower-case field
|
||||
names from different packages are always different.</li>
|
||||
<a href="#Exported_identifiers">Non-exported</a> field names from different
|
||||
packages are always different.</li>
|
||||
|
||||
<li>Two pointer types are identical if they have identical base types.</li>
|
||||
|
||||
@@ -1445,8 +1444,9 @@ literal structure and corresponding components have identical types. In detail:
|
||||
Parameter and result names are not required to match.</li>
|
||||
|
||||
<li>Two interface types are identical if they have the same set of methods
|
||||
with the same names and identical function types. Lower-case method names from
|
||||
different packages are always different. The order of the methods is irrelevant.</li>
|
||||
with the same names and identical function types.
|
||||
<a href="#Exported_identifiers">Non-exported</a> method names from different
|
||||
packages are always different. The order of the methods is irrelevant.</li>
|
||||
|
||||
<li>Two map types are identical if they have identical key and value types.</li>
|
||||
|
||||
@@ -1891,7 +1891,7 @@ type NewMutex Mutex
|
||||
type PtrMutex *Mutex
|
||||
|
||||
// The method set of *PrintableMutex contains the methods
|
||||
// Lock and Unlock bound to its anonymous field Mutex.
|
||||
// Lock and Unlock bound to its embedded field Mutex.
|
||||
type PrintableMutex struct {
|
||||
Mutex
|
||||
}
|
||||
@@ -2492,13 +2492,13 @@ If <code>x</code> is a package name, see the section on
|
||||
A selector <code>f</code> may denote a field or method <code>f</code> of
|
||||
a type <code>T</code>, or it may refer
|
||||
to a field or method <code>f</code> of a nested
|
||||
<a href="#Struct_types">anonymous field</a> of <code>T</code>.
|
||||
The number of anonymous fields traversed
|
||||
<a href="#Struct_types">embedded field</a> of <code>T</code>.
|
||||
The number of embedded fields traversed
|
||||
to reach <code>f</code> is called its <i>depth</i> in <code>T</code>.
|
||||
The depth of a field or method <code>f</code>
|
||||
declared in <code>T</code> is zero.
|
||||
The depth of a field or method <code>f</code> declared in
|
||||
an anonymous field <code>A</code> in <code>T</code> is the
|
||||
an embedded field <code>A</code> in <code>T</code> is the
|
||||
depth of <code>f</code> in <code>A</code> plus one.
|
||||
</p>
|
||||
|
||||
|
||||
@@ -221,7 +221,7 @@ To build without <code>cgo</code>, set the environment variable
|
||||
Change to the directory that will be its parent
|
||||
and make sure the <code>go</code> directory does not exist.
|
||||
Then clone the repository and check out the latest release tag
|
||||
(<code class="versionTag">go1.8</code>, for example):</p>
|
||||
(<code class="versionTag">go1.8.1</code>, for example):</p>
|
||||
|
||||
<pre>
|
||||
$ git clone https://go.googlesource.com/go
|
||||
@@ -409,7 +409,7 @@ New releases are announced on the
|
||||
<a href="//groups.google.com/group/golang-announce">golang-announce</a>
|
||||
mailing list.
|
||||
Each announcement mentions the latest release tag, for instance,
|
||||
<code class="versionTag">go1.8</code>.
|
||||
<code class="versionTag">go1.8.1</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -179,6 +179,13 @@ if test "$output" != "PASS"; then
|
||||
status=1
|
||||
fi
|
||||
|
||||
if test "$libext" = "dylib"; then
|
||||
# make sure dylibs are well-formed
|
||||
if ! otool -l libgo*.dylib >/dev/null; then
|
||||
status=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if test $status = 0; then
|
||||
echo "ok"
|
||||
fi
|
||||
|
||||
@@ -27,4 +27,5 @@ func Init() {
|
||||
gc.Thearch.SSAMarkMoves = ssaMarkMoves
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
gc.Thearch.ZeroAuto = zeroAuto
|
||||
}
|
||||
|
||||
@@ -166,6 +166,27 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32, x0 *uin
|
||||
return p
|
||||
}
|
||||
|
||||
func zeroAuto(n *gc.Node, pp *obj.Prog) {
|
||||
// Note: this code must not clobber any registers.
|
||||
op := x86.AMOVQ
|
||||
if gc.Widthptr == 4 {
|
||||
op = x86.AMOVL
|
||||
}
|
||||
sym := gc.Linksym(n.Sym)
|
||||
size := n.Type.Size()
|
||||
for i := int64(0); i < size; i += int64(gc.Widthptr) {
|
||||
p := gc.AddAsmAfter(op, pp)
|
||||
pp = p
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = 0
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
p.To.Reg = x86.REG_SP
|
||||
p.To.Offset = n.Xoffset + i
|
||||
p.To.Sym = sym
|
||||
}
|
||||
}
|
||||
|
||||
func ginsnop() {
|
||||
// This is actually not the x86 NOP anymore,
|
||||
// but at the point where it gets used, AX is dead
|
||||
|
||||
@@ -21,4 +21,5 @@ func Init() {
|
||||
gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
gc.Thearch.ZeroAuto = zeroAuto
|
||||
}
|
||||
|
||||
@@ -92,6 +92,27 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, r0 *uint32) *obj.Pr
|
||||
|
||||
return p
|
||||
}
|
||||
func zeroAuto(n *gc.Node, pp *obj.Prog) {
|
||||
// Note: this code must not clobber any registers.
|
||||
sym := gc.Linksym(n.Sym)
|
||||
size := n.Type.Size()
|
||||
p := gc.Prog(arm.AMOVW)
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = 0
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = arm.REGTMP
|
||||
for i := int64(0); i < size; i += 4 {
|
||||
p := gc.AddAsmAfter(arm.AMOVW, pp)
|
||||
pp = p
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = arm.REGTMP
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
p.To.Reg = arm.REGSP
|
||||
p.To.Offset = n.Xoffset + i
|
||||
p.To.Sym = sym
|
||||
}
|
||||
}
|
||||
|
||||
func ginsnop() {
|
||||
p := gc.Prog(arm.AAND)
|
||||
|
||||
@@ -21,4 +21,5 @@ func Init() {
|
||||
gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
gc.Thearch.ZeroAuto = zeroAuto
|
||||
}
|
||||
|
||||
@@ -103,6 +103,23 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
|
||||
return p
|
||||
}
|
||||
|
||||
func zeroAuto(n *gc.Node, pp *obj.Prog) {
|
||||
// Note: this code must not clobber any registers.
|
||||
sym := gc.Linksym(n.Sym)
|
||||
size := n.Type.Size()
|
||||
for i := int64(0); i < size; i += 8 {
|
||||
p := gc.AddAsmAfter(arm64.AMOVD, pp)
|
||||
pp = p
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = arm64.REGZERO
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
p.To.Reg = arm64.REGSP
|
||||
p.To.Offset = n.Xoffset + i
|
||||
p.To.Sym = sym
|
||||
}
|
||||
}
|
||||
|
||||
func ginsnop() {
|
||||
p := gc.Prog(arm64.AHINT)
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
|
||||
@@ -74,7 +74,13 @@ func widstruct(errtype *Type, t *Type, o int64, flag int) int64 {
|
||||
lastzero = o
|
||||
}
|
||||
o += w
|
||||
if o >= Thearch.MAXWIDTH {
|
||||
maxwidth := Thearch.MAXWIDTH
|
||||
// On 32-bit systems, reflect tables impose an additional constraint
|
||||
// that each field start offset must fit in 31 bits.
|
||||
if maxwidth < 1<<32 {
|
||||
maxwidth = 1<<31 - 1
|
||||
}
|
||||
if o >= maxwidth {
|
||||
yyerror("type %L too large", errtype)
|
||||
o = 8 // small but nonzero
|
||||
}
|
||||
|
||||
@@ -140,11 +140,12 @@ const debugFormat = false // default: false
|
||||
const forceObjFileStability = true
|
||||
|
||||
// Current export format version. Increase with each format change.
|
||||
// 3: added aliasTag and export of aliases
|
||||
// 2: removed unused bool in ODCL export
|
||||
// 4: type name objects support type aliases, uses aliasTag
|
||||
// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used)
|
||||
// 2: removed unused bool in ODCL export (compiler only)
|
||||
// 1: header format change (more regular), export package for _ struct fields
|
||||
// 0: Go1.7 encoding
|
||||
const exportVersion = 3
|
||||
const exportVersion = 4
|
||||
|
||||
// exportInlined enables the export of inlined function bodies and related
|
||||
// dependencies. The compiler should work w/o any loss of functionality with
|
||||
@@ -351,8 +352,8 @@ func export(out *bufio.Writer, trace bool) int {
|
||||
p.tracef("\n")
|
||||
}
|
||||
|
||||
if sym.Flags&SymAlias != 0 {
|
||||
Fatalf("exporter: unexpected alias %v in inlined function body", sym)
|
||||
if sym.isAlias() {
|
||||
Fatalf("exporter: unexpected type alias %v in inlined function body", sym)
|
||||
}
|
||||
|
||||
p.obj(sym)
|
||||
@@ -446,30 +447,6 @@ func unidealType(typ *Type, val Val) *Type {
|
||||
}
|
||||
|
||||
func (p *exporter) obj(sym *Sym) {
|
||||
if sym.Flags&SymAlias != 0 {
|
||||
p.tag(aliasTag)
|
||||
p.pos(nil) // TODO(gri) fix position information
|
||||
// Aliases can only be exported from the package that
|
||||
// declares them (aliases to aliases are resolved to the
|
||||
// original object, and so are uses of aliases in inlined
|
||||
// exported function bodies). Thus, we only need the alias
|
||||
// name without package qualification.
|
||||
if sym.Pkg != localpkg {
|
||||
Fatalf("exporter: export of non-local alias: %v", sym)
|
||||
}
|
||||
p.string(sym.Name)
|
||||
orig := sym.Def.Sym
|
||||
if orig.Flags&SymAlias != 0 {
|
||||
Fatalf("exporter: original object %v marked as alias", sym)
|
||||
}
|
||||
p.qualifiedName(orig)
|
||||
return
|
||||
}
|
||||
|
||||
if sym != sym.Def.Sym {
|
||||
Fatalf("exporter: exported object %v is not original %v", sym, sym.Def.Sym)
|
||||
}
|
||||
|
||||
// Exported objects may be from different packages because they
|
||||
// may be re-exported via an exported alias or as dependencies in
|
||||
// exported inlined function bodies. Thus, exported object names
|
||||
@@ -509,7 +486,13 @@ func (p *exporter) obj(sym *Sym) {
|
||||
Fatalf("exporter: export of incomplete type %v", sym)
|
||||
}
|
||||
|
||||
p.tag(typeTag)
|
||||
if sym.isAlias() {
|
||||
p.tag(aliasTag)
|
||||
p.pos(n)
|
||||
p.qualifiedName(sym)
|
||||
} else {
|
||||
p.tag(typeTag)
|
||||
}
|
||||
p.typ(t)
|
||||
|
||||
case ONAME:
|
||||
@@ -868,19 +851,29 @@ func (p *exporter) methodList(t *Type) {
|
||||
|
||||
func (p *exporter) method(m *Field) {
|
||||
p.pos(m.Nname)
|
||||
p.fieldName(m)
|
||||
p.methodName(m.Sym)
|
||||
p.paramList(m.Type.Params(), false)
|
||||
p.paramList(m.Type.Results(), false)
|
||||
}
|
||||
|
||||
// fieldName is like qualifiedName but it doesn't record the package for exported names.
|
||||
func (p *exporter) fieldName(t *Field) {
|
||||
name := t.Sym.Name
|
||||
if t.Embedded != 0 {
|
||||
name = "" // anonymous field
|
||||
if bname := basetypeName(t.Type); bname != "" && !exportname(bname) {
|
||||
// anonymous field with unexported base type name
|
||||
name = "?" // unexported name to force export of package
|
||||
// anonymous field - we distinguish between 3 cases:
|
||||
// 1) field name matches base type name and is exported
|
||||
// 2) field name matches base type name and is not exported
|
||||
// 3) field name doesn't match base type name (alias name)
|
||||
bname := basetypeName(t.Type)
|
||||
if name == bname {
|
||||
if exportname(name) {
|
||||
name = "" // 1) we don't need to know the field name or package
|
||||
} else {
|
||||
name = "?" // 2) use unexported name "?" to force package export
|
||||
}
|
||||
} else {
|
||||
// 3) indicate alias and export name as is
|
||||
// (this requires an extra "@" but this is a rare case)
|
||||
p.string("@")
|
||||
}
|
||||
}
|
||||
p.string(name)
|
||||
@@ -889,16 +882,23 @@ func (p *exporter) fieldName(t *Field) {
|
||||
}
|
||||
}
|
||||
|
||||
// methodName is like qualifiedName but it doesn't record the package for exported names.
|
||||
func (p *exporter) methodName(sym *Sym) {
|
||||
p.string(sym.Name)
|
||||
if !exportname(sym.Name) {
|
||||
p.pkg(sym.Pkg)
|
||||
}
|
||||
}
|
||||
|
||||
func basetypeName(t *Type) string {
|
||||
s := t.Sym
|
||||
if s == nil && t.IsPtr() {
|
||||
s = t.Elem().Sym // deref
|
||||
}
|
||||
// s should exist, but be conservative
|
||||
if s != nil {
|
||||
return s.Name
|
||||
}
|
||||
return ""
|
||||
return "" // unnamed type
|
||||
}
|
||||
|
||||
func (p *exporter) paramList(params *Type, numbered bool) {
|
||||
@@ -1797,7 +1797,7 @@ const (
|
||||
nilTag
|
||||
unknownTag // not used by gc (only appears in packages with errors)
|
||||
|
||||
// Aliases
|
||||
// Type aliases
|
||||
aliasTag
|
||||
)
|
||||
|
||||
@@ -1835,7 +1835,7 @@ var tagString = [...]string{
|
||||
-nilTag: "nil",
|
||||
-unknownTag: "unknown",
|
||||
|
||||
// Aliases
|
||||
// Type aliases
|
||||
-aliasTag: "alias",
|
||||
}
|
||||
|
||||
@@ -1889,7 +1889,7 @@ func predeclared() []*Type {
|
||||
Types[TCOMPLEX128],
|
||||
Types[TSTRING],
|
||||
|
||||
// aliases
|
||||
// basic type aliases
|
||||
bytetype,
|
||||
runetype,
|
||||
|
||||
|
||||
@@ -86,10 +86,10 @@ func Import(in *bufio.Reader) {
|
||||
|
||||
// read version specific flags - extend as necessary
|
||||
switch p.version {
|
||||
// case 4:
|
||||
// case 5:
|
||||
// ...
|
||||
// fallthrough
|
||||
case 3, 2, 1:
|
||||
case 4, 3, 2, 1:
|
||||
p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
|
||||
p.trackAllTypes = p.bool()
|
||||
p.posInfoFormat = p.bool()
|
||||
@@ -317,6 +317,12 @@ func (p *importer) obj(tag int) {
|
||||
val := p.value(typ)
|
||||
importconst(sym, idealType(typ), nodlit(val))
|
||||
|
||||
case aliasTag:
|
||||
p.pos()
|
||||
sym := p.qualifiedName()
|
||||
typ := p.typ()
|
||||
importalias(sym, typ)
|
||||
|
||||
case typeTag:
|
||||
p.typ()
|
||||
|
||||
@@ -356,17 +362,6 @@ func (p *importer) obj(tag int) {
|
||||
}
|
||||
}
|
||||
|
||||
case aliasTag:
|
||||
p.pos()
|
||||
alias := importpkg.Lookup(p.string())
|
||||
orig := p.qualifiedName()
|
||||
|
||||
// Although the protocol allows the alias to precede the original,
|
||||
// this never happens in files produced by gc.
|
||||
alias.Flags |= SymAlias
|
||||
alias.Def = orig.Def
|
||||
importsym(alias, orig.Def.Op)
|
||||
|
||||
default:
|
||||
formatErrorf("unexpected object (tag = %d)", tag)
|
||||
}
|
||||
@@ -473,14 +468,7 @@ func (p *importer) typ() *Type {
|
||||
result := p.paramList()
|
||||
nointerface := p.bool()
|
||||
|
||||
base := recv[0].Type
|
||||
star := false
|
||||
if base.IsPtr() {
|
||||
base = base.Elem()
|
||||
star = true
|
||||
}
|
||||
|
||||
n := methodname0(sym, star, base.Sym)
|
||||
n := newfuncname(methodname(sym, recv[0].Type))
|
||||
n.Type = functypefield(recv[0], params, result)
|
||||
checkwidth(n.Type)
|
||||
addmethod(sym, n.Type, false, nointerface)
|
||||
@@ -583,19 +571,22 @@ func (p *importer) fieldList() (fields []*Field) {
|
||||
|
||||
func (p *importer) field() *Field {
|
||||
p.pos()
|
||||
sym := p.fieldName()
|
||||
sym, alias := p.fieldName()
|
||||
typ := p.typ()
|
||||
note := p.string()
|
||||
|
||||
f := newField()
|
||||
if sym.Name == "" {
|
||||
// anonymous field - typ must be T or *T and T must be a type name
|
||||
// anonymous field: typ must be T or *T and T must be a type name
|
||||
s := typ.Sym
|
||||
if s == nil && typ.IsPtr() {
|
||||
s = typ.Elem().Sym // deref
|
||||
}
|
||||
sym = sym.Pkg.Lookup(s.Name)
|
||||
f.Embedded = 1
|
||||
} else if alias {
|
||||
// anonymous field: we have an explicit name because it's a type alias
|
||||
f.Embedded = 1
|
||||
}
|
||||
|
||||
f.Sym = sym
|
||||
@@ -618,7 +609,7 @@ func (p *importer) methodList() (methods []*Field) {
|
||||
|
||||
func (p *importer) method() *Field {
|
||||
p.pos()
|
||||
sym := p.fieldName()
|
||||
sym := p.methodName()
|
||||
params := p.paramList()
|
||||
result := p.paramList()
|
||||
|
||||
@@ -629,18 +620,44 @@ func (p *importer) method() *Field {
|
||||
return f
|
||||
}
|
||||
|
||||
func (p *importer) fieldName() *Sym {
|
||||
func (p *importer) fieldName() (*Sym, bool) {
|
||||
name := p.string()
|
||||
if p.version == 0 && name == "_" {
|
||||
// version 0 didn't export a package for _ fields
|
||||
// version 0 didn't export a package for _ field names
|
||||
// but used the builtin package instead
|
||||
return builtinpkg.Lookup(name), false
|
||||
}
|
||||
pkg := localpkg
|
||||
alias := false
|
||||
switch name {
|
||||
case "":
|
||||
// 1) field name matches base type name and is exported: nothing to do
|
||||
case "?":
|
||||
// 2) field name matches base type name and is not exported: need package
|
||||
name = ""
|
||||
pkg = p.pkg()
|
||||
case "@":
|
||||
// 3) field name doesn't match base type name (alias name): need name and possibly package
|
||||
name = p.string()
|
||||
alias = true
|
||||
fallthrough
|
||||
default:
|
||||
if !exportname(name) {
|
||||
pkg = p.pkg()
|
||||
}
|
||||
}
|
||||
return pkg.Lookup(name), alias
|
||||
}
|
||||
|
||||
func (p *importer) methodName() *Sym {
|
||||
name := p.string()
|
||||
if p.version == 0 && name == "_" {
|
||||
// version 0 didn't export a package for _ method names
|
||||
// but used the builtin package instead
|
||||
return builtinpkg.Lookup(name)
|
||||
}
|
||||
pkg := localpkg
|
||||
if name != "" && !exportname(name) {
|
||||
if name == "?" {
|
||||
name = ""
|
||||
}
|
||||
if !exportname(name) {
|
||||
pkg = p.pkg()
|
||||
}
|
||||
return pkg.Lookup(name)
|
||||
|
||||
@@ -519,10 +519,6 @@ func funchdr(n *Node) {
|
||||
Fatalf("funchdr: dclcontext = %d", dclcontext)
|
||||
}
|
||||
|
||||
if Ctxt.Flag_dynlink && importpkg == nil && n.Func.Nname != nil {
|
||||
makefuncsym(n.Func.Nname.Sym)
|
||||
}
|
||||
|
||||
dclcontext = PAUTO
|
||||
funcstart(n)
|
||||
|
||||
@@ -695,10 +691,20 @@ func typedcl0(s *Sym) *Node {
|
||||
|
||||
// node n, which was returned by typedcl0
|
||||
// is being declared to have uncompiled type t.
|
||||
// return the ODCLTYPE node to use.
|
||||
func typedcl1(n *Node, t *Node, local bool) *Node {
|
||||
n.Name.Param.Ntype = t
|
||||
n.Local = local
|
||||
// returns the ODCLTYPE node to use.
|
||||
func typedcl1(n *Node, t *Node, pragma Pragma, alias bool) *Node {
|
||||
if pragma != 0 && alias {
|
||||
yyerror("cannot specify directive with type alias")
|
||||
pragma = 0
|
||||
}
|
||||
|
||||
n.Local = true
|
||||
|
||||
p := n.Name.Param
|
||||
p.Ntype = t
|
||||
p.Pragma = pragma
|
||||
p.Alias = alias
|
||||
|
||||
return nod(ODCLTYPE, n, nil)
|
||||
}
|
||||
|
||||
@@ -1153,19 +1159,19 @@ bad:
|
||||
return nil
|
||||
}
|
||||
|
||||
func methodname(n *Node, t *Node) *Node {
|
||||
// methodname is a misnomer because this now returns a Sym, rather
|
||||
// than an ONAME.
|
||||
// TODO(mdempsky): Reconcile with methodsym.
|
||||
func methodname(s *Sym, recv *Type) *Sym {
|
||||
star := false
|
||||
if t.Op == OIND {
|
||||
if recv.IsPtr() {
|
||||
star = true
|
||||
t = t.Left
|
||||
recv = recv.Elem()
|
||||
}
|
||||
|
||||
return methodname0(n.Sym, star, t.Sym)
|
||||
}
|
||||
|
||||
func methodname0(s *Sym, star bool, tsym *Sym) *Node {
|
||||
tsym := recv.Sym
|
||||
if tsym == nil || isblanksym(s) {
|
||||
return newfuncname(s)
|
||||
return s
|
||||
}
|
||||
|
||||
var p string
|
||||
@@ -1181,14 +1187,13 @@ func methodname0(s *Sym, star bool, tsym *Sym) *Node {
|
||||
s = Pkglookup(p, tsym.Pkg)
|
||||
}
|
||||
|
||||
return newfuncname(s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Add a method, declared as a function.
|
||||
// - msym is the method symbol
|
||||
// - t is function type (with receiver)
|
||||
func addmethod(msym *Sym, t *Type, local, nointerface bool) {
|
||||
// get field sym
|
||||
if msym == nil {
|
||||
Fatalf("no method symbol")
|
||||
}
|
||||
@@ -1309,7 +1314,7 @@ func funcsym(s *Sym) *Sym {
|
||||
s1 := Pkglookup(s.Name+"·f", s.Pkg)
|
||||
if !Ctxt.Flag_dynlink && s1.Def == nil {
|
||||
s1.Def = newfuncname(s1)
|
||||
s1.Def.Func.Shortname = newname(s)
|
||||
s1.Def.Func.Shortname = s
|
||||
funcsyms = append(funcsyms, s1.Def)
|
||||
}
|
||||
s.Fsym = s1
|
||||
@@ -1326,8 +1331,11 @@ func makefuncsym(s *Sym) {
|
||||
return
|
||||
}
|
||||
s1 := funcsym(s)
|
||||
if s1.Def != nil {
|
||||
return
|
||||
}
|
||||
s1.Def = newfuncname(s1)
|
||||
s1.Def.Func.Shortname = newname(s)
|
||||
s1.Def.Func.Shortname = s
|
||||
funcsyms = append(funcsyms, s1.Def)
|
||||
}
|
||||
|
||||
|
||||
@@ -477,6 +477,10 @@ func escAnalyze(all []*Node, recursive bool) {
|
||||
for _, n := range all {
|
||||
if n.Op == ODCLFUNC {
|
||||
n.Esc = EscFuncPlanned
|
||||
if Debug['m'] > 3 {
|
||||
Dump("escAnalyze", n)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1675,7 +1679,10 @@ func (e *EscState) escflows(dst, src *Node, why *EscStep) {
|
||||
}
|
||||
|
||||
// Don't bother building a graph for scalars.
|
||||
if src.Type != nil && !haspointers(src.Type) {
|
||||
if src.Type != nil && !haspointers(src.Type) && !isReflectHeaderDataField(src) {
|
||||
if Debug['m'] > 3 {
|
||||
fmt.Printf("%v::NOT flows:: %S <- %S\n", linestr(lineno), dst, src)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -45,8 +45,8 @@ func exportsym(n *Node) {
|
||||
fmt.Printf("export symbol %v\n", n.Sym)
|
||||
}
|
||||
|
||||
// Ensure original object is on exportlist before aliases.
|
||||
if n.Sym.Flags&SymAlias != 0 {
|
||||
// Ensure original types are on exportlist before type aliases.
|
||||
if n.Sym.isAlias() {
|
||||
exportlist = append(exportlist, n.Sym.Def)
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func autoexport(n *Node, ctxt Class) {
|
||||
if (ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN {
|
||||
return
|
||||
}
|
||||
if n.Name.Param != nil && n.Name.Param.Ntype != nil && n.Name.Param.Ntype.Op == OTFUNC && n.Name.Param.Ntype.Left != nil { // method
|
||||
if n.Type != nil && n.Type.IsKind(TFUNC) && n.Type.Recv() != nil { // method
|
||||
return
|
||||
}
|
||||
|
||||
@@ -348,6 +348,27 @@ func importvar(s *Sym, t *Type) {
|
||||
}
|
||||
}
|
||||
|
||||
// importalias declares symbol s as an imported type alias with type t.
|
||||
func importalias(s *Sym, t *Type) {
|
||||
importsym(s, OTYPE)
|
||||
if s.Def != nil && s.Def.Op == OTYPE {
|
||||
if eqtype(t, s.Def.Type) {
|
||||
return
|
||||
}
|
||||
yyerror("inconsistent definition for type alias %v during import\n\t%v (in %q)\n\t%v (in %q)", s, s.Def.Type, s.Importdef.Path, t, importpkg.Path)
|
||||
}
|
||||
|
||||
n := newname(s)
|
||||
n.Op = OTYPE
|
||||
s.Importdef = importpkg
|
||||
n.Type = t
|
||||
declare(n, PEXTERN)
|
||||
|
||||
if Debug['E'] != 0 {
|
||||
fmt.Printf("import type %v = %L\n", s, t)
|
||||
}
|
||||
}
|
||||
|
||||
func dumpasmhdr() {
|
||||
b, err := bio.Create(asmhdr)
|
||||
if err != nil {
|
||||
|
||||
@@ -1077,6 +1077,7 @@ var opprec = []int{
|
||||
OSEND: 3,
|
||||
OANDAND: 2,
|
||||
OOROR: 1,
|
||||
|
||||
// Statements handled by stmtfmt
|
||||
OAS: -1,
|
||||
OAS2: -1,
|
||||
@@ -1104,7 +1105,8 @@ var opprec = []int{
|
||||
OSWITCH: -1,
|
||||
OXCASE: -1,
|
||||
OXFALL: -1,
|
||||
OEND: 0,
|
||||
|
||||
OEND: 0,
|
||||
}
|
||||
|
||||
func (n *Node) exprfmt(s fmt.State, prec int) {
|
||||
|
||||
@@ -63,9 +63,12 @@ const (
|
||||
SymSiggen
|
||||
SymAsm
|
||||
SymAlgGen
|
||||
SymAlias // alias, original is Sym.Def.Sym
|
||||
)
|
||||
|
||||
func (sym *Sym) isAlias() bool {
|
||||
return sym.Def != nil && sym.Def.Sym != sym
|
||||
}
|
||||
|
||||
// The Class of a variable/function describes the "storage class"
|
||||
// of a variable or function. During parsing, storage classes are
|
||||
// called declaration contexts.
|
||||
@@ -87,7 +90,7 @@ const (
|
||||
// of the compilers arrays.
|
||||
//
|
||||
// typedef struct
|
||||
// { // must not move anything
|
||||
// { // must not move anything
|
||||
// uchar array[8]; // pointer to data
|
||||
// uchar nel[4]; // number of elements
|
||||
// uchar cap[4]; // allocated number of elements
|
||||
@@ -104,7 +107,7 @@ var sizeof_Array int // runtime sizeof(Array)
|
||||
// of the compilers strings.
|
||||
//
|
||||
// typedef struct
|
||||
// { // must not move anything
|
||||
// { // must not move anything
|
||||
// uchar array[8]; // pointer to data
|
||||
// uchar nel[4]; // number of elements
|
||||
// } String;
|
||||
@@ -361,6 +364,12 @@ type Arch struct {
|
||||
// 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)
|
||||
|
||||
// ZeroAuto emits code to zero the given auto stack variable.
|
||||
// Code is added immediately after pp.
|
||||
// ZeroAuto must not use any non-temporary registers.
|
||||
// ZeroAuto will only be called for variables which contain a pointer.
|
||||
ZeroAuto func(n *Node, pp *obj.Prog)
|
||||
}
|
||||
|
||||
var pcloc int32
|
||||
|
||||
@@ -72,6 +72,15 @@ func Appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int16, foffset in
|
||||
return q
|
||||
}
|
||||
|
||||
func AddAsmAfter(as obj.As, p *obj.Prog) *obj.Prog {
|
||||
q := Ctxt.NewProg()
|
||||
Clearp(q)
|
||||
q.As = as
|
||||
q.Link = p.Link
|
||||
p.Link = q
|
||||
return q
|
||||
}
|
||||
|
||||
func ggloblnod(nam *Node) {
|
||||
s := Linksym(nam.Sym)
|
||||
s.Gotype = Linksym(ngotype(nam))
|
||||
|
||||
@@ -340,13 +340,16 @@ func Main() {
|
||||
// Phase 1: const, type, and names and types of funcs.
|
||||
// This will gather all the information about types
|
||||
// and methods but doesn't depend on any of it.
|
||||
// We also defer type alias declarations until phase 2
|
||||
// to avoid cycles like #18640.
|
||||
defercheckwidth()
|
||||
|
||||
// Don't use range--typecheck can add closures to xtop.
|
||||
timings.Start("fe", "typecheck", "top1")
|
||||
for i := 0; i < len(xtop); i++ {
|
||||
if xtop[i].Op != ODCL && xtop[i].Op != OAS && xtop[i].Op != OAS2 {
|
||||
xtop[i] = typecheck(xtop[i], Etop)
|
||||
n := xtop[i]
|
||||
if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias) {
|
||||
xtop[i] = typecheck(n, Etop)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,8 +359,9 @@ func Main() {
|
||||
// Don't use range--typecheck can add closures to xtop.
|
||||
timings.Start("fe", "typecheck", "top2")
|
||||
for i := 0; i < len(xtop); i++ {
|
||||
if xtop[i].Op == ODCL || xtop[i].Op == OAS || xtop[i].Op == OAS2 {
|
||||
xtop[i] = typecheck(xtop[i], Etop)
|
||||
n := xtop[i]
|
||||
if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias {
|
||||
xtop[i] = typecheck(n, Etop)
|
||||
}
|
||||
}
|
||||
resumecheckwidth()
|
||||
@@ -367,8 +371,9 @@ func Main() {
|
||||
timings.Start("fe", "typecheck", "func")
|
||||
var fcount int64
|
||||
for i := 0; i < len(xtop); i++ {
|
||||
if xtop[i].Op == ODCLFUNC || xtop[i].Op == OCLOSURE {
|
||||
Curfn = xtop[i]
|
||||
n := xtop[i]
|
||||
if op := n.Op; op == ODCLFUNC || op == OCLOSURE {
|
||||
Curfn = n
|
||||
decldepth = 1
|
||||
saveerrors()
|
||||
typecheckslice(Curfn.Nbody.Slice(), Etop)
|
||||
@@ -460,8 +465,9 @@ func Main() {
|
||||
timings.Start("be", "compilefuncs")
|
||||
fcount = 0
|
||||
for i := 0; i < len(xtop); i++ {
|
||||
if xtop[i].Op == ODCLFUNC {
|
||||
funccompile(xtop[i])
|
||||
n := xtop[i]
|
||||
if n.Op == ODCLFUNC {
|
||||
funccompile(n)
|
||||
fcount++
|
||||
}
|
||||
}
|
||||
@@ -924,7 +930,7 @@ func mkpackage(pkgname string) {
|
||||
continue
|
||||
}
|
||||
|
||||
if s.Def.Sym != s && s.Flags&SymAlias == 0 {
|
||||
if s.isAlias() {
|
||||
// throw away top-level name left over
|
||||
// from previous import . "x"
|
||||
if s.Def.Name != nil && s.Def.Name.Pack != nil && !s.Def.Name.Pack.Used && nsyntaxerrors == 0 {
|
||||
|
||||
@@ -154,11 +154,7 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) {
|
||||
|
||||
func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
|
||||
names := p.declNames(decl.NameList)
|
||||
|
||||
var typ *Node
|
||||
if decl.Type != nil {
|
||||
typ = p.typeExpr(decl.Type)
|
||||
}
|
||||
typ := p.typeExprOrNil(decl.Type)
|
||||
|
||||
var exprs []*Node
|
||||
if decl.Values != nil {
|
||||
@@ -171,11 +167,7 @@ func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
|
||||
|
||||
func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node {
|
||||
names := p.declNames(decl.NameList)
|
||||
|
||||
var typ *Node
|
||||
if decl.Type != nil {
|
||||
typ = p.typeExpr(decl.Type)
|
||||
}
|
||||
typ := p.typeExprOrNil(decl.Type)
|
||||
|
||||
var exprs []*Node
|
||||
if decl.Values != nil {
|
||||
@@ -187,14 +179,11 @@ func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node {
|
||||
|
||||
func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
|
||||
name := typedcl0(p.name(decl.Name))
|
||||
name.Name.Param.Pragma = Pragma(decl.Pragma)
|
||||
|
||||
var typ *Node
|
||||
if decl.Type != nil {
|
||||
typ = p.typeExpr(decl.Type)
|
||||
}
|
||||
// decl.Type may be nil but in that case we got a syntax error during parsing
|
||||
typ := p.typeExprOrNil(decl.Type)
|
||||
|
||||
return typedcl1(name, typ, true)
|
||||
return typedcl1(name, typ, Pragma(decl.Pragma), decl.Alias)
|
||||
}
|
||||
|
||||
func (p *noder) declNames(names []*syntax.Name) []*Node {
|
||||
@@ -259,19 +248,19 @@ func (p *noder) funcHeader(fun *syntax.FuncDecl) *Node {
|
||||
yyerror("func main must have no arguments and no return values")
|
||||
}
|
||||
}
|
||||
|
||||
f.Func.Nname = newfuncname(name)
|
||||
} else {
|
||||
// Receiver MethodName Signature
|
||||
|
||||
f.Func.Shortname = newfuncname(name)
|
||||
f.Func.Nname = methodname(f.Func.Shortname, t.Left.Right)
|
||||
f.Func.Shortname = name
|
||||
name = nblank.Sym // filled in by typecheckfunc
|
||||
}
|
||||
|
||||
f.Func.Nname = newfuncname(name)
|
||||
f.Func.Nname.Name.Defn = f
|
||||
f.Func.Nname.Name.Param.Ntype = t // TODO: check if nname already has an ntype
|
||||
|
||||
declare(f.Func.Nname, PFUNC)
|
||||
if fun.Recv == nil {
|
||||
declare(f.Func.Nname, PFUNC)
|
||||
}
|
||||
|
||||
funchdr(f)
|
||||
return f
|
||||
}
|
||||
@@ -467,6 +456,13 @@ func (p *noder) typeExpr(typ syntax.Expr) *Node {
|
||||
return p.expr(typ)
|
||||
}
|
||||
|
||||
func (p *noder) typeExprOrNil(typ syntax.Expr) *Node {
|
||||
if typ != nil {
|
||||
return p.expr(typ)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *noder) chanDir(dir syntax.ChanDir) ChanDir {
|
||||
switch dir {
|
||||
case 0:
|
||||
|
||||
@@ -213,7 +213,7 @@ func dumpglobls() {
|
||||
}
|
||||
|
||||
for _, n := range funcsyms {
|
||||
dsymptr(n.Sym, 0, n.Sym.Def.Func.Shortname.Sym, 0)
|
||||
dsymptr(n.Sym, 0, n.Sym.Def.Func.Shortname, 0)
|
||||
ggloblsym(n.Sym, int32(Widthptr), obj.DUPOK|obj.RODATA)
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,30 @@ func Gvarlive(n *Node) {
|
||||
}
|
||||
|
||||
func removevardef(firstp *obj.Prog) {
|
||||
// At VARKILLs, zero variable if it is ambiguously live.
|
||||
// After the VARKILL anything this variable references
|
||||
// might be collected. If it were to become live again later,
|
||||
// the GC will see references to already-collected objects.
|
||||
// See issue 20029.
|
||||
for p := firstp; p != nil; p = p.Link {
|
||||
if p.As != obj.AVARKILL {
|
||||
continue
|
||||
}
|
||||
n := p.To.Node.(*Node)
|
||||
if !n.Name.Needzero {
|
||||
continue
|
||||
}
|
||||
if n.Class != PAUTO {
|
||||
Fatalf("zero of variable which isn't PAUTO %v", n)
|
||||
}
|
||||
if n.Type.Size()%int64(Widthptr) != 0 {
|
||||
Fatalf("zero of variable not a multiple of ptr size %v", n)
|
||||
}
|
||||
Thearch.ZeroAuto(n, p)
|
||||
}
|
||||
|
||||
for p := firstp; p != nil; p = p.Link {
|
||||
|
||||
for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL || p.Link.As == obj.AVARLIVE) {
|
||||
p.Link = p.Link.Link
|
||||
}
|
||||
|
||||
@@ -511,7 +511,7 @@ func isExportedField(ft *Field) (bool, *Pkg) {
|
||||
// dnameField dumps a reflect.name for a struct field.
|
||||
func dnameField(s *Sym, ot int, spkg *Pkg, ft *Field) int {
|
||||
var name string
|
||||
if ft.Sym != nil && ft.Embedded == 0 {
|
||||
if ft.Sym != nil {
|
||||
name = ft.Sym.Name
|
||||
}
|
||||
isExported, fpkg := isExportedField(ft)
|
||||
@@ -1345,7 +1345,14 @@ ok:
|
||||
// ../../../../runtime/type.go:/structField
|
||||
ot = dnameField(s, ot, pkg, f)
|
||||
ot = dsymptr(s, ot, dtypesym(f.Type), 0)
|
||||
ot = duintptr(s, ot, uint64(f.Offset))
|
||||
offsetAnon := uint64(f.Offset) << 1
|
||||
if offsetAnon>>1 != uint64(f.Offset) {
|
||||
Fatalf("%v: bad field offset for %s", t, f.Sym.Name)
|
||||
}
|
||||
if f.Embedded != 0 {
|
||||
offsetAnon |= 1
|
||||
}
|
||||
ot = duintptr(s, ot, offsetAnon)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3470,8 +3470,13 @@ func (s *state) insertWBstore(t *Type, left, right *ssa.Value, line int32, skip
|
||||
if s.WBLineno == 0 {
|
||||
s.WBLineno = left.Line
|
||||
}
|
||||
s.storeTypeScalars(t, left, right, skip)
|
||||
s.storeTypePtrsWB(t, left, right)
|
||||
if t == Types[TUINTPTR] {
|
||||
// Stores to reflect.{Slice,String}Header.Data.
|
||||
s.vars[&memVar] = s.newValue3I(ssa.OpStoreWB, ssa.TypeMem, s.config.PtrSize, left, right, s.mem())
|
||||
} else {
|
||||
s.storeTypeScalars(t, left, right, skip)
|
||||
s.storeTypePtrsWB(t, left, right)
|
||||
}
|
||||
|
||||
// WB ops will be expanded to branches at writebarrier phase.
|
||||
// To make it easy, we put WB ops at the end of a block, so
|
||||
|
||||
@@ -63,6 +63,9 @@ func TestArithmeticConst(t *testing.T) { runTest(t, "arithConst.go") }
|
||||
|
||||
func TestChan(t *testing.T) { runTest(t, "chan.go") }
|
||||
|
||||
// TestComparisonsConst tests results for comparison operations against constants.
|
||||
func TestComparisonsConst(t *testing.T) { runTest(t, "cmpConst.go") }
|
||||
|
||||
func TestCompound(t *testing.T) { runTest(t, "compound.go") }
|
||||
|
||||
func TestCtl(t *testing.T) { runTest(t, "ctl.go") }
|
||||
|
||||
@@ -27,7 +27,7 @@ type Node struct {
|
||||
// func
|
||||
Func *Func
|
||||
|
||||
// ONAME
|
||||
// ONAME, OTYPE, OPACK, OLABEL, some OLITERAL
|
||||
Name *Name
|
||||
|
||||
Sym *Sym // various
|
||||
@@ -59,8 +59,8 @@ type Node struct {
|
||||
Noescape bool // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360)
|
||||
Walkdef uint8 // tracks state during typecheckdef; 2 == loop detected
|
||||
Typecheck uint8 // tracks state during typechecking; 2 == loop detected
|
||||
Local bool
|
||||
IsStatic bool // whether this Node will be converted to purely static data
|
||||
Local bool // type created in this file (see also Type.Local); TODO(gri): move this into flags
|
||||
IsStatic bool // whether this Node will be converted to purely static data
|
||||
Initorder uint8
|
||||
Used bool // for variable/label declared and not used error
|
||||
Isddd bool // is the argument variadic
|
||||
@@ -180,14 +180,14 @@ func (n *Node) SetIota(x int64) {
|
||||
n.Xoffset = x
|
||||
}
|
||||
|
||||
// Name holds Node fields used only by named nodes (ONAME, OPACK, OLABEL, some OLITERAL).
|
||||
// Name holds Node fields used only by named nodes (ONAME, OTYPE, OPACK, OLABEL, some OLITERAL).
|
||||
type Name struct {
|
||||
Pack *Node // real package for import . names
|
||||
Pkg *Pkg // pkg for OPACK nodes
|
||||
Heapaddr *Node // temp holding heap address of param (could move to Param?)
|
||||
Defn *Node // initializing assignment
|
||||
Curfn *Node // function for local variables
|
||||
Param *Param // additional fields for ONAME
|
||||
Param *Param // additional fields for ONAME, OTYPE
|
||||
Decldepth int32 // declaration loop depth, increased for every loop or label
|
||||
Vargen int32 // unique name for ONAME within a function. Function outputs are numbered starting at one.
|
||||
Funcdepth int32
|
||||
@@ -280,15 +280,16 @@ type Param struct {
|
||||
Innermost *Node
|
||||
Outer *Node
|
||||
|
||||
// OTYPE pragmas
|
||||
// OTYPE
|
||||
//
|
||||
// TODO: Should Func pragmas also be stored on the Name?
|
||||
Pragma Pragma
|
||||
Alias bool // node is alias for Ntype (only used when type-checking ODCLTYPE)
|
||||
}
|
||||
|
||||
// Func holds Node fields used only with function-like nodes.
|
||||
type Func struct {
|
||||
Shortname *Node
|
||||
Shortname *Sym
|
||||
Enter Nodes // for example, allocate and initialize memory for escaping parameters
|
||||
Exit Nodes
|
||||
Cvars Nodes // closure params
|
||||
@@ -382,7 +383,7 @@ const (
|
||||
ODCLFUNC // func f() or func (r) f()
|
||||
ODCLFIELD // struct field, interface field, or func/method argument/return value.
|
||||
ODCLCONST // const pi = 3.14
|
||||
ODCLTYPE // type Int int
|
||||
ODCLTYPE // type Int int or type Int = int
|
||||
|
||||
ODELETE // delete(Left, Right)
|
||||
ODOT // Left.Sym (Left is of struct type)
|
||||
|
||||
11
src/cmd/compile/internal/gc/testdata/arith.go
vendored
11
src/cmd/compile/internal/gc/testdata/arith.go
vendored
@@ -488,6 +488,17 @@ func testLrot() {
|
||||
wantA, wantB, wantC, wantD, ", got", a, b, c, d)
|
||||
failed = true
|
||||
}
|
||||
// Also test inputs with the top bit set, and make sure
|
||||
// sub-word right shift has high bits cleared first.
|
||||
// See issue #19270.
|
||||
wantA, wantB, wantC, wantD = uint8(0xdf), uint16(0xdfff),
|
||||
uint32(0xdfffffff), uint64(0xdfffffffffffffff)
|
||||
a, b, c, d = lrot1_ssa(0xfe, 0xfffe, 0xfffffffe, 0xfffffffffffffffe)
|
||||
if a != wantA || b != wantB || c != wantC || d != wantD {
|
||||
println("lrot1_ssa(0xfe, 0xfffe, 0xfffffffe, 0xfffffffffffffffe)=",
|
||||
wantA, wantB, wantC, wantD, ", got", a, b, c, d)
|
||||
failed = true
|
||||
}
|
||||
x := lrot2_ssa(0xb0000001, 32)
|
||||
wantX := uint32(0xb0000001)
|
||||
if x != wantX {
|
||||
|
||||
2217
src/cmd/compile/internal/gc/testdata/cmpConst.go
vendored
Normal file
2217
src/cmd/compile/internal/gc/testdata/cmpConst.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
248
src/cmd/compile/internal/gc/testdata/gen/cmpConstGen.go
vendored
Normal file
248
src/cmd/compile/internal/gc/testdata/gen/cmpConstGen.go
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
// 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 program generates a test to verify that the standard comparison
|
||||
// operators properly handle one const operand. The test file should be
|
||||
// generated with a known working version of go.
|
||||
// launch with `go run cmpConstGen.go` a file called cmpConst.go
|
||||
// will be written into the parent directory containing the tests
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/big"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const (
|
||||
maxU64 = (1 << 64) - 1
|
||||
maxU32 = (1 << 32) - 1
|
||||
maxU16 = (1 << 16) - 1
|
||||
maxU8 = (1 << 8) - 1
|
||||
|
||||
maxI64 = (1 << 63) - 1
|
||||
maxI32 = (1 << 31) - 1
|
||||
maxI16 = (1 << 15) - 1
|
||||
maxI8 = (1 << 7) - 1
|
||||
|
||||
minI64 = -(1 << 63)
|
||||
minI32 = -(1 << 31)
|
||||
minI16 = -(1 << 15)
|
||||
minI8 = -(1 << 7)
|
||||
)
|
||||
|
||||
func cmp(left *big.Int, op string, right *big.Int) bool {
|
||||
switch left.Cmp(right) {
|
||||
case -1: // less than
|
||||
return op == "<" || op == "<=" || op == "!="
|
||||
case 0: // equal
|
||||
return op == "==" || op == "<=" || op == ">="
|
||||
case 1: // greater than
|
||||
return op == ">" || op == ">=" || op == "!="
|
||||
}
|
||||
panic("unexpected comparison value")
|
||||
}
|
||||
|
||||
func inRange(typ string, val *big.Int) bool {
|
||||
min, max := &big.Int{}, &big.Int{}
|
||||
switch typ {
|
||||
case "uint64":
|
||||
max = max.SetUint64(maxU64)
|
||||
case "uint32":
|
||||
max = max.SetUint64(maxU32)
|
||||
case "uint16":
|
||||
max = max.SetUint64(maxU16)
|
||||
case "uint8":
|
||||
max = max.SetUint64(maxU8)
|
||||
case "int64":
|
||||
min = min.SetInt64(minI64)
|
||||
max = max.SetInt64(maxI64)
|
||||
case "int32":
|
||||
min = min.SetInt64(minI32)
|
||||
max = max.SetInt64(maxI32)
|
||||
case "int16":
|
||||
min = min.SetInt64(minI16)
|
||||
max = max.SetInt64(maxI16)
|
||||
case "int8":
|
||||
min = min.SetInt64(minI8)
|
||||
max = max.SetInt64(maxI8)
|
||||
default:
|
||||
panic("unexpected type")
|
||||
}
|
||||
return cmp(min, "<=", val) && cmp(val, "<=", max)
|
||||
}
|
||||
|
||||
func getValues(typ string) []*big.Int {
|
||||
Uint := func(v uint64) *big.Int { return big.NewInt(0).SetUint64(v) }
|
||||
Int := func(v int64) *big.Int { return big.NewInt(0).SetInt64(v) }
|
||||
values := []*big.Int{
|
||||
// limits
|
||||
Uint(maxU64),
|
||||
Uint(maxU64 - 1),
|
||||
Uint(maxI64 + 1),
|
||||
Uint(maxI64),
|
||||
Uint(maxI64 - 1),
|
||||
Uint(maxU32 + 1),
|
||||
Uint(maxU32),
|
||||
Uint(maxU32 - 1),
|
||||
Uint(maxI32 + 1),
|
||||
Uint(maxI32),
|
||||
Uint(maxI32 - 1),
|
||||
Uint(maxU16 + 1),
|
||||
Uint(maxU16),
|
||||
Uint(maxU16 - 1),
|
||||
Uint(maxI16 + 1),
|
||||
Uint(maxI16),
|
||||
Uint(maxI16 - 1),
|
||||
Uint(maxU8 + 1),
|
||||
Uint(maxU8),
|
||||
Uint(maxU8 - 1),
|
||||
Uint(maxI8 + 1),
|
||||
Uint(maxI8),
|
||||
Uint(maxI8 - 1),
|
||||
Uint(0),
|
||||
Int(minI8 + 1),
|
||||
Int(minI8),
|
||||
Int(minI8 - 1),
|
||||
Int(minI16 + 1),
|
||||
Int(minI16),
|
||||
Int(minI16 - 1),
|
||||
Int(minI32 + 1),
|
||||
Int(minI32),
|
||||
Int(minI32 - 1),
|
||||
Int(minI64 + 1),
|
||||
Int(minI64),
|
||||
|
||||
// other possibly interesting values
|
||||
Uint(1),
|
||||
Int(-1),
|
||||
Uint(0xff << 56),
|
||||
Uint(0xff << 32),
|
||||
Uint(0xff << 24),
|
||||
}
|
||||
sort.Slice(values, func(i, j int) bool { return values[i].Cmp(values[j]) == -1 })
|
||||
var ret []*big.Int
|
||||
for _, val := range values {
|
||||
if !inRange(typ, val) {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, val)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func sigString(v *big.Int) string {
|
||||
var t big.Int
|
||||
t.Abs(v)
|
||||
if v.Sign() == -1 {
|
||||
return "neg" + t.String()
|
||||
}
|
||||
return t.String()
|
||||
}
|
||||
|
||||
func main() {
|
||||
types := []string{
|
||||
"uint64", "uint32", "uint16", "uint8",
|
||||
"int64", "int32", "int16", "int8",
|
||||
}
|
||||
|
||||
w := new(bytes.Buffer)
|
||||
fmt.Fprintf(w, "// run\n")
|
||||
fmt.Fprintf(w, "// Code generated by gen/cmpConstGen.go. DO NOT EDIT.\n\n")
|
||||
fmt.Fprintf(w, "package main;\n")
|
||||
fmt.Fprintf(w, "import (\"fmt\"; \"reflect\"; \"runtime\";)\n")
|
||||
fmt.Fprintf(w, "// results show the expected result for the elements left of, equal to and right of the index.\n")
|
||||
fmt.Fprintf(w, "type result struct{l, e, r bool}\n")
|
||||
fmt.Fprintf(w, "var (\n")
|
||||
fmt.Fprintf(w, " eq = result{l: false, e: true, r: false}\n")
|
||||
fmt.Fprintf(w, " ne = result{l: true, e: false, r: true}\n")
|
||||
fmt.Fprintf(w, " lt = result{l: true, e: false, r: false}\n")
|
||||
fmt.Fprintf(w, " le = result{l: true, e: true, r: false}\n")
|
||||
fmt.Fprintf(w, " gt = result{l: false, e: false, r: true}\n")
|
||||
fmt.Fprintf(w, " ge = result{l: false, e: true, r: true}\n")
|
||||
fmt.Fprintf(w, ")\n")
|
||||
|
||||
operators := []struct{ op, name string }{
|
||||
{"<", "lt"},
|
||||
{"<=", "le"},
|
||||
{">", "gt"},
|
||||
{">=", "ge"},
|
||||
{"==", "eq"},
|
||||
{"!=", "ne"},
|
||||
}
|
||||
|
||||
for _, typ := range types {
|
||||
// generate a slice containing valid values for this type
|
||||
fmt.Fprintf(w, "\n// %v tests\n", typ)
|
||||
values := getValues(typ)
|
||||
fmt.Fprintf(w, "var %v_vals = []%v{\n", typ, typ)
|
||||
for _, val := range values {
|
||||
fmt.Fprintf(w, "%v,\n", val.String())
|
||||
}
|
||||
fmt.Fprintf(w, "}\n")
|
||||
|
||||
// generate test functions
|
||||
for _, r := range values {
|
||||
// TODO: could also test constant on lhs.
|
||||
sig := sigString(r)
|
||||
for _, op := range operators {
|
||||
// no need for go:noinline because the function is called indirectly
|
||||
fmt.Fprintf(w, "func %v_%v_%v(x %v) bool { return x %v %v; }\n", op.name, sig, typ, typ, op.op, r.String())
|
||||
}
|
||||
}
|
||||
|
||||
// generate a table of test cases
|
||||
fmt.Fprintf(w, "var %v_tests = []struct{\n", typ)
|
||||
fmt.Fprintf(w, " idx int // index of the constant used\n")
|
||||
fmt.Fprintf(w, " exp result // expected results\n")
|
||||
fmt.Fprintf(w, " fn func(%v) bool\n", typ)
|
||||
fmt.Fprintf(w, "}{\n")
|
||||
for i, r := range values {
|
||||
sig := sigString(r)
|
||||
for _, op := range operators {
|
||||
fmt.Fprintf(w, "{idx: %v,", i)
|
||||
fmt.Fprintf(w, "exp: %v,", op.name)
|
||||
fmt.Fprintf(w, "fn: %v_%v_%v},\n", op.name, sig, typ)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(w, "}\n")
|
||||
}
|
||||
|
||||
// emit the main function, looping over all test cases
|
||||
fmt.Fprintf(w, "func main() {\n")
|
||||
for _, typ := range types {
|
||||
fmt.Fprintf(w, "for i, test := range %v_tests {\n", typ)
|
||||
fmt.Fprintf(w, " for j, x := range %v_vals {\n", typ)
|
||||
fmt.Fprintf(w, " want := test.exp.l\n")
|
||||
fmt.Fprintf(w, " if j == test.idx {\nwant = test.exp.e\n}")
|
||||
fmt.Fprintf(w, " else if j > test.idx {\nwant = test.exp.r\n}\n")
|
||||
fmt.Fprintf(w, " if test.fn(x) != want {\n")
|
||||
fmt.Fprintf(w, " fn := runtime.FuncForPC(reflect.ValueOf(test.fn).Pointer()).Name()\n")
|
||||
fmt.Fprintf(w, " msg := fmt.Sprintf(\"test failed: %%v(%%v) != %%v [type=%v i=%%v j=%%v idx=%%v]\", fn, x, want, i, j, test.idx)\n", typ)
|
||||
fmt.Fprintf(w, " panic(msg)\n")
|
||||
fmt.Fprintf(w, " }\n")
|
||||
fmt.Fprintf(w, " }\n")
|
||||
fmt.Fprintf(w, "}\n")
|
||||
}
|
||||
fmt.Fprintf(w, "}\n")
|
||||
|
||||
// gofmt result
|
||||
b := w.Bytes()
|
||||
src, err := format.Source(b)
|
||||
if err != nil {
|
||||
fmt.Printf("%s\n", b)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// write to file
|
||||
err = ioutil.WriteFile("../cmpConst.go", src, 0666)
|
||||
if err != nil {
|
||||
log.Fatalf("can't write output: %v\n", err)
|
||||
}
|
||||
}
|
||||
@@ -96,16 +96,16 @@ func typekind(t *Type) string {
|
||||
return fmt.Sprintf("etype=%d", et)
|
||||
}
|
||||
|
||||
// sprint_depchain prints a dependency chain of nodes into fmt.
|
||||
// sprint_depchain prints a dependency chain of nodes into trace.
|
||||
// It is used by typecheck in the case of OLITERAL nodes
|
||||
// to print constant definition loops.
|
||||
func sprint_depchain(fmt_ *string, stack []*Node, cur *Node, first *Node) {
|
||||
func sprint_depchain(trace *string, stack []*Node, cur *Node, first *Node) {
|
||||
for i := len(stack) - 1; i >= 0; i-- {
|
||||
if n := stack[i]; n.Op == cur.Op {
|
||||
if n != first {
|
||||
sprint_depchain(fmt_, stack[:i], n, first)
|
||||
sprint_depchain(trace, stack[:i], n, first)
|
||||
}
|
||||
*fmt_ += fmt.Sprintf("\n\t%v: %v uses %v", n.Line(), n, cur)
|
||||
*trace += fmt.Sprintf("\n\t%v: %v uses %v", n.Line(), n, cur)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -152,7 +152,6 @@ func typecheck(n *Node, top int) *Node {
|
||||
if n.Typecheck == 2 {
|
||||
// Typechecking loop. Trying printing a meaningful message,
|
||||
// otherwise a stack trace of typechecking.
|
||||
var fmt_ string
|
||||
switch n.Op {
|
||||
// We can already diagnose variables used as types.
|
||||
case ONAME:
|
||||
@@ -160,22 +159,30 @@ func typecheck(n *Node, top int) *Node {
|
||||
yyerror("%v is not a type", n)
|
||||
}
|
||||
|
||||
case OTYPE:
|
||||
if top&Etype == Etype {
|
||||
var trace string
|
||||
sprint_depchain(&trace, typecheck_tcstack, n, n)
|
||||
yyerrorl(n.Lineno, "invalid recursive type alias %v%s", n, trace)
|
||||
}
|
||||
|
||||
case OLITERAL:
|
||||
if top&(Erv|Etype) == Etype {
|
||||
yyerror("%v is not a type", n)
|
||||
break
|
||||
}
|
||||
sprint_depchain(&fmt_, typecheck_tcstack, n, n)
|
||||
yyerrorl(n.Lineno, "constant definition loop%s", fmt_)
|
||||
var trace string
|
||||
sprint_depchain(&trace, typecheck_tcstack, n, n)
|
||||
yyerrorl(n.Lineno, "constant definition loop%s", trace)
|
||||
}
|
||||
|
||||
if nsavederrors+nerrors == 0 {
|
||||
fmt_ = ""
|
||||
var trace string
|
||||
for i := len(typecheck_tcstack) - 1; i >= 0; i-- {
|
||||
x := typecheck_tcstack[i]
|
||||
fmt_ += fmt.Sprintf("\n\t%v %v", x.Line(), x)
|
||||
trace += fmt.Sprintf("\n\t%v %v", x.Line(), x)
|
||||
}
|
||||
yyerror("typechecking loop involving %v%s", n, fmt_)
|
||||
yyerror("typechecking loop involving %v%s", n, trace)
|
||||
}
|
||||
|
||||
lineno = lno
|
||||
@@ -1107,7 +1114,7 @@ OpSwitch:
|
||||
|
||||
case OSLICE, OSLICE3:
|
||||
ok |= Erv
|
||||
n.Left = typecheck(n.Left, top)
|
||||
n.Left = typecheck(n.Left, Erv)
|
||||
low, high, max := n.SliceBounds()
|
||||
hasmax := n.Op.IsSlice3()
|
||||
low = typecheck(low, Erv)
|
||||
@@ -1119,6 +1126,10 @@ OpSwitch:
|
||||
max = indexlit(max)
|
||||
n.SetSliceBounds(low, high, max)
|
||||
l := n.Left
|
||||
if l.Type == nil {
|
||||
n.Type = nil
|
||||
return n
|
||||
}
|
||||
if l.Type.IsArray() {
|
||||
if !islvalue(n.Left) {
|
||||
yyerror("invalid operation %v (slice of unaddressable value)", n)
|
||||
@@ -1131,12 +1142,7 @@ OpSwitch:
|
||||
n.Left = typecheck(n.Left, Erv)
|
||||
l = n.Left
|
||||
}
|
||||
|
||||
t := l.Type
|
||||
if t == nil {
|
||||
n.Type = nil
|
||||
return n
|
||||
}
|
||||
var tp *Type
|
||||
if t.IsString() {
|
||||
if hasmax {
|
||||
@@ -3429,7 +3435,14 @@ func typecheckfunc(n *Node) {
|
||||
t.SetNname(n.Func.Nname)
|
||||
rcvr := t.Recv()
|
||||
if rcvr != nil && n.Func.Shortname != nil {
|
||||
addmethod(n.Func.Shortname.Sym, t, true, n.Func.Pragma&Nointerface != 0)
|
||||
n.Func.Nname.Sym = methodname(n.Func.Shortname, rcvr.Type)
|
||||
declare(n.Func.Nname, PFUNC)
|
||||
|
||||
addmethod(n.Func.Shortname, t, true, n.Func.Pragma&Nointerface != 0)
|
||||
}
|
||||
|
||||
if Ctxt.Flag_dynlink && importpkg == nil && n.Func.Nname != nil {
|
||||
makefuncsym(n.Func.Nname.Sym)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3578,8 +3591,6 @@ func typecheckdeftype(n *Node) {
|
||||
|
||||
// copy new type and clear fields
|
||||
// that don't come along.
|
||||
// anything zeroed here must be zeroed in
|
||||
// typedcl2 too.
|
||||
copytype(n, t)
|
||||
|
||||
ret:
|
||||
@@ -3758,12 +3769,29 @@ func typecheckdef(n *Node) *Node {
|
||||
n.Name.Defn = typecheck(n.Name.Defn, Etop) // fills in n->type
|
||||
|
||||
case OTYPE:
|
||||
if p := n.Name.Param; p.Alias {
|
||||
// Type alias declaration: Simply use the rhs type - no need
|
||||
// to create a new type.
|
||||
// If we have a syntax error, p.Ntype may be nil.
|
||||
if p.Ntype != nil {
|
||||
p.Ntype = typecheck(p.Ntype, Etype)
|
||||
n.Type = p.Ntype.Type
|
||||
if n.Type == nil {
|
||||
n.Diag = true
|
||||
goto ret
|
||||
}
|
||||
n.Sym.Def = p.Ntype
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// regular type declaration
|
||||
if Curfn != nil {
|
||||
defercheckwidth()
|
||||
}
|
||||
n.Walkdef = 1
|
||||
n.Type = typ(TFORW)
|
||||
n.Type.Sym = n.Sym
|
||||
n.Type.Sym = n.Sym // TODO(gri) this also happens in typecheckdeftype(n) - where should it happen?
|
||||
nerrors0 := nerrors
|
||||
typecheckdeftype(n)
|
||||
if n.Type.Etype == TFORW && nerrors > nerrors0 {
|
||||
@@ -3771,7 +3799,6 @@ func typecheckdef(n *Node) *Node {
|
||||
// but it was reported. Silence future errors.
|
||||
n.Type.Broke = true
|
||||
}
|
||||
|
||||
if Curfn != nil {
|
||||
resumecheckwidth()
|
||||
}
|
||||
|
||||
@@ -398,6 +398,14 @@ func lexinit1() {
|
||||
// errortype.Orig = makeErrorInterface()
|
||||
s.Def = typenod(errortype)
|
||||
|
||||
// We create separate byte and rune types for better error messages
|
||||
// rather than just creating type alias *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 = Pkglookup("byte", builtinpkg)
|
||||
bytetype = typ(TUINT8)
|
||||
|
||||
@@ -2071,6 +2071,29 @@ func isstack(n *Node) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// isReflectHeaderDataField reports whether l is an expression p.Data
|
||||
// where p has type reflect.SliceHeader or reflect.StringHeader.
|
||||
func isReflectHeaderDataField(l *Node) bool {
|
||||
if l.Type != Types[TUINTPTR] {
|
||||
return false
|
||||
}
|
||||
|
||||
var tsym *Sym
|
||||
switch l.Op {
|
||||
case ODOT:
|
||||
tsym = l.Left.Type.Sym
|
||||
case ODOTPTR:
|
||||
tsym = l.Left.Type.Elem().Sym
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
if tsym == nil || l.Sym.Name != "Data" || tsym.Pkg.Path != "reflect" {
|
||||
return false
|
||||
}
|
||||
return tsym.Name == "SliceHeader" || tsym.Name == "StringHeader"
|
||||
}
|
||||
|
||||
// Do we need a write barrier for the assignment l = r?
|
||||
func needwritebarrier(l *Node, r *Node) bool {
|
||||
if !use_writebarrier {
|
||||
@@ -2081,15 +2104,21 @@ func needwritebarrier(l *Node, r *Node) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// No write barrier for write of non-pointers.
|
||||
dowidth(l.Type)
|
||||
|
||||
if !haspointers(l.Type) {
|
||||
// No write barrier for write to stack.
|
||||
if isstack(l) {
|
||||
return false
|
||||
}
|
||||
|
||||
// No write barrier for write to stack.
|
||||
if isstack(l) {
|
||||
// Package unsafe's documentation says storing pointers into
|
||||
// reflect.SliceHeader and reflect.StringHeader's Data fields
|
||||
// is valid, even though they have type uintptr (#19168).
|
||||
if isReflectHeaderDataField(l) {
|
||||
return true
|
||||
}
|
||||
|
||||
// No write barrier for write of non-pointers.
|
||||
dowidth(l.Type)
|
||||
if !haspointers(l.Type) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -23,4 +23,5 @@ func Init() {
|
||||
gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
gc.Thearch.ZeroAuto = zeroAuto
|
||||
}
|
||||
|
||||
@@ -92,6 +92,23 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
|
||||
return p
|
||||
}
|
||||
|
||||
func zeroAuto(n *gc.Node, pp *obj.Prog) {
|
||||
// Note: this code must not clobber any registers.
|
||||
sym := gc.Linksym(n.Sym)
|
||||
size := n.Type.Size()
|
||||
for i := int64(0); i < size; i += 4 {
|
||||
p := gc.AddAsmAfter(mips.AMOVW, pp)
|
||||
pp = p
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = mips.REGZERO
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
p.To.Reg = mips.REGSP
|
||||
p.To.Offset = n.Xoffset + i
|
||||
p.To.Sym = sym
|
||||
}
|
||||
}
|
||||
|
||||
func ginsnop() {
|
||||
p := gc.Prog(mips.ANOR)
|
||||
p.From.Type = obj.TYPE_REG
|
||||
|
||||
@@ -25,4 +25,5 @@ func Init() {
|
||||
gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
gc.Thearch.ZeroAuto = zeroAuto
|
||||
}
|
||||
|
||||
@@ -95,6 +95,23 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
|
||||
return p
|
||||
}
|
||||
|
||||
func zeroAuto(n *gc.Node, pp *obj.Prog) {
|
||||
// Note: this code must not clobber any registers.
|
||||
sym := gc.Linksym(n.Sym)
|
||||
size := n.Type.Size()
|
||||
for i := int64(0); i < size; i += 8 {
|
||||
p := gc.AddAsmAfter(mips.AMOVV, pp)
|
||||
pp = p
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = mips.REGZERO
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
p.To.Reg = mips.REGSP
|
||||
p.To.Offset = n.Xoffset + i
|
||||
p.To.Sym = sym
|
||||
}
|
||||
}
|
||||
|
||||
func ginsnop() {
|
||||
p := gc.Prog(mips.ANOR)
|
||||
p.From.Type = obj.TYPE_REG
|
||||
|
||||
@@ -24,6 +24,7 @@ func Init() {
|
||||
gc.Thearch.SSAMarkMoves = ssaMarkMoves
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
gc.Thearch.ZeroAuto = zeroAuto
|
||||
|
||||
initvariants()
|
||||
initproginfo()
|
||||
|
||||
@@ -90,6 +90,23 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
|
||||
return p
|
||||
}
|
||||
|
||||
func zeroAuto(n *gc.Node, pp *obj.Prog) {
|
||||
// Note: this code must not clobber any registers.
|
||||
sym := gc.Linksym(n.Sym)
|
||||
size := n.Type.Size()
|
||||
for i := int64(0); i < size; i += 8 {
|
||||
p := gc.AddAsmAfter(ppc64.AMOVD, pp)
|
||||
pp = p
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = ppc64.REGZERO
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
p.To.Reg = ppc64.REGSP
|
||||
p.To.Offset = n.Xoffset + i
|
||||
p.To.Sym = sym
|
||||
}
|
||||
}
|
||||
|
||||
func ginsnop() {
|
||||
p := gc.Prog(ppc64.AOR)
|
||||
p.From.Type = obj.TYPE_REG
|
||||
|
||||
@@ -20,4 +20,5 @@ func Init() {
|
||||
gc.Thearch.SSAMarkMoves = ssaMarkMoves
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
gc.Thearch.ZeroAuto = zeroAuto
|
||||
}
|
||||
|
||||
@@ -143,6 +143,19 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
|
||||
return p
|
||||
}
|
||||
|
||||
func zeroAuto(n *gc.Node, pp *obj.Prog) {
|
||||
// Note: this code must not clobber any registers.
|
||||
p := gc.AddAsmAfter(s390x.ACLEAR, pp)
|
||||
pp = p
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = n.Type.Size()
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
p.To.Reg = s390x.REGSP
|
||||
p.To.Offset = n.Xoffset
|
||||
p.To.Sym = gc.Linksym(n.Sym)
|
||||
}
|
||||
|
||||
func ginsnop() {
|
||||
p := gc.Prog(s390x.AOR)
|
||||
p.From.Type = obj.TYPE_REG
|
||||
|
||||
@@ -294,6 +294,39 @@ func checkFunc(f *Func) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that if a tuple has a memory type, it is second.
|
||||
for _, b := range f.Blocks {
|
||||
for _, v := range b.Values {
|
||||
if v.Type.IsTuple() && v.Type.FieldType(0).IsMemory() {
|
||||
f.Fatalf("memory is first in a tuple: %s\n", v.LongString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that only one memory is live at any point.
|
||||
// TODO: make this check examine interblock.
|
||||
if f.scheduled {
|
||||
for _, b := range f.Blocks {
|
||||
var mem *Value // the live memory
|
||||
for _, v := range b.Values {
|
||||
if v.Op != OpPhi {
|
||||
for _, a := range v.Args {
|
||||
if a.Type.IsMemory() || a.Type.IsTuple() && a.Type.FieldType(1).IsMemory() {
|
||||
if mem == nil {
|
||||
mem = a
|
||||
} else if mem != a {
|
||||
f.Fatalf("two live mems @ %s: %s and %s", v, mem, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() {
|
||||
mem = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// domCheck reports whether x dominates y (including x==y).
|
||||
|
||||
@@ -313,9 +313,13 @@ func cmpVal(v, w *Value, auxIDs auxmap) Cmp {
|
||||
// that generate memory.
|
||||
return lt2Cmp(v.ID < w.ID)
|
||||
}
|
||||
|
||||
if tc := v.Type.Compare(w.Type); tc != CMPeq {
|
||||
return tc
|
||||
// OpSelect is a pseudo-op. We need to be more agressive
|
||||
// regarding CSE to keep multiple OpSelect's of the same
|
||||
// argument from existing.
|
||||
if v.Op != OpSelect0 && v.Op != OpSelect1 {
|
||||
if tc := v.Type.Compare(w.Type); tc != CMPeq {
|
||||
return tc
|
||||
}
|
||||
}
|
||||
|
||||
if v.Aux != w.Aux {
|
||||
|
||||
@@ -64,7 +64,7 @@ func liveValues(f *Func, reachable []bool) []bool {
|
||||
q = append(q, v)
|
||||
}
|
||||
for _, v := range b.Values {
|
||||
if opcodeTable[v.Op].call && !live[v.ID] {
|
||||
if (opcodeTable[v.Op].call || opcodeTable[v.Op].hasSideEffects) && !live[v.ID] {
|
||||
live[v.ID] = true
|
||||
q = append(q, v)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package ssa
|
||||
|
||||
import (
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/obj/s390x"
|
||||
"cmd/internal/obj/x86"
|
||||
"testing"
|
||||
)
|
||||
@@ -21,6 +22,10 @@ func testConfig(t testing.TB) *Config {
|
||||
return NewConfig("amd64", DummyFrontend{t}, testCtxt, true)
|
||||
}
|
||||
|
||||
func testConfigS390X(t testing.TB) *Config {
|
||||
return NewConfig("s390x", DummyFrontend{t}, obj.Linknew(&s390x.Links390x), true)
|
||||
}
|
||||
|
||||
// DummyFrontend is a test-only frontend.
|
||||
// It assumes 64 bit integers and pointers.
|
||||
type DummyFrontend struct {
|
||||
@@ -30,8 +35,20 @@ type DummyFrontend struct {
|
||||
func (DummyFrontend) StringData(s string) interface{} {
|
||||
return nil
|
||||
}
|
||||
func (DummyFrontend) Auto(t Type) GCNode {
|
||||
return nil
|
||||
|
||||
type dummyGCNode struct {
|
||||
typ Type
|
||||
name string
|
||||
}
|
||||
|
||||
func (d *dummyGCNode) Typ() Type {
|
||||
return d.typ
|
||||
}
|
||||
func (d *dummyGCNode) String() string {
|
||||
return d.name
|
||||
}
|
||||
func (d DummyFrontend) Auto(t Type) GCNode {
|
||||
return &dummyGCNode{typ: t, name: "dummy"}
|
||||
}
|
||||
func (d DummyFrontend) SplitString(s LocalSlot) (LocalSlot, LocalSlot) {
|
||||
return LocalSlot{s.N, d.TypeBytePtr(), s.Off}, LocalSlot{s.N, d.TypeInt(), s.Off + 8}
|
||||
|
||||
@@ -522,15 +522,15 @@ func init() {
|
||||
// store arg0 to arg1+auxint+aux, arg2=mem.
|
||||
// These ops return a tuple of <old contents of *(arg1+auxint+aux), memory>.
|
||||
// Note: arg0 and arg1 are backwards compared to MOVLstore (to facilitate resultInArg0)!
|
||||
{name: "XCHGL", argLength: 3, reg: gpstorexchg, asm: "XCHGL", aux: "SymOff", resultInArg0: true, faultOnNilArg1: true},
|
||||
{name: "XCHGQ", argLength: 3, reg: gpstorexchg, asm: "XCHGQ", aux: "SymOff", resultInArg0: true, faultOnNilArg1: true},
|
||||
{name: "XCHGL", argLength: 3, reg: gpstorexchg, asm: "XCHGL", aux: "SymOff", resultInArg0: true, faultOnNilArg1: true, hasSideEffects: true},
|
||||
{name: "XCHGQ", argLength: 3, reg: gpstorexchg, asm: "XCHGQ", aux: "SymOff", resultInArg0: true, faultOnNilArg1: true, hasSideEffects: true},
|
||||
|
||||
// Atomic adds.
|
||||
// *(arg1+auxint+aux) += arg0. arg2=mem.
|
||||
// Returns a tuple of <old contents of *(arg1+auxint+aux), memory>.
|
||||
// Note: arg0 and arg1 are backwards compared to MOVLstore (to facilitate resultInArg0)!
|
||||
{name: "XADDLlock", argLength: 3, reg: gpstorexchg, asm: "XADDL", typ: "(UInt32,Mem)", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true},
|
||||
{name: "XADDQlock", argLength: 3, reg: gpstorexchg, asm: "XADDQ", typ: "(UInt64,Mem)", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true},
|
||||
{name: "XADDLlock", argLength: 3, reg: gpstorexchg, asm: "XADDL", typ: "(UInt32,Mem)", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, hasSideEffects: true},
|
||||
{name: "XADDQlock", argLength: 3, reg: gpstorexchg, asm: "XADDQ", typ: "(UInt64,Mem)", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, hasSideEffects: true},
|
||||
{name: "AddTupleFirst32", argLength: 2}, // arg0=tuple <x,y>. Returns <x+arg1,y>.
|
||||
{name: "AddTupleFirst64", argLength: 2}, // arg0=tuple <x,y>. Returns <x+arg1,y>.
|
||||
|
||||
@@ -553,12 +553,12 @@ func init() {
|
||||
// JEQ ...
|
||||
// but we can't do that because memory-using ops can't generate flags yet
|
||||
// (flagalloc wants to move flag-generating instructions around).
|
||||
{name: "CMPXCHGLlock", argLength: 4, reg: cmpxchg, asm: "CMPXCHGL", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
|
||||
{name: "CMPXCHGQlock", argLength: 4, reg: cmpxchg, asm: "CMPXCHGQ", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
|
||||
{name: "CMPXCHGLlock", argLength: 4, reg: cmpxchg, asm: "CMPXCHGL", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
{name: "CMPXCHGQlock", argLength: 4, reg: cmpxchg, asm: "CMPXCHGQ", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
|
||||
// Atomic memory updates.
|
||||
{name: "ANDBlock", argLength: 3, reg: gpstore, asm: "ANDB", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true}, // *(arg0+auxint+aux) &= arg1
|
||||
{name: "ORBlock", argLength: 3, reg: gpstore, asm: "ORB", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true}, // *(arg0+auxint+aux) |= arg1
|
||||
{name: "ANDBlock", argLength: 3, reg: gpstore, asm: "ANDB", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true}, // *(arg0+auxint+aux) &= arg1
|
||||
{name: "ORBlock", argLength: 3, reg: gpstore, asm: "ORB", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true}, // *(arg0+auxint+aux) |= arg1
|
||||
}
|
||||
|
||||
var AMD64blocks = []blockData{
|
||||
|
||||
@@ -178,8 +178,8 @@
|
||||
(Rsh8x64 x (Const64 [c])) && uint64(c) >= 8 -> (SRAconst (SLLconst <config.fe.TypeUInt32()> x [24]) [31])
|
||||
|
||||
(Lrot32 x [c]) -> (SRRconst x [32-c&31])
|
||||
(Lrot16 <t> x [c]) -> (OR (SLLconst <t> x [c&15]) (SRLconst <t> x [16-c&15]))
|
||||
(Lrot8 <t> x [c]) -> (OR (SLLconst <t> x [c&7]) (SRLconst <t> x [8-c&7]))
|
||||
(Lrot16 <t> x [c]) -> (OR (SLLconst <t> x [c&15]) (SRLconst <t> (ZeroExt16to32 x) [16-c&15]))
|
||||
(Lrot8 <t> x [c]) -> (OR (SLLconst <t> x [c&7]) (SRLconst <t> (ZeroExt8to32 x) [8-c&7]))
|
||||
|
||||
// constants
|
||||
(Const8 [val]) -> (MOVWconst [val])
|
||||
|
||||
@@ -534,69 +534,69 @@
|
||||
(MOVBUload [off1] {sym} (ADDconst [off2] ptr) mem) && is32Bit(off1+off2) ->
|
||||
(MOVBUload [off1+off2] {sym} ptr mem)
|
||||
(MOVHload [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(sym)
|
||||
&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym)) ->
|
||||
(MOVHload [off1+off2] {sym} ptr mem)
|
||||
(MOVHUload [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(sym)
|
||||
&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym)) ->
|
||||
(MOVHUload [off1+off2] {sym} ptr mem)
|
||||
(MOVWload [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(sym)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym)) ->
|
||||
(MOVWload [off1+off2] {sym} ptr mem)
|
||||
(MOVWUload [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(sym)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym)) ->
|
||||
(MOVWUload [off1+off2] {sym} ptr mem)
|
||||
(MOVDload [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(sym)
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym)) ->
|
||||
(MOVDload [off1+off2] {sym} ptr mem)
|
||||
(FMOVSload [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(sym)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym)) ->
|
||||
(FMOVSload [off1+off2] {sym} ptr mem)
|
||||
(FMOVDload [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(sym)
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym)) ->
|
||||
(FMOVDload [off1+off2] {sym} ptr mem)
|
||||
|
||||
(MOVBstore [off1] {sym} (ADDconst [off2] ptr) val mem) && is32Bit(off1+off2) ->
|
||||
(MOVBstore [off1+off2] {sym} ptr val mem)
|
||||
(MOVHstore [off1] {sym} (ADDconst [off2] ptr) val mem)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(sym)
|
||||
&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym)) ->
|
||||
(MOVHstore [off1+off2] {sym} ptr val mem)
|
||||
(MOVWstore [off1] {sym} (ADDconst [off2] ptr) val mem)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(sym)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym)) ->
|
||||
(MOVWstore [off1+off2] {sym} ptr val mem)
|
||||
(MOVDstore [off1] {sym} (ADDconst [off2] ptr) val mem)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(sym)
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym)) ->
|
||||
(MOVDstore [off1+off2] {sym} ptr val mem)
|
||||
(FMOVSstore [off1] {sym} (ADDconst [off2] ptr) val mem)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(sym)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym)) ->
|
||||
(FMOVSstore [off1+off2] {sym} ptr val mem)
|
||||
(FMOVDstore [off1] {sym} (ADDconst [off2] ptr) val mem)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(sym)
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym)) ->
|
||||
(FMOVDstore [off1+off2] {sym} ptr val mem)
|
||||
(MOVBstorezero [off1] {sym} (ADDconst [off2] ptr) mem) && is32Bit(off1+off2) ->
|
||||
(MOVBstorezero [off1+off2] {sym} ptr mem)
|
||||
(MOVHstorezero [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(sym)
|
||||
&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym)) ->
|
||||
(MOVHstorezero [off1+off2] {sym} ptr mem)
|
||||
(MOVWstorezero [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(sym)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym)) ->
|
||||
(MOVWstorezero [off1+off2] {sym} ptr mem)
|
||||
(MOVDstorezero [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%2==8 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(sym)
|
||||
&& ((off1+off2)%2==8 || off1+off2<256 && off1+off2>-256 && !isAuto(sym)) ->
|
||||
(MOVDstorezero [off1+off2] {sym} ptr mem)
|
||||
|
||||
(MOVBload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
|
||||
@@ -606,71 +606,71 @@
|
||||
&& is32Bit(off1+off2) ->
|
||||
(MOVBUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVHload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2))
|
||||
&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2))) ->
|
||||
(MOVHload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVHUload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2))
|
||||
&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2))) ->
|
||||
(MOVHUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVWload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2))
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2))) ->
|
||||
(MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVWUload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2))
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2))) ->
|
||||
(MOVWUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVDload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2))
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2))) ->
|
||||
(MOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(FMOVSload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2))
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2))) ->
|
||||
(FMOVSload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(FMOVDload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2))
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2))) ->
|
||||
(FMOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
|
||||
(MOVBstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2) ->
|
||||
(MOVBstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
(MOVHstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2))
|
||||
&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2))) ->
|
||||
(MOVHstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
(MOVWstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2))
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2))) ->
|
||||
(MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
(MOVDstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2))
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2))) ->
|
||||
(MOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
(FMOVSstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2))
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2))) ->
|
||||
(FMOVSstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
(FMOVDstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2))
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2))) ->
|
||||
(FMOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
(MOVBstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2) ->
|
||||
(MOVBstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVHstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2))
|
||||
&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2))) ->
|
||||
(MOVHstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVWstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2))
|
||||
&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2))) ->
|
||||
(MOVWstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
(MOVDstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
|
||||
&& is32Bit(off1+off2)
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
|
||||
&& is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2))
|
||||
&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2))) ->
|
||||
(MOVDstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
|
||||
// store zero
|
||||
|
||||
@@ -456,16 +456,16 @@ func init() {
|
||||
|
||||
// atomic stores.
|
||||
// store arg1 to arg0. arg2=mem. returns memory. auxint must be zero.
|
||||
{name: "STLR", argLength: 3, reg: gpstore, asm: "STLR", faultOnNilArg0: true},
|
||||
{name: "STLRW", argLength: 3, reg: gpstore, asm: "STLRW", faultOnNilArg0: true},
|
||||
{name: "STLR", argLength: 3, reg: gpstore, asm: "STLR", faultOnNilArg0: true, hasSideEffects: true},
|
||||
{name: "STLRW", argLength: 3, reg: gpstore, asm: "STLRW", faultOnNilArg0: true, hasSideEffects: true},
|
||||
|
||||
// atomic exchange.
|
||||
// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>. auxint must be zero.
|
||||
// LDAXR (Rarg0), Rout
|
||||
// STLXR Rarg1, (Rarg0), Rtmp
|
||||
// CBNZ Rtmp, -2(PC)
|
||||
{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
|
||||
// atomic add.
|
||||
// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>. auxint must be zero.
|
||||
@@ -473,8 +473,8 @@ func init() {
|
||||
// ADD Rarg1, Rout
|
||||
// STLXR Rout, (Rarg0), Rtmp
|
||||
// CBNZ Rtmp, -3(PC)
|
||||
{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
|
||||
// atomic compare and swap.
|
||||
// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. auxint must be zero.
|
||||
@@ -490,8 +490,8 @@ func init() {
|
||||
// STLXR Rarg2, (Rarg0), Rtmp
|
||||
// CBNZ Rtmp, -4(PC)
|
||||
// CSET EQ, Rout
|
||||
{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
|
||||
// atomic and/or.
|
||||
// *arg0 &= (|=) arg1. arg2=mem. returns memory. auxint must be zero.
|
||||
@@ -499,8 +499,8 @@ func init() {
|
||||
// AND/OR Rarg1, Rtmp
|
||||
// STLXRB Rtmp, (Rarg0), Rtmp
|
||||
// CBNZ Rtmp, -3(PC)
|
||||
{name: "LoweredAtomicAnd8", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicOr8", argLength: 3, reg: gpstore, asm: "ORR", faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicAnd8", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true},
|
||||
{name: "LoweredAtomicOr8", argLength: 3, reg: gpstore, asm: "ORR", faultOnNilArg0: true, hasSideEffects: true},
|
||||
}
|
||||
|
||||
blocks := []blockData{
|
||||
|
||||
@@ -119,8 +119,8 @@ func init() {
|
||||
fp01 = regInfo{inputs: nil, outputs: []regMask{fp}}
|
||||
fp11 = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
|
||||
fp1flags = regInfo{inputs: []regMask{fp}}
|
||||
fpgp = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
|
||||
gpfp = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
|
||||
fpgp = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}, clobbers: buildReg("F15")} // int-float conversion uses F15 as tmp
|
||||
gpfp = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}, clobbers: buildReg("F15")}
|
||||
fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
|
||||
fp2flags = regInfo{inputs: []regMask{fp, fp}}
|
||||
fpload = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
|
||||
|
||||
@@ -404,8 +404,7 @@
|
||||
(ANDconst <config.fe.TypeUInt32()> [3] ptr)))
|
||||
(NORconst [0] <config.fe.TypeUInt32()> (SLL <config.fe.TypeUInt32()>
|
||||
(MOVWconst [0xff]) (SLLconst <config.fe.TypeUInt32()> [3]
|
||||
(ANDconst <config.fe.TypeUInt32()> [3]
|
||||
(XORconst <config.fe.TypeUInt32()> [3] ptr)))))) mem)
|
||||
(ANDconst <config.fe.TypeUInt32()> [3] ptr))))) mem)
|
||||
|
||||
// AtomicOr8(ptr,val) -> LoweredAtomicOr(ptr&^3,uint32(val) << (((ptr^3) & 3) * 8))
|
||||
(AtomicOr8 ptr val mem) && config.BigEndian ->
|
||||
|
||||
@@ -267,8 +267,8 @@ func init() {
|
||||
// SYNC
|
||||
// MOVW Rarg1, (Rarg0)
|
||||
// SYNC
|
||||
{name: "LoweredAtomicStore", argLength: 3, reg: gpstore, faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicStorezero", argLength: 2, reg: gpstore0, faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicStore", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
|
||||
{name: "LoweredAtomicStorezero", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
|
||||
|
||||
// atomic exchange.
|
||||
// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>.
|
||||
@@ -278,7 +278,7 @@ func init() {
|
||||
// SC Rtmp, (Rarg0)
|
||||
// BEQ Rtmp, -3(PC)
|
||||
// SYNC
|
||||
{name: "LoweredAtomicExchange", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicExchange", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
|
||||
// atomic add.
|
||||
// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>.
|
||||
@@ -289,8 +289,8 @@ func init() {
|
||||
// BEQ Rtmp, -3(PC)
|
||||
// SYNC
|
||||
// ADDU Rarg1, Rout
|
||||
{name: "LoweredAtomicAdd", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicAddconst", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicAdd", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
{name: "LoweredAtomicAddconst", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
|
||||
// atomic compare and swap.
|
||||
// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
|
||||
@@ -308,7 +308,7 @@ func init() {
|
||||
// SC Rout, (Rarg0)
|
||||
// BEQ Rout, -4(PC)
|
||||
// SYNC
|
||||
{name: "LoweredAtomicCas", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicCas", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
|
||||
// atomic and/or.
|
||||
// *arg0 &= (|=) arg1. arg2=mem. returns memory.
|
||||
@@ -318,8 +318,8 @@ func init() {
|
||||
// SC Rtmp, (Rarg0)
|
||||
// BEQ Rtmp, -3(PC)
|
||||
// SYNC
|
||||
{name: "LoweredAtomicAnd", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicOr", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicAnd", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true},
|
||||
{name: "LoweredAtomicOr", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true, hasSideEffects: true},
|
||||
|
||||
// large or unaligned zeroing
|
||||
// arg0 = address of memory to zero (in R1, changed as side effect)
|
||||
|
||||
@@ -440,7 +440,7 @@
|
||||
(If (MOVDGTnoinv (MOVDconst [0]) (MOVDconst [1]) cmp) yes no) -> (GTF cmp yes no)
|
||||
(If (MOVDGEnoinv (MOVDconst [0]) (MOVDconst [1]) cmp) yes no) -> (GEF cmp yes no)
|
||||
|
||||
(If cond yes no) -> (NE (CMPWconst [0] (MOVBZreg cond)) yes no)
|
||||
(If cond yes no) -> (NE (CMPWconst [0] (MOVBZreg <config.fe.TypeBool()> cond)) yes no)
|
||||
|
||||
// ***************************
|
||||
// Above: lowering rules
|
||||
@@ -448,7 +448,15 @@
|
||||
// ***************************
|
||||
// TODO: Should the optimizations be a separate pass?
|
||||
|
||||
// if a register move has only 1 use, just use the same register without emitting instruction
|
||||
// Fold unnecessary type conversions.
|
||||
(MOVDreg <t> x) && t.Compare(x.Type) == CMPeq -> x
|
||||
(MOVDnop <t> x) && t.Compare(x.Type) == CMPeq -> x
|
||||
|
||||
// Propagate constants through type conversions.
|
||||
(MOVDreg (MOVDconst [c])) -> (MOVDconst [c])
|
||||
(MOVDnop (MOVDconst [c])) -> (MOVDconst [c])
|
||||
|
||||
// If a register move has only 1 use, just use the same register without emitting instruction.
|
||||
// MOVDnop doesn't emit instruction, only for ensuring the type.
|
||||
(MOVDreg x) && x.Uses == 1 -> (MOVDnop x)
|
||||
|
||||
@@ -529,8 +537,8 @@
|
||||
(CMP (MOVDconst [c]) x) && is32Bit(c) -> (InvertFlags (CMPconst x [c]))
|
||||
(CMPW x (MOVDconst [c])) -> (CMPWconst x [c])
|
||||
(CMPW (MOVDconst [c]) x) -> (InvertFlags (CMPWconst x [c]))
|
||||
(CMPU x (MOVDconst [c])) && is32Bit(c) -> (CMPUconst x [int64(uint32(c))])
|
||||
(CMPU (MOVDconst [c]) x) && is32Bit(c) -> (InvertFlags (CMPUconst x [int64(uint32(c))]))
|
||||
(CMPU x (MOVDconst [c])) && isU32Bit(c) -> (CMPUconst x [int64(uint32(c))])
|
||||
(CMPU (MOVDconst [c]) x) && isU32Bit(c) -> (InvertFlags (CMPUconst x [int64(uint32(c))]))
|
||||
(CMPWU x (MOVDconst [c])) -> (CMPWUconst x [int64(uint32(c))])
|
||||
(CMPWU (MOVDconst [c]) x) -> (InvertFlags (CMPWUconst x [int64(uint32(c))]))
|
||||
|
||||
|
||||
@@ -429,14 +429,14 @@ func init() {
|
||||
|
||||
// Atomic stores. These are just normal stores.
|
||||
// store arg1 to arg0+auxint+aux. arg2=mem.
|
||||
{name: "MOVWatomicstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},
|
||||
{name: "MOVDatomicstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},
|
||||
{name: "MOVWatomicstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
{name: "MOVDatomicstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
|
||||
// Atomic adds.
|
||||
// *(arg0+auxint+aux) += arg1. arg2=mem.
|
||||
// Returns a tuple of <old contents of *(arg0+auxint+aux), memory>.
|
||||
{name: "LAA", argLength: 3, reg: gpstorelaa, asm: "LAA", typ: "(UInt32,Mem)", aux: "SymOff", faultOnNilArg0: true},
|
||||
{name: "LAAG", argLength: 3, reg: gpstorelaa, asm: "LAAG", typ: "(UInt64,Mem)", aux: "SymOff", faultOnNilArg0: true},
|
||||
{name: "LAA", argLength: 3, reg: gpstorelaa, asm: "LAA", typ: "(UInt32,Mem)", aux: "SymOff", faultOnNilArg0: true, hasSideEffects: true},
|
||||
{name: "LAAG", argLength: 3, reg: gpstorelaa, asm: "LAAG", typ: "(UInt64,Mem)", aux: "SymOff", faultOnNilArg0: true, hasSideEffects: true},
|
||||
{name: "AddTupleFirst32", argLength: 2}, // arg0=tuple <x,y>. Returns <x+arg1,y>.
|
||||
{name: "AddTupleFirst64", argLength: 2}, // arg0=tuple <x,y>. Returns <x+arg1,y>.
|
||||
|
||||
@@ -461,13 +461,13 @@ func init() {
|
||||
// BEQ ...
|
||||
// but we can't do that because memory-using ops can't generate flags yet
|
||||
// (flagalloc wants to move flag-generating instructions around).
|
||||
{name: "LoweredAtomicCas32", argLength: 4, reg: cas, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicCas64", argLength: 4, reg: cas, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicCas32", argLength: 4, reg: cas, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
{name: "LoweredAtomicCas64", argLength: 4, reg: cas, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
|
||||
// Lowered atomic swaps, emulated using compare-and-swap.
|
||||
// store arg1 to arg0+auxint+aux, arg2=mem.
|
||||
{name: "LoweredAtomicExchange32", argLength: 3, reg: exchange, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicExchange64", argLength: 3, reg: exchange, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
|
||||
{name: "LoweredAtomicExchange32", argLength: 3, reg: exchange, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
{name: "LoweredAtomicExchange64", argLength: 3, reg: exchange, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
|
||||
|
||||
// find leftmost one
|
||||
{
|
||||
|
||||
@@ -460,20 +460,20 @@ var genericOps = []opData{
|
||||
// Atomic loads return a new memory so that the loads are properly ordered
|
||||
// with respect to other loads and stores.
|
||||
// TODO: use for sync/atomic at some point.
|
||||
{name: "AtomicLoad32", argLength: 2, typ: "(UInt32,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory.
|
||||
{name: "AtomicLoad64", argLength: 2, typ: "(UInt64,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory.
|
||||
{name: "AtomicLoadPtr", argLength: 2, typ: "(BytePtr,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory.
|
||||
{name: "AtomicStore32", argLength: 3, typ: "Mem"}, // Store arg1 to *arg0. arg2=memory. Returns memory.
|
||||
{name: "AtomicStore64", argLength: 3, typ: "Mem"}, // Store arg1 to *arg0. arg2=memory. Returns memory.
|
||||
{name: "AtomicStorePtrNoWB", argLength: 3, typ: "Mem"}, // Store arg1 to *arg0. arg2=memory. Returns memory.
|
||||
{name: "AtomicExchange32", argLength: 3, typ: "(UInt32,Mem)"}, // Store arg1 to *arg0. arg2=memory. Returns old contents of *arg0 and new memory.
|
||||
{name: "AtomicExchange64", argLength: 3, typ: "(UInt64,Mem)"}, // Store arg1 to *arg0. arg2=memory. Returns old contents of *arg0 and new memory.
|
||||
{name: "AtomicAdd32", argLength: 3, typ: "(UInt32,Mem)"}, // Do *arg0 += arg1. arg2=memory. Returns sum and new memory.
|
||||
{name: "AtomicAdd64", argLength: 3, typ: "(UInt64,Mem)"}, // Do *arg0 += arg1. arg2=memory. Returns sum and new memory.
|
||||
{name: "AtomicCompareAndSwap32", argLength: 4, typ: "(Bool,Mem)"}, // if *arg0==arg1, then set *arg0=arg2. Returns true iff store happens and new memory.
|
||||
{name: "AtomicCompareAndSwap64", argLength: 4, typ: "(Bool,Mem)"}, // if *arg0==arg1, then set *arg0=arg2. Returns true iff store happens and new memory.
|
||||
{name: "AtomicAnd8", argLength: 3, typ: "Mem"}, // *arg0 &= arg1. arg2=memory. Returns memory.
|
||||
{name: "AtomicOr8", argLength: 3, typ: "Mem"}, // *arg0 |= arg1. arg2=memory. Returns memory.
|
||||
{name: "AtomicLoad32", argLength: 2, typ: "(UInt32,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory.
|
||||
{name: "AtomicLoad64", argLength: 2, typ: "(UInt64,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory.
|
||||
{name: "AtomicLoadPtr", argLength: 2, typ: "(BytePtr,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory.
|
||||
{name: "AtomicStore32", argLength: 3, typ: "Mem", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Returns memory.
|
||||
{name: "AtomicStore64", argLength: 3, typ: "Mem", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Returns memory.
|
||||
{name: "AtomicStorePtrNoWB", argLength: 3, typ: "Mem", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Returns memory.
|
||||
{name: "AtomicExchange32", argLength: 3, typ: "(UInt32,Mem)", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Returns old contents of *arg0 and new memory.
|
||||
{name: "AtomicExchange64", argLength: 3, typ: "(UInt64,Mem)", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Returns old contents of *arg0 and new memory.
|
||||
{name: "AtomicAdd32", argLength: 3, typ: "(UInt32,Mem)", hasSideEffects: true}, // Do *arg0 += arg1. arg2=memory. Returns sum and new memory.
|
||||
{name: "AtomicAdd64", argLength: 3, typ: "(UInt64,Mem)", hasSideEffects: true}, // Do *arg0 += arg1. arg2=memory. Returns sum and new memory.
|
||||
{name: "AtomicCompareAndSwap32", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // if *arg0==arg1, then set *arg0=arg2. Returns true iff store happens and new memory.
|
||||
{name: "AtomicCompareAndSwap64", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // if *arg0==arg1, then set *arg0=arg2. Returns true iff store happens and new memory.
|
||||
{name: "AtomicAnd8", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 &= arg1. arg2=memory. Returns memory.
|
||||
{name: "AtomicOr8", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 |= arg1. arg2=memory. Returns memory.
|
||||
}
|
||||
|
||||
// kind control successors implicit exit
|
||||
|
||||
@@ -52,6 +52,7 @@ type opData struct {
|
||||
faultOnNilArg0 bool // this op will fault if arg0 is nil (and aux encodes a small offset)
|
||||
faultOnNilArg1 bool // this op will fault if arg1 is nil (and aux encodes a small offset)
|
||||
usesScratch bool // this op requires scratch memory space
|
||||
hasSideEffects bool // for "reasons", not to be eliminated. E.g., atomic store, #19182.
|
||||
}
|
||||
|
||||
type blockData struct {
|
||||
@@ -208,6 +209,9 @@ func genOp() {
|
||||
if v.usesScratch {
|
||||
fmt.Fprintln(w, "usesScratch: true,")
|
||||
}
|
||||
if v.hasSideEffects {
|
||||
fmt.Fprintln(w, "hasSideEffects: true,")
|
||||
}
|
||||
if a.name == "generic" {
|
||||
fmt.Fprintln(w, "generic:true,")
|
||||
fmt.Fprintln(w, "},") // close op
|
||||
|
||||
@@ -33,13 +33,24 @@ type loop struct {
|
||||
|
||||
// outerinner records that outer contains inner
|
||||
func (sdom SparseTree) outerinner(outer, inner *loop) {
|
||||
// There could be other outer loops found in some random order,
|
||||
// locate the new outer loop appropriately among them.
|
||||
oldouter := inner.outer
|
||||
if oldouter == nil || sdom.isAncestorEq(oldouter.header, outer.header) {
|
||||
inner.outer = outer
|
||||
outer.isInner = false
|
||||
if inner.containsCall {
|
||||
outer.setContainsCall()
|
||||
}
|
||||
for oldouter != nil && sdom.isAncestor(outer.header, oldouter.header) {
|
||||
inner = oldouter
|
||||
oldouter = inner.outer
|
||||
}
|
||||
if outer == oldouter {
|
||||
return
|
||||
}
|
||||
if oldouter != nil {
|
||||
outer.outer = oldouter
|
||||
}
|
||||
|
||||
inner.outer = outer
|
||||
outer.isInner = false
|
||||
if inner.containsCall {
|
||||
outer.setContainsCall()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
87
src/cmd/compile/internal/ssa/loop_test.go
Normal file
87
src/cmd/compile/internal/ssa/loop_test.go
Normal file
@@ -0,0 +1,87 @@
|
||||
// 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.
|
||||
|
||||
package ssa
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLoopConditionS390X(t *testing.T) {
|
||||
// Test that a simple loop condition does not generate a conditional
|
||||
// move (issue #19227).
|
||||
//
|
||||
// MOVDLT is generated when Less64 is lowered but should be
|
||||
// optimized into an LT branch.
|
||||
//
|
||||
// For example, compiling the following loop:
|
||||
//
|
||||
// for i := 0; i < N; i++ {
|
||||
// sum += 3
|
||||
// }
|
||||
//
|
||||
// should generate assembly similar to:
|
||||
// loop:
|
||||
// CMP R0, R1
|
||||
// BGE done
|
||||
// ADD $3, R4
|
||||
// ADD $1, R1
|
||||
// BR loop
|
||||
// done:
|
||||
//
|
||||
// rather than:
|
||||
// loop:
|
||||
// MOVD $0, R2
|
||||
// MOVD $1, R3
|
||||
// CMP R0, R1
|
||||
// MOVDLT R2, R3
|
||||
// CMPW R2, $0
|
||||
// BNE done
|
||||
// ADD $3, R4
|
||||
// ADD $1, R1
|
||||
// BR loop
|
||||
// done:
|
||||
//
|
||||
c := testConfigS390X(t)
|
||||
fun := Fun(c, "entry",
|
||||
Bloc("entry",
|
||||
Valu("mem", OpInitMem, TypeMem, 0, nil),
|
||||
Valu("SP", OpSP, TypeUInt64, 0, nil),
|
||||
Valu("Nptr", OpOffPtr, TypeInt64Ptr, 8, nil, "SP"),
|
||||
Valu("ret", OpOffPtr, TypeInt64Ptr, 16, nil, "SP"),
|
||||
Valu("N", OpLoad, TypeInt64, 0, nil, "Nptr", "mem"),
|
||||
Valu("starti", OpConst64, TypeInt64, 0, nil),
|
||||
Valu("startsum", OpConst64, TypeInt64, 0, nil),
|
||||
Goto("b1")),
|
||||
Bloc("b1",
|
||||
Valu("phii", OpPhi, TypeInt64, 0, nil, "starti", "i"),
|
||||
Valu("phisum", OpPhi, TypeInt64, 0, nil, "startsum", "sum"),
|
||||
Valu("cmp1", OpLess64, TypeBool, 0, nil, "phii", "N"),
|
||||
If("cmp1", "b2", "b3")),
|
||||
Bloc("b2",
|
||||
Valu("c1", OpConst64, TypeInt64, 1, nil),
|
||||
Valu("i", OpAdd64, TypeInt64, 0, nil, "phii", "c1"),
|
||||
Valu("c3", OpConst64, TypeInt64, 3, nil),
|
||||
Valu("sum", OpAdd64, TypeInt64, 0, nil, "phisum", "c3"),
|
||||
Goto("b1")),
|
||||
Bloc("b3",
|
||||
Valu("store", OpStore, TypeMem, 8, nil, "ret", "phisum", "mem"),
|
||||
Exit("store")))
|
||||
CheckFunc(fun.f)
|
||||
Compile(fun.f)
|
||||
CheckFunc(fun.f)
|
||||
|
||||
checkOpcodeCounts(t, fun.f, map[Op]int{
|
||||
OpS390XMOVDLT: 0,
|
||||
OpS390XMOVDGT: 0,
|
||||
OpS390XMOVDLE: 0,
|
||||
OpS390XMOVDGE: 0,
|
||||
OpS390XMOVDEQ: 0,
|
||||
OpS390XMOVDNE: 0,
|
||||
OpS390XCMP: 1,
|
||||
OpS390XCMPWconst: 0,
|
||||
})
|
||||
|
||||
fun.f.Free()
|
||||
}
|
||||
@@ -34,6 +34,7 @@ type opInfo struct {
|
||||
faultOnNilArg0 bool // this op will fault if arg0 is nil (and aux encodes a small offset)
|
||||
faultOnNilArg1 bool // this op will fault if arg1 is nil (and aux encodes a small offset)
|
||||
usesScratch bool // this op requires scratch memory space
|
||||
hasSideEffects bool // for "reasons", not to be eliminated. E.g., atomic store, #19182.
|
||||
}
|
||||
|
||||
type inputInfo struct {
|
||||
|
||||
@@ -7307,6 +7307,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 3,
|
||||
resultInArg0: true,
|
||||
faultOnNilArg1: true,
|
||||
hasSideEffects: true,
|
||||
asm: x86.AXCHGL,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -7324,6 +7325,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 3,
|
||||
resultInArg0: true,
|
||||
faultOnNilArg1: true,
|
||||
hasSideEffects: true,
|
||||
asm: x86.AXCHGQ,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -7342,6 +7344,7 @@ var opcodeTable = [...]opInfo{
|
||||
resultInArg0: true,
|
||||
clobberFlags: true,
|
||||
faultOnNilArg1: true,
|
||||
hasSideEffects: true,
|
||||
asm: x86.AXADDL,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -7360,6 +7363,7 @@ var opcodeTable = [...]opInfo{
|
||||
resultInArg0: true,
|
||||
clobberFlags: true,
|
||||
faultOnNilArg1: true,
|
||||
hasSideEffects: true,
|
||||
asm: x86.AXADDQ,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -7387,6 +7391,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 4,
|
||||
clobberFlags: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: x86.ACMPXCHGL,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -7407,6 +7412,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 4,
|
||||
clobberFlags: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: x86.ACMPXCHGQ,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -7427,6 +7433,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 3,
|
||||
clobberFlags: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: x86.AANDB,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -7441,6 +7448,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 3,
|
||||
clobberFlags: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: x86.AORB,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -10075,6 +10083,7 @@ var opcodeTable = [...]opInfo{
|
||||
inputs: []inputInfo{
|
||||
{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
|
||||
},
|
||||
clobbers: 2147483648, // F15
|
||||
outputs: []outputInfo{
|
||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||
},
|
||||
@@ -10088,6 +10097,7 @@ var opcodeTable = [...]opInfo{
|
||||
inputs: []inputInfo{
|
||||
{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
|
||||
},
|
||||
clobbers: 2147483648, // F15
|
||||
outputs: []outputInfo{
|
||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||
},
|
||||
@@ -10101,6 +10111,7 @@ var opcodeTable = [...]opInfo{
|
||||
inputs: []inputInfo{
|
||||
{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
|
||||
},
|
||||
clobbers: 2147483648, // F15
|
||||
outputs: []outputInfo{
|
||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||
},
|
||||
@@ -10114,6 +10125,7 @@ var opcodeTable = [...]opInfo{
|
||||
inputs: []inputInfo{
|
||||
{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
|
||||
},
|
||||
clobbers: 2147483648, // F15
|
||||
outputs: []outputInfo{
|
||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||
},
|
||||
@@ -10127,6 +10139,7 @@ var opcodeTable = [...]opInfo{
|
||||
inputs: []inputInfo{
|
||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||
},
|
||||
clobbers: 2147483648, // F15
|
||||
outputs: []outputInfo{
|
||||
{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
|
||||
},
|
||||
@@ -10140,6 +10153,7 @@ var opcodeTable = [...]opInfo{
|
||||
inputs: []inputInfo{
|
||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||
},
|
||||
clobbers: 2147483648, // F15
|
||||
outputs: []outputInfo{
|
||||
{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
|
||||
},
|
||||
@@ -10153,6 +10167,7 @@ var opcodeTable = [...]opInfo{
|
||||
inputs: []inputInfo{
|
||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||
},
|
||||
clobbers: 2147483648, // F15
|
||||
outputs: []outputInfo{
|
||||
{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
|
||||
},
|
||||
@@ -10166,6 +10181,7 @@ var opcodeTable = [...]opInfo{
|
||||
inputs: []inputInfo{
|
||||
{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
|
||||
},
|
||||
clobbers: 2147483648, // F15
|
||||
outputs: []outputInfo{
|
||||
{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
|
||||
},
|
||||
@@ -12657,6 +12673,7 @@ var opcodeTable = [...]opInfo{
|
||||
name: "STLR",
|
||||
argLen: 3,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: arm64.ASTLR,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -12669,6 +12686,7 @@ var opcodeTable = [...]opInfo{
|
||||
name: "STLRW",
|
||||
argLen: 3,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: arm64.ASTLRW,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -12682,6 +12700,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 3,
|
||||
resultNotInArgs: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
|
||||
@@ -12697,6 +12716,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 3,
|
||||
resultNotInArgs: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
|
||||
@@ -12712,6 +12732,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 3,
|
||||
resultNotInArgs: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
|
||||
@@ -12727,6 +12748,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 3,
|
||||
resultNotInArgs: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
|
||||
@@ -12743,6 +12765,7 @@ var opcodeTable = [...]opInfo{
|
||||
resultNotInArgs: true,
|
||||
clobberFlags: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
|
||||
@@ -12760,6 +12783,7 @@ var opcodeTable = [...]opInfo{
|
||||
resultNotInArgs: true,
|
||||
clobberFlags: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
|
||||
@@ -12775,6 +12799,7 @@ var opcodeTable = [...]opInfo{
|
||||
name: "LoweredAtomicAnd8",
|
||||
argLen: 3,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: arm64.AAND,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -12787,6 +12812,7 @@ var opcodeTable = [...]opInfo{
|
||||
name: "LoweredAtomicOr8",
|
||||
argLen: 3,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: arm64.AORR,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -13977,6 +14003,7 @@ var opcodeTable = [...]opInfo{
|
||||
name: "LoweredAtomicStore",
|
||||
argLen: 3,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
|
||||
@@ -13988,6 +14015,7 @@ var opcodeTable = [...]opInfo{
|
||||
name: "LoweredAtomicStorezero",
|
||||
argLen: 2,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
|
||||
@@ -13999,6 +14027,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 3,
|
||||
resultNotInArgs: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
|
||||
@@ -14014,6 +14043,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 3,
|
||||
resultNotInArgs: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
|
||||
@@ -14030,6 +14060,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 2,
|
||||
resultNotInArgs: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
|
||||
@@ -14044,6 +14075,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 4,
|
||||
resultNotInArgs: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
|
||||
@@ -14059,6 +14091,7 @@ var opcodeTable = [...]opInfo{
|
||||
name: "LoweredAtomicAnd",
|
||||
argLen: 3,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: mips.AAND,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -14071,6 +14104,7 @@ var opcodeTable = [...]opInfo{
|
||||
name: "LoweredAtomicOr",
|
||||
argLen: 3,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: mips.AOR,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -19500,6 +19534,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 3,
|
||||
clobberFlags: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: s390x.AMOVW,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -19514,6 +19549,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 3,
|
||||
clobberFlags: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: s390x.AMOVD,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -19527,6 +19563,7 @@ var opcodeTable = [...]opInfo{
|
||||
auxType: auxSymOff,
|
||||
argLen: 3,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: s390x.ALAA,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -19543,6 +19580,7 @@ var opcodeTable = [...]opInfo{
|
||||
auxType: auxSymOff,
|
||||
argLen: 3,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: s390x.ALAAG,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -19570,6 +19608,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 4,
|
||||
clobberFlags: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: s390x.ACS,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -19590,6 +19629,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 4,
|
||||
clobberFlags: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: s390x.ACSG,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -19610,6 +19650,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 3,
|
||||
clobberFlags: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: s390x.ACS,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -19628,6 +19669,7 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 3,
|
||||
clobberFlags: true,
|
||||
faultOnNilArg0: true,
|
||||
hasSideEffects: true,
|
||||
asm: s390x.ACSG,
|
||||
reg: regInfo{
|
||||
inputs: []inputInfo{
|
||||
@@ -21415,59 +21457,70 @@ var opcodeTable = [...]opInfo{
|
||||
generic: true,
|
||||
},
|
||||
{
|
||||
name: "AtomicStore32",
|
||||
argLen: 3,
|
||||
generic: true,
|
||||
name: "AtomicStore32",
|
||||
argLen: 3,
|
||||
hasSideEffects: true,
|
||||
generic: true,
|
||||
},
|
||||
{
|
||||
name: "AtomicStore64",
|
||||
argLen: 3,
|
||||
generic: true,
|
||||
name: "AtomicStore64",
|
||||
argLen: 3,
|
||||
hasSideEffects: true,
|
||||
generic: true,
|
||||
},
|
||||
{
|
||||
name: "AtomicStorePtrNoWB",
|
||||
argLen: 3,
|
||||
generic: true,
|
||||
name: "AtomicStorePtrNoWB",
|
||||
argLen: 3,
|
||||
hasSideEffects: true,
|
||||
generic: true,
|
||||
},
|
||||
{
|
||||
name: "AtomicExchange32",
|
||||
argLen: 3,
|
||||
generic: true,
|
||||
name: "AtomicExchange32",
|
||||
argLen: 3,
|
||||
hasSideEffects: true,
|
||||
generic: true,
|
||||
},
|
||||
{
|
||||
name: "AtomicExchange64",
|
||||
argLen: 3,
|
||||
generic: true,
|
||||
name: "AtomicExchange64",
|
||||
argLen: 3,
|
||||
hasSideEffects: true,
|
||||
generic: true,
|
||||
},
|
||||
{
|
||||
name: "AtomicAdd32",
|
||||
argLen: 3,
|
||||
generic: true,
|
||||
name: "AtomicAdd32",
|
||||
argLen: 3,
|
||||
hasSideEffects: true,
|
||||
generic: true,
|
||||
},
|
||||
{
|
||||
name: "AtomicAdd64",
|
||||
argLen: 3,
|
||||
generic: true,
|
||||
name: "AtomicAdd64",
|
||||
argLen: 3,
|
||||
hasSideEffects: true,
|
||||
generic: true,
|
||||
},
|
||||
{
|
||||
name: "AtomicCompareAndSwap32",
|
||||
argLen: 4,
|
||||
generic: true,
|
||||
name: "AtomicCompareAndSwap32",
|
||||
argLen: 4,
|
||||
hasSideEffects: true,
|
||||
generic: true,
|
||||
},
|
||||
{
|
||||
name: "AtomicCompareAndSwap64",
|
||||
argLen: 4,
|
||||
generic: true,
|
||||
name: "AtomicCompareAndSwap64",
|
||||
argLen: 4,
|
||||
hasSideEffects: true,
|
||||
generic: true,
|
||||
},
|
||||
{
|
||||
name: "AtomicAnd8",
|
||||
argLen: 3,
|
||||
generic: true,
|
||||
name: "AtomicAnd8",
|
||||
argLen: 3,
|
||||
hasSideEffects: true,
|
||||
generic: true,
|
||||
},
|
||||
{
|
||||
name: "AtomicOr8",
|
||||
argLen: 3,
|
||||
generic: true,
|
||||
name: "AtomicOr8",
|
||||
argLen: 3,
|
||||
hasSideEffects: true,
|
||||
generic: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -1699,6 +1699,24 @@ sinking:
|
||||
}
|
||||
p := d.Preds[0].b // block in loop exiting to d.
|
||||
|
||||
// Check that the spill value is still live at the start of d.
|
||||
// If it isn't, we can't move the spill here because some other value
|
||||
// may be using the same stack slot. See issue 20472.
|
||||
// The spill value can't be defined in d, so that makes our lives easier.
|
||||
for _, x := range stacklive[d.ID] {
|
||||
if x == vsp.ID {
|
||||
goto stillLive
|
||||
}
|
||||
}
|
||||
for _, v := range d.Values {
|
||||
if v.Op == OpLoadReg && v.Args[0] == vsp {
|
||||
goto stillLive
|
||||
}
|
||||
}
|
||||
// Spill is not live - abort sinking this spill.
|
||||
continue sinking
|
||||
stillLive:
|
||||
|
||||
endregs := s.endRegs[p.ID]
|
||||
for _, regrec := range endregs {
|
||||
if regrec.v == e && regrec.r != noRegister && regrec.c == e { // TODO: regrec.c != e implies different spill possible.
|
||||
|
||||
@@ -31,3 +31,68 @@ func TestLiveControlOps(t *testing.T) {
|
||||
regalloc(f.f)
|
||||
checkFunc(f.f)
|
||||
}
|
||||
|
||||
func TestSpillMove(t *testing.T) {
|
||||
// Test for issue 20472. We shouldn't move a spill out to exit blocks
|
||||
// if there is an exit block where the spill is dead but the pre-spill
|
||||
// value is live.
|
||||
c := testConfig(t)
|
||||
ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing
|
||||
arg1Aux := c.fe.Auto(TypeInt64)
|
||||
arg2Aux := c.fe.Auto(ptrType)
|
||||
f := Fun(c, "entry",
|
||||
Bloc("entry",
|
||||
Valu("mem", OpInitMem, TypeMem, 0, nil),
|
||||
Valu("x", OpArg, TypeInt64, 0, arg1Aux),
|
||||
Valu("p", OpArg, ptrType, 0, arg2Aux),
|
||||
Valu("a", OpAMD64TESTQ, TypeFlags, 0, nil, "x", "x"),
|
||||
Goto("loop1"),
|
||||
),
|
||||
Bloc("loop1",
|
||||
Valu("y", OpAMD64MULQ, TypeInt64, 0, nil, "x", "x"),
|
||||
Eq("a", "loop2", "exit1"),
|
||||
),
|
||||
Bloc("loop2",
|
||||
Eq("a", "loop1", "exit2"),
|
||||
),
|
||||
Bloc("exit1",
|
||||
// store before call, y is available in a register
|
||||
Valu("mem2", OpAMD64MOVQstore, TypeMem, 0, nil, "p", "y", "mem"),
|
||||
Valu("mem3", OpAMD64CALLstatic, TypeMem, 0, nil, "mem2"),
|
||||
Exit("mem3"),
|
||||
),
|
||||
Bloc("exit2",
|
||||
// store after call, y must be loaded from a spill location
|
||||
Valu("mem4", OpAMD64CALLstatic, TypeMem, 0, nil, "mem"),
|
||||
Valu("mem5", OpAMD64MOVQstore, TypeMem, 0, nil, "p", "y", "mem4"),
|
||||
Exit("mem5"),
|
||||
),
|
||||
)
|
||||
flagalloc(f.f)
|
||||
regalloc(f.f)
|
||||
checkFunc(f.f)
|
||||
// There should still be a spill in Loop1, and nowhere else.
|
||||
if numSpills(f.blocks["loop1"]) != 1 {
|
||||
t.Errorf("spill missing from loop1")
|
||||
}
|
||||
if numSpills(f.blocks["loop2"]) != 0 {
|
||||
t.Errorf("spill present in loop2")
|
||||
}
|
||||
if numSpills(f.blocks["exit1"]) != 0 {
|
||||
t.Errorf("spill present in exit1")
|
||||
}
|
||||
if numSpills(f.blocks["exit2"]) != 0 {
|
||||
t.Errorf("spill present in exit2")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func numSpills(b *Block) int {
|
||||
n := 0
|
||||
for _, v := range b.Values {
|
||||
if v.Op == OpStoreReg {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -14517,7 +14517,7 @@ func rewriteValueARM_OpLrot16(v *Value, config *Config) bool {
|
||||
_ = b
|
||||
// match: (Lrot16 <t> x [c])
|
||||
// cond:
|
||||
// result: (OR (SLLconst <t> x [c&15]) (SRLconst <t> x [16-c&15]))
|
||||
// result: (OR (SLLconst <t> x [c&15]) (SRLconst <t> (ZeroExt16to32 x) [16-c&15]))
|
||||
for {
|
||||
t := v.Type
|
||||
c := v.AuxInt
|
||||
@@ -14529,7 +14529,9 @@ func rewriteValueARM_OpLrot16(v *Value, config *Config) bool {
|
||||
v.AddArg(v0)
|
||||
v1 := b.NewValue0(v.Line, OpARMSRLconst, t)
|
||||
v1.AuxInt = 16 - c&15
|
||||
v1.AddArg(x)
|
||||
v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
|
||||
v2.AddArg(x)
|
||||
v1.AddArg(v2)
|
||||
v.AddArg(v1)
|
||||
return true
|
||||
}
|
||||
@@ -14554,7 +14556,7 @@ func rewriteValueARM_OpLrot8(v *Value, config *Config) bool {
|
||||
_ = b
|
||||
// match: (Lrot8 <t> x [c])
|
||||
// cond:
|
||||
// result: (OR (SLLconst <t> x [c&7]) (SRLconst <t> x [8-c&7]))
|
||||
// result: (OR (SLLconst <t> x [c&7]) (SRLconst <t> (ZeroExt8to32 x) [8-c&7]))
|
||||
for {
|
||||
t := v.Type
|
||||
c := v.AuxInt
|
||||
@@ -14566,7 +14568,9 @@ func rewriteValueARM_OpLrot8(v *Value, config *Config) bool {
|
||||
v.AddArg(v0)
|
||||
v1 := b.NewValue0(v.Line, OpARMSRLconst, t)
|
||||
v1.AuxInt = 8 - c&7
|
||||
v1.AddArg(x)
|
||||
v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
|
||||
v2.AddArg(x)
|
||||
v1.AddArg(v2)
|
||||
v.AddArg(v1)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -2625,7 +2625,7 @@ func rewriteValueARM64_OpARM64FMOVDload(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (FMOVDload [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
// cond: is32Bit(off1+off2) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym))
|
||||
// cond: is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym))
|
||||
// result: (FMOVDload [off1+off2] {sym} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -2637,7 +2637,7 @@ func rewriteValueARM64_OpARM64FMOVDload(v *Value, config *Config) bool {
|
||||
off2 := v_0.AuxInt
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(is32Bit(off1+off2) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym))) {
|
||||
if !(is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(sym))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64FMOVDload)
|
||||
@@ -2648,7 +2648,7 @@ func rewriteValueARM64_OpARM64FMOVDload(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (FMOVDload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2)) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2)))
|
||||
// result: (FMOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -2661,7 +2661,7 @@ func rewriteValueARM64_OpARM64FMOVDload(v *Value, config *Config) bool {
|
||||
sym2 := v_0.Aux
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1, sym2)) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(mergeSym(sym1, sym2)))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64FMOVDload)
|
||||
@@ -2677,7 +2677,7 @@ func rewriteValueARM64_OpARM64FMOVDstore(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (FMOVDstore [off1] {sym} (ADDconst [off2] ptr) val mem)
|
||||
// cond: is32Bit(off1+off2) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym))
|
||||
// cond: is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym))
|
||||
// result: (FMOVDstore [off1+off2] {sym} ptr val mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -2690,7 +2690,7 @@ func rewriteValueARM64_OpARM64FMOVDstore(v *Value, config *Config) bool {
|
||||
ptr := v_0.Args[0]
|
||||
val := v.Args[1]
|
||||
mem := v.Args[2]
|
||||
if !(is32Bit(off1+off2) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym))) {
|
||||
if !(is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(sym))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64FMOVDstore)
|
||||
@@ -2702,7 +2702,7 @@ func rewriteValueARM64_OpARM64FMOVDstore(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (FMOVDstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2)) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2)))
|
||||
// result: (FMOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -2716,7 +2716,7 @@ func rewriteValueARM64_OpARM64FMOVDstore(v *Value, config *Config) bool {
|
||||
ptr := v_0.Args[0]
|
||||
val := v.Args[1]
|
||||
mem := v.Args[2]
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1, sym2)) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(mergeSym(sym1, sym2)))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64FMOVDstore)
|
||||
@@ -2733,7 +2733,7 @@ func rewriteValueARM64_OpARM64FMOVSload(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (FMOVSload [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
// cond: is32Bit(off1+off2) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym))
|
||||
// cond: is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym))
|
||||
// result: (FMOVSload [off1+off2] {sym} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -2745,7 +2745,7 @@ func rewriteValueARM64_OpARM64FMOVSload(v *Value, config *Config) bool {
|
||||
off2 := v_0.AuxInt
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(is32Bit(off1+off2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym))) {
|
||||
if !(is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(sym))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64FMOVSload)
|
||||
@@ -2756,7 +2756,7 @@ func rewriteValueARM64_OpARM64FMOVSload(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (FMOVSload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2)) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2)))
|
||||
// result: (FMOVSload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -2769,7 +2769,7 @@ func rewriteValueARM64_OpARM64FMOVSload(v *Value, config *Config) bool {
|
||||
sym2 := v_0.Aux
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1, sym2)) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(mergeSym(sym1, sym2)))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64FMOVSload)
|
||||
@@ -2785,7 +2785,7 @@ func rewriteValueARM64_OpARM64FMOVSstore(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (FMOVSstore [off1] {sym} (ADDconst [off2] ptr) val mem)
|
||||
// cond: is32Bit(off1+off2) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym))
|
||||
// cond: is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym))
|
||||
// result: (FMOVSstore [off1+off2] {sym} ptr val mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -2798,7 +2798,7 @@ func rewriteValueARM64_OpARM64FMOVSstore(v *Value, config *Config) bool {
|
||||
ptr := v_0.Args[0]
|
||||
val := v.Args[1]
|
||||
mem := v.Args[2]
|
||||
if !(is32Bit(off1+off2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym))) {
|
||||
if !(is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(sym))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64FMOVSstore)
|
||||
@@ -2810,7 +2810,7 @@ func rewriteValueARM64_OpARM64FMOVSstore(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (FMOVSstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2)) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2)))
|
||||
// result: (FMOVSstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -2824,7 +2824,7 @@ func rewriteValueARM64_OpARM64FMOVSstore(v *Value, config *Config) bool {
|
||||
ptr := v_0.Args[0]
|
||||
val := v.Args[1]
|
||||
mem := v.Args[2]
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1, sym2)) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(mergeSym(sym1, sym2)))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64FMOVSstore)
|
||||
@@ -3997,7 +3997,7 @@ func rewriteValueARM64_OpARM64MOVDload(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (MOVDload [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
// cond: is32Bit(off1+off2) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym))
|
||||
// cond: is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym))
|
||||
// result: (MOVDload [off1+off2] {sym} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4009,7 +4009,7 @@ func rewriteValueARM64_OpARM64MOVDload(v *Value, config *Config) bool {
|
||||
off2 := v_0.AuxInt
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(is32Bit(off1+off2) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym))) {
|
||||
if !(is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(sym))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDload)
|
||||
@@ -4020,7 +4020,7 @@ func rewriteValueARM64_OpARM64MOVDload(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (MOVDload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2)) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2)))
|
||||
// result: (MOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4033,7 +4033,7 @@ func rewriteValueARM64_OpARM64MOVDload(v *Value, config *Config) bool {
|
||||
sym2 := v_0.Aux
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1, sym2)) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(mergeSym(sym1, sym2)))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDload)
|
||||
@@ -4100,7 +4100,7 @@ func rewriteValueARM64_OpARM64MOVDstore(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (MOVDstore [off1] {sym} (ADDconst [off2] ptr) val mem)
|
||||
// cond: is32Bit(off1+off2) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym))
|
||||
// cond: is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym))
|
||||
// result: (MOVDstore [off1+off2] {sym} ptr val mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4113,7 +4113,7 @@ func rewriteValueARM64_OpARM64MOVDstore(v *Value, config *Config) bool {
|
||||
ptr := v_0.Args[0]
|
||||
val := v.Args[1]
|
||||
mem := v.Args[2]
|
||||
if !(is32Bit(off1+off2) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym))) {
|
||||
if !(is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(sym))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDstore)
|
||||
@@ -4125,7 +4125,7 @@ func rewriteValueARM64_OpARM64MOVDstore(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (MOVDstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2)) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2)))
|
||||
// result: (MOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4139,7 +4139,7 @@ func rewriteValueARM64_OpARM64MOVDstore(v *Value, config *Config) bool {
|
||||
ptr := v_0.Args[0]
|
||||
val := v.Args[1]
|
||||
mem := v.Args[2]
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1, sym2)) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(mergeSym(sym1, sym2)))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDstore)
|
||||
@@ -4178,7 +4178,7 @@ func rewriteValueARM64_OpARM64MOVDstorezero(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (MOVDstorezero [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
// cond: is32Bit(off1+off2) && ((off1+off2)%2==8 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym))
|
||||
// cond: is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%2==8 || off1+off2<256 && off1+off2>-256 && !isAuto(sym))
|
||||
// result: (MOVDstorezero [off1+off2] {sym} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4190,7 +4190,7 @@ func rewriteValueARM64_OpARM64MOVDstorezero(v *Value, config *Config) bool {
|
||||
off2 := v_0.AuxInt
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(is32Bit(off1+off2) && ((off1+off2)%2 == 8 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym))) {
|
||||
if !(is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%2 == 8 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(sym))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDstorezero)
|
||||
@@ -4201,7 +4201,7 @@ func rewriteValueARM64_OpARM64MOVDstorezero(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (MOVDstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2)) && ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2)))
|
||||
// result: (MOVDstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4214,7 +4214,7 @@ func rewriteValueARM64_OpARM64MOVDstorezero(v *Value, config *Config) bool {
|
||||
sym2 := v_0.Aux
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1, sym2)) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(mergeSym(sym1, sym2)))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVDstorezero)
|
||||
@@ -4230,7 +4230,7 @@ func rewriteValueARM64_OpARM64MOVHUload(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (MOVHUload [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
// cond: is32Bit(off1+off2) && ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym))
|
||||
// cond: is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym))
|
||||
// result: (MOVHUload [off1+off2] {sym} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4242,7 +4242,7 @@ func rewriteValueARM64_OpARM64MOVHUload(v *Value, config *Config) bool {
|
||||
off2 := v_0.AuxInt
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(is32Bit(off1+off2) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym))) {
|
||||
if !(is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(sym))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVHUload)
|
||||
@@ -4253,7 +4253,7 @@ func rewriteValueARM64_OpARM64MOVHUload(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (MOVHUload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2)) && ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2)))
|
||||
// result: (MOVHUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4266,7 +4266,7 @@ func rewriteValueARM64_OpARM64MOVHUload(v *Value, config *Config) bool {
|
||||
sym2 := v_0.Aux
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1, sym2)) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(mergeSym(sym1, sym2)))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVHUload)
|
||||
@@ -4369,7 +4369,7 @@ func rewriteValueARM64_OpARM64MOVHload(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (MOVHload [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
// cond: is32Bit(off1+off2) && ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym))
|
||||
// cond: is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym))
|
||||
// result: (MOVHload [off1+off2] {sym} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4381,7 +4381,7 @@ func rewriteValueARM64_OpARM64MOVHload(v *Value, config *Config) bool {
|
||||
off2 := v_0.AuxInt
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(is32Bit(off1+off2) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym))) {
|
||||
if !(is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(sym))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVHload)
|
||||
@@ -4392,7 +4392,7 @@ func rewriteValueARM64_OpARM64MOVHload(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (MOVHload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2)) && ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2)))
|
||||
// result: (MOVHload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4405,7 +4405,7 @@ func rewriteValueARM64_OpARM64MOVHload(v *Value, config *Config) bool {
|
||||
sym2 := v_0.Aux
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1, sym2)) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(mergeSym(sym1, sym2)))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVHload)
|
||||
@@ -4532,7 +4532,7 @@ func rewriteValueARM64_OpARM64MOVHstore(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (MOVHstore [off1] {sym} (ADDconst [off2] ptr) val mem)
|
||||
// cond: is32Bit(off1+off2) && ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym))
|
||||
// cond: is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym))
|
||||
// result: (MOVHstore [off1+off2] {sym} ptr val mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4545,7 +4545,7 @@ func rewriteValueARM64_OpARM64MOVHstore(v *Value, config *Config) bool {
|
||||
ptr := v_0.Args[0]
|
||||
val := v.Args[1]
|
||||
mem := v.Args[2]
|
||||
if !(is32Bit(off1+off2) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym))) {
|
||||
if !(is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(sym))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVHstore)
|
||||
@@ -4557,7 +4557,7 @@ func rewriteValueARM64_OpARM64MOVHstore(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (MOVHstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2)) && ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2)))
|
||||
// result: (MOVHstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4571,7 +4571,7 @@ func rewriteValueARM64_OpARM64MOVHstore(v *Value, config *Config) bool {
|
||||
ptr := v_0.Args[0]
|
||||
val := v.Args[1]
|
||||
mem := v.Args[2]
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1, sym2)) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(mergeSym(sym1, sym2)))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVHstore)
|
||||
@@ -4694,7 +4694,7 @@ func rewriteValueARM64_OpARM64MOVHstorezero(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (MOVHstorezero [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
// cond: is32Bit(off1+off2) && ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym))
|
||||
// cond: is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym))
|
||||
// result: (MOVHstorezero [off1+off2] {sym} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4706,7 +4706,7 @@ func rewriteValueARM64_OpARM64MOVHstorezero(v *Value, config *Config) bool {
|
||||
off2 := v_0.AuxInt
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(is32Bit(off1+off2) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym))) {
|
||||
if !(is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(sym))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVHstorezero)
|
||||
@@ -4717,7 +4717,7 @@ func rewriteValueARM64_OpARM64MOVHstorezero(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (MOVHstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2)) && ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2)))
|
||||
// result: (MOVHstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4730,7 +4730,7 @@ func rewriteValueARM64_OpARM64MOVHstorezero(v *Value, config *Config) bool {
|
||||
sym2 := v_0.Aux
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1, sym2)) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(mergeSym(sym1, sym2)))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVHstorezero)
|
||||
@@ -4746,7 +4746,7 @@ func rewriteValueARM64_OpARM64MOVWUload(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (MOVWUload [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
// cond: is32Bit(off1+off2) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym))
|
||||
// cond: is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym))
|
||||
// result: (MOVWUload [off1+off2] {sym} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4758,7 +4758,7 @@ func rewriteValueARM64_OpARM64MOVWUload(v *Value, config *Config) bool {
|
||||
off2 := v_0.AuxInt
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(is32Bit(off1+off2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym))) {
|
||||
if !(is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(sym))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVWUload)
|
||||
@@ -4769,7 +4769,7 @@ func rewriteValueARM64_OpARM64MOVWUload(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (MOVWUload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2)) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2)))
|
||||
// result: (MOVWUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4782,7 +4782,7 @@ func rewriteValueARM64_OpARM64MOVWUload(v *Value, config *Config) bool {
|
||||
sym2 := v_0.Aux
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1, sym2)) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(mergeSym(sym1, sym2)))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVWUload)
|
||||
@@ -4909,7 +4909,7 @@ func rewriteValueARM64_OpARM64MOVWload(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (MOVWload [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
// cond: is32Bit(off1+off2) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym))
|
||||
// cond: is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym))
|
||||
// result: (MOVWload [off1+off2] {sym} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4921,7 +4921,7 @@ func rewriteValueARM64_OpARM64MOVWload(v *Value, config *Config) bool {
|
||||
off2 := v_0.AuxInt
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(is32Bit(off1+off2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym))) {
|
||||
if !(is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(sym))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVWload)
|
||||
@@ -4932,7 +4932,7 @@ func rewriteValueARM64_OpARM64MOVWload(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (MOVWload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2)) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2)))
|
||||
// result: (MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -4945,7 +4945,7 @@ func rewriteValueARM64_OpARM64MOVWload(v *Value, config *Config) bool {
|
||||
sym2 := v_0.Aux
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1, sym2)) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(mergeSym(sym1, sym2)))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVWload)
|
||||
@@ -5120,7 +5120,7 @@ func rewriteValueARM64_OpARM64MOVWstore(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (MOVWstore [off1] {sym} (ADDconst [off2] ptr) val mem)
|
||||
// cond: is32Bit(off1+off2) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym))
|
||||
// cond: is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym))
|
||||
// result: (MOVWstore [off1+off2] {sym} ptr val mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -5133,7 +5133,7 @@ func rewriteValueARM64_OpARM64MOVWstore(v *Value, config *Config) bool {
|
||||
ptr := v_0.Args[0]
|
||||
val := v.Args[1]
|
||||
mem := v.Args[2]
|
||||
if !(is32Bit(off1+off2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym))) {
|
||||
if !(is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(sym))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVWstore)
|
||||
@@ -5145,7 +5145,7 @@ func rewriteValueARM64_OpARM64MOVWstore(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (MOVWstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2)) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2)))
|
||||
// result: (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -5159,7 +5159,7 @@ func rewriteValueARM64_OpARM64MOVWstore(v *Value, config *Config) bool {
|
||||
ptr := v_0.Args[0]
|
||||
val := v.Args[1]
|
||||
mem := v.Args[2]
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1, sym2)) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(mergeSym(sym1, sym2)))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVWstore)
|
||||
@@ -5240,7 +5240,7 @@ func rewriteValueARM64_OpARM64MOVWstorezero(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (MOVWstorezero [off1] {sym} (ADDconst [off2] ptr) mem)
|
||||
// cond: is32Bit(off1+off2) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym))
|
||||
// cond: is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(sym))
|
||||
// result: (MOVWstorezero [off1+off2] {sym} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -5252,7 +5252,7 @@ func rewriteValueARM64_OpARM64MOVWstorezero(v *Value, config *Config) bool {
|
||||
off2 := v_0.AuxInt
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(is32Bit(off1+off2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym))) {
|
||||
if !(is32Bit(off1+off2) && !isArg(sym) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(sym))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVWstorezero)
|
||||
@@ -5263,7 +5263,7 @@ func rewriteValueARM64_OpARM64MOVWstorezero(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (MOVWstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
|
||||
// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1,sym2)) && ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isAuto(mergeSym(sym1,sym2)))
|
||||
// result: (MOVWstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
|
||||
for {
|
||||
off1 := v.AuxInt
|
||||
@@ -5276,7 +5276,7 @@ func rewriteValueARM64_OpARM64MOVWstorezero(v *Value, config *Config) bool {
|
||||
sym2 := v_0.Aux
|
||||
ptr := v_0.Args[0]
|
||||
mem := v.Args[1]
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
|
||||
if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2) && !isArg(mergeSym(sym1, sym2)) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isAuto(mergeSym(sym1, sym2)))) {
|
||||
break
|
||||
}
|
||||
v.reset(OpARM64MOVWstorezero)
|
||||
|
||||
@@ -712,7 +712,7 @@ func rewriteValueMIPS_OpAtomicAnd8(v *Value, config *Config) bool {
|
||||
_ = b
|
||||
// match: (AtomicAnd8 ptr val mem)
|
||||
// cond: !config.BigEndian
|
||||
// result: (LoweredAtomicAnd (AND <config.fe.TypeUInt32().PtrTo()> (MOVWconst [^3]) ptr) (OR <config.fe.TypeUInt32()> (SLL <config.fe.TypeUInt32()> (ZeroExt8to32 val) (SLLconst <config.fe.TypeUInt32()> [3] (ANDconst <config.fe.TypeUInt32()> [3] ptr))) (NORconst [0] <config.fe.TypeUInt32()> (SLL <config.fe.TypeUInt32()> (MOVWconst [0xff]) (SLLconst <config.fe.TypeUInt32()> [3] (ANDconst <config.fe.TypeUInt32()> [3] (XORconst <config.fe.TypeUInt32()> [3] ptr)))))) mem)
|
||||
// result: (LoweredAtomicAnd (AND <config.fe.TypeUInt32().PtrTo()> (MOVWconst [^3]) ptr) (OR <config.fe.TypeUInt32()> (SLL <config.fe.TypeUInt32()> (ZeroExt8to32 val) (SLLconst <config.fe.TypeUInt32()> [3] (ANDconst <config.fe.TypeUInt32()> [3] ptr))) (NORconst [0] <config.fe.TypeUInt32()> (SLL <config.fe.TypeUInt32()> (MOVWconst [0xff]) (SLLconst <config.fe.TypeUInt32()> [3] (ANDconst <config.fe.TypeUInt32()> [3] ptr))))) mem)
|
||||
for {
|
||||
ptr := v.Args[0]
|
||||
val := v.Args[1]
|
||||
@@ -750,10 +750,7 @@ func rewriteValueMIPS_OpAtomicAnd8(v *Value, config *Config) bool {
|
||||
v10.AuxInt = 3
|
||||
v11 := b.NewValue0(v.Line, OpMIPSANDconst, config.fe.TypeUInt32())
|
||||
v11.AuxInt = 3
|
||||
v12 := b.NewValue0(v.Line, OpMIPSXORconst, config.fe.TypeUInt32())
|
||||
v12.AuxInt = 3
|
||||
v12.AddArg(ptr)
|
||||
v11.AddArg(v12)
|
||||
v11.AddArg(ptr)
|
||||
v10.AddArg(v11)
|
||||
v8.AddArg(v10)
|
||||
v7.AddArg(v8)
|
||||
|
||||
@@ -524,6 +524,8 @@ func rewriteValueS390X(v *Value, config *Config) bool {
|
||||
return rewriteValueS390X_OpS390XMOVDload(v, config)
|
||||
case OpS390XMOVDloadidx:
|
||||
return rewriteValueS390X_OpS390XMOVDloadidx(v, config)
|
||||
case OpS390XMOVDnop:
|
||||
return rewriteValueS390X_OpS390XMOVDnop(v, config)
|
||||
case OpS390XMOVDreg:
|
||||
return rewriteValueS390X_OpS390XMOVDreg(v, config)
|
||||
case OpS390XMOVDstore:
|
||||
@@ -6782,7 +6784,7 @@ func rewriteValueS390X_OpS390XCMPU(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (CMPU x (MOVDconst [c]))
|
||||
// cond: is32Bit(c)
|
||||
// cond: isU32Bit(c)
|
||||
// result: (CMPUconst x [int64(uint32(c))])
|
||||
for {
|
||||
x := v.Args[0]
|
||||
@@ -6791,7 +6793,7 @@ func rewriteValueS390X_OpS390XCMPU(v *Value, config *Config) bool {
|
||||
break
|
||||
}
|
||||
c := v_1.AuxInt
|
||||
if !(is32Bit(c)) {
|
||||
if !(isU32Bit(c)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpS390XCMPUconst)
|
||||
@@ -6800,7 +6802,7 @@ func rewriteValueS390X_OpS390XCMPU(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (CMPU (MOVDconst [c]) x)
|
||||
// cond: is32Bit(c)
|
||||
// cond: isU32Bit(c)
|
||||
// result: (InvertFlags (CMPUconst x [int64(uint32(c))]))
|
||||
for {
|
||||
v_0 := v.Args[0]
|
||||
@@ -6809,7 +6811,7 @@ func rewriteValueS390X_OpS390XCMPU(v *Value, config *Config) bool {
|
||||
}
|
||||
c := v_0.AuxInt
|
||||
x := v.Args[1]
|
||||
if !(is32Bit(c)) {
|
||||
if !(isU32Bit(c)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpS390XInvertFlags)
|
||||
@@ -10195,9 +10197,68 @@ func rewriteValueS390X_OpS390XMOVDloadidx(v *Value, config *Config) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
func rewriteValueS390X_OpS390XMOVDnop(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (MOVDnop <t> x)
|
||||
// cond: t.Compare(x.Type) == CMPeq
|
||||
// result: x
|
||||
for {
|
||||
t := v.Type
|
||||
x := v.Args[0]
|
||||
if !(t.Compare(x.Type) == CMPeq) {
|
||||
break
|
||||
}
|
||||
v.reset(OpCopy)
|
||||
v.Type = x.Type
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVDnop (MOVDconst [c]))
|
||||
// cond:
|
||||
// result: (MOVDconst [c])
|
||||
for {
|
||||
v_0 := v.Args[0]
|
||||
if v_0.Op != OpS390XMOVDconst {
|
||||
break
|
||||
}
|
||||
c := v_0.AuxInt
|
||||
v.reset(OpS390XMOVDconst)
|
||||
v.AuxInt = c
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func rewriteValueS390X_OpS390XMOVDreg(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (MOVDreg <t> x)
|
||||
// cond: t.Compare(x.Type) == CMPeq
|
||||
// result: x
|
||||
for {
|
||||
t := v.Type
|
||||
x := v.Args[0]
|
||||
if !(t.Compare(x.Type) == CMPeq) {
|
||||
break
|
||||
}
|
||||
v.reset(OpCopy)
|
||||
v.Type = x.Type
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVDreg (MOVDconst [c]))
|
||||
// cond:
|
||||
// result: (MOVDconst [c])
|
||||
for {
|
||||
v_0 := v.Args[0]
|
||||
if v_0.Op != OpS390XMOVDconst {
|
||||
break
|
||||
}
|
||||
c := v_0.AuxInt
|
||||
v.reset(OpS390XMOVDconst)
|
||||
v.AuxInt = c
|
||||
return true
|
||||
}
|
||||
// match: (MOVDreg x)
|
||||
// cond: x.Uses == 1
|
||||
// result: (MOVDnop x)
|
||||
@@ -18181,7 +18242,7 @@ func rewriteBlockS390X(b *Block, config *Config) bool {
|
||||
}
|
||||
// match: (If cond yes no)
|
||||
// cond:
|
||||
// result: (NE (CMPWconst [0] (MOVBZreg cond)) yes no)
|
||||
// result: (NE (CMPWconst [0] (MOVBZreg <config.fe.TypeBool()> cond)) yes no)
|
||||
for {
|
||||
v := b.Control
|
||||
_ = v
|
||||
@@ -18191,7 +18252,7 @@ func rewriteBlockS390X(b *Block, config *Config) bool {
|
||||
b.Kind = BlockS390XNE
|
||||
v0 := b.NewValue0(v.Line, OpS390XCMPWconst, TypeFlags)
|
||||
v0.AuxInt = 0
|
||||
v1 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
|
||||
v1 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeBool())
|
||||
v1.AddArg(cond)
|
||||
v0.AddArg(v1)
|
||||
b.SetControl(v0)
|
||||
|
||||
@@ -14,6 +14,7 @@ const (
|
||||
ScoreMemory
|
||||
ScoreDefault
|
||||
ScoreFlags
|
||||
ScoreSelectCall
|
||||
ScoreControl // towards bottom of block
|
||||
)
|
||||
|
||||
@@ -110,10 +111,25 @@ func schedule(f *Func) {
|
||||
// We want all the vardefs next.
|
||||
score[v.ID] = ScoreVarDef
|
||||
case v.Type.IsMemory():
|
||||
// Schedule stores as early as possible. This tends to
|
||||
// reduce register pressure. It also helps make sure
|
||||
// VARDEF ops are scheduled before the corresponding LEA.
|
||||
score[v.ID] = ScoreMemory
|
||||
// Don't schedule independent operations after call to those functions.
|
||||
// runtime.selectgo will jump to next instruction after this call,
|
||||
// causing extra execution of those operations. Prevent it, by setting
|
||||
// priority to high value.
|
||||
if (v.Op == OpAMD64CALLstatic || v.Op == OpPPC64CALLstatic ||
|
||||
v.Op == OpARMCALLstatic || v.Op == OpARM64CALLstatic ||
|
||||
v.Op == Op386CALLstatic || v.Op == OpMIPS64CALLstatic ||
|
||||
v.Op == OpS390XCALLstatic || v.Op == OpMIPSCALLstatic) &&
|
||||
(isSameSym(v.Aux, "runtime.selectrecv") ||
|
||||
isSameSym(v.Aux, "runtime.selectrecv2") ||
|
||||
isSameSym(v.Aux, "runtime.selectsend") ||
|
||||
isSameSym(v.Aux, "runtime.selectdefault")) {
|
||||
score[v.ID] = ScoreSelectCall
|
||||
} else {
|
||||
// Schedule stores as early as possible. This tends to
|
||||
// reduce register pressure. It also helps make sure
|
||||
// VARDEF ops are scheduled before the corresponding LEA.
|
||||
score[v.ID] = ScoreMemory
|
||||
}
|
||||
case v.Op == OpSelect0 || v.Op == OpSelect1:
|
||||
// Schedule the pseudo-op of reading part of a tuple
|
||||
// immediately after the tuple-generating op, since
|
||||
@@ -132,19 +148,20 @@ func schedule(f *Func) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: make this logic permanent in types.IsMemory?
|
||||
isMem := func(v *Value) bool {
|
||||
return v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory()
|
||||
}
|
||||
|
||||
for _, b := range f.Blocks {
|
||||
// Find store chain for block.
|
||||
// Store chains for different blocks overwrite each other, so
|
||||
// the calculated store chain is good only for this block.
|
||||
for _, v := range b.Values {
|
||||
if v.Op != OpPhi && v.Type.IsMemory() {
|
||||
mem := v
|
||||
if v.Op == OpSelect1 {
|
||||
v = v.Args[0]
|
||||
}
|
||||
if v.Op != OpPhi && isMem(v) {
|
||||
for _, w := range v.Args {
|
||||
if w.Type.IsMemory() {
|
||||
nextMem[w.ID] = mem
|
||||
if isMem(w) {
|
||||
nextMem[w.ID] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,15 +180,15 @@ func schedule(f *Func) {
|
||||
uses[w.ID]++
|
||||
}
|
||||
// Any load must come before the following store.
|
||||
if v.Type.IsMemory() || !w.Type.IsMemory() {
|
||||
continue // not a load
|
||||
if !isMem(v) && isMem(w) {
|
||||
// v is a load.
|
||||
s := nextMem[w.ID]
|
||||
if s == nil || s.Block != b {
|
||||
continue
|
||||
}
|
||||
additionalArgs[s.ID] = append(additionalArgs[s.ID], v)
|
||||
uses[v.ID]++
|
||||
}
|
||||
s := nextMem[w.ID]
|
||||
if s == nil || s.Block != b {
|
||||
continue
|
||||
}
|
||||
additionalArgs[s.ID] = append(additionalArgs[s.ID], v)
|
||||
uses[v.ID]++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ type (
|
||||
// Name Type
|
||||
TypeDecl struct {
|
||||
Name *Name
|
||||
Alias bool
|
||||
Type Expr
|
||||
Group *Group // nil means not part of a group
|
||||
Pragma Pragma
|
||||
|
||||
@@ -325,7 +325,7 @@ func (p *parser) constDecl(group *Group) Decl {
|
||||
return d
|
||||
}
|
||||
|
||||
// TypeSpec = identifier Type .
|
||||
// TypeSpec = identifier [ "=" ] Type .
|
||||
func (p *parser) typeDecl(group *Group) Decl {
|
||||
if trace {
|
||||
defer p.trace("typeDecl")()
|
||||
@@ -335,6 +335,7 @@ func (p *parser) typeDecl(group *Group) Decl {
|
||||
d.init(p)
|
||||
|
||||
d.Name = p.name()
|
||||
d.Alias = p.got(_Assign)
|
||||
d.Type = p.tryType()
|
||||
if d.Type == nil {
|
||||
p.syntax_error("in type declaration")
|
||||
|
||||
@@ -619,7 +619,11 @@ func (p *printer) printRawNode(n Node) {
|
||||
if n.Group == nil {
|
||||
p.print(_Type, blank)
|
||||
}
|
||||
p.print(n.Name, blank, n.Type)
|
||||
p.print(n.Name, blank)
|
||||
if n.Alias {
|
||||
p.print(_Assign, blank)
|
||||
}
|
||||
p.print(n.Type)
|
||||
|
||||
case *VarDecl:
|
||||
if n.Group == nil {
|
||||
|
||||
@@ -22,3 +22,20 @@ func TestPrint(t *testing.T) {
|
||||
Fprint(os.Stdout, ast, true)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func TestPrintString(t *testing.T) {
|
||||
for _, want := range []string{
|
||||
"package p",
|
||||
"package p; type _ = int; type T1 = struct{}; type ( _ = *struct{}; T2 = float32 )",
|
||||
// TODO(gri) expand
|
||||
} {
|
||||
ast, err := ParseBytes([]byte(want), nil, nil, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
if got := String(ast); got != want {
|
||||
t.Errorf("%q: got %q", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,4 +31,5 @@ func Init() {
|
||||
gc.Thearch.SSAMarkMoves = ssaMarkMoves
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
gc.Thearch.ZeroAuto = zeroAuto
|
||||
}
|
||||
|
||||
@@ -84,6 +84,23 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32) *obj.Pr
|
||||
return p
|
||||
}
|
||||
|
||||
func zeroAuto(n *gc.Node, pp *obj.Prog) {
|
||||
// Note: this code must not clobber any registers.
|
||||
sym := gc.Linksym(n.Sym)
|
||||
size := n.Type.Size()
|
||||
for i := int64(0); i < size; i += 4 {
|
||||
p := gc.AddAsmAfter(x86.AMOVL, pp)
|
||||
pp = p
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = 0
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
p.To.Reg = x86.REG_SP
|
||||
p.To.Offset = n.Xoffset + i
|
||||
p.To.Sym = sym
|
||||
}
|
||||
}
|
||||
|
||||
func ginsnop() {
|
||||
p := gc.Prog(x86.AXCHGL)
|
||||
p.From.Type = obj.TYPE_REG
|
||||
|
||||
1
src/cmd/dist/test.go
vendored
1
src/cmd/dist/test.go
vendored
@@ -600,6 +600,7 @@ func (t *tester) registerTests() {
|
||||
})
|
||||
}
|
||||
}
|
||||
return // skip API check on go1.8.typealias branch
|
||||
if t.goos != "nacl" && t.goos != "android" && !t.iOS() {
|
||||
t.tests = append(t.tests, distTest{
|
||||
name: "api",
|
||||
|
||||
@@ -71,6 +71,7 @@ var tests = []test{
|
||||
`const MultiLineConst = ...`, // Multi line constant.
|
||||
`var MultiLineVar = map\[struct{ ... }\]struct{ ... }{ ... }`, // Multi line variable.
|
||||
`func MultiLineFunc\(x interface{ ... }\) \(r struct{ ... }\)`, // Multi line function.
|
||||
`type T1 = T2`, // Type alias
|
||||
},
|
||||
[]string{
|
||||
`const internalConstant = 2`, // No internal constants.
|
||||
@@ -89,6 +90,7 @@ var tests = []test{
|
||||
`unexportedTypedConstant`, // No unexported typed constant.
|
||||
`Field`, // No fields.
|
||||
`Method`, // No methods.
|
||||
`type T1 T2`, // Type alias does not display as type declaration.
|
||||
},
|
||||
},
|
||||
// Package dump -u
|
||||
@@ -265,6 +267,18 @@ var tests = []test{
|
||||
`error`, // No embedded error.
|
||||
},
|
||||
},
|
||||
// Type T1 dump (alias).
|
||||
{
|
||||
"type T1",
|
||||
[]string{p+".T1"},
|
||||
[]string{
|
||||
`type T1 = T2`,
|
||||
},
|
||||
[]string{
|
||||
`type T1 T2`,
|
||||
`type ExportedType`,
|
||||
},
|
||||
},
|
||||
// Type -u with unexported fields.
|
||||
{
|
||||
"type with unexported fields and -u",
|
||||
|
||||
@@ -258,7 +258,11 @@ func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string {
|
||||
return fmt.Sprintf("func %s%s%s", recv, name, fnc)
|
||||
|
||||
case *ast.TypeSpec:
|
||||
return fmt.Sprintf("type %s %s", n.Name.Name, pkg.oneLineNodeDepth(n.Type, depth))
|
||||
sep := " "
|
||||
if n.Assign.IsValid() {
|
||||
sep = " = "
|
||||
}
|
||||
return fmt.Sprintf("type %s%s%s", n.Name.Name, sep, pkg.oneLineNodeDepth(n.Type, depth))
|
||||
|
||||
case *ast.FuncType:
|
||||
var params []string
|
||||
|
||||
4
src/cmd/doc/testdata/pkg.go
vendored
4
src/cmd/doc/testdata/pkg.go
vendored
@@ -172,3 +172,7 @@ const (
|
||||
)
|
||||
|
||||
const ConstGroup4 ExportedType = ExportedType{}
|
||||
|
||||
type T2 int
|
||||
|
||||
type T1 = T2
|
||||
|
||||
@@ -3073,6 +3073,26 @@ func (b *builder) ccompile(p *Package, outfile string, flags []string, file stri
|
||||
desc := p.ImportPath
|
||||
output, err := b.runOut(p.Dir, desc, nil, compiler, flags, "-o", outfile, "-c", file)
|
||||
if len(output) > 0 {
|
||||
// On FreeBSD 11, when we pass -g to clang 3.8 it
|
||||
// invokes its internal assembler with -dwarf-version=2.
|
||||
// When it sees .section .note.GNU-stack, it warns
|
||||
// "DWARF2 only supports one section per compilation unit".
|
||||
// This warning makes no sense, since the section is empty,
|
||||
// but it confuses people.
|
||||
// We work around the problem by detecting the warning
|
||||
// and dropping -g and trying again.
|
||||
if bytes.Contains(output, []byte("DWARF2 only supports one section per compilation unit")) {
|
||||
newFlags := make([]string, 0, len(flags))
|
||||
for _, f := range flags {
|
||||
if !strings.HasPrefix(f, "-g") {
|
||||
newFlags = append(newFlags, f)
|
||||
}
|
||||
}
|
||||
if len(newFlags) < len(flags) {
|
||||
return b.ccompile(p, outfile, newFlags, file, compiler)
|
||||
}
|
||||
}
|
||||
|
||||
b.showOutput(p.Dir, desc, b.processOutput(output))
|
||||
if err != nil {
|
||||
err = errPrintedOutput
|
||||
|
||||
@@ -2227,6 +2227,24 @@ func TestTestEmpty(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTestRaceInstall(t *testing.T) {
|
||||
if !canRace {
|
||||
t.Skip("no race detector")
|
||||
}
|
||||
|
||||
tg := testgo(t)
|
||||
defer tg.cleanup()
|
||||
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
|
||||
|
||||
tg.tempDir("pkg")
|
||||
pkgdir := tg.path("pkg")
|
||||
tg.run("install", "-race", "-pkgdir="+pkgdir, "std")
|
||||
tg.run("test", "-race", "-pkgdir="+pkgdir, "-i", "-v", "empty/pkg")
|
||||
if tg.getStderr() != "" {
|
||||
t.Error("go test -i -race: rebuilds cached packages")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDryRunWithCgo(t *testing.T) {
|
||||
if !canCgo {
|
||||
t.Skip("skipping because cgo not enabled")
|
||||
|
||||
@@ -955,10 +955,6 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
|
||||
if p.Name == "main" && goarch == "arm" {
|
||||
importPaths = append(importPaths, "math")
|
||||
}
|
||||
// In coverage atomic mode everything depends on sync/atomic.
|
||||
if testCoverMode == "atomic" && (!p.Standard || (p.ImportPath != "runtime/cgo" && p.ImportPath != "runtime/race" && p.ImportPath != "sync/atomic")) {
|
||||
importPaths = append(importPaths, "sync/atomic")
|
||||
}
|
||||
}
|
||||
|
||||
// Runtime and its internal packages depend on runtime/internal/sys,
|
||||
|
||||
@@ -545,6 +545,10 @@ func runTest(cmd *Command, args []string) {
|
||||
|
||||
// Prepare build + run + print actions for all packages being tested.
|
||||
for _, p := range pkgs {
|
||||
// sync/atomic import is inserted by the cover tool. See #18486
|
||||
if testCover && testCoverMode == "atomic" {
|
||||
ensureImport(p, "sync/atomic")
|
||||
}
|
||||
buildTest, runTest, printTest, err := b.test(p)
|
||||
if err != nil {
|
||||
str := err.Error()
|
||||
@@ -636,6 +640,23 @@ func runTest(cmd *Command, args []string) {
|
||||
b.do(root)
|
||||
}
|
||||
|
||||
// ensures that package p imports the named package.
|
||||
func ensureImport(p *Package, pkg string) {
|
||||
for _, d := range p.deps {
|
||||
if d.Name == pkg {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
a := loadPackage(pkg, &importStack{})
|
||||
if a.Error != nil {
|
||||
fatalf("load %s: %v", pkg, a.Error)
|
||||
}
|
||||
computeStale(a)
|
||||
|
||||
p.imports = append(p.imports, a)
|
||||
}
|
||||
|
||||
func contains(x []string, s string) bool {
|
||||
for _, t := range x {
|
||||
if t == s {
|
||||
|
||||
24
src/cmd/gofmt/testdata/typealias.golden
vendored
Normal file
24
src/cmd/gofmt/testdata/typealias.golden
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package q
|
||||
|
||||
import "p"
|
||||
|
||||
type _ = int
|
||||
type a = struct{ x int }
|
||||
type b = p.B
|
||||
|
||||
type (
|
||||
_ = chan<- int
|
||||
aa = interface{}
|
||||
bb = p.BB
|
||||
)
|
||||
|
||||
// TODO(gri) We may want to put the '=' into a separate column if
|
||||
// we have mixed (regular and alias) type declarations in a group.
|
||||
type (
|
||||
_ chan<- int
|
||||
_ = chan<- int
|
||||
aa0 interface{}
|
||||
aaa = interface{}
|
||||
bb0 p.BB
|
||||
bbb = p.BB
|
||||
)
|
||||
24
src/cmd/gofmt/testdata/typealias.input
vendored
Normal file
24
src/cmd/gofmt/testdata/typealias.input
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package q
|
||||
|
||||
import "p"
|
||||
|
||||
type _ = int
|
||||
type a = struct{ x int }
|
||||
type b = p.B
|
||||
|
||||
type (
|
||||
_ = chan<- int
|
||||
aa = interface{}
|
||||
bb = p.BB
|
||||
)
|
||||
|
||||
// TODO(gri) We may want to put the '=' into a separate column if
|
||||
// we have mixed (regular and alias) type declarations in a group.
|
||||
type (
|
||||
_ chan<- int
|
||||
_ = chan<- int
|
||||
aa0 interface{}
|
||||
aaa = interface{}
|
||||
bb0 p.BB
|
||||
bbb = p.BB
|
||||
)
|
||||
125
src/cmd/link/dwarf_test.go
Normal file
125
src/cmd/link/dwarf_test.go
Normal file
@@ -0,0 +1,125 @@
|
||||
// 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.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmd/internal/objfile"
|
||||
"debug/dwarf"
|
||||
"internal/testenv"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDWARF(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("DWARF is not supported on Windows")
|
||||
}
|
||||
|
||||
testenv.MustHaveCGO(t)
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
if runtime.GOOS == "plan9" {
|
||||
t.Skip("skipping on plan9; no DWARF symbol table in executables")
|
||||
}
|
||||
|
||||
out, err := exec.Command(testenv.GoToolPath(t), "list", "-f", "{{.Stale}}", "cmd/link").CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("go list: %v\n%s", err, out)
|
||||
}
|
||||
if string(out) != "false\n" {
|
||||
t.Fatalf("cmd/link is stale - run go install cmd/link")
|
||||
}
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "go-link-TestDWARF")
|
||||
if err != nil {
|
||||
t.Fatal("TempDir failed: ", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
for _, prog := range []string{"testprog", "testprogcgo"} {
|
||||
t.Run(prog, func(t *testing.T) {
|
||||
exe := filepath.Join(tmpDir, prog+".exe")
|
||||
dir := "../../runtime/testdata/" + prog
|
||||
out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, dir).CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("go build -o %v %v: %v\n%s", exe, dir, err, out)
|
||||
}
|
||||
|
||||
f, err := objfile.Open(exe)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
syms, err := f.Symbols()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var addr uint64
|
||||
for _, sym := range syms {
|
||||
if sym.Name == "main.main" {
|
||||
addr = sym.Addr
|
||||
break
|
||||
}
|
||||
}
|
||||
if addr == 0 {
|
||||
t.Fatal("cannot find main.main in symbols")
|
||||
}
|
||||
|
||||
d, err := f.DWARF()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// TODO: We'd like to use filepath.Join here.
|
||||
// Also related: golang.org/issue/19784.
|
||||
wantFile := path.Join(prog, "main.go")
|
||||
wantLine := 24
|
||||
r := d.Reader()
|
||||
var line dwarf.LineEntry
|
||||
for {
|
||||
cu, err := r.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cu == nil {
|
||||
break
|
||||
}
|
||||
if cu.Tag != dwarf.TagCompileUnit {
|
||||
r.SkipChildren()
|
||||
continue
|
||||
}
|
||||
lr, err := d.LineReader(cu)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for {
|
||||
err := lr.Next(&line)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if line.Address == addr {
|
||||
if !strings.HasSuffix(line.File.Name, wantFile) || line.Line != wantLine {
|
||||
t.Errorf("%#x is %s:%d, want %s:%d", addr, line.File.Name, line.Line, filepath.Join("...", wantFile), wantLine)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
t.Fatalf("did not find file:line for %#x (main.main)", addr)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -255,7 +255,7 @@ func decodetypeStructFieldType(s *Symbol, i int) *Symbol {
|
||||
|
||||
func decodetypeStructFieldOffs(arch *sys.Arch, s *Symbol, i int) int64 {
|
||||
off := decodetypeStructFieldArrayOff(s, i)
|
||||
return int64(decodeInuxi(arch, s.P[off+2*SysArch.PtrSize:], SysArch.IntSize))
|
||||
return int64(decodeInuxi(arch, s.P[off+2*SysArch.PtrSize:], SysArch.IntSize) >> 1)
|
||||
}
|
||||
|
||||
// InterfaceType.methods.length
|
||||
|
||||
@@ -430,6 +430,10 @@ func (ctxt *Link) loadlib() {
|
||||
// We now have enough information to determine the link mode.
|
||||
determineLinkMode(ctxt)
|
||||
|
||||
if Headtype == obj.Hdarwin && Linkmode == LinkExternal {
|
||||
*FlagTextAddr = 0
|
||||
}
|
||||
|
||||
if Linkmode == LinkExternal && SysArch.Family == sys.PPC64 {
|
||||
toc := ctxt.Syms.Lookup(".TOC.", 0)
|
||||
toc.Type = obj.SDYNIMPORT
|
||||
@@ -998,6 +1002,10 @@ func (l *Link) hostlink() {
|
||||
|
||||
if !*FlagS && !debug_s {
|
||||
argv = append(argv, "-gdwarf-2")
|
||||
} else if Headtype == obj.Hdarwin {
|
||||
// Recent versions of macOS print
|
||||
// ld: warning: option -s is obsolete and being ignored
|
||||
// so do not pass any arguments.
|
||||
} else {
|
||||
argv = append(argv, "-s")
|
||||
}
|
||||
@@ -1219,7 +1227,7 @@ func (l *Link) hostlink() {
|
||||
l.Logf("%s", out)
|
||||
}
|
||||
|
||||
if !*FlagS && !debug_s && Headtype == obj.Hdarwin {
|
||||
if !*FlagS && !*FlagW && !debug_s && Headtype == obj.Hdarwin {
|
||||
// Skip combining dwarf on arm.
|
||||
if !SysArch.InFamily(sys.ARM, sys.ARM64) {
|
||||
dsym := filepath.Join(*flagTmpdir, "go.dwarf")
|
||||
|
||||
@@ -449,7 +449,7 @@ func Asmbmacho(ctxt *Link) {
|
||||
ms.filesize = Segdata.Fileoff + Segdata.Filelen - Segtext.Fileoff
|
||||
} else {
|
||||
ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff
|
||||
ms.vsize = ms.filesize
|
||||
ms.vsize = Segdwarf.Vaddr + Segdwarf.Length - Segtext.Vaddr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
var realdwarf, linkseg *macho.Segment
|
||||
var dwarfstart, linkstart int64
|
||||
var dwarfaddr, linkaddr int64
|
||||
var linkoffset uint32
|
||||
|
||||
const (
|
||||
@@ -41,8 +42,7 @@ const (
|
||||
LC_DYLIB_CODE_SIGN_DRS = 0x2B
|
||||
LC_ENCRYPTION_INFO_64 = 0x2C
|
||||
|
||||
dwarfMinAlign = 6 // 64 = 1 << 6
|
||||
pageAlign = 12 // 4096 = 1 << 12
|
||||
pageAlign = 12 // 4096 = 1 << 12
|
||||
)
|
||||
|
||||
type loadCmd struct {
|
||||
@@ -157,16 +157,13 @@ func machoCombineDwarf(inexe, dsym, outexe string) error {
|
||||
}
|
||||
|
||||
// Now copy the dwarf data into the output.
|
||||
maxalign := uint32(dwarfMinAlign) //
|
||||
for _, sect := range dwarfm.Sections {
|
||||
if sect.Align > maxalign {
|
||||
maxalign = sect.Align
|
||||
}
|
||||
}
|
||||
dwarfstart = machoCalcStart(realdwarf.Offset, linkseg.Offset, maxalign)
|
||||
// Kernel requires all loaded segments to be page-aligned in the file,
|
||||
// even though we mark this one as being 0 bytes of virtual address space.
|
||||
dwarfstart = machoCalcStart(realdwarf.Offset, linkseg.Offset, pageAlign)
|
||||
if _, err = outf.Seek(dwarfstart, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
dwarfaddr = int64((linkseg.Addr + linkseg.Memsz + 1<<pageAlign - 1) &^ (1<<pageAlign - 1))
|
||||
|
||||
if _, err = dwarff.Seek(int64(realdwarf.Offset), 0); err != nil {
|
||||
return err
|
||||
@@ -277,10 +274,10 @@ func machoUpdateSegment(r loadCmdReader, seg, sect interface{}) error {
|
||||
return err
|
||||
}
|
||||
// There shouldn't be any sections, but just to make sure...
|
||||
return machoUpdateSections(r, segValue, reflect.ValueOf(sect), uint64(linkoffset))
|
||||
return machoUpdateSections(r, segValue, reflect.ValueOf(sect), uint64(linkoffset), 0)
|
||||
}
|
||||
|
||||
func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, delta uint64) error {
|
||||
func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, deltaOffset, deltaAddr uint64) error {
|
||||
iseg := reflect.Indirect(seg)
|
||||
nsect := iseg.FieldByName("Nsect").Uint()
|
||||
if nsect == 0 {
|
||||
@@ -291,16 +288,20 @@ func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, delta uint64)
|
||||
isect := reflect.Indirect(sect)
|
||||
offsetField := isect.FieldByName("Offset")
|
||||
reloffField := isect.FieldByName("Reloff")
|
||||
addrField := isect.FieldByName("Addr")
|
||||
sectSize := int64(isect.Type().Size())
|
||||
for i := uint64(0); i < nsect; i++ {
|
||||
if err := r.ReadAt(sectOffset, sect.Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
if offsetField.Uint() != 0 {
|
||||
offsetField.SetUint(offsetField.Uint() + delta)
|
||||
offsetField.SetUint(offsetField.Uint() + deltaOffset)
|
||||
}
|
||||
if reloffField.Uint() != 0 {
|
||||
reloffField.SetUint(reloffField.Uint() + delta)
|
||||
reloffField.SetUint(reloffField.Uint() + deltaOffset)
|
||||
}
|
||||
if addrField.Uint() != 0 {
|
||||
addrField.SetUint(addrField.Uint() + deltaAddr)
|
||||
}
|
||||
if err := r.WriteAt(sectOffset, sect.Interface()); err != nil {
|
||||
return err
|
||||
@@ -327,15 +328,30 @@ func machoUpdateDwarfHeader(r *loadCmdReader) error {
|
||||
if err := r.ReadAt(0, seg); err != nil {
|
||||
return err
|
||||
}
|
||||
segValue := reflect.ValueOf(seg)
|
||||
offset := reflect.Indirect(segValue).FieldByName("Offset")
|
||||
segv := reflect.ValueOf(seg).Elem()
|
||||
|
||||
segv.FieldByName("Offset").SetUint(uint64(dwarfstart))
|
||||
segv.FieldByName("Addr").SetUint(uint64(dwarfaddr))
|
||||
|
||||
deltaOffset := uint64(dwarfstart) - realdwarf.Offset
|
||||
deltaAddr := uint64(dwarfaddr) - realdwarf.Addr
|
||||
|
||||
// If we set Memsz to 0 (and might as well set Addr too),
|
||||
// then the xnu kernel will bail out halfway through load_segment
|
||||
// and not apply further sanity checks that we might fail in the future.
|
||||
// We don't need the DWARF information actually available in memory.
|
||||
// But if we do this for buildmode=c-shared then the user-space
|
||||
// dynamic loader complains about memsz < filesz. Sigh.
|
||||
if Buildmode != BuildmodeCShared {
|
||||
segv.FieldByName("Addr").SetUint(0)
|
||||
segv.FieldByName("Memsz").SetUint(0)
|
||||
deltaAddr = 0
|
||||
}
|
||||
|
||||
delta := uint64(dwarfstart) - realdwarf.Offset
|
||||
offset.SetUint(offset.Uint() + delta)
|
||||
if err := r.WriteAt(0, seg); err != nil {
|
||||
return err
|
||||
}
|
||||
return machoUpdateSections(*r, segValue, reflect.ValueOf(sect), delta)
|
||||
return machoUpdateSections(*r, segv, reflect.ValueOf(sect), deltaOffset, deltaAddr)
|
||||
}
|
||||
|
||||
func machoUpdateLoadCommand(r loadCmdReader, cmd interface{}, fields ...string) error {
|
||||
|
||||
@@ -87,6 +87,7 @@ func genplt(ctxt *ld.Link) {
|
||||
//
|
||||
// This assumes "case 1" from the ABI, where the caller needs
|
||||
// us to save and restore the TOC pointer.
|
||||
var stubs []*ld.Symbol
|
||||
for _, s := range ctxt.Textp {
|
||||
for i := range s.R {
|
||||
r := &s.R[i]
|
||||
@@ -108,7 +109,7 @@ func genplt(ctxt *ld.Link) {
|
||||
if stub.Size == 0 {
|
||||
// Need outer to resolve .TOC.
|
||||
stub.Outer = s
|
||||
ctxt.Textp = append(ctxt.Textp, stub)
|
||||
stubs = append(stubs, stub)
|
||||
gencallstub(ctxt, 1, stub, r.Sym)
|
||||
}
|
||||
|
||||
@@ -121,6 +122,11 @@ func genplt(ctxt *ld.Link) {
|
||||
ctxt.Arch.ByteOrder.PutUint32(s.P[r.Off+4:], o1)
|
||||
}
|
||||
}
|
||||
// Put call stubs at the beginning (instead of the end).
|
||||
// So when resolving the relocations to calls to the stubs,
|
||||
// the addresses are known and trampolines can be inserted
|
||||
// when necessary.
|
||||
ctxt.Textp = append(stubs, ctxt.Textp...)
|
||||
}
|
||||
|
||||
func genaddmoduledata(ctxt *ld.Link) {
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
|
||||
func TestLargeText(t *testing.T) {
|
||||
if testing.Short() || (obj.GOARCH != "ppc64le" && obj.GOARCH != "ppc64" && obj.GOARCH != "arm") {
|
||||
t.Skip("Skipping large text section test in short mode or on %s", obj.GOARCH)
|
||||
t.Skipf("Skipping large text section test in short mode or on %s", obj.GOARCH)
|
||||
}
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
|
||||
@@ -300,6 +300,29 @@ var p224BaseMultTests = []baseMultTest{
|
||||
},
|
||||
}
|
||||
|
||||
type scalarMultTest struct {
|
||||
k string
|
||||
xIn, yIn string
|
||||
xOut, yOut string
|
||||
}
|
||||
|
||||
var p256MultTests = []scalarMultTest{
|
||||
{
|
||||
"2a265f8bcbdcaf94d58519141e578124cb40d64a501fba9c11847b28965bc737",
|
||||
"023819813ac969847059028ea88a1f30dfbcde03fc791d3a252c6b41211882ea",
|
||||
"f93e4ae433cc12cf2a43fc0ef26400c0e125508224cdb649380f25479148a4ad",
|
||||
"4d4de80f1534850d261075997e3049321a0864082d24a917863366c0724f5ae3",
|
||||
"a22d2b7f7818a3563e0f7a76c9bf0921ac55e06e2e4d11795b233824b1db8cc0",
|
||||
},
|
||||
{
|
||||
"313f72ff9fe811bf573176231b286a3bdb6f1b14e05c40146590727a71c3bccd",
|
||||
"cc11887b2d66cbae8f4d306627192522932146b42f01d3c6f92bd5c8ba739b06",
|
||||
"a2f08a029cd06b46183085bae9248b0ed15b70280c7ef13a457f5af382426031",
|
||||
"831c3f6b5f762d2f461901577af41354ac5f228c2591f84f8a6e51e2e3f17991",
|
||||
"93f90934cd0ef2c698cc471c60a93524e87ab31ca2412252337f364513e43684",
|
||||
},
|
||||
}
|
||||
|
||||
func TestBaseMult(t *testing.T) {
|
||||
p224 := P224()
|
||||
for i, e := range p224BaseMultTests {
|
||||
@@ -379,6 +402,19 @@ func TestP256Mult(t *testing.T) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for i, e := range p256MultTests {
|
||||
x, _ := new(big.Int).SetString(e.xIn, 16)
|
||||
y, _ := new(big.Int).SetString(e.yIn, 16)
|
||||
k, _ := new(big.Int).SetString(e.k, 16)
|
||||
expectedX, _ := new(big.Int).SetString(e.xOut, 16)
|
||||
expectedY, _ := new(big.Int).SetString(e.yOut, 16)
|
||||
|
||||
xx, yy := p256.ScalarMult(x, y, k.Bytes())
|
||||
if xx.Cmp(expectedX) != 0 || yy.Cmp(expectedY) != 0 {
|
||||
t.Errorf("#%d: got (%x, %x), want (%x, %x)", i, xx, yy, expectedX, expectedY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInfinity(t *testing.T) {
|
||||
|
||||
@@ -1314,12 +1314,12 @@ TEXT p256SubInternal(SB),NOSPLIT,$0
|
||||
ADCQ p256const0<>(SB), acc5
|
||||
ADCQ $0, acc6
|
||||
ADCQ p256const1<>(SB), acc7
|
||||
ADCQ $0, mul0
|
||||
ANDQ $1, mul0
|
||||
|
||||
CMOVQNE acc0, acc4
|
||||
CMOVQNE acc1, acc5
|
||||
CMOVQNE acc2, acc6
|
||||
CMOVQNE acc3, acc7
|
||||
CMOVQEQ acc0, acc4
|
||||
CMOVQEQ acc1, acc5
|
||||
CMOVQEQ acc2, acc6
|
||||
CMOVQEQ acc3, acc7
|
||||
|
||||
RET
|
||||
/* ---------------------------------------*/
|
||||
|
||||
@@ -563,6 +563,7 @@ func (c *Config) Clone() *Config {
|
||||
Certificates: c.Certificates,
|
||||
NameToCertificate: c.NameToCertificate,
|
||||
GetCertificate: c.GetCertificate,
|
||||
GetClientCertificate: c.GetClientCertificate,
|
||||
GetConfigForClient: c.GetConfigForClient,
|
||||
VerifyPeerCertificate: c.VerifyPeerCertificate,
|
||||
RootCAs: c.RootCAs,
|
||||
|
||||
@@ -13,13 +13,11 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -568,11 +566,50 @@ func TestConnCloseWrite(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestClone(t *testing.T) {
|
||||
func TestCloneFuncFields(t *testing.T) {
|
||||
const expectedCount = 5
|
||||
called := 0
|
||||
|
||||
c1 := Config{
|
||||
Time: func() time.Time {
|
||||
called |= 1 << 0
|
||||
return time.Time{}
|
||||
},
|
||||
GetCertificate: func(*ClientHelloInfo) (*Certificate, error) {
|
||||
called |= 1 << 1
|
||||
return nil, nil
|
||||
},
|
||||
GetClientCertificate: func(*CertificateRequestInfo) (*Certificate, error) {
|
||||
called |= 1 << 2
|
||||
return nil, nil
|
||||
},
|
||||
GetConfigForClient: func(*ClientHelloInfo) (*Config, error) {
|
||||
called |= 1 << 3
|
||||
return nil, nil
|
||||
},
|
||||
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
called |= 1 << 4
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
c2 := c1.Clone()
|
||||
|
||||
c2.Time()
|
||||
c2.GetCertificate(nil)
|
||||
c2.GetClientCertificate(nil)
|
||||
c2.GetConfigForClient(nil)
|
||||
c2.VerifyPeerCertificate(nil, nil)
|
||||
|
||||
if called != (1<<expectedCount)-1 {
|
||||
t.Fatalf("expected %d calls but saw calls %b", expectedCount, called)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloneNonFuncFields(t *testing.T) {
|
||||
var c1 Config
|
||||
v := reflect.ValueOf(&c1).Elem()
|
||||
|
||||
rnd := rand.New(rand.NewSource(time.Now().Unix()))
|
||||
typ := v.Type()
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
f := v.Field(i)
|
||||
@@ -581,40 +618,49 @@ func TestClone(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
|
||||
// testing/quick can't handle functions or interfaces.
|
||||
fn := typ.Field(i).Name
|
||||
switch fn {
|
||||
// testing/quick can't handle functions or interfaces and so
|
||||
// isn't used here.
|
||||
switch fn := typ.Field(i).Name; fn {
|
||||
case "Rand":
|
||||
f.Set(reflect.ValueOf(io.Reader(os.Stdin)))
|
||||
continue
|
||||
case "Time", "GetCertificate", "GetConfigForClient", "VerifyPeerCertificate", "GetClientCertificate":
|
||||
// DeepEqual can't compare functions.
|
||||
continue
|
||||
// DeepEqual can't compare functions. If you add a
|
||||
// function field to this list, you must also change
|
||||
// TestCloneFuncFields to ensure that the func field is
|
||||
// cloned.
|
||||
case "Certificates":
|
||||
f.Set(reflect.ValueOf([]Certificate{
|
||||
{Certificate: [][]byte{{'b'}}},
|
||||
}))
|
||||
continue
|
||||
case "NameToCertificate":
|
||||
f.Set(reflect.ValueOf(map[string]*Certificate{"a": nil}))
|
||||
continue
|
||||
case "RootCAs", "ClientCAs":
|
||||
f.Set(reflect.ValueOf(x509.NewCertPool()))
|
||||
continue
|
||||
case "ClientSessionCache":
|
||||
f.Set(reflect.ValueOf(NewLRUClientSessionCache(10)))
|
||||
continue
|
||||
case "KeyLogWriter":
|
||||
f.Set(reflect.ValueOf(io.Writer(os.Stdout)))
|
||||
continue
|
||||
|
||||
case "NextProtos":
|
||||
f.Set(reflect.ValueOf([]string{"a", "b"}))
|
||||
case "ServerName":
|
||||
f.Set(reflect.ValueOf("b"))
|
||||
case "ClientAuth":
|
||||
f.Set(reflect.ValueOf(VerifyClientCertIfGiven))
|
||||
case "InsecureSkipVerify", "SessionTicketsDisabled", "DynamicRecordSizingDisabled", "PreferServerCipherSuites":
|
||||
f.Set(reflect.ValueOf(true))
|
||||
case "MinVersion", "MaxVersion":
|
||||
f.Set(reflect.ValueOf(uint16(VersionTLS12)))
|
||||
case "SessionTicketKey":
|
||||
f.Set(reflect.ValueOf([32]byte{}))
|
||||
case "CipherSuites":
|
||||
f.Set(reflect.ValueOf([]uint16{1, 2}))
|
||||
case "CurvePreferences":
|
||||
f.Set(reflect.ValueOf([]CurveID{CurveP256}))
|
||||
case "Renegotiation":
|
||||
f.Set(reflect.ValueOf(RenegotiateOnceAsClient))
|
||||
default:
|
||||
t.Errorf("all fields must be accounted for, but saw unknown field %q", fn)
|
||||
}
|
||||
|
||||
q, ok := quick.Value(f.Type(), rnd)
|
||||
if !ok {
|
||||
t.Fatalf("quick.Value failed on field %s", fn)
|
||||
}
|
||||
f.Set(q)
|
||||
}
|
||||
|
||||
c2 := c1.Clone()
|
||||
|
||||
@@ -1955,12 +1955,12 @@ func (s *Stmt) QueryContext(ctx context.Context, args ...interface{}) (*Rows, er
|
||||
rowsi: rowsi,
|
||||
// releaseConn set below
|
||||
}
|
||||
rows.initContextClose(ctx)
|
||||
s.db.addDep(s, rows)
|
||||
rows.releaseConn = func(err error) {
|
||||
releaseConn(err)
|
||||
s.db.removeDep(s, rows)
|
||||
}
|
||||
rows.initContextClose(ctx)
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user