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

Change-Id: Ia661c871e14445672b7d36a443455302e47cc2a1
This commit is contained in:
Filippo Valsorda 2018-10-25 19:30:18 -04:00
commit 13bf5b80e8
534 changed files with 23488 additions and 5204 deletions

View file

@ -6,6 +6,8 @@ pkg os, const ModeType = 2399141888
pkg os, const ModeType = 2399666176
pkg os (linux-arm), const O_SYNC = 4096
pkg os (linux-arm-cgo), const O_SYNC = 4096
pkg os (linux-arm), const O_SYNC = 1052672
pkg os (linux-arm-cgo), const O_SYNC = 1052672
pkg syscall (darwin-386), const ImplementsGetwd = false
pkg syscall (darwin-386), func Fchflags(string, int) error
pkg syscall (darwin-386-cgo), const ImplementsGetwd = false
@ -381,3 +383,101 @@ pkg syscall (windows-amd64), type CertRevocationInfo struct, CrlInfo uintptr
pkg syscall (windows-amd64), type CertRevocationInfo struct, OidSpecificInfo uintptr
pkg syscall (windows-amd64), type CertSimpleChain struct, TrustListInfo uintptr
pkg syscall (windows-amd64), type RawSockaddrAny struct, Pad [96]int8
pkg syscall (freebsd-386), func Mknod(string, uint32, int) error
pkg syscall (freebsd-386), type Dirent struct, Fileno uint32
pkg syscall (freebsd-386), type Dirent struct, Namlen uint8
pkg syscall (freebsd-386), type Stat_t struct, Atimespec Timespec
pkg syscall (freebsd-386), type Stat_t struct, Birthtimespec Timespec
pkg syscall (freebsd-386), type Stat_t struct, Blksize uint32
pkg syscall (freebsd-386), type Stat_t struct, Ctimespec Timespec
pkg syscall (freebsd-386), type Stat_t struct, Dev uint32
pkg syscall (freebsd-386), type Stat_t struct, Gen uint32
pkg syscall (freebsd-386), type Stat_t struct, Ino uint32
pkg syscall (freebsd-386), type Stat_t struct, Lspare int32
pkg syscall (freebsd-386), type Stat_t struct, Mtimespec Timespec
pkg syscall (freebsd-386), type Stat_t struct, Nlink uint16
pkg syscall (freebsd-386), type Stat_t struct, Pad_cgo_0 [8]uint8
pkg syscall (freebsd-386), type Stat_t struct, Rdev uint32
pkg syscall (freebsd-386), type Statfs_t struct, Mntfromname [88]int8
pkg syscall (freebsd-386), type Statfs_t struct, Mntonname [88]int8
pkg syscall (freebsd-386-cgo), func Mknod(string, uint32, int) error
pkg syscall (freebsd-386-cgo), type Dirent struct, Fileno uint32
pkg syscall (freebsd-386-cgo), type Dirent struct, Namlen uint8
pkg syscall (freebsd-386-cgo), type Stat_t struct, Atimespec Timespec
pkg syscall (freebsd-386-cgo), type Stat_t struct, Birthtimespec Timespec
pkg syscall (freebsd-386-cgo), type Stat_t struct, Blksize uint32
pkg syscall (freebsd-386-cgo), type Stat_t struct, Ctimespec Timespec
pkg syscall (freebsd-386-cgo), type Stat_t struct, Dev uint32
pkg syscall (freebsd-386-cgo), type Stat_t struct, Gen uint32
pkg syscall (freebsd-386-cgo), type Stat_t struct, Ino uint32
pkg syscall (freebsd-386-cgo), type Stat_t struct, Lspare int32
pkg syscall (freebsd-386-cgo), type Stat_t struct, Mtimespec Timespec
pkg syscall (freebsd-386-cgo), type Stat_t struct, Nlink uint16
pkg syscall (freebsd-386-cgo), type Stat_t struct, Pad_cgo_0 [8]uint8
pkg syscall (freebsd-386-cgo), type Stat_t struct, Rdev uint32
pkg syscall (freebsd-386-cgo), type Statfs_t struct, Mntfromname [88]int8
pkg syscall (freebsd-386-cgo), type Statfs_t struct, Mntonname [88]int8
pkg syscall (freebsd-amd64), func Mknod(string, uint32, int) error
pkg syscall (freebsd-amd64), type Dirent struct, Fileno uint32
pkg syscall (freebsd-amd64), type Dirent struct, Namlen uint8
pkg syscall (freebsd-amd64), type Stat_t struct, Atimespec Timespec
pkg syscall (freebsd-amd64), type Stat_t struct, Birthtimespec Timespec
pkg syscall (freebsd-amd64), type Stat_t struct, Blksize uint32
pkg syscall (freebsd-amd64), type Stat_t struct, Ctimespec Timespec
pkg syscall (freebsd-amd64), type Stat_t struct, Dev uint32
pkg syscall (freebsd-amd64), type Stat_t struct, Gen uint32
pkg syscall (freebsd-amd64), type Stat_t struct, Ino uint32
pkg syscall (freebsd-amd64), type Stat_t struct, Lspare int32
pkg syscall (freebsd-amd64), type Stat_t struct, Mtimespec Timespec
pkg syscall (freebsd-amd64), type Stat_t struct, Nlink uint16
pkg syscall (freebsd-amd64), type Stat_t struct, Rdev uint32
pkg syscall (freebsd-amd64), type Statfs_t struct, Mntfromname [88]int8
pkg syscall (freebsd-amd64), type Statfs_t struct, Mntonname [88]int8
pkg syscall (freebsd-amd64-cgo), func Mknod(string, uint32, int) error
pkg syscall (freebsd-amd64-cgo), type Dirent struct, Fileno uint32
pkg syscall (freebsd-amd64-cgo), type Dirent struct, Namlen uint8
pkg syscall (freebsd-amd64-cgo), type Stat_t struct, Atimespec Timespec
pkg syscall (freebsd-amd64-cgo), type Stat_t struct, Birthtimespec Timespec
pkg syscall (freebsd-amd64-cgo), type Stat_t struct, Blksize uint32
pkg syscall (freebsd-amd64-cgo), type Stat_t struct, Ctimespec Timespec
pkg syscall (freebsd-amd64-cgo), type Stat_t struct, Dev uint32
pkg syscall (freebsd-amd64-cgo), type Stat_t struct, Gen uint32
pkg syscall (freebsd-amd64-cgo), type Stat_t struct, Ino uint32
pkg syscall (freebsd-amd64-cgo), type Stat_t struct, Lspare int32
pkg syscall (freebsd-amd64-cgo), type Stat_t struct, Mtimespec Timespec
pkg syscall (freebsd-amd64-cgo), type Stat_t struct, Nlink uint16
pkg syscall (freebsd-amd64-cgo), type Stat_t struct, Rdev uint32
pkg syscall (freebsd-amd64-cgo), type Statfs_t struct, Mntfromname [88]int8
pkg syscall (freebsd-amd64-cgo), type Statfs_t struct, Mntonname [88]int8
pkg syscall (freebsd-arm), func Mknod(string, uint32, int) error
pkg syscall (freebsd-arm), type Dirent struct, Fileno uint32
pkg syscall (freebsd-arm), type Dirent struct, Namlen uint8
pkg syscall (freebsd-arm), type Stat_t struct, Atimespec Timespec
pkg syscall (freebsd-arm), type Stat_t struct, Birthtimespec Timespec
pkg syscall (freebsd-arm), type Stat_t struct, Blksize uint32
pkg syscall (freebsd-arm), type Stat_t struct, Ctimespec Timespec
pkg syscall (freebsd-arm), type Stat_t struct, Dev uint32
pkg syscall (freebsd-arm), type Stat_t struct, Gen uint32
pkg syscall (freebsd-arm), type Stat_t struct, Ino uint32
pkg syscall (freebsd-arm), type Stat_t struct, Lspare int32
pkg syscall (freebsd-arm), type Stat_t struct, Mtimespec Timespec
pkg syscall (freebsd-arm), type Stat_t struct, Nlink uint16
pkg syscall (freebsd-arm), type Stat_t struct, Rdev uint32
pkg syscall (freebsd-arm), type Statfs_t struct, Mntfromname [88]int8
pkg syscall (freebsd-arm), type Statfs_t struct, Mntonname [88]int8
pkg syscall (freebsd-arm-cgo), func Mknod(string, uint32, int) error
pkg syscall (freebsd-arm-cgo), type Dirent struct, Fileno uint32
pkg syscall (freebsd-arm-cgo), type Dirent struct, Namlen uint8
pkg syscall (freebsd-arm-cgo), type Stat_t struct, Atimespec Timespec
pkg syscall (freebsd-arm-cgo), type Stat_t struct, Birthtimespec Timespec
pkg syscall (freebsd-arm-cgo), type Stat_t struct, Blksize uint32
pkg syscall (freebsd-arm-cgo), type Stat_t struct, Ctimespec Timespec
pkg syscall (freebsd-arm-cgo), type Stat_t struct, Dev uint32
pkg syscall (freebsd-arm-cgo), type Stat_t struct, Gen uint32
pkg syscall (freebsd-arm-cgo), type Stat_t struct, Ino uint32
pkg syscall (freebsd-arm-cgo), type Stat_t struct, Lspare int32
pkg syscall (freebsd-arm-cgo), type Stat_t struct, Mtimespec Timespec
pkg syscall (freebsd-arm-cgo), type Stat_t struct, Nlink uint16
pkg syscall (freebsd-arm-cgo), type Stat_t struct, Rdev uint32
pkg syscall (freebsd-arm-cgo), type Statfs_t struct, Mntfromname [88]int8
pkg syscall (freebsd-arm-cgo), type Statfs_t struct, Mntonname [88]int8

View file

@ -50,10 +50,10 @@ learned. You can {{if not $.GoogleCN}}<a href="//tour.golang.org/">take the tour
online</a> or{{end}} install it locally with:
</p>
<pre>
$ go get golang.org/x/tour/gotour
$ go get golang.org/x/tour
</pre>
<p>
This will place the <code>gotour</code> binary in your workspace's <code>bin</code> directory.
This will place the <code>tour</code> binary in your workspace's <code>bin</code> directory.
</p>
<h3 id="code"><a href="code.html">How to write Go code</a></h3>

View file

@ -28,7 +28,7 @@ or as a plugin for IntelliJ IDEA Ultimate</li>
</ul>
<p>
Note that these are only a few top solutions; a more comphensive
Note that these are only a few top solutions; a more comprehensive
community-maintained list of
<a href="https://github.com/golang/go/wiki/IDEsAndTextEditorPlugins">IDEs and text editor plugins</a>
is available at the Wiki.

View file

@ -1402,11 +1402,11 @@ the moment, the following snippet would also read the first 32 bytes of the buff
var err error
for i := 0; i &lt; 32; i++ {
nbytes, e := f.Read(buf[i:i+1]) // Read one byte.
n += nbytes
if nbytes == 0 || e != nil {
err = e
break
}
n += nbytes
}
</pre>
<p>
@ -2762,7 +2762,7 @@ type Job struct {
}
</pre>
<p>
The <code>Job</code> type now has the <code>Log</code>, <code>Logf</code>
The <code>Job</code> type now has the <code>Print</code>, <code>Printf</code>, <code>Println</code>
and other
methods of <code>*log.Logger</code>. We could have given the <code>Logger</code>
a field name, of course, but it's not necessary to do so. And now, once
@ -2770,7 +2770,7 @@ initialized, we can
log to the <code>Job</code>:
</p>
<pre>
job.Log("starting now...")
job.Println("starting now...")
</pre>
<p>
The <code>Logger</code> is a regular field of the <code>Job</code> struct,
@ -2797,8 +2797,8 @@ we would write <code>job.Logger</code>,
which would be useful if we wanted to refine the methods of <code>Logger</code>.
</p>
<pre>
func (job *Job) Logf(format string, args ...interface{}) {
job.Logger.Logf("%q: %s", job.Command, fmt.Sprintf(format, args...))
func (job *Job) Printf(format string, args ...interface{}) {
job.Logger.Printf("%q: %s", job.Command, fmt.Sprintf(format, args...))
}
</pre>
<p>

View file

@ -1,6 +1,6 @@
<!--{
"Title": "The Go Programming Language Specification",
"Subtitle": "Version of September 24, 2018",
"Subtitle": "Version of October 23, 2018",
"Path": "/ref/spec"
}-->
@ -811,7 +811,7 @@ To avoid portability issues all numeric types are <a href="#Type_definitions">de
types</a> and thus distinct except
<code>byte</code>, which is an <a href="#Alias_declarations">alias</a> for <code>uint8</code>, and
<code>rune</code>, which is an alias for <code>int32</code>.
Conversions
Explicit conversions
are required when different numeric types are mixed in an expression
or assignment. For instance, <code>int32</code> and <code>int</code>
are not the same type even though they may have the same size on a
@ -1348,7 +1348,7 @@ ChannelType = ( "chan" | "chan" "&lt;-" | "&lt;-" "chan" ) ElementType .
The optional <code>&lt;-</code> operator specifies the channel <i>direction</i>,
<i>send</i> or <i>receive</i>. If no direction is given, the channel is
<i>bidirectional</i>.
A channel may be constrained only to send or only to receive by
A channel may be constrained only to send or only to receive by explicit
<a href="#Conversions">conversion</a> or <a href="#Assignments">assignment</a>.
</p>
@ -2069,9 +2069,9 @@ Otherwise, each variable is initialized to its <a href="#The_zero_value">zero va
If a type is present, each variable is given that type.
Otherwise, each variable is given the type of the corresponding
initialization value in the assignment.
If that value is an untyped constant, it is first
If that value is an untyped constant, it is first implicitly
<a href="#Conversions">converted</a> to its <a href="#Constants">default type</a>;
if it is an untyped boolean value, it is first converted to type <code>bool</code>.
if it is an untyped boolean value, it is first implicitly converted to type <code>bool</code>.
The predeclared value <code>nil</code> cannot be used to initialize a variable
with no explicit type.
</p>
@ -2202,11 +2202,11 @@ Receiver = Parameters .
<p>
The receiver is specified via an extra parameter section preceding the method
name. That parameter section must declare a single non-variadic parameter, the receiver.
Its type must be of the form <code>T</code> or <code>*T</code> (possibly using
parentheses) where <code>T</code> is a type name. The type denoted by <code>T</code> is called
the receiver <i>base type</i>; it must not be a pointer or interface type and
it must be <a href="#Type_definitions">defined</a> in the same package as the method.
The method is said to be <i>bound</i> to the base type and the method name
Its type must be a <a href="#Type_definitions">defined</a> type <code>T</code> or a
pointer to a defined type <code>T</code>. <code>T</code> is called the receiver
<i>base type</i>. A receiver base type cannot be a pointer or interface type and
it must be defined in the same package as the method.
The method is said to be <i>bound</i> to its receiver base type and the method name
is visible only within <a href="#Selectors">selectors</a> for type <code>T</code>
or <code>*T</code>.
</p>
@ -2226,7 +2226,7 @@ the non-blank method and field names must be distinct.
</p>
<p>
Given type <code>Point</code>, the declarations
Given defined type <code>Point</code>, the declarations
</p>
<pre>
@ -3260,7 +3260,7 @@ var v, ok T1 = x.(T)
yields an additional untyped boolean value. The value of <code>ok</code> is <code>true</code>
if the assertion holds. Otherwise it is <code>false</code> and the value of <code>v</code> is
the <a href="#The_zero_value">zero value</a> for type <code>T</code>.
No run-time panic occurs in this case.
No <a href="#Run_time_panics">run-time panic</a> occurs in this case.
</p>
@ -3433,7 +3433,7 @@ For operations involving constants only, see the section on
<p>
Except for shift operations, if one operand is an untyped <a href="#Constants">constant</a>
and the other operand is not, the constant is <a href="#Conversions">converted</a>
and the other operand is not, the constant is implicitly <a href="#Conversions">converted</a>
to the type of the other operand.
</p>
@ -3442,7 +3442,7 @@ The right operand in a shift expression must have unsigned integer type
or be an untyped constant <a href="#Representability">representable</a> by a
value of type <code>uint</code>.
If the left operand of a non-constant shift expression is an untyped constant,
it is first converted to the type it would assume if the shift expression were
it is first implicitly converted to the type it would assume if the shift expression were
replaced by its left operand alone.
</p>
@ -3624,7 +3624,7 @@ For signed integers, the operations <code>+</code>,
<code>-</code>, <code>*</code>, <code>/</code>, and <code>&lt;&lt;</code> may legally
overflow and the resulting value exists and is deterministically defined
by the signed integer representation, the operation, and its operands.
No exception is raised as a result of overflow.
Overflow does not cause a <a href="#Run_time_panics">run-time panic</a>.
A compiler may not optimize code under the assumption that overflow does
not occur. For instance, it may not assume that <code>x &lt; x + 1</code> is always true.
</p>
@ -3645,7 +3645,7 @@ occurs is implementation-specific.
An implementation may combine multiple floating-point operations into a single
fused operation, possibly across statements, and produce a result that differs
from the value obtained by executing and rounding the instructions individually.
A floating-point type <a href="#Conversions">conversion</a> explicitly rounds to
An explicit floating-point type <a href="#Conversions">conversion</a> rounds to
the precision of the target type, preventing fusion that would discard that rounding.
</p>
@ -3907,7 +3907,14 @@ channel is closed and empty.
<h3 id="Conversions">Conversions</h3>
<p>
Conversions are expressions of the form <code>T(x)</code>
A conversion changes the <a href="#Types">type</a> of an expression
to the type specified by the conversion.
A conversion may appear literally in the source, or it may be <i>implied</i>
by the context in which an expression appears.
</p>
<p>
An <i>explicit</i> conversion is an expression of the form <code>T(x)</code>
where <code>T</code> is a type and <code>x</code> is an expression
that can be converted to type <code>T</code>.
</p>
@ -3938,7 +3945,7 @@ func() int(x) // x is converted to func() int (unambiguous)
A <a href="#Constants">constant</a> value <code>x</code> can be converted to
type <code>T</code> if <code>x</code> is <a href="#Representability">representable</a>
by a value of <code>T</code>.
As a special case, an integer constant <code>x</code> can be converted to a
As a special case, an integer constant <code>x</code> can be explicitly converted to a
<a href="#String_types">string type</a> using the
<a href="#Conversions_to_and_from_a_string_type">same rule</a>
as for non-constant <code>x</code>.
@ -4672,13 +4679,13 @@ to the type of the operand to which it is assigned, with the following special c
<li>
If an untyped constant
is assigned to a variable of interface type or the blank identifier,
the constant is first <a href="#Conversions">converted</a> to its
the constant is first implicitly <a href="#Conversions">converted</a> to its
<a href="#Constants">default type</a>.
</li>
<li>
If an untyped boolean value is assigned to a variable of interface type or
the blank identifier, it is first converted to type <code>bool</code>.
the blank identifier, it is first implicitly converted to type <code>bool</code>.
</li>
</ol>
@ -4764,14 +4771,14 @@ ExprSwitchCase = "case" ExpressionList | "default" .
</pre>
<p>
If the switch expression evaluates to an untyped constant, it is first
If the switch expression evaluates to an untyped constant, it is first implicitly
<a href="#Conversions">converted</a> to its <a href="#Constants">default type</a>;
if it is an untyped boolean value, it is first converted to type <code>bool</code>.
if it is an untyped boolean value, it is first implicitly converted to type <code>bool</code>.
The predeclared untyped value <code>nil</code> cannot be used as a switch expression.
</p>
<p>
If a case expression is untyped, it is first <a href="#Conversions">converted</a>
If a case expression is untyped, it is first implicitly <a href="#Conversions">converted</a>
to the type of the switch expression.
For each (possibly converted) case expression <code>x</code> and the value <code>t</code>
of the switch expression, <code>x == t</code> must be a valid <a href="#Comparison_operators">comparison</a>.
@ -5881,7 +5888,7 @@ floating-point type and the return type is the complex type
with the corresponding floating-point constituents:
<code>complex64</code> for <code>float32</code> arguments, and
<code>complex128</code> for <code>float64</code> arguments.
If one of the arguments evaluates to an untyped constant, it is first
If one of the arguments evaluates to an untyped constant, it is first implicitly
<a href="#Conversions">converted</a> to the type of the other argument.
If both arguments evaluate to untyped constants, they must be non-complex
numbers or their imaginary parts must be zero, and the return value of

View file

@ -27,6 +27,11 @@ The <a href="https://forum.golangbridge.org/">Go Forum</a> is a discussion
forum for Go programmers.
</p>
<h3 id="discord"><a href="https://discord.gg/64C346U">Gophers Discord</a></h3>
<p>
Get live support and talk with other gophers on the Go Discord.
</p>
<h3 id="slack"><a href="https://blog.gopheracademy.com/gophers-slack-community/">Gopher Slack</a></h3>
<p>Get live support from other users in the Go slack channel.</p>

View file

@ -295,7 +295,7 @@ func goWithString(s string) {
}
func testCallbackStack(t *testing.T) {
// Make cgo call and callback with different amount of stack stack available.
// Make cgo call and callback with different amount of stack available.
// We do not do any explicit checks, just ensure that it does not crash.
for _, f := range splitTests {
f()

View file

@ -9,7 +9,7 @@ import "C"
func FuncInt() int { return 1 }
// Add a recursive type to to check that type equality across plugins doesn't
// Add a recursive type to check that type equality across plugins doesn't
// crash. See https://golang.org/issues/19258
func FuncRecursive() X { return X{} }

View file

@ -374,7 +374,7 @@ func (c *config) checkRuntime() (skip bool, err error) {
}
// libcgo.h sets CGO_TSAN if it detects TSAN support in the C compiler.
// Dump the preprocessor defines to check that that works.
// Dump the preprocessor defines to check that works.
// (Sometimes it doesn't: see https://golang.org/issue/15983.)
cmd, err := cc(c.cFlags...)
if err != nil {

View file

@ -578,7 +578,7 @@ func TestNotes(t *testing.T) {
}
// Build a GOPATH package (depBase) into a shared library that links against the goroot
// runtime, another package (dep2) that links against the first, and and an
// runtime, another package (dep2) that links against the first, and an
// executable that links against dep2.
func TestTwoGopathShlibs(t *testing.T) {
goCmd(t, "install", "-buildmode=shared", "-linkshared", "depBase")

View file

@ -26,7 +26,7 @@ scheme.
# Download NaCl
Download nacl_sdk.zip file from
https://developers.google.com/native-client/dev/sdk/download
https://developer.chrome.com/native-client/sdk/download
and unpack it. I chose /opt/nacl_sdk.
# Update
@ -37,7 +37,7 @@ sdk. These are released every 6-8 weeks, in line with Chrome releases.
% cd /opt/nacl_sdk
% ./naclsdk update
At this time pepper_40 is the stable version. The NaCl port needs at least pepper_39
At this time pepper_49 is the stable version. The NaCl port needs at least pepper_39
to work. If naclsdk downloads a later version, please adjust accordingly.
The cmd/go helper scripts expect that the loaders sel_ldr_{x86_{32,64},arm} and

View file

@ -37,6 +37,9 @@ go src=..
buildid
testdata
+
xcoff
testdata
+
gofmt
gofmt.go
gofmt_test.go
@ -151,6 +154,9 @@ go src=..
trace
testdata
+
traceparser
testdata
+
io
+
mime

View file

@ -47,10 +47,20 @@
}
return buf.length;
},
openSync(path, flags, mode) {
write(fd, buf, offset, length, position, callback) {
if (offset !== 0 || length !== buf.length || position !== null) {
throw new Error("not implemented");
}
const n = this.writeSync(fd, buf);
callback(null, n);
},
open(path, flags, mode, callback) {
const err = new Error("not implemented");
err.code = "ENOSYS";
throw err;
callback(err);
},
fsync(fd, callback) {
callback(null);
},
};
}
@ -88,6 +98,9 @@
const loadValue = (addr) => {
const f = mem().getFloat64(addr, true);
if (f === 0) {
return undefined;
}
if (!isNaN(f)) {
return f;
}
@ -105,14 +118,18 @@
mem().setUint32(addr, 0, true);
return;
}
if (v === 0) {
mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 1, true);
return;
}
mem().setFloat64(addr, v, true);
return;
}
switch (v) {
case undefined:
mem().setUint32(addr + 4, nanHead, true);
mem().setUint32(addr, 1, true);
mem().setFloat64(addr, 0, true);
return;
case null:
mem().setUint32(addr + 4, nanHead, true);
@ -327,7 +344,7 @@
this._inst = instance;
this._values = [ // TODO: garbage collection
NaN,
undefined,
0,
null,
true,
false,
@ -389,14 +406,14 @@
}
static _makeCallbackHelper(id, pendingCallbacks, go) {
return function() {
return function () {
pendingCallbacks.push({ id: id, args: arguments });
go._resolveCallbackPromise();
};
}
static _makeEventCallbackHelper(preventDefault, stopPropagation, stopImmediatePropagation, fn) {
return function(event) {
return function (event) {
if (preventDefault) {
event.preventDefault();
}

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux dragonfly openbsd solaris
// +build linux dragonfly freebsd openbsd solaris
package tar

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin freebsd netbsd
// +build darwin netbsd
package tar

View file

@ -178,7 +178,7 @@ func (w *Writer) Close() error {
return err
}
// store max values in the regular end record to signal that
// store max values in the regular end record to signal
// that the zip64 values should be used instead
records = uint16max
size = uint32max

View file

@ -41,9 +41,16 @@ var compareTests = []struct {
func TestCompare(t *testing.T) {
for _, tt := range compareTests {
cmp := Compare(tt.a, tt.b)
if cmp != tt.i {
t.Errorf(`Compare(%q, %q) = %v`, tt.a, tt.b, cmp)
numShifts := 16
buffer := make([]byte, len(tt.b)+numShifts)
// vary the input alignment of tt.b
for offset := 0; offset <= numShifts; offset++ {
shiftedB := buffer[offset : len(tt.b)+offset]
copy(shiftedB, tt.b)
cmp := Compare(tt.a, shiftedB)
if cmp != tt.i {
t.Errorf(`Compare(%q, %q), offset %d = %v; want %v`, tt.a, tt.b, offset, cmp, tt.i)
}
}
}
}

View file

@ -385,9 +385,7 @@ func (w *Walker) parseFile(dir, file string) (*ast.File, error) {
return f, nil
}
// The package cache doesn't operate correctly in rare (so far artificial)
// circumstances (issue 8425). Disable before debugging non-obvious errors
// from the type-checker.
// Disable before debugging non-obvious errors from the type-checker.
const usePkgCache = true
var (
@ -398,7 +396,7 @@ var (
// tagKey returns the tag-based key to use in the pkgCache.
// It is a comma-separated string; the first part is dir, the rest tags.
// The satisfied tags are derived from context but only those that
// matter (the ones listed in the tags argument) are used.
// matter (the ones listed in the tags argument plus GOOS and GOARCH) are used.
// The tags list, which came from go/build's Package.AllTags,
// is known to be sorted.
func tagKey(dir string, context *build.Context, tags []string) string {
@ -414,9 +412,17 @@ func tagKey(dir string, context *build.Context, tags []string) string {
}
// TODO: ReleaseTags (need to load default)
key := dir
// explicit on GOOS and GOARCH as global cache will use "all" cached packages for
// an indirect imported package. See https://github.com/golang/go/issues/21181
// for more detail.
tags = append(tags, context.GOOS, context.GOARCH)
sort.Strings(tags)
for _, tag := range tags {
if ctags[tag] {
key += "," + tag
ctags[tag] = false
}
}
return key

View file

@ -188,3 +188,18 @@ func BenchmarkAll(b *testing.B) {
}
}
}
func TestIssue21181(t *testing.T) {
for _, c := range contexts {
c.Compiler = build.Default.Compiler
}
for _, context := range contexts {
w := NewWalker(context, "testdata/src/issue21181")
pkg, err := w.Import("p")
if err != nil {
t.Fatalf("%s: (%s-%s) %s %v", err, context.GOOS, context.GOARCH,
pkg.Name(), w.imported)
}
w.export(pkg)
}
}

View file

@ -0,0 +1,5 @@
package dep
type Interface interface {
N([]byte)
}

View file

@ -0,0 +1 @@
package dep

View file

@ -0,0 +1,5 @@
package indirect
import "dep"
func F(dep.Interface) {}

View file

@ -0,0 +1,9 @@
package p
import (
"dep"
)
type algo struct {
indrt func(dep.Interface)
}

View file

@ -0,0 +1,7 @@
package p
import "indirect"
var in = []algo{
{indirect.F},
}

View file

@ -0,0 +1,11 @@
// +build !amd64
package p
import (
"indirect"
)
var in = []algo{
{indirect.F},
}

View file

@ -308,6 +308,28 @@ func (p *Parser) asmPCData(operands [][]lex.Token) {
p.append(prog, "", true)
}
// asmPCAlign assembles a PCALIGN pseudo-op.
// PCALIGN $16
func (p *Parser) asmPCAlign(operands [][]lex.Token) {
if len(operands) != 1 {
p.errorf("expect one operand for PCALIGN")
return
}
// Operand 0 must be an immediate constant.
key := p.address(operands[0])
if !p.validImmediate("PCALIGN", &key) {
return
}
prog := &obj.Prog{
Ctxt: p.ctxt,
As: obj.APCALIGN,
From: key,
}
p.append(prog, "", true)
}
// asmFuncData assembles a FUNCDATA pseudo-op.
// FUNCDATA $1, funcdata<>+4(SB)
func (p *Parser) asmFuncData(operands [][]lex.Token) {

View file

@ -227,6 +227,8 @@ func (p *Parser) pseudo(word string, operands [][]lex.Token) bool {
p.asmGlobl(operands)
case "PCDATA":
p.asmPCData(operands)
case "PCALIGN":
p.asmPCAlign(operands)
case "TEXT":
p.asmText(operands)
default:

View file

@ -18,7 +18,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
MOVL -2147483648(AX), AX // 8b8000000080
ADDL 2147483648(AX), AX // 038000000080
ADDL -2147483648(AX), AX // 038000000080
// Make sure MOV CR/DR continues to work after changing it's movtabs.
// Make sure MOV CR/DR continues to work after changing its movtabs.
MOVL CR0, AX // 0f20c0
MOVL CR0, DX // 0f20c2
MOVL CR4, DI // 0f20e7

View file

@ -302,7 +302,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
// Check that LEAL is permitted to use overflowing offset.
LEAL 2400959708(BP)(R10*1), BP // 428dac15dcbc1b8f
LEAL 3395469782(AX)(R10*1), AX // 428d8410d6c162ca
// Make sure MOV CR/DR continues to work after changing it's movtabs.
// Make sure MOV CR/DR continues to work after changing its movtabs.
MOVQ CR0, AX // 0f20c0
MOVQ CR0, DX // 0f20c2
MOVQ CR4, DI // 0f20e7

View file

@ -25,6 +25,18 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
ADD R1, R2, R3
ADD R1, ZR, R3
ADD $1, R2, R3
ADD $0x000aaa, R2, R3 // ADD $2730, R2, R3 // 43a82a91
ADD $0x000aaa, R2 // ADD $2730, R2 // 42a82a91
ADD $0xaaa000, R2, R3 // ADD $11182080, R2, R3 // 43a86a91
ADD $0xaaa000, R2 // ADD $11182080, R2 // 42a86a91
ADD $0xaaaaaa, R2, R3 // ADD $11184810, R2, R3 // 43a82a9163a86a91
ADD $0xaaaaaa, R2 // ADD $11184810, R2 // 42a82a9142a86a91
SUB $0x000aaa, R2, R3 // SUB $2730, R2, R3 // 43a82ad1
SUB $0x000aaa, R2 // SUB $2730, R2 // 42a82ad1
SUB $0xaaa000, R2, R3 // SUB $11182080, R2, R3 // 43a86ad1
SUB $0xaaa000, R2 // SUB $11182080, R2 // 42a86ad1
SUB $0xaaaaaa, R2, R3 // SUB $11184810, R2, R3 // 43a82ad163a86ad1
SUB $0xaaaaaa, R2 // SUB $11184810, R2 // 42a82ad142a86ad1
ADD R1>>11, R2, R3
ADD R1<<22, R2, R3
ADD R1->33, R2, R3
@ -179,6 +191,11 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
FMOVD F4, (R2)(R6) // FMOVD F4, (R2)(R6*1) // 446826fc
FMOVD F4, (R2)(R6<<3) // 447826fc
CMPW $40960, R0 // 1f284071
CMPW $27745, R2 // 3b8c8d525f001b6b
CMNW $0x3fffffc0, R2 // CMNW $1073741760, R2 // fb5f1a325f001b2b
CMPW $0xffff0, R1 // CMPW $1048560, R1 // fb3f1c323f001b6b
ADD $0x3fffffffc000, R5 // ADD $70368744161280, R5 // fb7f72b2a5001b8b
// LTYPE1 imsr ',' spreg ','
// {
// outcode($1, &$2, $4, &nullgen);
@ -214,6 +231,16 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
ANDS $0x22220000, R3, R4 // ANDS $572653568, R3, R4 // 5b44a4d264001bea
BICS $0x22220000, R3, R4 // BICS $572653568, R3, R4 // 5b44a4d264003bea
EOR $0xe03fffffffffffff, R20, R22 // EOR $-2287828610704211969, R20, R22 // 96e243d2
TSTW $0x600000006, R1 // TSTW $25769803782, R1 // 3f041f72
ANDS $0xffff, R2 // ANDS $65535, R2 // 423c40f2
AND $0x7fffffff, R3 // AND $2147483647, R3 // 63784092
ANDS $0x0ffffffff80000000, R2 // ANDS $-2147483648, R2 // 428061f2
AND $0xfffff, R2 // AND $1048575, R2 // 424c4092
ANDW $0xf00fffff, R1 // ANDW $4027580415, R1 // 215c0412
ANDSW $0xff00ffff, R1 // ANDSW $4278255615, R1 // 215c0872
TSTW $0xff00ff, R1 // TSTW $16711935, R1 // 3f9c0072
AND $8, R0, RSP // 1f007d92
ORR $8, R0, RSP // 1f007db2
EOR $8, R0, RSP // 1f007dd2
@ -221,6 +248,19 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
ORN $8, R0, RSP // 1ff87cb2
EON $8, R0, RSP // 1ff87cd2
MOVD $0x3fffffffc000, R0 // MOVD $70368744161280, R0 // e07f72b2
MOVW $0xaaaa0000, R1 // MOVW $2863267840, R1 // 4155b552
MOVW $0xaaaaffff, R1 // MOVW $2863333375, R1 // a1aaaa12
MOVW $0xaaaa, R1 // MOVW $43690, R1 // 41559552
MOVW $0xffffaaaa, R1 // MOVW $4294945450, R1 // a1aa8a12
MOVW $0xffff0000, R1 // MOVW $4294901760, R1 // e1ffbf52
MOVD $0xffff00000000000, R1 // MOVD $1152903912420802560, R1 // e13f54b2
MOVD $0x11110000, R1 // MOVD $286326784, R1 // 2122a2d2
MOVD $0, R1 // 010080d2
MOVD $-1, R1 // 01008092
MOVD $0x210000, R0 // MOVD $2162688, R0 // 2004a0d2
MOVD $0xffffffffffffaaaa, R1 // MOVD $-21846, R1 // a1aa8a92
//
// CLS
//
@ -416,7 +456,7 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
CMP R22.SXTX, RSP // ffe336eb
CMP $0x22220000, RSP // CMP $572653568, RSP // 5b44a4d2ff633beb
CMPW $0x22220000, RSP // CMPW $572653568, RSP // 5b44a4d2ff633b6b
CMPW $0x22220000, RSP // CMPW $572653568, RSP // 5b44a452ff633b6b
// TST
TST $15, R2 // 5f0c40f2

View file

@ -98,4 +98,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
LDAR (R4),$0,R5 // 7ca020a8
LDAR (R3),R5 // 7ca018a8
// float constants
FMOVD $(0.0), F1 // f0210cd0
FMOVD $(-0.0), F1 // f0210cd0fc200850
RET

View file

@ -9,6 +9,7 @@ package main
import (
"bytes"
"cmd/internal/xcoff"
"debug/dwarf"
"debug/elf"
"debug/macho"
@ -188,6 +189,7 @@ func (p *Package) Translate(f *File) {
break
}
}
p.prepareNames(f)
if p.rewriteCalls(f) {
// Add `import _cgo_unsafe "unsafe"` after the package statement.
f.Edit.Insert(f.offset(f.AST.Name.End()), "; import _cgo_unsafe \"unsafe\"")
@ -679,6 +681,27 @@ func (p *Package) recordTypedefs1(dtype dwarf.Type, visited map[dwarf.Type]bool)
}
}
// prepareNames finalizes the Kind field of not-type names and sets
// the mangled name of all names.
func (p *Package) prepareNames(f *File) {
for _, n := range f.Name {
if n.Kind == "not-type" {
if n.Define == "" {
n.Kind = "var"
} else {
n.Kind = "macro"
n.FuncType = &FuncType{
Result: n.Type,
Go: &ast.FuncType{
Results: &ast.FieldList{List: []*ast.Field{{Type: n.Type.Go}}},
},
}
}
}
p.mangleName(n)
}
}
// mangleName does name mangling to translate names
// from the original Go source files to the names
// used in the final Go files generated by cgo.
@ -722,16 +745,19 @@ func (p *Package) rewriteCalls(f *File) bool {
// argument and then calls the original function.
// This returns whether the package needs to import unsafe as _cgo_unsafe.
func (p *Package) rewriteCall(f *File, call *Call, name *Name) bool {
params := name.FuncType.Params
args := call.Call.Args
// Avoid a crash if the number of arguments is
// less than the number of parameters.
// This will be caught when the generated file is compiled.
if len(call.Call.Args) < len(name.FuncType.Params) {
if len(args) < len(params) {
return false
}
any := false
for i, param := range name.FuncType.Params {
if p.needsPointerCheck(f, param.Go, call.Call.Args[i]) {
for i, param := range params {
if p.needsPointerCheck(f, param.Go, args[i]) {
any = true
break
}
@ -750,127 +776,108 @@ func (p *Package) rewriteCall(f *File, call *Call, name *Name) bool {
// Using a function literal like this lets us do correct
// argument type checking, and works correctly if the call is
// deferred.
var sb bytes.Buffer
sb.WriteString("func(")
needsUnsafe := false
params := make([]*ast.Field, len(name.FuncType.Params))
nargs := make([]ast.Expr, len(name.FuncType.Params))
var stmts []ast.Stmt
for i, param := range name.FuncType.Params {
// params is going to become the parameters of the
// function literal.
// nargs is going to become the list of arguments made
// by the call within the function literal.
// nparam is the parameter of the function literal that
// corresponds to param.
origArg := call.Call.Args[i]
nparam := ast.NewIdent(fmt.Sprintf("_cgo%d", i))
nargs[i] = nparam
for i, param := range params {
if i > 0 {
sb.WriteString(", ")
}
fmt.Fprintf(&sb, "_cgo%d ", i)
// The Go version of the C type might use unsafe.Pointer,
// but the file might not import unsafe.
// Rewrite the Go type if necessary to use _cgo_unsafe.
ptype := p.rewriteUnsafe(param.Go)
if ptype != param.Go {
needsUnsafe = true
}
sb.WriteString(gofmtLine(ptype))
}
params[i] = &ast.Field{
Names: []*ast.Ident{nparam},
Type: ptype,
}
sb.WriteString(")")
if !p.needsPointerCheck(f, param.Go, origArg) {
result := false
twoResults := false
// Check whether this call expects two results.
for _, ref := range f.Ref {
if ref.Expr != &call.Call.Fun {
continue
}
// Run the cgo pointer checks on nparam.
// Change the function literal to call the real function
// with the parameter passed through _cgoCheckPointer.
c := &ast.CallExpr{
Fun: ast.NewIdent("_cgoCheckPointer"),
Args: []ast.Expr{
nparam,
},
if ref.Context == ctxCall2 {
sb.WriteString(" (")
result = true
twoResults = true
}
// Add optional additional arguments for an address
// expression.
c.Args = p.checkAddrArgs(f, c.Args, origArg)
stmt := &ast.ExprStmt{
X: c,
}
stmts = append(stmts, stmt)
break
}
const cgoMarker = "__cgo__###__marker__"
fcall := &ast.CallExpr{
Fun: ast.NewIdent(cgoMarker),
Args: nargs,
}
ftype := &ast.FuncType{
Params: &ast.FieldList{
List: params,
},
}
// Add the result type, if any.
if name.FuncType.Result != nil {
rtype := p.rewriteUnsafe(name.FuncType.Result.Go)
if rtype != name.FuncType.Result.Go {
needsUnsafe = true
}
ftype.Results = &ast.FieldList{
List: []*ast.Field{
&ast.Field{
Type: rtype,
},
},
if !twoResults {
sb.WriteString(" ")
}
sb.WriteString(gofmtLine(rtype))
result = true
}
// If this call expects two results, we have to
// adjust the results of the function we generated.
for _, ref := range f.Ref {
if ref.Expr == &call.Call.Fun && ref.Context == ctxCall2 {
if ftype.Results == nil {
// An explicit void argument
// looks odd but it seems to
// be how cgo has worked historically.
ftype.Results = &ast.FieldList{
List: []*ast.Field{
&ast.Field{
Type: ast.NewIdent("_Ctype_void"),
},
},
}
}
ftype.Results.List = append(ftype.Results.List,
&ast.Field{
Type: ast.NewIdent("error"),
})
// Add the second result type, if any.
if twoResults {
if name.FuncType.Result == nil {
// An explicit void result looks odd but it
// seems to be how cgo has worked historically.
sb.WriteString("_Ctype_void")
}
sb.WriteString(", error)")
}
var fbody ast.Stmt
if ftype.Results == nil {
fbody = &ast.ExprStmt{
X: fcall,
sb.WriteString(" { ")
for i, param := range params {
arg := args[i]
if !p.needsPointerCheck(f, param.Go, arg) {
continue
}
} else {
fbody = &ast.ReturnStmt{
Results: []ast.Expr{fcall},
// Check for &a[i].
if p.checkIndex(&sb, f, arg, i) {
continue
}
// Check for &x.
if p.checkAddr(&sb, arg, i) {
continue
}
fmt.Fprintf(&sb, "_cgoCheckPointer(_cgo%d); ", i)
}
lit := &ast.FuncLit{
Type: ftype,
Body: &ast.BlockStmt{
List: append(stmts, fbody),
},
if result {
sb.WriteString("return ")
}
text := strings.Replace(gofmt(lit), "\n", ";", -1)
repl := strings.Split(text, cgoMarker)
f.Edit.Insert(f.offset(call.Call.Fun.Pos()), repl[0])
f.Edit.Insert(f.offset(call.Call.Fun.End()), repl[1])
// Now we are ready to call the C function.
// To work smoothly with rewriteRef we leave the call in place
// and just insert our new arguments between the function
// and the old arguments.
f.Edit.Insert(f.offset(call.Call.Fun.Pos()), sb.String())
sb.Reset()
sb.WriteString("(")
for i := range params {
if i > 0 {
sb.WriteString(", ")
}
fmt.Fprintf(&sb, "_cgo%d", i)
}
sb.WriteString("); }")
f.Edit.Insert(f.offset(call.Call.Lparen), sb.String())
return needsUnsafe
}
@ -979,19 +986,13 @@ func (p *Package) hasPointer(f *File, t ast.Expr, top bool) bool {
}
}
// checkAddrArgs tries to add arguments to the call of
// _cgoCheckPointer when the argument is an address expression. We
// pass true to mean that the argument is an address operation of
// something other than a slice index, which means that it's only
// necessary to check the specific element pointed to, not the entire
// object. This is for &s.f, where f is a field in a struct. We can
// pass a slice or array, meaning that we should check the entire
// slice or array but need not check any other part of the object.
// This is for &s.a[i], where we need to check all of a. However, we
// only pass the slice or array if we can refer to it without side
// effects.
func (p *Package) checkAddrArgs(f *File, args []ast.Expr, x ast.Expr) []ast.Expr {
// checkIndex checks whether arg the form &a[i], possibly inside type
// conversions. If so, and if a has no side effects, it writes
// _cgoCheckPointer(_cgoNN, a) to sb and returns true. This tells
// _cgoCheckPointer to check the complete contents of the slice.
func (p *Package) checkIndex(sb *bytes.Buffer, f *File, arg ast.Expr, i int) bool {
// Strip type conversions.
x := arg
for {
c, ok := x.(*ast.CallExpr)
if !ok || len(c.Args) != 1 || !p.isType(c.Fun) {
@ -1001,22 +1002,46 @@ func (p *Package) checkAddrArgs(f *File, args []ast.Expr, x ast.Expr) []ast.Expr
}
u, ok := x.(*ast.UnaryExpr)
if !ok || u.Op != token.AND {
return args
return false
}
index, ok := u.X.(*ast.IndexExpr)
if !ok {
// This is the address of something that is not an
// index expression. We only need to examine the
// single value to which it points.
// TODO: what if true is shadowed?
return append(args, ast.NewIdent("true"))
return false
}
if !p.hasSideEffects(f, index.X) {
// Examine the entire slice.
return append(args, index.X)
if p.hasSideEffects(f, index.X) {
return false
}
// Treat the pointer as unknown.
return args
fmt.Fprintf(sb, "_cgoCheckPointer(_cgo%d, %s); ", i, gofmtLine(index.X))
return true
}
// checkAddr checks whether arg has the form &x, possibly inside type
// conversions. If so it writes _cgoCheckPointer(_cgoNN, true) to sb
// and returns true. This tells _cgoCheckPointer to check just the
// contents of the pointer being passed, not any other part of the
// memory allocation. This is run after checkIndex, which looks for
// the special case of &a[i], which requires different checks.
func (p *Package) checkAddr(sb *bytes.Buffer, arg ast.Expr, i int) bool {
// Strip type conversions.
px := &arg
for {
c, ok := (*px).(*ast.CallExpr)
if !ok || len(c.Args) != 1 || !p.isType(c.Fun) {
break
}
px = &c.Args[0]
}
if u, ok := (*px).(*ast.UnaryExpr); !ok || u.Op != token.AND {
return false
}
// Use "0 == 0" to do the right thing in the unlikely event
// that "true" is shadowed.
fmt.Fprintf(sb, "_cgoCheckPointer(_cgo%d, 0 == 0); ", i)
return true
}
// hasSideEffects returns whether the expression x has any side
@ -1026,8 +1051,7 @@ func (p *Package) hasSideEffects(f *File, x ast.Expr) bool {
found := false
f.walk(x, ctxExpr,
func(f *File, x interface{}, context astContext) {
switch x.(type) {
case *ast.CallExpr:
if _, ok := x.(*ast.CallExpr); ok {
found = true
}
})
@ -1131,24 +1155,7 @@ func (p *Package) rewriteRef(f *File) {
// code for them.
functions := make(map[string]bool)
// Assign mangled names.
for _, n := range f.Name {
if n.Kind == "not-type" {
if n.Define == "" {
n.Kind = "var"
} else {
n.Kind = "macro"
n.FuncType = &FuncType{
Result: n.Type,
Go: &ast.FuncType{
Results: &ast.FieldList{List: []*ast.Field{{Type: n.Type.Go}}},
},
}
}
}
if n.Mangle == "" {
p.mangleName(n)
}
if n.Kind == "func" {
functions[n.Go] = false
}
@ -1162,104 +1169,16 @@ func (p *Package) rewriteRef(f *File) {
if r.Name.IsConst() && r.Name.Const == "" {
error_(r.Pos(), "unable to find value of constant C.%s", fixGo(r.Name.Go))
}
var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default
switch r.Context {
case ctxCall, ctxCall2:
if r.Name.Kind != "func" {
if r.Name.Kind == "type" {
r.Context = ctxType
if r.Name.Type == nil {
error_(r.Pos(), "invalid conversion to C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
break
}
expr = r.Name.Type.Go
break
}
error_(r.Pos(), "call of non-function C.%s", fixGo(r.Name.Go))
break
}
functions[r.Name.Go] = true
if r.Context == ctxCall2 {
if r.Name.Go == "_CMalloc" {
error_(r.Pos(), "no two-result form for C.malloc")
break
}
// Invent new Name for the two-result function.
n := f.Name["2"+r.Name.Go]
if n == nil {
n = new(Name)
*n = *r.Name
n.AddError = true
n.Mangle = "_C2func_" + n.Go
f.Name["2"+r.Name.Go] = n
}
expr = ast.NewIdent(n.Mangle)
r.Name = n
break
}
case ctxExpr:
switch r.Name.Kind {
case "func":
if builtinDefs[r.Name.C] != "" {
error_(r.Pos(), "use of builtin '%s' not in function call", fixGo(r.Name.C))
}
// Function is being used in an expression, to e.g. pass around a C function pointer.
// Create a new Name for this Ref which causes the variable to be declared in Go land.
fpName := "fp_" + r.Name.Go
name := f.Name[fpName]
if name == nil {
name = &Name{
Go: fpName,
C: r.Name.C,
Kind: "fpvar",
Type: &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*"), Go: ast.NewIdent("unsafe.Pointer")},
}
p.mangleName(name)
f.Name[fpName] = name
}
r.Name = name
// Rewrite into call to _Cgo_ptr to prevent assignments. The _Cgo_ptr
// function is defined in out.go and simply returns its argument. See
// issue 7757.
expr = &ast.CallExpr{
Fun: &ast.Ident{NamePos: (*r.Expr).Pos(), Name: "_Cgo_ptr"},
Args: []ast.Expr{ast.NewIdent(name.Mangle)},
}
case "type":
// Okay - might be new(T)
if r.Name.Type == nil {
error_(r.Pos(), "expression C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
break
}
expr = r.Name.Type.Go
case "var":
expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr}
case "macro":
expr = &ast.CallExpr{Fun: expr}
}
case ctxSelector:
if r.Name.Kind == "var" {
expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr}
} else {
error_(r.Pos(), "only C variables allowed in selector expression %s", fixGo(r.Name.Go))
}
case ctxType:
if r.Name.Kind != "type" {
error_(r.Pos(), "expression C.%s used as type", fixGo(r.Name.Go))
} else if r.Name.Type == nil {
// Use of C.enum_x, C.struct_x or C.union_x without C definition.
// GCC won't raise an error when using pointers to such unknown types.
error_(r.Pos(), "type C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
} else {
expr = r.Name.Type.Go
}
default:
if r.Name.Kind == "func" {
error_(r.Pos(), "must call C.%s", fixGo(r.Name.Go))
if r.Name.Kind == "func" {
switch r.Context {
case ctxCall, ctxCall2:
functions[r.Name.Go] = true
}
}
expr := p.rewriteName(f, r)
if *godefs {
// Substitute definition for mangled type name.
if id, ok := expr.(*ast.Ident); ok {
@ -1276,8 +1195,7 @@ func (p *Package) rewriteRef(f *File) {
// in case expression being replaced is first on line.
// See golang.org/issue/6563.
pos := (*r.Expr).Pos()
switch x := expr.(type) {
case *ast.Ident:
if x, ok := expr.(*ast.Ident); ok {
expr = &ast.Ident{NamePos: pos, Name: x.Name}
}
@ -1303,6 +1221,107 @@ func (p *Package) rewriteRef(f *File) {
}
}
// rewriteName returns the expression used to rewrite a reference.
func (p *Package) rewriteName(f *File, r *Ref) ast.Expr {
var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default
switch r.Context {
case ctxCall, ctxCall2:
if r.Name.Kind != "func" {
if r.Name.Kind == "type" {
r.Context = ctxType
if r.Name.Type == nil {
error_(r.Pos(), "invalid conversion to C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
break
}
expr = r.Name.Type.Go
break
}
error_(r.Pos(), "call of non-function C.%s", fixGo(r.Name.Go))
break
}
if r.Context == ctxCall2 {
if r.Name.Go == "_CMalloc" {
error_(r.Pos(), "no two-result form for C.malloc")
break
}
// Invent new Name for the two-result function.
n := f.Name["2"+r.Name.Go]
if n == nil {
n = new(Name)
*n = *r.Name
n.AddError = true
n.Mangle = "_C2func_" + n.Go
f.Name["2"+r.Name.Go] = n
}
expr = ast.NewIdent(n.Mangle)
r.Name = n
break
}
case ctxExpr:
switch r.Name.Kind {
case "func":
if builtinDefs[r.Name.C] != "" {
error_(r.Pos(), "use of builtin '%s' not in function call", fixGo(r.Name.C))
}
// Function is being used in an expression, to e.g. pass around a C function pointer.
// Create a new Name for this Ref which causes the variable to be declared in Go land.
fpName := "fp_" + r.Name.Go
name := f.Name[fpName]
if name == nil {
name = &Name{
Go: fpName,
C: r.Name.C,
Kind: "fpvar",
Type: &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*"), Go: ast.NewIdent("unsafe.Pointer")},
}
p.mangleName(name)
f.Name[fpName] = name
}
r.Name = name
// Rewrite into call to _Cgo_ptr to prevent assignments. The _Cgo_ptr
// function is defined in out.go and simply returns its argument. See
// issue 7757.
expr = &ast.CallExpr{
Fun: &ast.Ident{NamePos: (*r.Expr).Pos(), Name: "_Cgo_ptr"},
Args: []ast.Expr{ast.NewIdent(name.Mangle)},
}
case "type":
// Okay - might be new(T)
if r.Name.Type == nil {
error_(r.Pos(), "expression C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
break
}
expr = r.Name.Type.Go
case "var":
expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr}
case "macro":
expr = &ast.CallExpr{Fun: expr}
}
case ctxSelector:
if r.Name.Kind == "var" {
expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr}
} else {
error_(r.Pos(), "only C variables allowed in selector expression %s", fixGo(r.Name.Go))
}
case ctxType:
if r.Name.Kind != "type" {
error_(r.Pos(), "expression C.%s used as type", fixGo(r.Name.Go))
} else if r.Name.Type == nil {
// Use of C.enum_x, C.struct_x or C.union_x without C definition.
// GCC won't raise an error when using pointers to such unknown types.
error_(r.Pos(), "type C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
} else {
expr = r.Name.Type.Go
}
default:
if r.Name.Kind == "func" {
error_(r.Pos(), "must call C.%s", fixGo(r.Name.Go))
}
}
return expr
}
// gccBaseCmd returns the start of the compiler command line.
// It uses $CC if set, or else $GCC, or else the compiler recorded
// during the initial build as defaultCC.
@ -1377,6 +1396,9 @@ func (p *Package) gccCmd() []string {
c = append(c, p.GccOptions...)
c = append(c, p.gccMachine()...)
if goos == "aix" {
c = append(c, "-maix64")
}
c = append(c, "-") //read input from standard input
return c
}
@ -1663,7 +1685,77 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6
return d, ints, floats, strs
}
fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp())
if f, err := xcoff.Open(gccTmp()); err == nil {
defer f.Close()
d, err := f.DWARF()
if err != nil {
fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
}
bo := binary.BigEndian
for _, s := range f.Symbols {
switch {
case isDebugInts(s.Name):
if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
if s.Value < sect.Size {
if sdat, err := sect.Data(); err == nil {
data := sdat[s.Value:]
ints = make([]int64, len(data)/8)
for i := range ints {
ints[i] = int64(bo.Uint64(data[i*8:]))
}
}
}
}
case isDebugFloats(s.Name):
if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
if s.Value < sect.Size {
if sdat, err := sect.Data(); err == nil {
data := sdat[s.Value:]
floats = make([]float64, len(data)/8)
for i := range floats {
floats[i] = math.Float64frombits(bo.Uint64(data[i*8:]))
}
}
}
}
default:
if n := indexOfDebugStr(s.Name); n != -1 {
if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
if s.Value < sect.Size {
if sdat, err := sect.Data(); err == nil {
data := sdat[s.Value:]
strdata[n] = string(data)
}
}
}
break
}
if n := indexOfDebugStrlen(s.Name); n != -1 {
if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
if s.Value < sect.Size {
if sdat, err := sect.Data(); err == nil {
data := sdat[s.Value:]
strlen := bo.Uint64(data[:8])
if strlen > (1<<(uint(p.IntSize*8)-1) - 1) { // greater than MaxInt?
fatalf("string literal too big")
}
strlens[n] = int(strlen)
}
}
}
break
}
}
}
buildStrings()
return d, ints, floats, strs
}
fatalf("cannot parse gcc output %s as ELF, Mach-O, PE, XCOFF object", gccTmp())
panic("not reached")
}

View file

@ -126,3 +126,9 @@ func gofmt(n interface{}) string {
}
return gofmtBuf.String()
}
// gofmtLine returns the gofmt-formatted string for an AST node,
// ensuring that it is on a single line.
func gofmtLine(n interface{}) string {
return strings.Replace(gofmt(n), "\n", ";", -1)
}

View file

@ -6,6 +6,7 @@ package main
import (
"bytes"
"cmd/internal/xcoff"
"debug/elf"
"debug/macho"
"debug/pe"
@ -312,7 +313,25 @@ func dynimport(obj string) {
return
}
fatalf("cannot parse %s as ELF, Mach-O or PE", obj)
if f, err := xcoff.Open(obj); err == nil {
sym, err := f.ImportedSymbols()
if err != nil {
fatalf("cannot load imported symbols from XCOFF file %s: %v", obj, err)
}
for _, s := range sym {
fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, s.Name, s.Library)
}
lib, err := f.ImportedLibraries()
if err != nil {
fatalf("cannot load imported libraries from XCOFF file %s: %v", obj, err)
}
for _, l := range lib {
fmt.Fprintf(stdout, "//go:cgo_import_dynamic _ _ %q\n", l)
}
return
}
fatalf("cannot parse %s as ELF, Mach-O, PE or XCOFF", obj)
}
// Construct a gcc struct matching the gc argument frame.

View file

@ -92,8 +92,6 @@ Flags:
Compile with race detector enabled.
-trimpath prefix
Remove prefix from recorded source file paths.
-u
Disallow importing packages not marked as safe; implies -nolocalimports.
There are also a number of debugging flags; run the command with no arguments
for a usage message.
@ -125,7 +123,7 @@ directive can skip over a directive like any other comment.
// For a //line comment, this is the first character of the next line, and
// for a /*line comment this is the character position immediately following the closing */.
// If no filename is given, the recorded filename is empty if there is also no column number;
// otherwise is is the most recently recorded filename (actual filename or filename specified
// otherwise it is the most recently recorded filename (actual filename or filename specified
// by previous line directive).
// If a line directive doesn't specify a column number, the column is "unknown" until
// the next directive and the compiler does not report column numbers for that range.
@ -146,7 +144,7 @@ directive can skip over a directive like any other comment.
// will report positions in the original input to the generator.
/*
The line directive is an historical special case; all other directives are of the form
//go:name and must start at the begnning of a line, indicating that the directive is defined
//go:name and must start at the beginning of a line, indicating that the directive is defined
by the Go toolchain.
//go:noescape

View file

@ -583,7 +583,6 @@ var knownFormats = map[string]string{
"*cmd/compile/internal/ssa.sparseTreeMapEntry %v": "",
"*cmd/compile/internal/types.Field %p": "",
"*cmd/compile/internal/types.Field %v": "",
"*cmd/compile/internal/types.Sym %+v": "",
"*cmd/compile/internal/types.Sym %0S": "",
"*cmd/compile/internal/types.Sym %S": "",
"*cmd/compile/internal/types.Sym %p": "",

View file

@ -229,24 +229,27 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// Result[0] (the quotient) is in AX.
// Result[1] (the remainder) is in DX.
r := v.Args[1].Reg()
var j1 *obj.Prog
// CPU faults upon signed overflow, which occurs when the most
// negative int is divided by -1. Handle divide by -1 as a special case.
var c *obj.Prog
switch v.Op {
case ssa.OpAMD64DIVQ:
c = s.Prog(x86.ACMPQ)
case ssa.OpAMD64DIVL:
c = s.Prog(x86.ACMPL)
case ssa.OpAMD64DIVW:
c = s.Prog(x86.ACMPW)
if ssa.NeedsFixUp(v) {
var c *obj.Prog
switch v.Op {
case ssa.OpAMD64DIVQ:
c = s.Prog(x86.ACMPQ)
case ssa.OpAMD64DIVL:
c = s.Prog(x86.ACMPL)
case ssa.OpAMD64DIVW:
c = s.Prog(x86.ACMPW)
}
c.From.Type = obj.TYPE_REG
c.From.Reg = r
c.To.Type = obj.TYPE_CONST
c.To.Offset = -1
j1 = s.Prog(x86.AJEQ)
j1.To.Type = obj.TYPE_BRANCH
}
c.From.Type = obj.TYPE_REG
c.From.Reg = r
c.To.Type = obj.TYPE_CONST
c.To.Offset = -1
j1 := s.Prog(x86.AJEQ)
j1.To.Type = obj.TYPE_BRANCH
// Sign extend dividend.
switch v.Op {
@ -263,36 +266,38 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Type = obj.TYPE_REG
p.From.Reg = r
// Skip over -1 fixup code.
j2 := s.Prog(obj.AJMP)
j2.To.Type = obj.TYPE_BRANCH
if j1 != nil {
// Skip over -1 fixup code.
j2 := s.Prog(obj.AJMP)
j2.To.Type = obj.TYPE_BRANCH
// Issue -1 fixup code.
// n / -1 = -n
var n1 *obj.Prog
switch v.Op {
case ssa.OpAMD64DIVQ:
n1 = s.Prog(x86.ANEGQ)
case ssa.OpAMD64DIVL:
n1 = s.Prog(x86.ANEGL)
case ssa.OpAMD64DIVW:
n1 = s.Prog(x86.ANEGW)
// Issue -1 fixup code.
// n / -1 = -n
var n1 *obj.Prog
switch v.Op {
case ssa.OpAMD64DIVQ:
n1 = s.Prog(x86.ANEGQ)
case ssa.OpAMD64DIVL:
n1 = s.Prog(x86.ANEGL)
case ssa.OpAMD64DIVW:
n1 = s.Prog(x86.ANEGW)
}
n1.To.Type = obj.TYPE_REG
n1.To.Reg = x86.REG_AX
// n % -1 == 0
n2 := s.Prog(x86.AXORL)
n2.From.Type = obj.TYPE_REG
n2.From.Reg = x86.REG_DX
n2.To.Type = obj.TYPE_REG
n2.To.Reg = x86.REG_DX
// TODO(khr): issue only the -1 fixup code we need.
// For instance, if only the quotient is used, no point in zeroing the remainder.
j1.To.Val = n1
j2.To.Val = s.Pc()
}
n1.To.Type = obj.TYPE_REG
n1.To.Reg = x86.REG_AX
// n % -1 == 0
n2 := s.Prog(x86.AXORL)
n2.From.Type = obj.TYPE_REG
n2.From.Reg = x86.REG_DX
n2.To.Type = obj.TYPE_REG
n2.To.Reg = x86.REG_DX
// TODO(khr): issue only the -1 fixup code we need.
// For instance, if only the quotient is used, no point in zeroing the remainder.
j1.To.Val = n1
j2.To.Val = s.Pc()
case ssa.OpAMD64HMULQ, ssa.OpAMD64HMULL, ssa.OpAMD64HMULQU, ssa.OpAMD64HMULLU:
// the frontend rewrites constant division by 8/16/32 bit integers into
@ -315,6 +320,13 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
m.To.Reg = x86.REG_DX
}
case ssa.OpAMD64MULQU, ssa.OpAMD64MULLU:
// Arg[0] is already in AX as it's the only register we allow
// results lo in AX
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[1].Reg()
case ssa.OpAMD64MULQU2:
// Arg[0] is already in AX as it's the only register we allow
// results hi in DX, lo in AX
@ -653,43 +665,26 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
gc.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64MOVQloadidx8, ssa.OpAMD64MOVSDloadidx8, ssa.OpAMD64MOVLloadidx8:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
gc.AddAux(&p.From, v)
p.From.Scale = 8
p.From.Index = v.Args[1].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64MOVLloadidx4, ssa.OpAMD64MOVSSloadidx4:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
gc.AddAux(&p.From, v)
p.From.Scale = 4
p.From.Index = v.Args[1].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64MOVWloadidx2:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
gc.AddAux(&p.From, v)
p.From.Scale = 2
p.From.Index = v.Args[1].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64MOVBloadidx1, ssa.OpAMD64MOVWloadidx1, ssa.OpAMD64MOVLloadidx1, ssa.OpAMD64MOVQloadidx1, ssa.OpAMD64MOVSSloadidx1, ssa.OpAMD64MOVSDloadidx1:
case ssa.OpAMD64MOVBloadidx1, ssa.OpAMD64MOVWloadidx1, ssa.OpAMD64MOVLloadidx1, ssa.OpAMD64MOVQloadidx1, ssa.OpAMD64MOVSSloadidx1, ssa.OpAMD64MOVSDloadidx1,
ssa.OpAMD64MOVQloadidx8, ssa.OpAMD64MOVSDloadidx8, ssa.OpAMD64MOVLloadidx8, ssa.OpAMD64MOVLloadidx4, ssa.OpAMD64MOVSSloadidx4, ssa.OpAMD64MOVWloadidx2:
r := v.Args[0].Reg()
i := v.Args[1].Reg()
if i == x86.REG_SP {
r, i = i, r
}
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
switch v.Op {
case ssa.OpAMD64MOVBloadidx1, ssa.OpAMD64MOVWloadidx1, ssa.OpAMD64MOVLloadidx1, ssa.OpAMD64MOVQloadidx1, ssa.OpAMD64MOVSSloadidx1, ssa.OpAMD64MOVSDloadidx1:
if i == x86.REG_SP {
r, i = i, r
}
p.From.Scale = 1
case ssa.OpAMD64MOVQloadidx8, ssa.OpAMD64MOVSDloadidx8, ssa.OpAMD64MOVLloadidx8:
p.From.Scale = 8
case ssa.OpAMD64MOVLloadidx4, ssa.OpAMD64MOVSSloadidx4:
p.From.Scale = 4
case ssa.OpAMD64MOVWloadidx2:
p.From.Scale = 2
}
p.From.Reg = r
p.From.Scale = 1
p.From.Index = i
gc.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
@ -704,45 +699,28 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
gc.AddAux(&p.To, v)
case ssa.OpAMD64MOVQstoreidx8, ssa.OpAMD64MOVSDstoreidx8, ssa.OpAMD64MOVLstoreidx8:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[2].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
p.To.Scale = 8
p.To.Index = v.Args[1].Reg()
gc.AddAux(&p.To, v)
case ssa.OpAMD64MOVSSstoreidx4, ssa.OpAMD64MOVLstoreidx4:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[2].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
p.To.Scale = 4
p.To.Index = v.Args[1].Reg()
gc.AddAux(&p.To, v)
case ssa.OpAMD64MOVWstoreidx2:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[2].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
p.To.Scale = 2
p.To.Index = v.Args[1].Reg()
gc.AddAux(&p.To, v)
case ssa.OpAMD64MOVBstoreidx1, ssa.OpAMD64MOVWstoreidx1, ssa.OpAMD64MOVLstoreidx1, ssa.OpAMD64MOVQstoreidx1, ssa.OpAMD64MOVSSstoreidx1, ssa.OpAMD64MOVSDstoreidx1:
case ssa.OpAMD64MOVBstoreidx1, ssa.OpAMD64MOVWstoreidx1, ssa.OpAMD64MOVLstoreidx1, ssa.OpAMD64MOVQstoreidx1, ssa.OpAMD64MOVSSstoreidx1, ssa.OpAMD64MOVSDstoreidx1,
ssa.OpAMD64MOVQstoreidx8, ssa.OpAMD64MOVSDstoreidx8, ssa.OpAMD64MOVLstoreidx8, ssa.OpAMD64MOVSSstoreidx4, ssa.OpAMD64MOVLstoreidx4, ssa.OpAMD64MOVWstoreidx2:
r := v.Args[0].Reg()
i := v.Args[1].Reg()
if i == x86.REG_SP {
r, i = i, r
}
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[2].Reg()
p.To.Type = obj.TYPE_MEM
switch v.Op {
case ssa.OpAMD64MOVBstoreidx1, ssa.OpAMD64MOVWstoreidx1, ssa.OpAMD64MOVLstoreidx1, ssa.OpAMD64MOVQstoreidx1, ssa.OpAMD64MOVSSstoreidx1, ssa.OpAMD64MOVSDstoreidx1:
if i == x86.REG_SP {
r, i = i, r
}
p.To.Scale = 1
case ssa.OpAMD64MOVQstoreidx8, ssa.OpAMD64MOVSDstoreidx8, ssa.OpAMD64MOVLstoreidx8:
p.To.Scale = 8
case ssa.OpAMD64MOVSSstoreidx4, ssa.OpAMD64MOVLstoreidx4:
p.To.Scale = 4
case ssa.OpAMD64MOVWstoreidx2:
p.To.Scale = 2
}
p.To.Reg = r
p.To.Scale = 1
p.To.Index = i
gc.AddAux(&p.To, v)
case ssa.OpAMD64ADDQconstmodify, ssa.OpAMD64ADDLconstmodify:
@ -816,14 +794,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// Break false dependency on destination register.
opregreg(s, x86.AXORPS, r, r)
opregreg(s, v.Op.Asm(), r, v.Args[0].Reg())
case ssa.OpAMD64MOVQi2f, ssa.OpAMD64MOVQf2i:
p := s.Prog(x86.AMOVQ)
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64MOVLi2f, ssa.OpAMD64MOVLf2i:
p := s.Prog(x86.AMOVL)
case ssa.OpAMD64MOVQi2f, ssa.OpAMD64MOVQf2i, ssa.OpAMD64MOVLi2f, ssa.OpAMD64MOVLf2i:
var p *obj.Prog
switch v.Op {
case ssa.OpAMD64MOVQi2f, ssa.OpAMD64MOVQf2i:
p = s.Prog(x86.AMOVQ)
case ssa.OpAMD64MOVLi2f, ssa.OpAMD64MOVLf2i:
p = s.Prog(x86.AMOVL)
}
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
@ -968,24 +946,17 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p := s.Prog(v.Op.Asm())
p.To.Type = obj.TYPE_REG
p.To.Reg = r
case ssa.OpAMD64BSFQ, ssa.OpAMD64BSRQ:
case ssa.OpAMD64BSFQ, ssa.OpAMD64BSRQ, ssa.OpAMD64BSFL, ssa.OpAMD64BSRL, ssa.OpAMD64SQRTSD:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0()
case ssa.OpAMD64BSFL, ssa.OpAMD64BSRL:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64SQRTSD:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
switch v.Op {
case ssa.OpAMD64BSFQ, ssa.OpAMD64BSRQ:
p.To.Reg = v.Reg0()
case ssa.OpAMD64BSFL, ssa.OpAMD64BSRL, ssa.OpAMD64SQRTSD:
p.To.Reg = v.Reg()
}
case ssa.OpAMD64ROUNDSD:
p := s.Prog(v.Op.Asm())
val := v.AuxInt
@ -1020,7 +991,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
ssa.OpAMD64SETGF, ssa.OpAMD64SETGEF,
ssa.OpAMD64SETB, ssa.OpAMD64SETBE,
ssa.OpAMD64SETORD, ssa.OpAMD64SETNAN,
ssa.OpAMD64SETA, ssa.OpAMD64SETAE:
ssa.OpAMD64SETA, ssa.OpAMD64SETAE,
ssa.OpAMD64SETO:
p := s.Prog(v.Op.Asm())
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@ -1163,6 +1135,8 @@ var blockJump = [...]struct {
ssa.BlockAMD64GE: {x86.AJGE, x86.AJLT},
ssa.BlockAMD64LE: {x86.AJLE, x86.AJGT},
ssa.BlockAMD64GT: {x86.AJGT, x86.AJLE},
ssa.BlockAMD64OS: {x86.AJOS, x86.AJOC},
ssa.BlockAMD64OC: {x86.AJOC, x86.AJOS},
ssa.BlockAMD64ULT: {x86.AJCS, x86.AJCC},
ssa.BlockAMD64UGE: {x86.AJCC, x86.AJCS},
ssa.BlockAMD64UGT: {x86.AJHI, x86.AJLS},
@ -1224,6 +1198,7 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
case ssa.BlockAMD64EQ, ssa.BlockAMD64NE,
ssa.BlockAMD64LT, ssa.BlockAMD64GE,
ssa.BlockAMD64LE, ssa.BlockAMD64GT,
ssa.BlockAMD64OS, ssa.BlockAMD64OC,
ssa.BlockAMD64ULT, ssa.BlockAMD64UGT,
ssa.BlockAMD64ULE, ssa.BlockAMD64UGE:
jmp := blockJump[b.Kind]

View file

@ -102,7 +102,7 @@ func algtype1(t *types.Type) (AlgKind, *types.Type) {
case TINT8, TUINT8, TINT16, TUINT16,
TINT32, TUINT32, TINT64, TUINT64,
TINT, TUINT, TUINTPTR,
TBOOL, TPTR32, TPTR64,
TBOOL, TPTR,
TCHAN, TUNSAFEPTR:
return AMEM, nil
@ -300,18 +300,8 @@ func genhash(sym *types.Sym, t *types.Type) {
testdclstack()
}
// Disable safemode while compiling this code: the code we
// generate internally can refer to unsafe.Pointer.
// In this case it can happen if we need to generate an ==
// for a struct containing a reflect.Value, which itself has
// an unexported field of type unsafe.Pointer.
old_safemode := safemode
safemode = false
fn.Func.SetNilCheckDisabled(true)
funccompile(fn)
safemode = old_safemode
}
func hashfor(t *types.Type) *Node {
@ -484,22 +474,12 @@ func geneq(sym *types.Sym, t *types.Type) {
testdclstack()
}
// Disable safemode while compiling this code: the code we
// generate internally can refer to unsafe.Pointer.
// In this case it can happen if we need to generate an ==
// for a struct containing a reflect.Value, which itself has
// an unexported field of type unsafe.Pointer.
old_safemode := safemode
safemode = false
// Disable checknils while compiling this code.
// We are comparing a struct or an array,
// neither of which can be nil, and our comparisons
// are shallow.
fn.Func.SetNilCheckDisabled(true)
funccompile(fn)
safemode = old_safemode
}
// eqfield returns the node

View file

@ -250,12 +250,8 @@ func dowidth(t *types.Type) {
w = 16
t.Align = uint8(Widthreg)
case TPTR32:
w = 4
checkwidth(t.Elem())
case TPTR64:
w = 8
case TPTR:
w = int64(Widthptr)
checkwidth(t.Elem())
case TUNSAFEPTR:

View file

@ -43,7 +43,7 @@ func (p *exporter) markType(t *types.Type) {
// the user already needs some way to construct values of
// those types.
switch t.Etype {
case TPTR32, TPTR64, TARRAY, TSLICE, TCHAN:
case TPTR, TARRAY, TSLICE, TCHAN:
// TODO(mdempsky): Skip marking element type for
// send-only channels?
p.markType(t.Elem())

View file

@ -55,15 +55,15 @@ var runtimeDecls = [...]struct {
{"convT2E16", funcTag, 52},
{"convT2E32", funcTag, 52},
{"convT2E64", funcTag, 52},
{"convT2Estring", funcTag, 53},
{"convT2Eslice", funcTag, 53},
{"convT2Estring", funcTag, 52},
{"convT2Eslice", funcTag, 52},
{"convT2Enoptr", funcTag, 53},
{"convT2I", funcTag, 53},
{"convT2I16", funcTag, 52},
{"convT2I32", funcTag, 52},
{"convT2I64", funcTag, 52},
{"convT2Istring", funcTag, 53},
{"convT2Islice", funcTag, 53},
{"convT2Istring", funcTag, 52},
{"convT2Islice", funcTag, 52},
{"convT2Inoptr", funcTag, 53},
{"assertE2I", funcTag, 52},
{"assertE2I2", funcTag, 54},

View file

@ -68,16 +68,16 @@ func convT2E(typ *byte, elem *any) (ret any)
func convT2E16(typ *byte, val any) (ret any)
func convT2E32(typ *byte, val any) (ret any)
func convT2E64(typ *byte, val any) (ret any)
func convT2Estring(typ *byte, elem *any) (ret any)
func convT2Eslice(typ *byte, elem *any) (ret any)
func convT2Estring(typ *byte, val any) (ret any) // val must be a string
func convT2Eslice(typ *byte, val any) (ret any) // val must be a slice
func convT2Enoptr(typ *byte, elem *any) (ret any)
func convT2I(tab *byte, elem *any) (ret any)
func convT2I16(tab *byte, val any) (ret any)
func convT2I32(tab *byte, val any) (ret any)
func convT2I64(tab *byte, val any) (ret any)
func convT2Istring(tab *byte, elem *any) (ret any)
func convT2Islice(tab *byte, elem *any) (ret any)
func convT2Istring(tab *byte, val any) (ret any) // val must be a string
func convT2Islice(tab *byte, val any) (ret any) // val must be a slice
func convT2Inoptr(tab *byte, elem *any) (ret any)
// interface type assertions x.(T)

View file

@ -337,18 +337,10 @@ func closuredebugruntimecheck(clo *Node) {
}
}
func walkclosure(clo *Node, init *Nodes) *Node {
xfunc := clo.Func.Closure
// If no closure vars, don't bother wrapping.
if hasemptycvars(clo) {
if Debug_closure > 0 {
Warnl(clo.Pos, "closure converted to global")
}
return xfunc.Func.Nname
}
closuredebugruntimecheck(clo)
// closureType returns the struct type used to hold all the information
// needed in the closure for clo (clo must be a OCLOSURE node).
// The address of a variable of the returned type can be cast to a func.
func closureType(clo *Node) *types.Type {
// Create closure in the form of a composite literal.
// supposing the closure captures an int i and a string s
// and has one float64 argument and no results,
@ -362,11 +354,10 @@ func walkclosure(clo *Node, init *Nodes) *Node {
// The information appears in the binary in the form of type descriptors;
// the struct is unnamed so that closures in multiple packages with the
// same struct type can share the descriptor.
fields := []*Node{
namedfield(".F", types.Types[TUINTPTR]),
}
for _, v := range xfunc.Func.Cvars.Slice() {
for _, v := range clo.Func.Closure.Func.Cvars.Slice() {
typ := v.Type
if !v.Name.Byval() {
typ = types.NewPtr(typ)
@ -375,6 +366,22 @@ func walkclosure(clo *Node, init *Nodes) *Node {
}
typ := tostruct(fields)
typ.SetNoalg(true)
return typ
}
func walkclosure(clo *Node, init *Nodes) *Node {
xfunc := clo.Func.Closure
// If no closure vars, don't bother wrapping.
if hasemptycvars(clo) {
if Debug_closure > 0 {
Warnl(clo.Pos, "closure converted to global")
}
return xfunc.Func.Nname
}
closuredebugruntimecheck(clo)
typ := closureType(clo)
clos := nod(OCOMPLIT, nil, nod(OIND, typenod(typ), nil))
clos.Esc = clo.Esc
@ -389,10 +396,10 @@ func walkclosure(clo *Node, init *Nodes) *Node {
clos.Left.Esc = clo.Esc
// non-escaping temp to use, if any.
// orderexpr did not compute the type; fill it in now.
if x := prealloc[clo]; x != nil {
x.Type = clos.Left.Left.Type
x.Orig.Type = x.Type
if !types.Identical(typ, x.Type) {
panic("closure type does not match order's assigned type")
}
clos.Left.Right = x
delete(prealloc, clo)
}
@ -479,6 +486,18 @@ func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node {
return xfunc
}
// partialCallType returns the struct type used to hold all the information
// needed in the closure for n (n must be a OCALLPART node).
// The address of a variable of the returned type can be cast to a func.
func partialCallType(n *Node) *types.Type {
t := tostruct([]*Node{
namedfield("F", types.Types[TUINTPTR]),
namedfield("R", n.Left.Type),
})
t.SetNoalg(true)
return t
}
func walkpartialcall(n *Node, init *Nodes) *Node {
// Create closure in the form of a composite literal.
// For x.M with receiver (x) type T, the generated code looks like:
@ -495,30 +514,25 @@ func walkpartialcall(n *Node, init *Nodes) *Node {
checknil(n.Left, init)
}
typ := tostruct([]*Node{
namedfield("F", types.Types[TUINTPTR]),
namedfield("R", n.Left.Type),
})
typ.SetNoalg(true)
typ := partialCallType(n)
clos := nod(OCOMPLIT, nil, nod(OIND, typenod(typ), nil))
clos.Esc = n.Esc
clos.Right.SetImplicit(true)
clos.List.Set1(nod(OCFUNC, n.Func.Nname, nil))
clos.List.Append(n.Left)
clos.List.Set2(nod(OCFUNC, n.Func.Nname, nil), n.Left)
// Force type conversion from *struct to the func type.
clos = convnop(clos, n.Type)
// typecheck will insert a PTRLIT node under CONVNOP,
// tag it with escape analysis result.
// The typecheck inside convnop will insert a PTRLIT node under CONVNOP.
// Tag it with escape analysis result.
clos.Left.Esc = n.Esc
// non-escaping temp to use, if any.
// orderexpr did not compute the type; fill it in now.
if x := prealloc[n]; x != nil {
x.Type = clos.Left.Left.Type
x.Orig.Type = x.Type
if !types.Identical(typ, x.Type) {
panic("partial call type does not match order's assigned type")
}
clos.Left.Right = x
delete(prealloc, n)
}

View file

@ -40,7 +40,7 @@ func (v Val) Ctype() Ctype {
switch x := v.U.(type) {
default:
Fatalf("unexpected Ctype for %T", v.U)
panic("not reached")
panic("unreachable")
case nil:
return 0
case *NilVal:
@ -68,7 +68,7 @@ func eqval(a, b Val) bool {
switch x := a.U.(type) {
default:
Fatalf("unexpected Ctype for %T", a.U)
panic("not reached")
panic("unreachable")
case *NilVal:
return true
case bool:
@ -96,7 +96,7 @@ func (v Val) Interface() interface{} {
switch x := v.U.(type) {
default:
Fatalf("unexpected Interface for %T", v.U)
panic("not reached")
panic("unreachable")
case *NilVal:
return nil
case bool, string:
@ -311,7 +311,7 @@ func convlit1(n *Node, t *types.Type, explicit bool, reuse canReuseNode) *Node {
}
// avoid repeated calculations, errors
if eqtype(n.Type, t) {
if types.Identical(n.Type, t) {
return n
}
@ -347,7 +347,7 @@ func convlit1(n *Node, t *types.Type, explicit bool, reuse canReuseNode) *Node {
case TARRAY:
goto bad
case TPTR32, TPTR64, TUNSAFEPTR:
case TPTR, TUNSAFEPTR:
n.SetVal(Val{new(Mpint)})
case TCHAN, TFUNC, TINTER, TMAP, TSLICE:
@ -424,29 +424,6 @@ bad:
return n
}
func copyval(v Val) Val {
switch u := v.U.(type) {
case *Mpint:
i := new(Mpint)
i.Set(u)
i.Rune = u.Rune
v.U = i
case *Mpflt:
f := newMpflt()
f.Set(u)
v.U = f
case *Mpcplx:
c := new(Mpcplx)
c.Real.Set(&u.Real)
c.Imag.Set(&u.Imag)
v.U = c
}
return v
}
func tocplx(v Val) Val {
switch u := v.U.(type) {
case *Mpint:
@ -585,10 +562,6 @@ func tostr(v Val) Val {
i = u.Int64()
}
v.U = string(i)
case *NilVal:
// Can happen because of string([]byte(nil)).
v.U = ""
}
return v
@ -609,50 +582,55 @@ func Isconst(n *Node, ct Ctype) bool {
return t == ct || (ct == CTINT && t == CTRUNE)
}
// if n is constant, rewrite as OLITERAL node.
// evconst rewrites constant expressions into OLITERAL nodes.
func evconst(n *Node) {
// pick off just the opcodes that can be
// constant evaluated.
switch n.Op {
default:
return
nl, nr := n.Left, n.Right
case OADD,
OAND,
OANDAND,
OANDNOT,
OARRAYBYTESTR,
OCOM,
ODIV,
OEQ,
OGE,
OGT,
OLE,
OLSH,
OLT,
OMINUS,
OMOD,
OMUL,
ONE,
ONOT,
OOR,
OOROR,
OPLUS,
ORSH,
OSUB,
OXOR:
break
// Pick off just the opcodes that can be constant evaluated.
switch op := n.Op; op {
case OPLUS, OMINUS, OCOM, ONOT:
if nl.Op == OLITERAL {
setconst(n, unaryOp(op, nl.Val(), n.Type))
}
case OADD, OSUB, OMUL, ODIV, OMOD, OOR, OXOR, OAND, OANDNOT, OOROR, OANDAND:
if nl.Op == OLITERAL && nr.Op == OLITERAL {
setconst(n, binaryOp(nl.Val(), op, nr.Val()))
}
case OEQ, ONE, OLT, OLE, OGT, OGE:
if nl.Op == OLITERAL && nr.Op == OLITERAL {
if nl.Type.IsInterface() != nr.Type.IsInterface() {
// Mixed interface/non-interface
// constant comparison means comparing
// nil interface with some typed
// constant, which is always unequal.
// E.g., interface{}(nil) == (*int)(nil).
setboolconst(n, op == ONE)
} else {
setboolconst(n, compareOp(nl.Val(), op, nr.Val()))
}
}
case OLSH, ORSH:
if nl.Op == OLITERAL && nr.Op == OLITERAL {
setconst(n, shiftOp(nl.Val(), op, nr.Val()))
}
case OCONV:
if n.Type == nil {
return
}
if !okforconst[n.Type.Etype] && n.Type.Etype != TNIL {
return
if n.Type != nil && okforconst[n.Type.Etype] && nl.Op == OLITERAL {
// TODO(mdempsky): There should be a convval function.
setconst(n, convlit1(nl, n.Type, true, false).Val())
}
case OARRAYBYTESTR:
// string([]byte(nil)) or string([]rune(nil))
if nl.Op == OLITERAL && nl.Val().Ctype() == CTNIL {
setconst(n, Val{U: ""})
}
// merge adjacent constants in the argument list.
case OADDSTR:
// Merge adjacent constants in the argument list.
s := n.List.Slice()
for i1 := 0; i1 < len(s); i1++ {
if Isconst(s[i1], CTSTR) && i1+1 < len(s) && Isconst(s[i1+1], CTSTR) {
@ -678,521 +656,292 @@ func evconst(n *Node) {
} else {
n.List.Set(s)
}
}
}
return
func match(x, y Val) (Val, Val) {
switch {
case x.Ctype() == CTCPLX || y.Ctype() == CTCPLX:
return tocplx(x), tocplx(y)
case x.Ctype() == CTFLT || y.Ctype() == CTFLT:
return toflt(x), toflt(y)
}
nl := n.Left
if nl == nil || nl.Type == nil {
return
}
if consttype(nl) == 0 {
return
}
wl := nl.Type.Etype
if isInt[wl] || isFloat[wl] || isComplex[wl] {
wl = TIDEAL
}
// Mixed int/rune are fine.
return x, y
}
// avoid constant conversions in switches below
const (
CTINT_ = uint32(CTINT)
CTRUNE_ = uint32(CTRUNE)
CTFLT_ = uint32(CTFLT)
CTCPLX_ = uint32(CTCPLX)
CTSTR_ = uint32(CTSTR)
CTBOOL_ = uint32(CTBOOL)
CTNIL_ = uint32(CTNIL)
OCONV_ = uint32(OCONV) << 16
OARRAYBYTESTR_ = uint32(OARRAYBYTESTR) << 16
OPLUS_ = uint32(OPLUS) << 16
OMINUS_ = uint32(OMINUS) << 16
OCOM_ = uint32(OCOM) << 16
ONOT_ = uint32(ONOT) << 16
OLSH_ = uint32(OLSH) << 16
ORSH_ = uint32(ORSH) << 16
OADD_ = uint32(OADD) << 16
OSUB_ = uint32(OSUB) << 16
OMUL_ = uint32(OMUL) << 16
ODIV_ = uint32(ODIV) << 16
OMOD_ = uint32(OMOD) << 16
OOR_ = uint32(OOR) << 16
OAND_ = uint32(OAND) << 16
OANDNOT_ = uint32(OANDNOT) << 16
OXOR_ = uint32(OXOR) << 16
OEQ_ = uint32(OEQ) << 16
ONE_ = uint32(ONE) << 16
OLT_ = uint32(OLT) << 16
OLE_ = uint32(OLE) << 16
OGE_ = uint32(OGE) << 16
OGT_ = uint32(OGT) << 16
OOROR_ = uint32(OOROR) << 16
OANDAND_ = uint32(OANDAND) << 16
)
func compareOp(x Val, op Op, y Val) bool {
x, y = match(x, y)
nr := n.Right
var rv Val
var wr types.EType
var ctype uint32
var v Val
if nr == nil {
// copy numeric value to avoid modifying
// nl, in case someone still refers to it (e.g. iota).
v = copyval(nl.Val())
// rune values are int values for the purpose of constant folding.
ctype = uint32(v.Ctype())
if ctype == CTRUNE_ {
ctype = CTINT_
switch x.Ctype() {
case CTNIL:
_, _ = x.U.(*NilVal), y.U.(*NilVal) // assert dynamic types match
switch op {
case OEQ:
return true
case ONE:
return false
}
switch uint32(n.Op)<<16 | ctype {
default:
if !n.Diag() {
yyerror("illegal constant expression %v %v", n.Op, nl.Type)
n.SetDiag(true)
}
return
case CTBOOL:
x, y := x.U.(bool), y.U.(bool)
switch op {
case OEQ:
return x == y
case ONE:
return x != y
}
case OCONV_ | CTNIL_,
OARRAYBYTESTR_ | CTNIL_:
if n.Type.IsString() {
v = tostr(v)
nl.Type = n.Type
case CTINT, CTRUNE:
x, y := x.U.(*Mpint), y.U.(*Mpint)
return cmpZero(x.Cmp(y), op)
case CTFLT:
x, y := x.U.(*Mpflt), y.U.(*Mpflt)
return cmpZero(x.Cmp(y), op)
case CTCPLX:
x, y := x.U.(*Mpcplx), y.U.(*Mpcplx)
eq := x.Real.Cmp(&y.Real) == 0 && x.Imag.Cmp(&y.Imag) == 0
switch op {
case OEQ:
return eq
case ONE:
return !eq
}
case CTSTR:
x, y := x.U.(string), y.U.(string)
switch op {
case OEQ:
return x == y
case ONE:
return x != y
case OLT:
return x < y
case OLE:
return x <= y
case OGT:
return x > y
case OGE:
return x >= y
}
}
Fatalf("compareOp: bad comparison: %v %v %v", x, op, y)
panic("unreachable")
}
func cmpZero(x int, op Op) bool {
switch op {
case OEQ:
return x == 0
case ONE:
return x != 0
case OLT:
return x < 0
case OLE:
return x <= 0
case OGT:
return x > 0
case OGE:
return x >= 0
}
Fatalf("cmpZero: want comparison operator, got %v", op)
panic("unreachable")
}
func binaryOp(x Val, op Op, y Val) Val {
x, y = match(x, y)
Outer:
switch x.Ctype() {
case CTBOOL:
x, y := x.U.(bool), y.U.(bool)
switch op {
case OANDAND:
return Val{U: x && y}
case OOROR:
return Val{U: x || y}
}
case CTINT, CTRUNE:
x, y := x.U.(*Mpint), y.U.(*Mpint)
u := new(Mpint)
u.Rune = x.Rune || y.Rune
u.Set(x)
switch op {
case OADD:
u.Add(y)
case OSUB:
u.Sub(y)
case OMUL:
u.Mul(y)
case ODIV:
if y.CmpInt64(0) == 0 {
yyerror("division by zero")
u.SetOverflow()
break
}
fallthrough
case OCONV_ | CTINT_,
OCONV_ | CTFLT_,
OCONV_ | CTCPLX_,
OCONV_ | CTSTR_,
OCONV_ | CTBOOL_:
nl = convlit1(nl, n.Type, true, false)
v = nl.Val()
case OPLUS_ | CTINT_:
break
case OMINUS_ | CTINT_:
v.U.(*Mpint).Neg()
case OCOM_ | CTINT_:
et := Txxx
if nl.Type != nil {
et = nl.Type.Etype
u.Quo(y)
case OMOD:
if y.CmpInt64(0) == 0 {
yyerror("division by zero")
u.SetOverflow()
break
}
u.Rem(y)
case OOR:
u.Or(y)
case OAND:
u.And(y)
case OANDNOT:
u.AndNot(y)
case OXOR:
u.Xor(y)
default:
break Outer
}
return Val{U: u}
// calculate the mask in b
// result will be (a ^ mask)
var b Mpint
switch et {
// signed guys change sign
default:
b.SetInt64(-1)
case CTFLT:
x, y := x.U.(*Mpflt), y.U.(*Mpflt)
// unsigned guys invert their bits
case TUINT8,
TUINT16,
TUINT32,
TUINT64,
TUINT,
TUINTPTR:
b.Set(maxintval[et])
u := newMpflt()
u.Set(x)
switch op {
case OADD:
u.Add(y)
case OSUB:
u.Sub(y)
case OMUL:
u.Mul(y)
case ODIV:
if y.CmpFloat64(0) == 0 {
yyerror("division by zero")
u.SetFloat64(1)
break
}
v.U.(*Mpint).Xor(&b)
case OPLUS_ | CTFLT_:
break
case OMINUS_ | CTFLT_:
v.U.(*Mpflt).Neg()
case OPLUS_ | CTCPLX_:
break
case OMINUS_ | CTCPLX_:
v.U.(*Mpcplx).Real.Neg()
v.U.(*Mpcplx).Imag.Neg()
case ONOT_ | CTBOOL_:
if !v.U.(bool) {
goto settrue
}
goto setfalse
}
goto ret
}
if nr.Type == nil {
return
}
if consttype(nr) == 0 {
return
}
wr = nr.Type.Etype
if isInt[wr] || isFloat[wr] || isComplex[wr] {
wr = TIDEAL
}
// check for compatible general types (numeric, string, etc)
if wl != wr {
if wl == TINTER || wr == TINTER {
if n.Op == ONE {
goto settrue
}
goto setfalse
}
goto illegal
}
// check for compatible types.
switch n.Op {
// ideal const mixes with anything but otherwise must match.
default:
if nl.Type.Etype != TIDEAL {
nr = defaultlit(nr, nl.Type)
n.Right = nr
}
if nr.Type.Etype != TIDEAL {
nl = defaultlit(nl, nr.Type)
n.Left = nl
}
if nl.Type.Etype != nr.Type.Etype {
goto illegal
}
// right must be unsigned.
// left can be ideal.
case OLSH, ORSH:
nr = defaultlit(nr, types.Types[TUINT])
n.Right = nr
if nr.Type != nil && (nr.Type.IsSigned() || !nr.Type.IsInteger()) {
goto illegal
}
if nl.Val().Ctype() != CTRUNE {
nl.SetVal(toint(nl.Val()))
}
nr.SetVal(toint(nr.Val()))
}
// copy numeric value to avoid modifying
// n->left, in case someone still refers to it (e.g. iota).
v = copyval(nl.Val())
rv = nr.Val()
// convert to common ideal
if v.Ctype() == CTCPLX || rv.Ctype() == CTCPLX {
v = tocplx(v)
rv = tocplx(rv)
}
if v.Ctype() == CTFLT || rv.Ctype() == CTFLT {
v = toflt(v)
rv = toflt(rv)
}
// Rune and int turns into rune.
if v.Ctype() == CTRUNE && rv.Ctype() == CTINT {
i := new(Mpint)
i.Set(rv.U.(*Mpint))
i.Rune = true
rv.U = i
}
if v.Ctype() == CTINT && rv.Ctype() == CTRUNE {
if n.Op == OLSH || n.Op == ORSH {
i := new(Mpint)
i.Set(rv.U.(*Mpint))
rv.U = i
} else {
i := new(Mpint)
i.Set(v.U.(*Mpint))
i.Rune = true
v.U = i
}
}
if v.Ctype() != rv.Ctype() {
// Use of undefined name as constant?
if (v.Ctype() == 0 || rv.Ctype() == 0) && nerrors > 0 {
return
}
Fatalf("constant type mismatch %v(%d) %v(%d)", nl.Type, v.Ctype(), nr.Type, rv.Ctype())
}
// rune values are int values for the purpose of constant folding.
ctype = uint32(v.Ctype())
if ctype == CTRUNE_ {
ctype = CTINT_
}
// run op
switch uint32(n.Op)<<16 | ctype {
default:
goto illegal
case OADD_ | CTINT_:
v.U.(*Mpint).Add(rv.U.(*Mpint))
case OSUB_ | CTINT_:
v.U.(*Mpint).Sub(rv.U.(*Mpint))
case OMUL_ | CTINT_:
v.U.(*Mpint).Mul(rv.U.(*Mpint))
case ODIV_ | CTINT_:
if rv.U.(*Mpint).CmpInt64(0) == 0 {
yyerror("division by zero")
v.U.(*Mpint).SetOverflow()
break
}
v.U.(*Mpint).Quo(rv.U.(*Mpint))
case OMOD_ | CTINT_:
if rv.U.(*Mpint).CmpInt64(0) == 0 {
yyerror("division by zero")
v.U.(*Mpint).SetOverflow()
break
}
v.U.(*Mpint).Rem(rv.U.(*Mpint))
case OLSH_ | CTINT_:
v.U.(*Mpint).Lsh(rv.U.(*Mpint))
case ORSH_ | CTINT_:
v.U.(*Mpint).Rsh(rv.U.(*Mpint))
case OOR_ | CTINT_:
v.U.(*Mpint).Or(rv.U.(*Mpint))
case OAND_ | CTINT_:
v.U.(*Mpint).And(rv.U.(*Mpint))
case OANDNOT_ | CTINT_:
v.U.(*Mpint).AndNot(rv.U.(*Mpint))
case OXOR_ | CTINT_:
v.U.(*Mpint).Xor(rv.U.(*Mpint))
case OADD_ | CTFLT_:
v.U.(*Mpflt).Add(rv.U.(*Mpflt))
case OSUB_ | CTFLT_:
v.U.(*Mpflt).Sub(rv.U.(*Mpflt))
case OMUL_ | CTFLT_:
v.U.(*Mpflt).Mul(rv.U.(*Mpflt))
case ODIV_ | CTFLT_:
if rv.U.(*Mpflt).CmpFloat64(0) == 0 {
yyerror("division by zero")
v.U.(*Mpflt).SetFloat64(1.0)
break
}
v.U.(*Mpflt).Quo(rv.U.(*Mpflt))
// The default case above would print 'ideal % ideal',
// which is not quite an ideal error.
case OMOD_ | CTFLT_:
if !n.Diag() {
u.Quo(y)
case OMOD:
// TODO(mdempsky): Move to typecheck.
yyerror("illegal constant expression: floating-point %% operation")
n.SetDiag(true)
default:
break Outer
}
return Val{U: u}
return
case CTCPLX:
x, y := x.U.(*Mpcplx), y.U.(*Mpcplx)
case OADD_ | CTCPLX_:
v.U.(*Mpcplx).Real.Add(&rv.U.(*Mpcplx).Real)
v.U.(*Mpcplx).Imag.Add(&rv.U.(*Mpcplx).Imag)
case OSUB_ | CTCPLX_:
v.U.(*Mpcplx).Real.Sub(&rv.U.(*Mpcplx).Real)
v.U.(*Mpcplx).Imag.Sub(&rv.U.(*Mpcplx).Imag)
case OMUL_ | CTCPLX_:
v.U.(*Mpcplx).Mul(rv.U.(*Mpcplx))
case ODIV_ | CTCPLX_:
if !v.U.(*Mpcplx).Div(rv.U.(*Mpcplx)) {
yyerror("complex division by zero")
rv.U.(*Mpcplx).Real.SetFloat64(1.0)
rv.U.(*Mpcplx).Imag.SetFloat64(0.0)
break
u := new(Mpcplx)
u.Real.Set(&x.Real)
u.Imag.Set(&x.Imag)
switch op {
case OADD:
u.Real.Add(&y.Real)
u.Imag.Add(&y.Imag)
case OSUB:
u.Real.Sub(&y.Real)
u.Imag.Sub(&y.Imag)
case OMUL:
u.Mul(y)
case ODIV:
if !u.Div(y) {
yyerror("complex division by zero")
u.Real.SetFloat64(1)
u.Imag.SetFloat64(0)
}
default:
break Outer
}
case OEQ_ | CTNIL_:
goto settrue
case ONE_ | CTNIL_:
goto setfalse
case OEQ_ | CTINT_:
if v.U.(*Mpint).Cmp(rv.U.(*Mpint)) == 0 {
goto settrue
}
goto setfalse
case ONE_ | CTINT_:
if v.U.(*Mpint).Cmp(rv.U.(*Mpint)) != 0 {
goto settrue
}
goto setfalse
case OLT_ | CTINT_:
if v.U.(*Mpint).Cmp(rv.U.(*Mpint)) < 0 {
goto settrue
}
goto setfalse
case OLE_ | CTINT_:
if v.U.(*Mpint).Cmp(rv.U.(*Mpint)) <= 0 {
goto settrue
}
goto setfalse
case OGE_ | CTINT_:
if v.U.(*Mpint).Cmp(rv.U.(*Mpint)) >= 0 {
goto settrue
}
goto setfalse
case OGT_ | CTINT_:
if v.U.(*Mpint).Cmp(rv.U.(*Mpint)) > 0 {
goto settrue
}
goto setfalse
case OEQ_ | CTFLT_:
if v.U.(*Mpflt).Cmp(rv.U.(*Mpflt)) == 0 {
goto settrue
}
goto setfalse
case ONE_ | CTFLT_:
if v.U.(*Mpflt).Cmp(rv.U.(*Mpflt)) != 0 {
goto settrue
}
goto setfalse
case OLT_ | CTFLT_:
if v.U.(*Mpflt).Cmp(rv.U.(*Mpflt)) < 0 {
goto settrue
}
goto setfalse
case OLE_ | CTFLT_:
if v.U.(*Mpflt).Cmp(rv.U.(*Mpflt)) <= 0 {
goto settrue
}
goto setfalse
case OGE_ | CTFLT_:
if v.U.(*Mpflt).Cmp(rv.U.(*Mpflt)) >= 0 {
goto settrue
}
goto setfalse
case OGT_ | CTFLT_:
if v.U.(*Mpflt).Cmp(rv.U.(*Mpflt)) > 0 {
goto settrue
}
goto setfalse
case OEQ_ | CTCPLX_:
if v.U.(*Mpcplx).Real.Cmp(&rv.U.(*Mpcplx).Real) == 0 && v.U.(*Mpcplx).Imag.Cmp(&rv.U.(*Mpcplx).Imag) == 0 {
goto settrue
}
goto setfalse
case ONE_ | CTCPLX_:
if v.U.(*Mpcplx).Real.Cmp(&rv.U.(*Mpcplx).Real) != 0 || v.U.(*Mpcplx).Imag.Cmp(&rv.U.(*Mpcplx).Imag) != 0 {
goto settrue
}
goto setfalse
case OEQ_ | CTSTR_:
if strlit(nl) == strlit(nr) {
goto settrue
}
goto setfalse
case ONE_ | CTSTR_:
if strlit(nl) != strlit(nr) {
goto settrue
}
goto setfalse
case OLT_ | CTSTR_:
if strlit(nl) < strlit(nr) {
goto settrue
}
goto setfalse
case OLE_ | CTSTR_:
if strlit(nl) <= strlit(nr) {
goto settrue
}
goto setfalse
case OGE_ | CTSTR_:
if strlit(nl) >= strlit(nr) {
goto settrue
}
goto setfalse
case OGT_ | CTSTR_:
if strlit(nl) > strlit(nr) {
goto settrue
}
goto setfalse
case OOROR_ | CTBOOL_:
if v.U.(bool) || rv.U.(bool) {
goto settrue
}
goto setfalse
case OANDAND_ | CTBOOL_:
if v.U.(bool) && rv.U.(bool) {
goto settrue
}
goto setfalse
case OEQ_ | CTBOOL_:
if v.U.(bool) == rv.U.(bool) {
goto settrue
}
goto setfalse
case ONE_ | CTBOOL_:
if v.U.(bool) != rv.U.(bool) {
goto settrue
}
goto setfalse
return Val{U: u}
}
ret:
setconst(n, v)
return
Fatalf("binaryOp: bad operation: %v %v %v", x, op, y)
panic("unreachable")
}
settrue:
setconst(n, Val{true})
return
func unaryOp(op Op, x Val, t *types.Type) Val {
switch op {
case OPLUS:
switch x.Ctype() {
case CTINT, CTRUNE, CTFLT, CTCPLX:
return x
}
setfalse:
setconst(n, Val{false})
return
case OMINUS:
switch x.Ctype() {
case CTINT, CTRUNE:
x := x.U.(*Mpint)
u := new(Mpint)
u.Rune = x.Rune
u.Set(x)
u.Neg()
return Val{U: u}
illegal:
if !n.Diag() {
yyerror("illegal constant expression: %v %v %v", nl.Type, n.Op, nr.Type)
n.SetDiag(true)
case CTFLT:
x := x.U.(*Mpflt)
u := newMpflt()
u.Set(x)
u.Neg()
return Val{U: u}
case CTCPLX:
x := x.U.(*Mpcplx)
u := new(Mpcplx)
u.Real.Set(&x.Real)
u.Imag.Set(&x.Imag)
u.Real.Neg()
u.Imag.Neg()
return Val{U: u}
}
case OCOM:
x := x.U.(*Mpint)
u := new(Mpint)
u.Rune = x.Rune
if t.IsSigned() || t.IsUntyped() {
// Signed values change sign.
u.SetInt64(-1)
} else {
// Unsigned values invert their bits.
u.Set(maxintval[t.Etype])
}
u.Xor(x)
return Val{U: u}
case ONOT:
return Val{U: !x.U.(bool)}
}
Fatalf("unaryOp: bad operation: %v %v", op, x)
panic("unreachable")
}
func shiftOp(x Val, op Op, y Val) Val {
if x.Ctype() != CTRUNE {
x = toint(x)
}
y = toint(y)
u := new(Mpint)
u.Set(x.U.(*Mpint))
u.Rune = x.U.(*Mpint).Rune
switch op {
case OLSH:
u.Lsh(y.U.(*Mpint))
case ORSH:
u.Rsh(y.U.(*Mpint))
default:
Fatalf("shiftOp: bad operator: %v", op)
panic("unreachable")
}
return Val{U: u}
}
// setconst rewrites n as an OLITERAL with value v.
@ -1223,6 +972,10 @@ func setconst(n *Node, v Val) {
}
}
func setboolconst(n *Node, v bool) {
setconst(n, Val{U: v})
}
func setintconst(n *Node, v int64) {
u := new(Mpint)
u.SetInt64(v)
@ -1305,9 +1058,7 @@ func idealkind(n *Node) Ctype {
OLT,
ONE,
ONOT,
OOROR,
OCMPSTR,
OCMPIFACE:
OOROR:
return CTBOOL
// shifts (beware!).
@ -1479,11 +1230,10 @@ func smallintconst(n *Node) bool {
TUINT16,
TINT32,
TUINT32,
TBOOL,
TPTR32:
TBOOL:
return true
case TIDEAL, TINT64, TUINT64, TPTR64:
case TIDEAL, TINT64, TUINT64, TPTR:
v, ok := n.Val().U.(*Mpint)
if ok && v.Cmp(minintval[TINT32]) > 0 && v.Cmp(maxintval[TINT32]) < 0 {
return true

View file

@ -929,7 +929,7 @@ func addmethod(msym *types.Sym, t *types.Type, local, nointerface bool) *types.F
}
// eqtype only checks that incoming and result parameters match,
// so explicitly check that the receiver parameters match too.
if !eqtype(t, f.Type) || !eqtype(t.Recv().Type, f.Type.Recv().Type) {
if !types.Identical(t, f.Type) || !types.Identical(t.Recv().Type, f.Type.Recv().Type) {
yyerror("method redeclared: %v.%v\n\t%v\n\t%v", mt, msym, f.Type, t)
}
return f

View file

@ -798,9 +798,8 @@ func (e *EscState) esc(n *Node, parent *Node) {
// gathered here.
if n.Esc != EscHeap && n.Type != nil &&
(n.Type.Width > maxStackVarSize ||
(n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= 1<<16 ||
(n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= maxImplicitStackVarSize ||
n.Op == OMAKESLICE && !isSmallMakeSlice(n)) {
// isSmallMakeSlice returns false for non-constant len/cap.
// If that's the case, print a more accurate escape reason.
var msgVerb, escapeMsg string
@ -873,7 +872,7 @@ opSwitch:
// it is also a dereference, because it is implicitly
// dereferenced (see #12588)
if n.Type.IsArray() &&
!(n.Right.Type.IsPtr() && eqtype(n.Right.Type.Elem(), n.Type)) {
!(n.Right.Type.IsPtr() && types.Identical(n.Right.Type.Elem(), n.Type)) {
e.escassignWhyWhere(n.List.Second(), n.Right, "range", n)
} else {
e.escassignDereference(n.List.Second(), n.Right, e.stepAssignWhere(n.List.Second(), n.Right, "range-deref", n))
@ -946,7 +945,8 @@ opSwitch:
case OCALLMETH, OCALLFUNC, OCALLINTER:
e.esccall(n, parent)
// esccall already done on n.Rlist.First(). tie it's Retval to n.List
// esccall already done on n.Rlist.First()
// tie its Retval to n.List
case OAS2FUNC: // x,y = f()
rs := e.nodeEscState(n.Rlist.First()).Retval.Slice()
where := n
@ -1507,7 +1507,7 @@ func (e *EscState) addDereference(n *Node) *Node {
e.nodeEscState(ind).Loopdepth = e.nodeEscState(n).Loopdepth
ind.Pos = n.Pos
t := n.Type
if t.IsKind(types.Tptr) || t.IsSlice() {
if t.IsPtr() || t.IsSlice() {
// This should model our own sloppy use of OIND to encode
// decreasing levels of indirection; i.e., "indirecting" a slice
// yields the type of an element.

View file

@ -131,7 +131,7 @@ func importtype(ipkg *types.Pkg, pos src.XPos, s *types.Sym) *types.Type {
func importobj(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op Op, ctxt Class, t *types.Type) *Node {
n := importsym(ipkg, s, op)
if n.Op != ONONAME {
if n.Op == op && (n.Class() != ctxt || !eqtype(n.Type, t)) {
if n.Op == op && (n.Class() != ctxt || !types.Identical(n.Type, t)) {
redeclare(lineno, s, fmt.Sprintf("during import %q", ipkg.Path))
}
return nil

View file

@ -697,7 +697,7 @@ func typefmt(t *types.Type, flag FmtFlag, mode fmtMode, depth int) string {
}
switch t.Etype {
case TPTR32, TPTR64:
case TPTR:
switch mode {
case FTypeId, FTypeIdName:
if flag&FmtShort != 0 {
@ -1146,8 +1146,6 @@ var opprec = []int{
OGE: 4,
OGT: 4,
ONE: 4,
OCMPSTR: 4,
OCMPIFACE: 4,
OSEND: 3,
OANDAND: 2,
OOROR: 1,
@ -1507,11 +1505,6 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) {
n1.exprfmt(s, nprec, mode)
}
case OCMPSTR, OCMPIFACE:
n.Left.exprfmt(s, nprec, mode)
mode.Fprintf(s, " %#v ", n.SubOp())
n.Right.exprfmt(s, nprec+1, mode)
default:
mode.Fprintf(s, "<node %v>", n.Op)
}

View file

@ -13,8 +13,18 @@ import (
)
const (
BADWIDTH = types.BADWIDTH
BADWIDTH = types.BADWIDTH
// maximum size variable which we will allocate on the stack.
// This limit is for explicit variable declarations like "var x T" or "x := ...".
maxStackVarSize = 10 * 1024 * 1024
// maximum size of implicit variables that we will allocate on the stack.
// p := new(T) allocating T on the stack
// p := &T{} allocating T on the stack
// s := make([]T, n) allocating [n]T on the stack
// s := []byte("...") allocating [n]byte on the stack
maxImplicitStackVarSize = 64 * 1024
)
// isRuntimePkg reports whether p is package runtime.
@ -82,7 +92,6 @@ var pragcgobuf [][]string
var outfile string
var linkobj string
var dolinkobj bool
// nerrors is the number of compiler errors reported
// since the last call to saveerrors.
@ -96,8 +105,6 @@ var nsyntaxerrors int
var decldepth int32
var safemode bool
var nolocalimports bool
var Debug [256]int
@ -201,8 +208,6 @@ var compiling_runtime bool
// Compiling the standard library
var compiling_std bool
var compiling_wrappers bool
var use_writebarrier bool
var pure_go bool

View file

@ -617,7 +617,7 @@ func (w *exportWriter) doTyp(t *types.Type) {
}
switch t.Etype {
case TPTR32, TPTR64:
case TPTR:
w.startType(pointerType)
w.typ(t.Elem())
@ -743,7 +743,7 @@ func constTypeOf(typ *types.Type) Ctype {
return CTSTR
case TINT, TINT8, TINT16, TINT32, TINT64,
TUINT, TUINT8, TUINT16, TUINT32, TUINT64, TUINTPTR,
TPTR32, TPTR64, TUNSAFEPTR:
TPTR, TUNSAFEPTR:
return CTINT
case TFLOAT32, TFLOAT64:
return CTFLT
@ -1319,12 +1319,6 @@ func (w *exportWriter) expr(n *Node) {
w.pos(n.Pos)
w.exprList(n.List)
case OCMPSTR, OCMPIFACE:
w.op(n.SubOp())
w.pos(n.Pos)
w.expr(n.Left)
w.expr(n.Right)
case ODCLCONST:
// if exporting, DCLCONST should just be removed as its usage
// has already been replaced with literals

View file

@ -935,9 +935,6 @@ func (r *importReader) node() *Node {
}
return x
// case OCMPSTR, OCMPIFACE:
// unreachable - mapped to std comparison operators by exporter
// --------------------------------------------------------------------
// statements
case ODCL:

View file

@ -87,9 +87,6 @@ func typecheckinl(fn *Node) {
fmt.Printf("typecheck import [%v] %L { %#v }\n", fn.Sym, fn, asNodes(fn.Func.Inl.Body))
}
save_safemode := safemode
safemode = false
savefn := Curfn
Curfn = fn
typecheckslice(fn.Func.Inl.Body, Etop)
@ -102,8 +99,6 @@ func typecheckinl(fn *Node) {
fn.Func.Inl.Dcl = append(fn.Func.Inl.Dcl, fn.Func.Dcl...)
fn.Func.Dcl = nil
safemode = save_safemode
lineno = lno
}
@ -404,16 +399,6 @@ func (v *hairyVisitor) visit(n *Node) bool {
}
v.budget--
// TODO(mdempsky/josharian): Hacks to appease toolstash; remove.
// See issue 17566 and CL 31674 for discussion.
switch n.Op {
case OSTRUCTKEY:
v.budget--
case OSLICE, OSLICEARR, OSLICESTR:
v.budget--
case OSLICE3, OSLICE3ARR:
v.budget -= 2
}
// When debugging, don't stop early, to get full cost of inlining this function
if v.budget < 0 && Debug['m'] < 2 {
@ -813,23 +798,6 @@ func (v *reassignVisitor) visitList(l Nodes) *Node {
return nil
}
// The result of mkinlcall MUST be assigned back to n, e.g.
// n.Left = mkinlcall(n.Left, fn, isddd)
func mkinlcall(n *Node, fn *Node, maxCost int32) *Node {
save_safemode := safemode
// imported functions may refer to unsafe as long as the
// package was marked safe during import (already checked).
pkg := fnpkg(fn)
if pkg != localpkg && pkg != nil {
safemode = false
}
n = mkinlcall1(n, fn, maxCost)
safemode = save_safemode
return n
}
func tinlvar(t *types.Field, inlvars map[*Node]*Node) *Node {
if n := asNode(t.Nname); n != nil && !n.isBlank() {
inlvar := inlvars[n]
@ -849,9 +817,9 @@ var inlgen int
// On return ninit has the parameter assignments, the nbody is the
// inlined function body and list, rlist contain the input, output
// parameters.
// The result of mkinlcall1 MUST be assigned back to n, e.g.
// n.Left = mkinlcall1(n.Left, fn, isddd)
func mkinlcall1(n, fn *Node, maxCost int32) *Node {
// The result of mkinlcall MUST be assigned back to n, e.g.
// n.Left = mkinlcall(n.Left, fn, isddd)
func mkinlcall(n, fn *Node, maxCost int32) *Node {
if fn.Func.Inl == nil {
// No inlinable body.
return n

View file

@ -26,7 +26,8 @@ func TestIntendedInlining(t *testing.T) {
t.Parallel()
// want is the list of function names (by package) that should
// be inlined.
// be inlinable. If they have no callers in thier packages, they
// might not actually be inlined anywhere.
want := map[string][]string{
"runtime": {
// TODO(mvdan): enable these once mid-stack
@ -53,7 +54,6 @@ func TestIntendedInlining(t *testing.T) {
"getm",
"isDirectIface",
"itabHashFunc",
"maxSliceCap",
"noescape",
"readUnaligned32",
"readUnaligned64",
@ -96,6 +96,9 @@ func TestIntendedInlining(t *testing.T) {
"(*puintptr).set",
},
"runtime/internal/sys": {},
"runtime/internal/math": {
"MulUintptr",
},
"bytes": {
"(*Buffer).Bytes",
"(*Buffer).Cap",
@ -108,6 +111,11 @@ func TestIntendedInlining(t *testing.T) {
"(*Buffer).UnreadByte",
"(*Buffer).tryGrowByReslice",
},
"compress/flate": {
"byLiteral.Len",
"byLiteral.Less",
"byLiteral.Swap",
},
"unicode/utf8": {
"FullRune",
"FullRuneInString",
@ -159,6 +167,13 @@ func TestIntendedInlining(t *testing.T) {
want["runtime"] = append(want["runtime"], "rotl_31")
}
// Functions that must actually be inlined; they must have actual callers.
must := map[string]bool{
"compress/flate.byLiteral.Len": true,
"compress/flate.byLiteral.Less": true,
"compress/flate.byLiteral.Swap": true,
}
notInlinedReason := make(map[string]string)
pkgs := make([]string, 0, len(want))
for pname, fnames := range want {
@ -185,6 +200,7 @@ func TestIntendedInlining(t *testing.T) {
scanner := bufio.NewScanner(pr)
curPkg := ""
canInline := regexp.MustCompile(`: can inline ([^ ]*)`)
haveInlined := regexp.MustCompile(`: inlining call to ([^ ]*)`)
cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`)
for scanner.Scan() {
line := scanner.Text()
@ -192,11 +208,20 @@ func TestIntendedInlining(t *testing.T) {
curPkg = line[2:]
continue
}
if m := canInline.FindStringSubmatch(line); m != nil {
if m := haveInlined.FindStringSubmatch(line); m != nil {
fname := m[1]
delete(notInlinedReason, curPkg+"."+fname)
continue
}
if m := canInline.FindStringSubmatch(line); m != nil {
fname := m[1]
fullname := curPkg + "." + fname
// If function must be inlined somewhere, beeing inlinable is not enough
if _, ok := must[fullname]; !ok {
delete(notInlinedReason, fullname)
continue
}
}
if m := cannotInline.FindStringSubmatch(line); m != nil {
fname, reason := m[1], m[2]
fullName := curPkg + "." + fname

View file

@ -207,7 +207,6 @@ func Main(archInit func(*Arch)) {
objabi.Flagcount("e", "no limit on number of errors reported", &Debug['e'])
objabi.Flagcount("f", "debug stack frames", &Debug['f'])
objabi.Flagcount("h", "halt on error", &Debug['h'])
objabi.Flagcount("i", "debug line number stack", &Debug['i'])
objabi.Flagfn1("importmap", "add `definition` of the form source=actual to import map", addImportMap)
objabi.Flagfn1("importcfg", "read import configuration from `file`", readImportCfg)
flag.StringVar(&flag_installsuffix, "installsuffix", "", "set pkg directory `suffix`")
@ -219,7 +218,6 @@ func Main(archInit func(*Arch)) {
if sys.MSanSupported(objabi.GOOS, objabi.GOARCH) {
flag.BoolVar(&flag_msan, "msan", false, "build code compatible with C/C++ memory sanitizer")
}
flag.BoolVar(&dolinkobj, "dolinkobj", true, "generate linker-specific objects; if false, some invalid code may compile")
flag.BoolVar(&nolocalimports, "nolocalimports", false, "reject local (relative) imports")
flag.StringVar(&outfile, "o", "", "write output to `file`")
flag.StringVar(&myimportpath, "p", "", "set expected package import `path`")
@ -230,7 +228,6 @@ func Main(archInit func(*Arch)) {
}
objabi.Flagcount("s", "warn about composite literals that can be simplified", &Debug['s'])
flag.StringVar(&pathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths")
flag.BoolVar(&safemode, "u", false, "reject unsafe code")
flag.BoolVar(&Debug_vlog, "v", false, "increase debug verbosity")
objabi.Flagcount("w", "debug type checking", &Debug['w'])
flag.BoolVar(&use_writebarrier, "wb", true, "enable write barrier")
@ -535,7 +532,9 @@ func Main(archInit func(*Arch)) {
fcount++
}
}
// With all types ckecked, it's now safe to verify map keys.
// With all types ckecked, it's now safe to verify map keys. One single
// check past phase 9 isn't sufficient, as we may exit with other errors
// before then, thus skipping map key errors.
checkMapKeys()
timings.AddEvent(fcount, "funcs")
@ -605,71 +604,69 @@ func Main(archInit func(*Arch)) {
timings.Start("fe", "escapes")
escapes(xtop)
if dolinkobj {
// Collect information for go:nowritebarrierrec
// checking. This must happen before transformclosure.
// We'll do the final check after write barriers are
// inserted.
if compiling_runtime {
nowritebarrierrecCheck = newNowritebarrierrecChecker()
// Collect information for go:nowritebarrierrec
// checking. This must happen before transformclosure.
// We'll do the final check after write barriers are
// inserted.
if compiling_runtime {
nowritebarrierrecCheck = newNowritebarrierrecChecker()
}
// Phase 7: Transform closure bodies to properly reference captured variables.
// This needs to happen before walk, because closures must be transformed
// before walk reaches a call of a closure.
timings.Start("fe", "xclosures")
for _, n := range xtop {
if n.Op == ODCLFUNC && n.Func.Closure != nil {
Curfn = n
transformclosure(n)
}
}
// Phase 7: Transform closure bodies to properly reference captured variables.
// This needs to happen before walk, because closures must be transformed
// before walk reaches a call of a closure.
timings.Start("fe", "xclosures")
for _, n := range xtop {
if n.Op == ODCLFUNC && n.Func.Closure != nil {
Curfn = n
transformclosure(n)
}
// Prepare for SSA compilation.
// This must be before peekitabs, because peekitabs
// can trigger function compilation.
initssaconfig()
// Just before compilation, compile itabs found on
// the right side of OCONVIFACE so that methods
// can be de-virtualized during compilation.
Curfn = nil
peekitabs()
// Phase 8: Compile top level functions.
// Don't use range--walk can add functions to xtop.
timings.Start("be", "compilefuncs")
fcount = 0
for i := 0; i < len(xtop); i++ {
n := xtop[i]
if n.Op == ODCLFUNC {
funccompile(n)
fcount++
}
}
timings.AddEvent(fcount, "funcs")
// Prepare for SSA compilation.
// This must be before peekitabs, because peekitabs
// can trigger function compilation.
initssaconfig()
if nsavederrors+nerrors == 0 {
fninit(xtop)
}
// Just before compilation, compile itabs found on
// the right side of OCONVIFACE so that methods
// can be de-virtualized during compilation.
Curfn = nil
peekitabs()
compileFunctions()
// Phase 8: Compile top level functions.
// Don't use range--walk can add functions to xtop.
timings.Start("be", "compilefuncs")
fcount = 0
for i := 0; i < len(xtop); i++ {
n := xtop[i]
if n.Op == ODCLFUNC {
funccompile(n)
fcount++
}
}
timings.AddEvent(fcount, "funcs")
if nowritebarrierrecCheck != nil {
// Write barriers are now known. Check the
// call graph.
nowritebarrierrecCheck.check()
nowritebarrierrecCheck = nil
}
if nsavederrors+nerrors == 0 {
fninit(xtop)
}
compileFunctions()
if nowritebarrierrecCheck != nil {
// Write barriers are now known. Check the
// call graph.
nowritebarrierrecCheck.check()
nowritebarrierrecCheck = nil
}
// Finalize DWARF inline routine DIEs, then explicitly turn off
// DWARF inlining gen so as to avoid problems with generated
// method wrappers.
if Ctxt.DwFixups != nil {
Ctxt.DwFixups.Finalize(myimportpath, Debug_gendwarfinl != 0)
Ctxt.DwFixups = nil
genDwarfInline = 0
}
// Finalize DWARF inline routine DIEs, then explicitly turn off
// DWARF inlining gen so as to avoid problems with generated
// method wrappers.
if Ctxt.DwFixups != nil {
Ctxt.DwFixups.Finalize(myimportpath, Debug_gendwarfinl != 0)
Ctxt.DwFixups = nil
genDwarfInline = 0
}
// Phase 9: Check external declarations.
@ -679,6 +676,9 @@ func Main(archInit func(*Arch)) {
externdcl[i] = typecheck(externdcl[i], Erv)
}
}
// Check the map keys again, since we typechecked the external
// declarations.
checkMapKeys()
if nerrors+nsavederrors != 0 {
errorexit()
@ -839,7 +839,7 @@ func islocalname(name string) bool {
func findpkg(name string) (file string, ok bool) {
if islocalname(name) {
if safemode || nolocalimports {
if nolocalimports {
return "", false
}
@ -981,11 +981,6 @@ func importfile(f *Val) *types.Pkg {
}
if path_ == "unsafe" {
if safemode {
yyerror("cannot import package unsafe")
errorexit()
}
imported_unsafe = true
return unsafepkg
}
@ -1059,7 +1054,6 @@ func importfile(f *Val) *types.Pkg {
}
// process header lines
safe := false
for {
p, err = imp.ReadString('\n')
if err != nil {
@ -1069,13 +1063,6 @@ func importfile(f *Val) *types.Pkg {
if p == "\n" {
break // header ends with blank line
}
if strings.HasPrefix(p, "safe") {
safe = true
break // ok to ignore rest
}
}
if safemode && !safe {
yyerror("cannot import unsafe package %q", importpkg.Path)
}
// assume files move (get installed) so don't record the full path

View file

@ -43,10 +43,6 @@ const (
)
func dumpobj() {
if !dolinkobj {
dumpobj1(outfile, modeCompilerObj)
return
}
if linkobj == "" {
dumpobj1(outfile, modeCompilerObj|modeLinkerObj)
return
@ -85,12 +81,7 @@ func printObjHeader(bout *bio.Writer) {
if localpkg.Name == "main" {
fmt.Fprintf(bout, "main\n")
}
if safemode {
fmt.Fprintf(bout, "safe\n")
} else {
fmt.Fprintf(bout, "----\n") // room for some other tool to write "safe"
}
fmt.Fprintf(bout, "\n") // header ends with blank line
fmt.Fprintf(bout, "\n") // header ends with blank line
}
func startArchiveEntry(bout *bio.Writer) int64 {

View file

@ -4,9 +4,9 @@ package gc
import "strconv"
const _Op_name = "XXXNAMENONAMETYPEPACKLITERALADDSUBORXORADDSTRADDRANDANDAPPENDARRAYBYTESTRARRAYBYTESTRTMPARRAYRUNESTRSTRARRAYBYTESTRARRAYBYTETMPSTRARRAYRUNEASAS2AS2FUNCAS2RECVAS2MAPRAS2DOTTYPEASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECMPIFACECMPSTRCOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLFIELDDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTINDINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMULDIVMODLSHRSHANDANDNOTNEWNOTCOMPLUSMINUSORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRRECOVERRECVRUNESTRSELRECVSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFBLOCKBREAKCASEXCASECONTINUEDEFEREMPTYFALLFORFORUNTILGOTOIFLABELPROCRANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYDDDDDDARGINLCALLEFACEITABIDATASPTRCLOSUREVARCFUNCCHECKNILVARDEFVARKILLVARLIVEINDREGSPRETJMPGETGEND"
const _Op_name = "XXXNAMENONAMETYPEPACKLITERALADDSUBORXORADDSTRADDRANDANDAPPENDARRAYBYTESTRARRAYBYTESTRTMPARRAYRUNESTRSTRARRAYBYTESTRARRAYBYTETMPSTRARRAYRUNEASAS2AS2FUNCAS2RECVAS2MAPRAS2DOTTYPEASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLFIELDDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTINDINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMULDIVMODLSHRSHANDANDNOTNEWNOTCOMPLUSMINUSORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRRECOVERRECVRUNESTRSELRECVSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFBLOCKBREAKCASEXCASECONTINUEDEFEREMPTYFALLFORFORUNTILGOTOIFLABELPROCRANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYDDDDDDARGINLCALLEFACEITABIDATASPTRCLOSUREVARCFUNCCHECKNILVARDEFVARKILLVARLIVEINDREGSPRETJMPGETGEND"
var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 36, 39, 45, 49, 55, 61, 73, 88, 100, 112, 127, 139, 141, 144, 151, 158, 165, 175, 179, 183, 191, 199, 208, 216, 219, 224, 231, 239, 245, 252, 258, 267, 275, 283, 289, 293, 302, 309, 313, 316, 323, 331, 339, 346, 352, 355, 361, 368, 376, 380, 387, 395, 397, 399, 401, 403, 405, 407, 410, 415, 423, 426, 435, 438, 442, 450, 457, 466, 469, 472, 475, 478, 481, 484, 490, 493, 496, 499, 503, 508, 512, 517, 522, 528, 533, 537, 542, 550, 558, 564, 573, 580, 584, 591, 598, 606, 610, 614, 618, 625, 632, 640, 646, 651, 656, 660, 665, 673, 678, 683, 687, 690, 698, 702, 704, 709, 713, 718, 724, 730, 736, 742, 747, 751, 758, 764, 769, 775, 778, 784, 791, 796, 800, 805, 809, 819, 824, 832, 838, 845, 852, 860, 866, 870, 873}
var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 36, 39, 45, 49, 55, 61, 73, 88, 100, 112, 127, 139, 141, 144, 151, 158, 165, 175, 179, 183, 191, 199, 208, 216, 219, 224, 231, 238, 244, 253, 261, 269, 275, 279, 288, 295, 299, 302, 309, 317, 325, 332, 338, 341, 347, 354, 362, 366, 373, 381, 383, 385, 387, 389, 391, 393, 396, 401, 409, 412, 421, 424, 428, 436, 443, 452, 455, 458, 461, 464, 467, 470, 476, 479, 482, 485, 489, 494, 498, 503, 508, 514, 519, 523, 528, 536, 544, 550, 559, 566, 570, 577, 584, 592, 596, 600, 604, 611, 618, 626, 632, 637, 642, 646, 651, 659, 664, 669, 673, 676, 684, 688, 690, 695, 699, 704, 710, 716, 722, 728, 733, 737, 744, 750, 755, 761, 764, 770, 777, 782, 786, 791, 795, 805, 810, 818, 824, 831, 838, 846, 852, 856, 859}
func (i Op) String() string {
if i >= Op(len(_Op_index)-1) {

View file

@ -42,8 +42,9 @@ import (
// Order holds state during the ordering process.
type Order struct {
out []*Node // list of generated statements
temp []*Node // stack of temporary variables
out []*Node // list of generated statements
temp []*Node // stack of temporary variables
free map[string][]*Node // free list of unused temporaries, by type.LongString().
}
// Order rewrites fn.Nbody to apply the ordering constraints
@ -54,14 +55,30 @@ func order(fn *Node) {
dumplist(s, fn.Nbody)
}
orderBlock(&fn.Nbody)
orderBlock(&fn.Nbody, map[string][]*Node{})
}
// newTemp allocates a new temporary with the given type,
// pushes it onto the temp stack, and returns it.
// If clear is true, newTemp emits code to zero the temporary.
func (o *Order) newTemp(t *types.Type, clear bool) *Node {
v := temp(t)
var v *Node
// Note: LongString is close to the type equality we want,
// but not exactly. We still need to double-check with eqtype.
key := t.LongString()
a := o.free[key]
for i, n := range a {
if types.Identical(t, n.Type) {
v = a[i]
a[i] = a[len(a)-1]
a = a[:len(a)-1]
o.free[key] = a
break
}
}
if v == nil {
v = temp(t)
}
if clear {
a := nod(OAS, v, nil)
a = typecheck(a, Etop)
@ -216,6 +233,45 @@ func (o *Order) mapKeyTemp(t *types.Type, n *Node) *Node {
return n
}
// mapKeyReplaceStrConv replaces OARRAYBYTESTR by OARRAYBYTESTRTMP
// in n to avoid string allocations for keys in map lookups.
// Returns a bool that signals if a modification was made.
//
// For:
// x = m[string(k)]
// x = m[T1{... Tn{..., string(k), ...}]
// where k is []byte, T1 to Tn is a nesting of struct and array literals,
// the allocation of backing bytes for the string can be avoided
// by reusing the []byte backing array. These are special cases
// for avoiding allocations when converting byte slices to strings.
// It would be nice to handle these generally, but because
// []byte keys are not allowed in maps, the use of string(k)
// comes up in important cases in practice. See issue 3512.
func mapKeyReplaceStrConv(n *Node) bool {
var replaced bool
switch n.Op {
case OARRAYBYTESTR:
n.Op = OARRAYBYTESTRTMP
replaced = true
case OSTRUCTLIT:
for _, elem := range n.List.Slice() {
if mapKeyReplaceStrConv(elem.Left) {
replaced = true
}
}
case OARRAYLIT:
for _, elem := range n.List.Slice() {
if elem.Op == OKEY {
elem = elem.Right
}
if mapKeyReplaceStrConv(elem) {
replaced = true
}
}
}
return replaced
}
type ordermarker int
// Marktemp returns the top of the temporary variable stack.
@ -226,6 +282,10 @@ func (o *Order) markTemp() ordermarker {
// Poptemp pops temporaries off the stack until reaching the mark,
// which must have been returned by marktemp.
func (o *Order) popTemp(mark ordermarker) {
for _, n := range o.temp[mark:] {
key := n.Type.LongString()
o.free[key] = append(o.free[key], n)
}
o.temp = o.temp[:mark]
}
@ -266,8 +326,10 @@ func (o *Order) stmtList(l Nodes) {
// orderBlock orders the block of statements in n into a new slice,
// and then replaces the old slice in n with the new slice.
func orderBlock(n *Nodes) {
// free is a map that can be used to obtain temporary variables by type.
func orderBlock(n *Nodes, free map[string][]*Node) {
var order Order
order.free = free
mark := order.markTemp()
order.stmtList(*n)
order.cleanTemp(mark)
@ -280,6 +342,7 @@ func orderBlock(n *Nodes) {
// n.Left = o.exprInPlace(n.Left)
func (o *Order) exprInPlace(n *Node) *Node {
var order Order
order.free = o.free
n = order.expr(n, nil)
n = addinit(n, order.out)
@ -293,8 +356,10 @@ func (o *Order) exprInPlace(n *Node) *Node {
// and replaces it with the resulting statement list.
// The result of orderStmtInPlace MUST be assigned back to n, e.g.
// n.Left = orderStmtInPlace(n.Left)
func orderStmtInPlace(n *Node) *Node {
// free is a map that can be used to obtain temporary variables by type.
func orderStmtInPlace(n *Node, free map[string][]*Node) *Node {
var order Order
order.free = free
mark := order.markTemp()
order.stmt(n)
order.cleanTemp(mark)
@ -341,11 +406,13 @@ func (o *Order) copyRet(n *Node) []*Node {
Fatalf("copyret %v %d", n.Type, n.Left.Type.NumResults())
}
var l1, l2 []*Node
for _, f := range n.Type.Fields().Slice() {
tmp := temp(f.Type)
l1 = append(l1, tmp)
l2 = append(l2, tmp)
slice := n.Type.Fields().Slice()
l1 := make([]*Node, len(slice))
l2 := make([]*Node, len(slice))
for i, t := range slice {
tmp := temp(t.Type)
l1[i] = tmp
l2[i] = tmp
}
as := nod(OAS2, nil, nil)
@ -554,10 +621,9 @@ func (o *Order) stmt(n *Node) {
r.Left = o.expr(r.Left, nil)
r.Right = o.expr(r.Right, nil)
// See case OINDEXMAP below.
if r.Right.Op == OARRAYBYTESTR {
r.Right.Op = OARRAYBYTESTRTMP
}
// See similar conversion for OINDEXMAP below.
_ = mapKeyReplaceStrConv(r.Right)
r.Right = o.mapKeyTemp(r.Left.Type, r.Right)
o.okAs2(n)
o.cleanTemp(t)
@ -643,8 +709,8 @@ func (o *Order) stmt(n *Node) {
t := o.markTemp()
n.Left = o.exprInPlace(n.Left)
n.Nbody.Prepend(o.cleanTempNoPop(t)...)
orderBlock(&n.Nbody)
n.Right = orderStmtInPlace(n.Right)
orderBlock(&n.Nbody, o.free)
n.Right = orderStmtInPlace(n.Right, o.free)
o.out = append(o.out, n)
o.cleanTemp(t)
@ -656,8 +722,8 @@ func (o *Order) stmt(n *Node) {
n.Nbody.Prepend(o.cleanTempNoPop(t)...)
n.Rlist.Prepend(o.cleanTempNoPop(t)...)
o.popTemp(t)
orderBlock(&n.Nbody)
orderBlock(&n.Rlist)
orderBlock(&n.Nbody, o.free)
orderBlock(&n.Rlist, o.free)
o.out = append(o.out, n)
// Special: argument will be converted to interface using convT2E
@ -739,7 +805,7 @@ func (o *Order) stmt(n *Node) {
}
o.exprListInPlace(n.List)
if orderBody {
orderBlock(&n.Nbody)
orderBlock(&n.Nbody, o.free)
}
o.out = append(o.out, n)
o.cleanTemp(t)
@ -857,7 +923,7 @@ func (o *Order) stmt(n *Node) {
tmp2 = typecheck(tmp2, Etop)
n2.Ninit.Append(tmp2)
}
orderBlock(&n2.Ninit)
orderBlock(&n2.Ninit, o.free)
case OSEND:
if r.Ninit.Len() != 0 {
@ -882,7 +948,7 @@ func (o *Order) stmt(n *Node) {
// Also insert any ninit queued during the previous loop.
// (The temporary cleaning must follow that ninit work.)
for _, n3 := range n.List.Slice() {
orderBlock(&n3.Nbody)
orderBlock(&n3.Nbody, o.free)
n3.Nbody.Prepend(o.cleanTempNoPop(t)...)
// TODO(mdempsky): Is this actually necessary?
@ -924,7 +990,7 @@ func (o *Order) stmt(n *Node) {
Fatalf("order switch case %v", ncas.Op)
}
o.exprListInPlace(ncas.List)
orderBlock(&ncas.Nbody)
orderBlock(&ncas.Nbody, o.free)
}
o.out = append(o.out, n)
@ -1010,45 +1076,24 @@ func (o *Order) expr(n, lhs *Node) *Node {
}
}
case OCMPSTR:
n.Left = o.expr(n.Left, nil)
n.Right = o.expr(n.Right, nil)
// Mark string(byteSlice) arguments to reuse byteSlice backing
// buffer during conversion. String comparison does not
// memorize the strings for later use, so it is safe.
if n.Left.Op == OARRAYBYTESTR {
n.Left.Op = OARRAYBYTESTRTMP
}
if n.Right.Op == OARRAYBYTESTR {
n.Right.Op = OARRAYBYTESTRTMP
}
// key must be addressable
case OINDEXMAP:
n.Left = o.expr(n.Left, nil)
n.Right = o.expr(n.Right, nil)
needCopy := false
if !n.IndexMapLValue() && instrumenting {
// Race detector needs the copy so it can
// call treecopy on the result.
needCopy = true
}
if !n.IndexMapLValue() {
// Enforce that any []byte slices we are not copying
// can not be changed before the map index by forcing
// the map index to happen immediately following the
// conversions. See copyExpr a few lines below.
needCopy = mapKeyReplaceStrConv(n.Right)
// For x = m[string(k)] where k is []byte, the allocation of
// backing bytes for the string can be avoided by reusing
// the []byte backing array. This is a special case that it
// would be nice to handle more generally, but because
// there are no []byte-keyed maps, this specific case comes
// up in important cases in practice. See issue 3512.
// Nothing can change the []byte we are not copying before
// the map index, because the map access is going to
// be forced to happen immediately following this
// conversion (by the ordercopyexpr a few lines below).
if !n.IndexMapLValue() && n.Right.Op == OARRAYBYTESTR {
n.Right.Op = OARRAYBYTESTRTMP
needCopy = true
if instrumenting {
// Race detector needs the copy so it can
// call treecopy on the result.
needCopy = true
}
}
n.Right = o.mapKeyTemp(n.Left.Type, n.Right)
@ -1056,12 +1101,17 @@ func (o *Order) expr(n, lhs *Node) *Node {
n = o.copyExpr(n, n.Type, false)
}
// concrete type (not interface) argument must be addressable
// temporary to pass to runtime.
// concrete type (not interface) argument might need an addressable
// temporary to pass to the runtime conversion routine.
case OCONVIFACE:
n.Left = o.expr(n.Left, nil)
if !n.Left.Type.IsInterface() {
if n.Left.Type.IsInterface() {
break
}
if _, needsaddr := convFuncName(n.Left.Type, n.Type); needsaddr || consttype(n.Left) > 0 {
// Need a temp if we need to pass the address to the conversion function.
// We also process constants here, making a named static global whose
// address we can put directly in an interface (see OCONVIFACE case in walk).
n.Left = o.addrTemp(n.Left)
}
@ -1147,16 +1197,23 @@ func (o *Order) expr(n, lhs *Node) *Node {
case OCLOSURE:
if n.Noescape() && n.Func.Closure.Func.Cvars.Len() > 0 {
prealloc[n] = o.newTemp(types.Types[TUINT8], false) // walk will fill in correct type
prealloc[n] = o.newTemp(closureType(n), false)
}
case OARRAYLIT, OSLICELIT, OCALLPART:
case OSLICELIT, OCALLPART:
n.Left = o.expr(n.Left, nil)
n.Right = o.expr(n.Right, nil)
o.exprList(n.List)
o.exprList(n.Rlist)
if n.Noescape() {
prealloc[n] = o.newTemp(types.Types[TUINT8], false) // walk will fill in correct type
var t *types.Type
switch n.Op {
case OSLICELIT:
t = types.NewArray(n.Type.Elem(), n.Right.Int64())
case OCALLPART:
t = partialCallType(n)
}
prealloc[n] = o.newTemp(t, false)
}
case ODDDARG:
@ -1181,11 +1238,24 @@ func (o *Order) expr(n, lhs *Node) *Node {
n.Left = o.expr(n.Left, nil)
n = o.copyExpr(n, n.Type, true)
case OEQ, ONE:
case OEQ, ONE, OLT, OLE, OGT, OGE:
n.Left = o.expr(n.Left, nil)
n.Right = o.expr(n.Right, nil)
t := n.Left.Type
if t.IsStruct() || t.IsArray() {
switch {
case t.IsString():
// Mark string(byteSlice) arguments to reuse byteSlice backing
// buffer during conversion. String comparison does not
// memorize the strings for later use, so it is safe.
if n.Left.Op == OARRAYBYTESTR {
n.Left.Op = OARRAYBYTESTRTMP
}
if n.Right.Op == OARRAYBYTESTR {
n.Right.Op = OARRAYBYTESTRTMP
}
case t.IsStruct() || t.IsArray():
// for complex comparisons, we need both args to be
// addressable so we can pass them to the runtime.
n.Left = o.addrTemp(n.Left)
@ -1217,9 +1287,10 @@ func okas(ok, val *Node) *Node {
func (o *Order) as2(n *Node) {
tmplist := []*Node{}
left := []*Node{}
for _, l := range n.List.Slice() {
for ni, l := range n.List.Slice() {
if !l.isBlank() {
tmp := o.newTemp(l.Type, types.Haspointers(l.Type))
n.List.SetIndex(ni, tmp)
tmplist = append(tmplist, tmp)
left = append(left, l)
}
@ -1232,14 +1303,6 @@ func (o *Order) as2(n *Node) {
as.Rlist.Set(tmplist)
as = typecheck(as, Etop)
o.stmt(as)
ti := 0
for ni, l := range n.List.Slice() {
if !l.isBlank() {
n.List.SetIndex(ni, tmplist[ti])
ti++
}
}
}
// okAs2 orders OAS2 with ok.

View file

@ -20,7 +20,7 @@ func typeWithoutPointers() *types.Type {
func typeWithPointers() *types.Type {
t := types.New(TSTRUCT)
f := &types.Field{Type: types.New(TPTR64)}
f := &types.Field{Type: types.New(TPTR)}
t.SetFields([]*types.Field{f})
return t
}

View file

@ -534,7 +534,7 @@ func onebitwalktype1(t *types.Type, off int64, bv bvec) {
TINT, TUINT, TUINTPTR, TBOOL,
TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128:
case TPTR32, TPTR64, TUNSAFEPTR, TFUNC, TCHAN, TMAP:
case TPTR, TUNSAFEPTR, TFUNC, TCHAN, TMAP:
if off&int64(Widthptr-1) != 0 {
Fatalf("onebitwalktype1: invalid alignment, %v", t)
}
@ -1163,8 +1163,7 @@ func clobberWalk(b *ssa.Block, v *Node, offset int64, t *types.Type) {
return
}
switch t.Etype {
case TPTR32,
TPTR64,
case TPTR,
TUNSAFEPTR,
TFUNC,
TCHAN,

View file

@ -32,7 +32,15 @@ import (
// Do not instrument the following packages at all,
// at best instrumentation would cause infinite recursion.
var omit_pkgs = []string{"runtime/internal/atomic", "runtime/internal/sys", "runtime", "runtime/race", "runtime/msan", "internal/cpu"}
var omit_pkgs = []string{
"runtime/internal/atomic",
"runtime/internal/sys",
"runtime/internal/math",
"runtime",
"runtime/race",
"runtime/msan",
"internal/cpu",
}
// Only insert racefuncenterfp/racefuncexit into the following packages.
// Memory accesses in the packages are either uninteresting or will cause false positives.

View file

@ -286,13 +286,7 @@ func walkrange(n *Node) *Node {
// This runs *after* the condition check, so we know
// advancing the pointer is safe and won't go past the
// end of the allocation.
tmp = nod(OADD, hp, nodintconst(t.Elem().Width))
tmp.Type = hp.Type
tmp.SetTypecheck(1)
tmp.Right.Type = types.Types[types.Tptr]
tmp.Right.SetTypecheck(1)
a = nod(OAS, hp, tmp)
a = nod(OAS, hp, addptr(hp, t.Elem().Width))
a = typecheck(a, Etop)
n.List.Set1(a)
@ -613,3 +607,18 @@ func arrayClear(n, v1, v2, a *Node) bool {
n = walkstmt(n)
return true
}
// addptr returns (*T)(uintptr(p) + n).
func addptr(p *Node, n int64) *Node {
t := p.Type
p = nod(OCONVNOP, p, nil)
p.Type = types.Types[TUINTPTR]
p = nod(OADD, p, nodintconst(n))
p = nod(OCONVNOP, p, nil)
p.Type = t
return p
}

View file

@ -320,7 +320,12 @@ func hiter(t *types.Type) *types.Type {
// f is method type, with receiver.
// return function type, receiver as first argument (or not).
func methodfunc(f *types.Type, receiver *types.Type) *types.Type {
var in []*Node
inLen := f.Params().Fields().Len()
if receiver != nil {
inLen++
}
in := make([]*Node, 0, inLen)
if receiver != nil {
d := anonfield(receiver)
in = append(in, d)
@ -332,7 +337,8 @@ func methodfunc(f *types.Type, receiver *types.Type) *types.Type {
in = append(in, d)
}
var out []*Node
outLen := f.Results().Fields().Len()
out := make([]*Node, 0, outLen)
for _, t := range f.Results().Fields().Slice() {
d := anonfield(t.Type)
out = append(out, d)
@ -405,19 +411,15 @@ func methods(t *types.Type) []*Sig {
if !sig.isym.Siggen() {
sig.isym.SetSiggen(true)
if !eqtype(this, it) {
compiling_wrappers = true
if !types.Identical(this, it) {
genwrapper(it, f, sig.isym)
compiling_wrappers = false
}
}
if !sig.tsym.Siggen() {
sig.tsym.SetSiggen(true)
if !eqtype(this, t) {
compiling_wrappers = true
if !types.Identical(this, t) {
genwrapper(t, f, sig.tsym)
compiling_wrappers = false
}
}
}
@ -656,7 +658,7 @@ func typePkg(t *types.Type) *types.Pkg {
tsym := t.Sym
if tsym == nil {
switch t.Etype {
case TARRAY, TSLICE, TPTR32, TPTR64, TCHAN:
case TARRAY, TSLICE, TPTR, TCHAN:
if t.Elem() != nil {
tsym = t.Elem().Sym
}
@ -714,8 +716,7 @@ var kinds = []int{
TFLOAT64: objabi.KindFloat64,
TBOOL: objabi.KindBool,
TSTRING: objabi.KindString,
TPTR32: objabi.KindPtr,
TPTR64: objabi.KindPtr,
TPTR: objabi.KindPtr,
TSTRUCT: objabi.KindStruct,
TINTER: objabi.KindInterface,
TCHAN: objabi.KindChan,
@ -736,8 +737,7 @@ func typeptrdata(t *types.Type) int64 {
}
switch t.Etype {
case TPTR32,
TPTR64,
case TPTR,
TUNSAFEPTR,
TFUNC,
TCHAN,
@ -1035,8 +1035,7 @@ func isreflexive(t *types.Type) bool {
TINT64,
TUINT64,
TUINTPTR,
TPTR32,
TPTR64,
TPTR,
TUNSAFEPTR,
TSTRING,
TCHAN:
@ -1071,7 +1070,7 @@ func isreflexive(t *types.Type) bool {
func needkeyupdate(t *types.Type) bool {
switch t.Etype {
case TBOOL, TINT, TUINT, TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32,
TINT64, TUINT64, TUINTPTR, TPTR32, TPTR64, TUNSAFEPTR, TCHAN:
TINT64, TUINT64, TUINTPTR, TPTR, TUNSAFEPTR, TCHAN:
return false
case TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128, // floats and complex can be +0/-0
@ -1279,7 +1278,7 @@ func dtypesym(t *types.Type) *obj.LSym {
ot = duint8(lsym, ot, uint8(obj.Bool2int(needkeyupdate(t.Key()))))
ot = dextratype(lsym, ot, t, 0)
case TPTR32, TPTR64:
case TPTR:
if t.Elem().Etype == TANY {
// ../../../../runtime/type.go:/UnsafePointerType
ot = dcommontype(lsym, t)
@ -1383,7 +1382,7 @@ func dtypesym(t *types.Type) *obj.LSym {
// functions must return the existing type structure rather
// than creating a new one.
switch t.Etype {
case TPTR32, TPTR64, TARRAY, TCHAN, TFUNC, TMAP, TSLICE, TSTRUCT:
case TPTR, TARRAY, TCHAN, TFUNC, TMAP, TSLICE, TSTRUCT:
keep = true
}
}

View file

@ -288,7 +288,7 @@ func staticcopy(l *Node, r *Node, out *[]*Node) bool {
orig := r
r = r.Name.Defn.Right
for r.Op == OCONVNOP && !eqtype(r.Type, l.Type) {
for r.Op == OCONVNOP && !types.Identical(r.Type, l.Type) {
r = r.Left
}
@ -751,7 +751,7 @@ func fixedlit(ctxt initContext, kind initKind, n *Node, var_ *Node, init *Nodes)
case initKindStatic:
genAsStatic(a)
case initKindDynamic, initKindLocalCode:
a = orderStmtInPlace(a)
a = orderStmtInPlace(a, map[string][]*Node{})
a = walkstmt(a)
init.Append(a)
default:
@ -833,7 +833,9 @@ func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) {
var a *Node
if x := prealloc[n]; x != nil {
// temp allocated during order.go for dddarg
x.Type = t
if !types.Identical(t, x.Type) {
panic("dotdotdot base type does not match order's assigned type")
}
if vstat == nil {
a = nod(OAS, x, nil)
@ -909,7 +911,7 @@ func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) {
a = nod(OAS, a, value)
a = typecheck(a, Etop)
a = orderStmtInPlace(a)
a = orderStmtInPlace(a, map[string][]*Node{})
a = walkstmt(a)
init.Append(a)
}
@ -918,7 +920,7 @@ func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) {
a = nod(OAS, var_, nod(OSLICE, vauto, nil))
a = typecheck(a, Etop)
a = orderStmtInPlace(a)
a = orderStmtInPlace(a, map[string][]*Node{})
a = walkstmt(a)
init.Append(a)
}
@ -1152,7 +1154,7 @@ func oaslit(n *Node, init *Nodes) bool {
// not a special composite literal assignment
return false
}
if !eqtype(n.Left.Type, n.Right.Type) {
if !types.Identical(n.Left.Type, n.Right.Type) {
// not a special composite literal assignment
return false
}

View file

@ -147,6 +147,7 @@ func buildssa(fn *Node, worker int) *ssa.Func {
s.f.Cache.Reset()
s.f.DebugTest = s.f.DebugHashMatch("GOSSAHASH", name)
s.f.Name = name
s.f.PrintOrHtmlSSA = printssa
if fn.Func.Pragma&Nosplit != 0 {
s.f.NoSplit = true
}
@ -201,7 +202,9 @@ func buildssa(fn *Node, worker int) *ssa.Func {
// Populate SSAable arguments.
for _, n := range fn.Func.Dcl {
if n.Class() == PPARAM && s.canSSA(n) {
s.vars[n] = s.newValue0A(ssa.OpArg, n.Type, n)
v := s.newValue0A(ssa.OpArg, n.Type, n)
s.vars[n] = v
s.addNamedValue(n, v) // This helps with debugging information, not needed for compilation itself.
}
}
@ -1247,10 +1250,8 @@ var opToSSA = map[opAndType]ssa.Op{
opAndType{OADD, TUINT16}: ssa.OpAdd16,
opAndType{OADD, TINT32}: ssa.OpAdd32,
opAndType{OADD, TUINT32}: ssa.OpAdd32,
opAndType{OADD, TPTR32}: ssa.OpAdd32,
opAndType{OADD, TINT64}: ssa.OpAdd64,
opAndType{OADD, TUINT64}: ssa.OpAdd64,
opAndType{OADD, TPTR64}: ssa.OpAdd64,
opAndType{OADD, TFLOAT32}: ssa.OpAdd32F,
opAndType{OADD, TFLOAT64}: ssa.OpAdd64F,
@ -1365,8 +1366,7 @@ var opToSSA = map[opAndType]ssa.Op{
opAndType{OEQ, TFUNC}: ssa.OpEqPtr,
opAndType{OEQ, TMAP}: ssa.OpEqPtr,
opAndType{OEQ, TCHAN}: ssa.OpEqPtr,
opAndType{OEQ, TPTR32}: ssa.OpEqPtr,
opAndType{OEQ, TPTR64}: ssa.OpEqPtr,
opAndType{OEQ, TPTR}: ssa.OpEqPtr,
opAndType{OEQ, TUINTPTR}: ssa.OpEqPtr,
opAndType{OEQ, TUNSAFEPTR}: ssa.OpEqPtr,
opAndType{OEQ, TFLOAT64}: ssa.OpEq64F,
@ -1386,8 +1386,7 @@ var opToSSA = map[opAndType]ssa.Op{
opAndType{ONE, TFUNC}: ssa.OpNeqPtr,
opAndType{ONE, TMAP}: ssa.OpNeqPtr,
opAndType{ONE, TCHAN}: ssa.OpNeqPtr,
opAndType{ONE, TPTR32}: ssa.OpNeqPtr,
opAndType{ONE, TPTR64}: ssa.OpNeqPtr,
opAndType{ONE, TPTR}: ssa.OpNeqPtr,
opAndType{ONE, TUINTPTR}: ssa.OpNeqPtr,
opAndType{ONE, TUNSAFEPTR}: ssa.OpNeqPtr,
opAndType{ONE, TFLOAT64}: ssa.OpNeq64F,
@ -2335,7 +2334,7 @@ func (s *state) expr(n *Node) *ssa.Value {
if max != nil {
k = s.extendIndex(s.expr(max), panicslice)
}
p, l, c := s.slice(n.Left.Type, v, i, j, k)
p, l, c := s.slice(n.Left.Type, v, i, j, k, n.Bounded())
return s.newValue3(ssa.OpSliceMake, n.Type, p, l, c)
case OSLICESTR:
@ -2348,7 +2347,7 @@ func (s *state) expr(n *Node) *ssa.Value {
if high != nil {
j = s.extendIndex(s.expr(high), panicslice)
}
p, l, _ := s.slice(n.Left.Type, v, i, j, nil)
p, l, _ := s.slice(n.Left.Type, v, i, j, nil, n.Bounded())
return s.newValue2(ssa.OpStringMake, n.Type, p, l)
case OCALLFUNC:
@ -2863,6 +2862,7 @@ func init() {
var all []*sys.Arch
var p4 []*sys.Arch
var p8 []*sys.Arch
var lwatomics []*sys.Arch
for _, a := range sys.Archs {
all = append(all, a)
if a.PtrSize == 4 {
@ -2870,6 +2870,9 @@ func init() {
} else {
p8 = append(p8, a)
}
if a.Family != sys.PPC64 {
lwatomics = append(lwatomics, a)
}
}
// add adds the intrinsic b for pkg.fn for the given list of architectures.
@ -2916,6 +2919,14 @@ func init() {
},
all...)
}
addF("runtime/internal/math", "MulUintptr",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
if s.config.PtrSize == 4 {
return s.newValue2(ssa.OpMul32uover, types.NewTuple(types.Types[TUINT], types.Types[TUINT]), args[0], args[1])
}
return s.newValue2(ssa.OpMul64uover, types.NewTuple(types.Types[TUINT], types.Types[TUINT]), args[0], args[1])
},
sys.AMD64, sys.I386)
add("runtime", "KeepAlive",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
data := s.newValue1(ssa.OpIData, s.f.Config.Types.BytePtr, args[0])
@ -2978,6 +2989,13 @@ func init() {
return s.newValue1(ssa.OpSelect0, types.Types[TUINT64], v)
},
sys.AMD64, sys.ARM64, sys.S390X, sys.MIPS64, sys.PPC64)
addF("runtime/internal/atomic", "LoadAcq",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
v := s.newValue2(ssa.OpAtomicLoadAcq32, types.NewTuple(types.Types[TUINT32], types.TypeMem), args[0], s.mem())
s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v)
},
sys.PPC64)
addF("runtime/internal/atomic", "Loadp",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
v := s.newValue2(ssa.OpAtomicLoadPtr, types.NewTuple(s.f.Config.Types.BytePtr, types.TypeMem), args[0], s.mem())
@ -3004,6 +3022,12 @@ func init() {
return nil
},
sys.AMD64, sys.ARM64, sys.S390X, sys.MIPS, sys.MIPS64)
addF("runtime/internal/atomic", "StoreRel",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
s.vars[&memVar] = s.newValue3(ssa.OpAtomicStoreRel32, types.TypeMem, args[0], args[1], s.mem())
return nil
},
sys.PPC64)
addF("runtime/internal/atomic", "Xchg",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
@ -3091,6 +3115,13 @@ func init() {
return s.newValue1(ssa.OpSelect0, types.Types[TBOOL], v)
},
sys.AMD64, sys.ARM64, sys.S390X, sys.MIPS64, sys.PPC64)
addF("runtime/internal/atomic", "CasRel",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
v := s.newValue4(ssa.OpAtomicCompareAndSwap32, types.NewTuple(types.Types[TBOOL], types.TypeMem), args[0], args[1], args[2], s.mem())
s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
return s.newValue1(ssa.OpSelect0, types.Types[TBOOL], v)
},
sys.PPC64)
addF("runtime/internal/atomic", "And8",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
@ -3111,8 +3142,10 @@ func init() {
alias("runtime/internal/atomic", "Loaduint", "runtime/internal/atomic", "Load64", p8...)
alias("runtime/internal/atomic", "Loaduintptr", "runtime/internal/atomic", "Load", p4...)
alias("runtime/internal/atomic", "Loaduintptr", "runtime/internal/atomic", "Load64", p8...)
alias("runtime/internal/atomic", "LoadAcq", "runtime/internal/atomic", "Load", lwatomics...)
alias("runtime/internal/atomic", "Storeuintptr", "runtime/internal/atomic", "Store", p4...)
alias("runtime/internal/atomic", "Storeuintptr", "runtime/internal/atomic", "Store64", p8...)
alias("runtime/internal/atomic", "StoreRel", "runtime/internal/atomic", "Store", lwatomics...)
alias("runtime/internal/atomic", "Xchguintptr", "runtime/internal/atomic", "Xchg", p4...)
alias("runtime/internal/atomic", "Xchguintptr", "runtime/internal/atomic", "Xchg64", p8...)
alias("runtime/internal/atomic", "Xadduintptr", "runtime/internal/atomic", "Xadd", p4...)
@ -3121,6 +3154,7 @@ func init() {
alias("runtime/internal/atomic", "Casuintptr", "runtime/internal/atomic", "Cas64", p8...)
alias("runtime/internal/atomic", "Casp1", "runtime/internal/atomic", "Cas", p4...)
alias("runtime/internal/atomic", "Casp1", "runtime/internal/atomic", "Cas64", p8...)
alias("runtime/internal/atomic", "CasRel", "runtime/internal/atomic", "Cas", lwatomics...)
alias("runtime/internal/sys", "Ctz8", "math/bits", "TrailingZeros8", all...)
@ -3240,7 +3274,7 @@ func init() {
y := s.newValue2(ssa.OpOr64, types.Types[TUINT64], x, c)
return s.newValue1(ssa.OpCtz64, types.Types[TINT], y)
},
sys.ARM64, sys.S390X)
sys.ARM64, sys.S390X, sys.PPC64)
addF("math/bits", "TrailingZeros8",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
x := s.newValue1(ssa.OpZeroExt8to32, types.Types[TUINT32], args[0])
@ -3431,12 +3465,12 @@ func init() {
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
return s.newValue1(ssa.OpPopCount16, types.Types[TINT], args[0])
},
sys.ARM64, sys.S390X)
sys.ARM64, sys.S390X, sys.PPC64)
addF("math/bits", "OnesCount8",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
return s.newValue1(ssa.OpPopCount8, types.Types[TINT], args[0])
},
sys.S390X)
sys.S390X, sys.PPC64)
addF("math/bits", "OnesCount",
makeOnesCountAMD64(ssa.OpPopCount64, ssa.OpPopCount32),
sys.AMD64)
@ -3492,7 +3526,7 @@ func init() {
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
return s.newValue2(ssa.OpMul64uhilo, types.NewTuple(types.Types[TUINT64], types.Types[TUINT64]), args[0], args[1])
},
sys.ArchAMD64, sys.ArchARM64)
sys.ArchAMD64, sys.ArchARM64, sys.ArchPPC64LE, sys.ArchPPC64)
add("math/big", "divWW",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
return s.newValue3(ssa.OpDiv128u, types.NewTuple(types.Types[TUINT64], types.Types[TUINT64]), args[0], args[1], args[2])
@ -3551,59 +3585,34 @@ func (s *state) intrinsicCall(n *Node) *ssa.Value {
return v
}
type callArg struct {
offset int64
v *ssa.Value
}
type byOffset []callArg
func (x byOffset) Len() int { return len(x) }
func (x byOffset) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x byOffset) Less(i, j int) bool {
return x[i].offset < x[j].offset
}
// intrinsicArgs extracts args from n, evaluates them to SSA values, and returns them.
func (s *state) intrinsicArgs(n *Node) []*ssa.Value {
// This code is complicated because of how walk transforms calls. For a call node,
// each entry in n.List is either an assignment to OINDREGSP which actually
// stores an arg, or an assignment to a temporary which computes an arg
// which is later assigned.
// The args can also be out of order.
// TODO: when walk goes away someday, this code can go away also.
var args []callArg
// Construct map of temps; see comments in s.call about the structure of n.
temps := map[*Node]*ssa.Value{}
for _, a := range n.List.Slice() {
if a.Op != OAS {
s.Fatalf("non-assignment as a function argument %v", a.Op)
s.Fatalf("non-assignment as a temp function argument %v", a.Op)
}
l, r := a.Left, a.Right
switch l.Op {
case ONAME:
// Evaluate and store to "temporary".
// Walk ensures these temporaries are dead outside of n.
temps[l] = s.expr(r)
case OINDREGSP:
// Store a value to an argument slot.
var v *ssa.Value
if x, ok := temps[r]; ok {
// This is a previously computed temporary.
v = x
} else {
// This is an explicit value; evaluate it.
v = s.expr(r)
}
args = append(args, callArg{l.Xoffset, v})
default:
s.Fatalf("function argument assignment target not allowed: %v", l.Op)
if l.Op != ONAME {
s.Fatalf("non-ONAME temp function argument %v", a.Op)
}
// Evaluate and store to "temporary".
// Walk ensures these temporaries are dead outside of n.
temps[l] = s.expr(r)
}
sort.Sort(byOffset(args))
res := make([]*ssa.Value, len(args))
for i, a := range args {
res[i] = a.v
args := make([]*ssa.Value, n.Rlist.Len())
for i, n := range n.Rlist.Slice() {
// Store a value to an argument slot.
if x, ok := temps[n]; ok {
// This is a previously computed temporary.
args[i] = x
continue
}
// This is an explicit value; evaluate it.
args[i] = s.expr(n)
}
return res
return args
}
// Calls the function n using the specified call type.
@ -3644,7 +3653,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
n2.Pos = fn.Pos
n2.Type = types.Types[TUINT8] // dummy type for a static closure. Could use runtime.funcval if we had it.
closure = s.expr(n2)
// Note: receiver is already assigned in n.List, so we don't
// Note: receiver is already present in n.Rlist, so we don't
// want to set it here.
case OCALLINTER:
if fn.Op != ODOTINTER {
@ -3665,32 +3674,43 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
dowidth(fn.Type)
stksize := fn.Type.ArgWidth() // includes receiver
// Run all argument assignments. The arg slots have already
// been offset by the appropriate amount (+2*widthptr for go/defer,
// +widthptr for interface calls).
// For OCALLMETH, the receiver is set in these statements.
// Run all assignments of temps.
// The temps are introduced to avoid overwriting argument
// slots when arguments themselves require function calls.
s.stmtList(n.List)
// Set receiver (for interface calls)
if rcvr != nil {
argStart := Ctxt.FixedFrameSize()
if k != callNormal {
argStart += int64(2 * Widthptr)
}
addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart)
s.store(types.Types[TUINTPTR], addr, rcvr)
}
// Defer/go args
// Store arguments to stack, including defer/go arguments and receiver for method calls.
// These are written in SP-offset order.
argStart := Ctxt.FixedFrameSize()
// Defer/go args.
if k != callNormal {
// Write argsize and closure (args to newproc/deferproc).
argStart := Ctxt.FixedFrameSize()
argsize := s.constInt32(types.Types[TUINT32], int32(stksize))
addr := s.constOffPtrSP(s.f.Config.Types.UInt32Ptr, argStart)
s.store(types.Types[TUINT32], addr, argsize)
addr = s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart+int64(Widthptr))
s.store(types.Types[TUINTPTR], addr, closure)
stksize += 2 * int64(Widthptr)
argStart += 2 * int64(Widthptr)
}
// Set receiver (for interface calls).
if rcvr != nil {
addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart)
s.store(types.Types[TUINTPTR], addr, rcvr)
}
// Write args.
t := n.Left.Type
args := n.Rlist.Slice()
if n.Op == OCALLMETH {
f := t.Recv()
s.storeArg(args[0], f.Type, argStart+f.Offset)
args = args[1:]
}
for i, n := range args {
f := t.Params().Field(i)
s.storeArg(n, f.Type, argStart+f.Offset)
}
// call target
@ -4175,10 +4195,24 @@ func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) {
}
}
func (s *state) storeArg(n *Node, t *types.Type, off int64) {
pt := types.NewPtr(t)
sp := s.constOffPtrSP(pt, off)
if !canSSAType(t) {
a := s.addr(n, false)
s.move(t, sp, a)
return
}
a := s.expr(n)
s.storeType(t, sp, a, 0, false)
}
// slice computes the slice v[i:j:k] and returns ptr, len, and cap of result.
// i,j,k may be nil, in which case they are set to their default value.
// t is a slice, ptr to array, or string type.
func (s *state) slice(t *types.Type, v, i, j, k *ssa.Value) (p, l, c *ssa.Value) {
func (s *state) slice(t *types.Type, v, i, j, k *ssa.Value, bounded bool) (p, l, c *ssa.Value) {
var elemtype *types.Type
var ptrtype *types.Type
var ptr *ssa.Value
@ -4223,13 +4257,15 @@ func (s *state) slice(t *types.Type, v, i, j, k *ssa.Value) (p, l, c *ssa.Value)
k = cap
}
// Panic if slice indices are not in bounds.
s.sliceBoundsCheck(i, j)
if j != k {
s.sliceBoundsCheck(j, k)
}
if k != cap {
s.sliceBoundsCheck(k, cap)
if !bounded {
// Panic if slice indices are not in bounds.
s.sliceBoundsCheck(i, j)
if j != k {
s.sliceBoundsCheck(j, k)
}
if k != cap {
s.sliceBoundsCheck(k, cap)
}
}
// Generate the following code assuming that indexes are in bounds.
@ -5004,9 +5040,7 @@ func genssa(f *ssa.Func, pp *Progs) {
var progToValue map[*obj.Prog]*ssa.Value
var progToBlock map[*obj.Prog]*ssa.Block
var valueToProgAfter []*obj.Prog // The first Prog following computation of a value v; v is visible at this point.
var logProgs = e.log
if f.HTMLWriter != nil {
// logProgs can be false, meaning that we do not dump to the Stdout.
if f.PrintOrHtmlSSA {
progToValue = make(map[*obj.Prog]*ssa.Value, f.NumValues())
progToBlock = make(map[*obj.Prog]*ssa.Block, f.NumBlocks())
f.Logf("genssa %s\n", f.Name)
@ -5089,7 +5123,7 @@ func genssa(f *ssa.Func, pp *Progs) {
valueToProgAfter[v.ID] = s.pp.next
}
if logProgs {
if f.PrintOrHtmlSSA {
for ; x != s.pp.next; x = x.Link {
progToValue[x] = v
}
@ -5107,7 +5141,7 @@ func genssa(f *ssa.Func, pp *Progs) {
x := s.pp.next
s.SetPos(b.Pos)
thearch.SSAGenBlock(&s, b, next)
if logProgs {
if f.PrintOrHtmlSSA {
for ; x != s.pp.next; x = x.Link {
progToBlock[x] = b
}
@ -5140,7 +5174,7 @@ func genssa(f *ssa.Func, pp *Progs) {
}
}
if logProgs {
if e.log { // spew to stdout
filename := ""
for p := pp.Text; p != nil; p = p.Link {
if p.Pos.IsKnown() && p.InnermostFilename() != filename {
@ -5159,7 +5193,7 @@ func genssa(f *ssa.Func, pp *Progs) {
f.Logf(" %-6s\t%.5d (%s)\t%s\n", s, p.Pc, p.InnermostLineNumber(), p.InstructionString())
}
}
if f.HTMLWriter != nil {
if f.HTMLWriter != nil { // spew to ssa.html
var buf bytes.Buffer
buf.WriteString("<code>")
buf.WriteString("<dl class=\"ssa-gen\">")

View file

@ -529,119 +529,6 @@ func methtype(t *types.Type) *types.Type {
return nil
}
// eqtype reports whether t1 and t2 are identical, following the spec rules.
//
// Any cyclic type must go through a named type, and if one is
// named, it is only identical to the other if they are the same
// pointer (t1 == t2), so there's no chance of chasing cycles
// ad infinitum, so no need for a depth counter.
func eqtype(t1, t2 *types.Type) bool {
return eqtype1(t1, t2, true, nil)
}
// eqtypeIgnoreTags is like eqtype but it ignores struct tags for struct identity.
func eqtypeIgnoreTags(t1, t2 *types.Type) bool {
return eqtype1(t1, t2, false, nil)
}
type typePair struct {
t1 *types.Type
t2 *types.Type
}
func eqtype1(t1, t2 *types.Type, cmpTags bool, assumedEqual map[typePair]struct{}) bool {
if t1 == t2 {
return true
}
if t1 == nil || t2 == nil || t1.Etype != t2.Etype || t1.Broke() || t2.Broke() {
return false
}
if t1.Sym != nil || t2.Sym != nil {
// Special case: we keep byte/uint8 and rune/int32
// separate for error messages. Treat them as equal.
switch t1.Etype {
case TUINT8:
return (t1 == types.Types[TUINT8] || t1 == types.Bytetype) && (t2 == types.Types[TUINT8] || t2 == types.Bytetype)
case TINT32:
return (t1 == types.Types[TINT32] || t1 == types.Runetype) && (t2 == types.Types[TINT32] || t2 == types.Runetype)
default:
return false
}
}
if assumedEqual == nil {
assumedEqual = make(map[typePair]struct{})
} else if _, ok := assumedEqual[typePair{t1, t2}]; ok {
return true
}
assumedEqual[typePair{t1, t2}] = struct{}{}
switch t1.Etype {
case TINTER:
if t1.NumFields() != t2.NumFields() {
return false
}
for i, f1 := range t1.FieldSlice() {
f2 := t2.Field(i)
if f1.Sym != f2.Sym || !eqtype1(f1.Type, f2.Type, cmpTags, assumedEqual) {
return false
}
}
return true
case TSTRUCT:
if t1.NumFields() != t2.NumFields() {
return false
}
for i, f1 := range t1.FieldSlice() {
f2 := t2.Field(i)
if f1.Sym != f2.Sym || f1.Embedded != f2.Embedded || !eqtype1(f1.Type, f2.Type, cmpTags, assumedEqual) {
return false
}
if cmpTags && f1.Note != f2.Note {
return false
}
}
return true
case TFUNC:
// Check parameters and result parameters for type equality.
// We intentionally ignore receiver parameters for type
// equality, because they're never relevant.
for _, f := range types.ParamsResults {
// Loop over fields in structs, ignoring argument names.
fs1, fs2 := f(t1).FieldSlice(), f(t2).FieldSlice()
if len(fs1) != len(fs2) {
return false
}
for i, f1 := range fs1 {
f2 := fs2[i]
if f1.Isddd() != f2.Isddd() || !eqtype1(f1.Type, f2.Type, cmpTags, assumedEqual) {
return false
}
}
}
return true
case TARRAY:
if t1.NumElem() != t2.NumElem() {
return false
}
case TCHAN:
if t1.ChanDir() != t2.ChanDir() {
return false
}
case TMAP:
if !eqtype1(t1.Key(), t2.Key(), cmpTags, assumedEqual) {
return false
}
}
return eqtype1(t1.Elem(), t2.Elem(), cmpTags, assumedEqual)
}
// Are t1 and t2 equal struct types when field names are ignored?
// For deciding whether the result struct from g can be copied
// directly when compiling f(g()).
@ -655,7 +542,7 @@ func eqtypenoname(t1 *types.Type, t2 *types.Type) bool {
}
for i, f1 := range t1.FieldSlice() {
f2 := t2.Field(i)
if !eqtype(f1.Type, f2.Type) {
if !types.Identical(f1.Type, f2.Type) {
return false
}
}
@ -670,13 +557,6 @@ func assignop(src *types.Type, dst *types.Type, why *string) Op {
*why = ""
}
// TODO(rsc,lvd): This behaves poorly in the presence of inlining.
// https://golang.org/issue/2795
if safemode && !inimport && src != nil && src.Etype == TUNSAFEPTR {
yyerror("cannot use unsafe.Pointer")
errorexit()
}
if src == dst {
return OCONVNOP
}
@ -685,7 +565,7 @@ func assignop(src *types.Type, dst *types.Type, why *string) Op {
}
// 1. src type is identical to dst.
if eqtype(src, dst) {
if types.Identical(src, dst) {
return OCONVNOP
}
@ -696,7 +576,7 @@ func assignop(src *types.Type, dst *types.Type, why *string) Op {
// we want to recompute the itab. Recomputing the itab ensures
// that itabs are unique (thus an interface with a compile-time
// type I has an itab with interface type I).
if eqtype(src.Orig, dst.Orig) {
if types.Identical(src.Orig, dst.Orig) {
if src.IsEmptyInterface() {
// Conversion between two empty interfaces
// requires no code.
@ -764,7 +644,7 @@ func assignop(src *types.Type, dst *types.Type, why *string) Op {
// src and dst have identical element types, and
// either src or dst is not a named type.
if src.IsChan() && src.ChanDir() == types.Cboth && dst.IsChan() {
if eqtype(src.Elem(), dst.Elem()) && (src.Sym == nil || dst.Sym == nil) {
if types.Identical(src.Elem(), dst.Elem()) && (src.Sym == nil || dst.Sym == nil) {
return OCONVNOP
}
}
@ -772,8 +652,7 @@ func assignop(src *types.Type, dst *types.Type, why *string) Op {
// 5. src is the predeclared identifier nil and dst is a nillable type.
if src.Etype == TNIL {
switch dst.Etype {
case TPTR32,
TPTR64,
case TPTR,
TFUNC,
TMAP,
TCHAN,
@ -836,14 +715,14 @@ func convertop(src *types.Type, dst *types.Type, why *string) Op {
}
// 2. Ignoring struct tags, src and dst have identical underlying types.
if eqtypeIgnoreTags(src.Orig, dst.Orig) {
if types.IdenticalIgnoreTags(src.Orig, dst.Orig) {
return OCONVNOP
}
// 3. src and dst are unnamed pointer types and, ignoring struct tags,
// their base types have identical underlying types.
if src.IsPtr() && dst.IsPtr() && src.Sym == nil && dst.Sym == nil {
if eqtypeIgnoreTags(src.Elem().Orig, dst.Elem().Orig) {
if types.IdenticalIgnoreTags(src.Elem().Orig, dst.Elem().Orig) {
return OCONVNOP
}
}
@ -946,7 +825,7 @@ func assignconvfn(n *Node, t *types.Type, context func() string) *Node {
}
}
if eqtype(n.Type, t) {
if types.Identical(n.Type, t) {
return n
}
@ -1729,11 +1608,10 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) {
Curfn = fn
typecheckslice(fn.Nbody.Slice(), Etop)
// TODO(mdempsky): Investigate why this doesn't work with
// indexed export. For now, we disable even in non-indexed
// mode to ensure fair benchmark comparisons and to track down
// unintended compilation differences.
if false {
// Inline calls within (*T).M wrappers. This is safe because we only
// generate those wrappers within the same compilation unit as (T).M.
// TODO(mdempsky): Investigate why we can't enable this more generally.
if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type && rcvr.Elem().Sym != nil {
inlcalls(fn)
}
escAnalyze([]*Node{fn}, false)
@ -1813,7 +1691,7 @@ func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool
return false
}
tm := tms[i]
if !eqtype(tm.Type, im.Type) {
if !types.Identical(tm.Type, im.Type) {
*m = im
*samename = tm
*ptr = 0
@ -1845,7 +1723,7 @@ func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool
return false
}
tm := tms[i]
if tm.Nointerface() || !eqtype(tm.Type, im.Type) {
if tm.Nointerface() || !types.Identical(tm.Type, im.Type) {
*m = im
*samename = tm
*ptr = 0
@ -2003,8 +1881,7 @@ func isdirectiface(t *types.Type) bool {
}
switch t.Etype {
case TPTR32,
TPTR64,
case TPTR,
TCHAN,
TMAP,
TFUNC,

View file

@ -611,7 +611,7 @@ Outer:
continue
}
for _, n := range prev {
if eqtype(n.Left.Type, c.node.Left.Type) {
if types.Identical(n.Left.Type, c.node.Left.Type) {
yyerrorl(c.node.Pos, "duplicate case %v in type switch\n\tprevious case at %v", c.node.Left.Type, n.Line())
// avoid double-reporting errors
continue Outer

View file

@ -45,7 +45,7 @@ type Node struct {
// - ONAME nodes that refer to local variables use it to identify their stack frame position.
// - ODOT, ODOTPTR, and OINDREGSP use it to indicate offset relative to their base address.
// - OSTRUCTKEY uses it to store the named field's offset.
// - Named OLITERALs use it to to store their ambient iota value.
// - Named OLITERALs use it to store their ambient iota value.
// Possibly still more uses. If you find any, document them.
Xoffset int64
@ -65,7 +65,7 @@ func (n *Node) ResetAux() {
func (n *Node) SubOp() Op {
switch n.Op {
case OASOP, OCMPIFACE, OCMPSTR, ONAME:
case OASOP, ONAME:
default:
Fatalf("unexpected op: %v", n.Op)
}
@ -74,7 +74,7 @@ func (n *Node) SubOp() Op {
func (n *Node) SetSubOp(op Op) {
switch n.Op {
case OASOP, OCMPIFACE, OCMPSTR, ONAME:
case OASOP, ONAME:
default:
Fatalf("unexpected op: %v", n.Op)
}
@ -603,26 +603,32 @@ const (
OAS2DOTTYPE // List = Rlist (x, ok = I.(int))
OASOP // Left Etype= Right (x += y)
OCALL // Left(List) (function call, method call or type conversion)
OCALLFUNC // Left(List) (function call f(args))
OCALLMETH // Left(List) (direct method call x.Method(args))
OCALLINTER // Left(List) (interface method call x.Method(args))
OCALLPART // Left.Right (method expression x.Method, not called)
OCAP // cap(Left)
OCLOSE // close(Left)
OCLOSURE // func Type { Body } (func literal)
OCMPIFACE // Left Etype Right (interface comparison, x == y or x != y)
OCMPSTR // Left Etype Right (string comparison, x == y, x < y, etc)
OCOMPLIT // Right{List} (composite literal, not yet lowered to specific form)
OMAPLIT // Type{List} (composite literal, Type is map)
OSTRUCTLIT // Type{List} (composite literal, Type is struct)
OARRAYLIT // Type{List} (composite literal, Type is array)
OSLICELIT // Type{List} (composite literal, Type is slice)
OPTRLIT // &Left (left is composite literal)
OCONV // Type(Left) (type conversion)
OCONVIFACE // Type(Left) (type conversion, to interface)
OCONVNOP // Type(Left) (type conversion, no effect)
OCOPY // copy(Left, Right)
ODCL // var Left (declares Left of type Left.Type)
// OCALLFUNC, OCALLMETH, and OCALLINTER have the same structure.
// Prior to walk, they are: Left(List), where List is all regular arguments.
// If present, Right is an ODDDARG that holds the
// generated slice used in a call to a variadic function.
// After walk, List is a series of assignments to temporaries,
// and Rlist is an updated set of arguments, including any ODDDARG slice.
// TODO(josharian/khr): Use Ninit instead of List for the assignments to temporaries. See CL 114797.
OCALLFUNC // Left(List/Rlist) (function call f(args))
OCALLMETH // Left(List/Rlist) (direct method call x.Method(args))
OCALLINTER // Left(List/Rlist) (interface method call x.Method(args))
OCALLPART // Left.Right (method expression x.Method, not called)
OCAP // cap(Left)
OCLOSE // close(Left)
OCLOSURE // func Type { Body } (func literal)
OCOMPLIT // Right{List} (composite literal, not yet lowered to specific form)
OMAPLIT // Type{List} (composite literal, Type is map)
OSTRUCTLIT // Type{List} (composite literal, Type is struct)
OARRAYLIT // Type{List} (composite literal, Type is array)
OSLICELIT // Type{List} (composite literal, Type is slice)
OPTRLIT // &Left (left is composite literal)
OCONV // Type(Left) (type conversion)
OCONVIFACE // Type(Left) (type conversion, to interface)
OCONVNOP // Type(Left) (type conversion, no effect)
OCOPY // copy(Left, Right)
ODCL // var Left (declares Left of type Left.Type)
// Used during parsing but don't last.
ODCLFUNC // func f() or func (r) f()

View file

@ -7,6 +7,7 @@
package main
import (
"runtime"
"testing"
)
@ -14,6 +15,13 @@ const (
y = 0x0fffFFFF
)
var (
g8 int8
g16 int16
g32 int32
g64 int64
)
//go:noinline
func lshNop1(x uint64) uint64 {
// two outer shifts should be removed
@ -915,4 +923,32 @@ func TestArithmetic(t *testing.T) {
testLoadSymCombine(t)
testShiftRemoval(t)
testShiftedOps(t)
testDivFixUp(t)
}
// testDivFixUp ensures that signed division fix-ups are being generated.
func testDivFixUp(t *testing.T) {
defer func() {
if r := recover(); r != nil {
t.Error("testDivFixUp failed")
if e, ok := r.(runtime.Error); ok {
t.Logf("%v\n", e.Error())
}
}
}()
var w int8 = -128
var x int16 = -32768
var y int32 = -2147483648
var z int64 = -9223372036854775808
for i := -5; i < 0; i++ {
g8 = w / int8(i)
g16 = x / int16(i)
g32 = y / int32(i)
g64 = z / int64(i)
g8 = w % int8(i)
g16 = x % int16(i)
g32 = y % int32(i)
g64 = z % int64(i)
}
}

View file

@ -87,8 +87,7 @@ var _typekind = []string{
TFLOAT64: "float64",
TBOOL: "bool",
TSTRING: "string",
TPTR32: "pointer",
TPTR64: "pointer",
TPTR: "pointer",
TUNSAFEPTR: "unsafe.Pointer",
TSTRUCT: "struct",
TINTER: "interface",
@ -297,21 +296,21 @@ func indexlit(n *Node) *Node {
// n.Left = typecheck1(n.Left, top)
func typecheck1(n *Node, top int) *Node {
switch n.Op {
case OXDOT, ODOT, ODOTPTR, ODOTMETH, ODOTINTER, ORETJMP:
// n.Sym is a field/method name, not a variable.
default:
if n.Sym != nil {
if n.Op == ONAME && n.SubOp() != 0 && top&Ecall == 0 {
yyerror("use of builtin %v not in function call", n.Sym)
n.Type = nil
return n
}
case OLITERAL, ONAME, ONONAME, OTYPE:
if n.Sym == nil {
break
}
typecheckdef(n)
if n.Op == ONONAME {
n.Type = nil
return n
}
if n.Op == ONAME && n.SubOp() != 0 && top&Ecall == 0 {
yyerror("use of builtin %v not in function call", n.Sym)
n.Type = nil
return n
}
typecheckdef(n)
if n.Op == ONONAME {
n.Type = nil
return n
}
}
@ -633,7 +632,7 @@ func typecheck1(n *Node, top int) *Node {
et = TINT
}
aop := OXXX
if iscmp[n.Op] && t.Etype != TIDEAL && !eqtype(l.Type, r.Type) {
if iscmp[n.Op] && t.Etype != TIDEAL && !types.Identical(l.Type, r.Type) {
// comparison is okay as long as one side is
// assignable to the other. convert so they have
// the same type.
@ -688,7 +687,7 @@ func typecheck1(n *Node, top int) *Node {
et = t.Etype
}
if t.Etype != TIDEAL && !eqtype(l.Type, r.Type) {
if t.Etype != TIDEAL && !types.Identical(l.Type, r.Type) {
l, r = defaultlit2(l, r, true)
if r.Type.IsInterface() == l.Type.IsInterface() || aop == 0 {
yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type)
@ -748,43 +747,22 @@ func typecheck1(n *Node, top int) *Node {
}
}
if et == TSTRING {
if iscmp[n.Op] {
ot := n.Op
n.Op = OCMPSTR
n.SetSubOp(ot)
} else if n.Op == OADD {
// create OADDSTR node with list of strings in x + y + z + (w + v) + ...
n.Op = OADDSTR
if et == TSTRING && n.Op == OADD {
// create OADDSTR node with list of strings in x + y + z + (w + v) + ...
n.Op = OADDSTR
if l.Op == OADDSTR {
n.List.Set(l.List.Slice())
} else {
n.List.Set1(l)
}
if r.Op == OADDSTR {
n.List.AppendNodes(&r.List)
} else {
n.List.Append(r)
}
n.Left = nil
n.Right = nil
if l.Op == OADDSTR {
n.List.Set(l.List.Slice())
} else {
n.List.Set1(l)
}
}
if et == TINTER {
if l.Op == OLITERAL && l.Val().Ctype() == CTNIL {
// swap for back end
n.Left = r
n.Right = l
} else if r.Op == OLITERAL && r.Val().Ctype() == CTNIL {
} else // leave alone for back end
if r.Type.IsInterface() == l.Type.IsInterface() {
ot := n.Op
n.Op = OCMPIFACE
n.SetSubOp(ot)
if r.Op == OADDSTR {
n.List.AppendNodes(&r.List)
} else {
n.List.Append(r)
}
n.Left = nil
n.Right = nil
}
if (op == ODIV || op == OMOD) && Isconst(r, CTINT) {
@ -1255,7 +1233,7 @@ func typecheck1(n *Node, top int) *Node {
// It isn't necessary, so just do a sanity check.
tp := t.Recv().Type
if l.Left == nil || !eqtype(l.Left.Type, tp) {
if l.Left == nil || !types.Identical(l.Left.Type, tp) {
Fatalf("method receiver")
}
@ -1474,7 +1452,7 @@ func typecheck1(n *Node, top int) *Node {
n.Right = r
}
if !eqtype(l.Type, r.Type) {
if !types.Identical(l.Type, r.Type) {
yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type)
n.Type = nil
return n
@ -1679,7 +1657,7 @@ func typecheck1(n *Node, top int) *Node {
// copy([]byte, string)
if n.Left.Type.IsSlice() && n.Right.Type.IsString() {
if eqtype(n.Left.Type.Elem(), types.Bytetype) {
if types.Identical(n.Left.Type.Elem(), types.Bytetype) {
break
}
yyerror("arguments to copy have different element types: %L and string", n.Left.Type)
@ -1699,7 +1677,7 @@ func typecheck1(n *Node, top int) *Node {
return n
}
if !eqtype(n.Left.Type.Elem(), n.Right.Type.Elem()) {
if !types.Identical(n.Left.Type.Elem(), n.Right.Type.Elem()) {
yyerror("arguments to copy have different element types: %L and %L", n.Left.Type, n.Right.Type)
n.Type = nil
return n
@ -1741,14 +1719,14 @@ func typecheck1(n *Node, top int) *Node {
}
}
// do not use stringtoarraylit.
// do not convert to []byte literal. See CL 125796.
// generated code and compiler memory footprint is better without it.
case OSTRARRAYBYTE:
break
case OSTRARRAYRUNE:
if n.Left.Op == OLITERAL {
n = stringtoarraylit(n)
n = stringtoruneslit(n)
}
}
@ -2134,10 +2112,6 @@ func typecheck1(n *Node, top int) *Node {
}
}
if safemode && !inimport && !compiling_wrappers && t != nil && t.Etype == TUNSAFEPTR {
yyerror("cannot use unsafe.Pointer")
}
evconst(n)
if n.Op == OTYPE && top&Etype == 0 {
if !n.Type.Broke() {
@ -2505,17 +2479,17 @@ func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field {
tt := n.Left.Type
dowidth(tt)
rcvr := f2.Type.Recv().Type
if !eqtype(rcvr, tt) {
if rcvr.IsPtr() && eqtype(rcvr.Elem(), tt) {
if !types.Identical(rcvr, tt) {
if rcvr.IsPtr() && types.Identical(rcvr.Elem(), tt) {
checklvalue(n.Left, "call pointer method on")
n.Left = nod(OADDR, n.Left, nil)
n.Left.SetImplicit(true)
n.Left = typecheck(n.Left, Etype|Erv)
} else if tt.IsPtr() && !rcvr.IsPtr() && eqtype(tt.Elem(), rcvr) {
} else if tt.IsPtr() && !rcvr.IsPtr() && types.Identical(tt.Elem(), rcvr) {
n.Left = nod(OIND, n.Left, nil)
n.Left.SetImplicit(true)
n.Left = typecheck(n.Left, Etype|Erv)
} else if tt.IsPtr() && tt.Elem().IsPtr() && eqtype(derefall(tt), derefall(rcvr)) {
} else if tt.IsPtr() && tt.Elem().IsPtr() && types.Identical(derefall(tt), derefall(rcvr)) {
yyerror("calling method %v with receiver %L requires explicit dereference", n.Sym, n.Left)
for tt.IsPtr() {
// Stop one level early for method with pointer receiver.
@ -2857,7 +2831,7 @@ func keydup(n *Node, hash map[uint32][]*Node) {
if a.Op == OCONVIFACE && orign.Op == OCONVIFACE {
a = a.Left
}
if !eqtype(a.Type, n.Type) {
if !types.Identical(a.Type, n.Type) {
continue
}
cmp.Right = a
@ -2901,7 +2875,7 @@ func pushtype(n *Node, t *types.Type) {
n.Right.SetImplicit(true) // * is okay
} else if Debug['s'] != 0 {
n.Right = typecheck(n.Right, Etype)
if n.Right.Type != nil && eqtype(n.Right.Type, t) {
if n.Right.Type != nil && types.Identical(n.Right.Type, t) {
fmt.Printf("%v: redundant type: %v\n", n.Line(), t)
}
}
@ -3287,7 +3261,7 @@ func checkassignlist(stmt *Node, l Nodes) {
// lvalue expression is for OSLICE and OAPPEND optimizations, and it
// is correct in those settings.
func samesafeexpr(l *Node, r *Node) bool {
if l.Op != r.Op || !eqtype(l.Type, r.Type) {
if l.Op != r.Op || !types.Identical(l.Type, r.Type) {
return false
}
@ -3535,27 +3509,19 @@ func typecheckfunc(n *Node) {
}
}
// The result of stringtoarraylit MUST be assigned back to n, e.g.
// n.Left = stringtoarraylit(n.Left)
func stringtoarraylit(n *Node) *Node {
// The result of stringtoruneslit MUST be assigned back to n, e.g.
// n.Left = stringtoruneslit(n.Left)
func stringtoruneslit(n *Node) *Node {
if n.Left.Op != OLITERAL || n.Left.Val().Ctype() != CTSTR {
Fatalf("stringtoarraylit %v", n)
}
s := n.Left.Val().U.(string)
var l []*Node
if n.Type.Elem().Etype == TUINT8 {
// []byte
for i := 0; i < len(s); i++ {
l = append(l, nod(OKEY, nodintconst(int64(i)), nodintconst(int64(s[0]))))
}
} else {
// []rune
i := 0
for _, r := range s {
l = append(l, nod(OKEY, nodintconst(int64(i)), nodintconst(int64(r))))
i++
}
s := n.Left.Val().U.(string)
i := 0
for _, r := range s {
l = append(l, nod(OKEY, nodintconst(int64(i)), nodintconst(int64(r))))
i++
}
nn := nod(OCOMPLIT, nil, typenod(n.Type))
@ -3688,9 +3654,6 @@ func typecheckdef(n *Node) {
default:
Fatalf("typecheckdef %v", n.Op)
case OGOTO, OLABEL, OPACK:
// nothing to do here
case OLITERAL:
if n.Name.Param.Ntype != nil {
n.Name.Param.Ntype = typecheck(n.Name.Param.Ntype, Etype)
@ -3731,7 +3694,7 @@ func typecheckdef(n *Node) {
goto ret
}
if !e.Type.IsUntyped() && !eqtype(t, e.Type) {
if !e.Type.IsUntyped() && !types.Identical(t, e.Type) {
yyerrorl(n.Pos, "cannot use %L as type %v in const initializer", e, t)
goto ret
}

View file

@ -32,9 +32,7 @@ const (
TBOOL = types.TBOOL
TPTR32 = types.TPTR32
TPTR64 = types.TPTR64
TPTR = types.TPTR
TFUNC = types.TFUNC
TSLICE = types.TSLICE
TARRAY = types.TARRAY

View file

@ -177,11 +177,8 @@ func typeinit() {
simtype[et] = et
}
types.Types[TPTR32] = types.New(TPTR32)
dowidth(types.Types[TPTR32])
types.Types[TPTR64] = types.New(TPTR64)
dowidth(types.Types[TPTR64])
types.Types[TPTR] = types.New(TPTR)
dowidth(types.Types[TPTR])
t := types.New(TUNSAFEPTR)
types.Types[TUNSAFEPTR] = t
@ -190,11 +187,6 @@ func typeinit() {
asNode(t.Sym.Def).Name = new(Name)
dowidth(types.Types[TUNSAFEPTR])
types.Tptr = TPTR32
if Widthptr == 8 {
types.Tptr = TPTR64
}
for et := TINT8; et <= TUINT64; et++ {
isInt[et] = true
}
@ -263,8 +255,7 @@ func typeinit() {
okforlen[TSLICE] = true
okforlen[TSTRING] = true
okforeq[TPTR32] = true
okforeq[TPTR64] = true
okforeq[TPTR] = true
okforeq[TUNSAFEPTR] = true
okforeq[TINTER] = true
okforeq[TCHAN] = true
@ -357,10 +348,10 @@ func typeinit() {
types.Types[TINTER] = types.New(TINTER)
// simple aliases
simtype[TMAP] = types.Tptr
simtype[TCHAN] = types.Tptr
simtype[TFUNC] = types.Tptr
simtype[TUNSAFEPTR] = types.Tptr
simtype[TMAP] = TPTR
simtype[TCHAN] = TPTR
simtype[TFUNC] = TPTR
simtype[TUNSAFEPTR] = TPTR
array_array = int(Rnd(0, int64(Widthptr)))
array_nel = int(Rnd(int64(array_array)+int64(Widthptr), int64(Widthptr)))

File diff suppressed because it is too large Load diff

View file

@ -313,9 +313,11 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
}
arg0 := v.Args[0].Reg()
out := v.Reg0()
// SYNC
psync := s.Prog(ppc64.ASYNC)
psync.To.Type = obj.TYPE_NONE
// SYNC when AuxInt == 1; otherwise, load-acquire
if v.AuxInt == 1 {
psync := s.Prog(ppc64.ASYNC)
psync.To.Type = obj.TYPE_NONE
}
// Load
p := s.Prog(ld)
p.From.Type = obj.TYPE_MEM
@ -338,7 +340,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
case ssa.OpPPC64LoweredAtomicStore32,
ssa.OpPPC64LoweredAtomicStore64:
// SYNC
// SYNC or LWSYNC
// MOVD/MOVW arg1,(arg0)
st := ppc64.AMOVD
if v.Op == ssa.OpPPC64LoweredAtomicStore32 {
@ -346,8 +348,13 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
}
arg0 := v.Args[0].Reg()
arg1 := v.Args[1].Reg()
// If AuxInt == 0, LWSYNC (Store-Release), else SYNC
// SYNC
psync := s.Prog(ppc64.ASYNC)
syncOp := ppc64.ASYNC
if v.AuxInt == 0 {
syncOp = ppc64.ALWSYNC
}
psync := s.Prog(syncOp)
psync.To.Type = obj.TYPE_NONE
// Store
p := s.Prog(st)
@ -360,12 +367,12 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
ssa.OpPPC64LoweredAtomicCas32:
// LWSYNC
// loop:
// LDAR (Rarg0), Rtmp
// LDAR (Rarg0), MutexHint, Rtmp
// CMP Rarg1, Rtmp
// BNE fail
// STDCCC Rarg2, (Rarg0)
// BNE loop
// LWSYNC
// LWSYNC // Only for sequential consistency; not required in CasRel.
// MOVD $1, Rout
// BR end
// fail:
@ -393,6 +400,11 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = r0
p.To.Type = obj.TYPE_REG
p.To.Reg = ppc64.REGTMP
// If it is a Compare-and-Swap-Release operation, set the EH field with
// the release hint.
if v.AuxInt == 0 {
p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: 0})
}
// CMP reg1,reg2
p1 := s.Prog(cmp)
p1.From.Type = obj.TYPE_REG
@ -414,8 +426,11 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
gc.Patch(p4, p)
// LWSYNC - Assuming shared data not write-through-required nor
// caching-inhibited. See Appendix B.2.1.1 in the ISA 2.07b.
plwsync2 := s.Prog(ppc64.ALWSYNC)
plwsync2.To.Type = obj.TYPE_NONE
// If the operation is a CAS-Release, then synchronization is not necessary.
if v.AuxInt != 0 {
plwsync2 := s.Prog(ppc64.ALWSYNC)
plwsync2.To.Type = obj.TYPE_NONE
}
// return true
p5 := s.Prog(ppc64.AMOVD)
p5.From.Type = obj.TYPE_CONST
@ -967,7 +982,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
case ssa.OpPPC64LoweredMove:
// This will be used when moving more
// than 8 bytes. Moves start with as
// than 8 bytes. Moves start with
// as many 8 byte moves as possible, then
// 4, 2, or 1 byte(s) as remaining. This will
// work and be efficient for power8 or later.

View file

@ -373,6 +373,7 @@ var passes = [...]pass{
{name: "phiopt", fn: phiopt},
{name: "nilcheckelim", fn: nilcheckelim},
{name: "prove", fn: prove},
{name: "fuse plain", fn: fusePlain},
{name: "decompose builtin", fn: decomposeBuiltIn, required: true},
{name: "softfloat", fn: softfloat, required: true},
{name: "late opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules
@ -380,7 +381,7 @@ var passes = [...]pass{
{name: "generic deadcode", fn: deadcode, required: true}, // remove dead stores, which otherwise mess up store chain
{name: "check bce", fn: checkbce},
{name: "branchelim", fn: branchelim},
{name: "fuse", fn: fuse},
{name: "fuse", fn: fuseAll},
{name: "dse", fn: dse},
{name: "writebarrier", fn: writebarrier, required: true}, // expand write barrier ops
{name: "insert resched checks", fn: insertLoopReschedChecks,

View file

@ -178,6 +178,7 @@ type GCNode interface {
Typ() *types.Type
String() string
IsSynthetic() bool
IsAutoTmp() bool
StorageClass() StorageClass
}

View file

@ -153,8 +153,12 @@ var BlockEnd = &Value{
// RegisterSet is a bitmap of registers, indexed by Register.num.
type RegisterSet uint64
// logf prints debug-specific logging to stdout (always stdout) if the current
// function is tagged by GOSSAFUNC (for ssa output directed either to stdout or html).
func (s *debugState) logf(msg string, args ...interface{}) {
s.f.Logf(msg, args...)
if s.f.PrintOrHtmlSSA {
fmt.Printf(msg, args...)
}
}
type debugState struct {

View file

@ -86,6 +86,10 @@ func (d *DummyAuto) IsSynthetic() bool {
return false
}
func (d *DummyAuto) IsAutoTmp() bool {
return true
}
func (DummyFrontend) StringData(s string) interface{} {
return nil
}
@ -163,7 +167,6 @@ func init() {
}
types.Dowidth = func(t *types.Type) {}
types.Tptr = types.TPTR64
for _, typ := range [...]struct {
width int64
et types.EType

View file

@ -37,9 +37,10 @@ type Func struct {
// Given an environment variable used for debug hash match,
// what file (if any) receives the yes/no logging?
logfiles map[string]writeSyncer
HTMLWriter *HTMLWriter // html writer, for debugging
DebugTest bool // default true unless $GOSSAHASH != ""; as a debugging aid, make new code conditional on this and use GOSSAHASH to binary search for failing cases
logfiles map[string]writeSyncer
HTMLWriter *HTMLWriter // html writer, for debugging
DebugTest bool // default true unless $GOSSAHASH != ""; as a debugging aid, make new code conditional on this and use GOSSAHASH to binary search for failing cases
PrintOrHtmlSSA bool // true if GOSSAFUNC matches, true even if fe.Log() (spew phase results to stdout) is false.
scheduled bool // Values in Blocks are in final order
NoSplit bool // true if function is marked as nosplit. Used by schedule check pass.

View file

@ -8,15 +8,33 @@ import (
"cmd/internal/src"
)
// fusePlain runs fuse(f, fuseTypePlain).
func fusePlain(f *Func) { fuse(f, fuseTypePlain) }
// fuseAll runs fuse(f, fuseTypeAll).
func fuseAll(f *Func) { fuse(f, fuseTypeAll) }
type fuseType uint8
const (
fuseTypePlain fuseType = 1 << iota
fuseTypeIf
fuseTypeAll = fuseTypePlain | fuseTypeIf
)
// fuse simplifies control flow by joining basic blocks.
func fuse(f *Func) {
func fuse(f *Func, typ fuseType) {
for changed := true; changed; {
changed = false
// Fuse from end to beginning, to avoid quadratic behavior in fuseBlockPlain. See issue 13554.
for i := len(f.Blocks) - 1; i >= 0; i-- {
b := f.Blocks[i]
changed = fuseBlockIf(b) || changed
changed = fuseBlockPlain(b) || changed
if typ&fuseTypeIf != 0 {
changed = fuseBlockIf(b) || changed
}
if typ&fuseTypePlain != 0 {
changed = fuseBlockPlain(b) || changed
}
}
}
}

View file

@ -26,7 +26,7 @@ func TestFuseEliminatesOneBranch(t *testing.T) {
Exit("mem")))
CheckFunc(fun.f)
fuse(fun.f)
fuseAll(fun.f)
for _, b := range fun.f.Blocks {
if b == fun.blocks["then"] && b.Kind != BlockInvalid {
@ -56,7 +56,7 @@ func TestFuseEliminatesBothBranches(t *testing.T) {
Exit("mem")))
CheckFunc(fun.f)
fuse(fun.f)
fuseAll(fun.f)
for _, b := range fun.f.Blocks {
if b == fun.blocks["then"] && b.Kind != BlockInvalid {
@ -90,7 +90,7 @@ func TestFuseHandlesPhis(t *testing.T) {
Exit("mem")))
CheckFunc(fun.f)
fuse(fun.f)
fuseAll(fun.f)
for _, b := range fun.f.Blocks {
if b == fun.blocks["then"] && b.Kind != BlockInvalid {
@ -122,7 +122,7 @@ func TestFuseEliminatesEmptyBlocks(t *testing.T) {
))
CheckFunc(fun.f)
fuse(fun.f)
fuseAll(fun.f)
for k, b := range fun.blocks {
if k[:1] == "z" && b.Kind != BlockInvalid {
@ -162,7 +162,7 @@ func BenchmarkFuse(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
fun := c.Fun("entry", blocks...)
fuse(fun.f)
fuseAll(fun.f)
}
})
}

View file

@ -17,14 +17,17 @@
(Mul(32|64)F x y) -> (MULS(S|D) x y)
(Mul32uhilo x y) -> (MULLQU x y)
(Select0 (Mul32uover x y)) -> (Select0 <typ.UInt32> (MULLU x y))
(Select1 (Mul32uover x y)) -> (SETO (Select1 <types.TypeFlags> (MULLU x y)))
(Avg32u x y) -> (AVGLU x y)
(Div32F x y) -> (DIVSS x y)
(Div64F x y) -> (DIVSD x y)
(Div32 x y) -> (DIVL x y)
(Div32 [a] x y) -> (DIVL [a] x y)
(Div32u x y) -> (DIVLU x y)
(Div16 x y) -> (DIVW x y)
(Div16 [a] x y) -> (DIVW [a] x y)
(Div16u x y) -> (DIVWU x y)
(Div8 x y) -> (DIVW (SignExt8to16 x) (SignExt8to16 y))
(Div8u x y) -> (DIVWU (ZeroExt8to16 x) (ZeroExt8to16 y))
@ -32,9 +35,9 @@
(Hmul32 x y) -> (HMULL x y)
(Hmul32u x y) -> (HMULLU x y)
(Mod32 x y) -> (MODL x y)
(Mod32 [a] x y) -> (MODL [a] x y)
(Mod32u x y) -> (MODLU x y)
(Mod16 x y) -> (MODW x y)
(Mod16 [a] x y) -> (MODW [a] x y)
(Mod16u x y) -> (MODWU x y)
(Mod8 x y) -> (MODW (SignExt8to16 x) (SignExt8to16 y))
(Mod8u x y) -> (MODWU (ZeroExt8to16 x) (ZeroExt8to16 y))
@ -369,6 +372,7 @@
(If (SETBE cmp) yes no) -> (ULE cmp yes no)
(If (SETA cmp) yes no) -> (UGT cmp yes no)
(If (SETAE cmp) yes no) -> (UGE cmp yes no)
(If (SETO cmp) yes no) -> (OS cmp yes no)
// Special case for floating point - LF/LEF not generated
(If (SETGF cmp) yes no) -> (UGT cmp yes no)
@ -398,6 +402,7 @@
(NE (TESTB (SETBE cmp) (SETBE cmp)) yes no) -> (ULE cmp yes no)
(NE (TESTB (SETA cmp) (SETA cmp)) yes no) -> (UGT cmp yes no)
(NE (TESTB (SETAE cmp) (SETAE cmp)) yes no) -> (UGE cmp yes no)
(NE (TESTB (SETO cmp) (SETO cmp)) yes no) -> (OS cmp yes no)
// Special case for floating point - LF/LEF not generated
(NE (TESTB (SETGF cmp) (SETGF cmp)) yes no) -> (UGT cmp yes no)
@ -614,38 +619,39 @@
(MOVWLSX (ANDLconst [c] x)) && c & 0x8000 == 0 -> (ANDLconst [c & 0x7fff] x)
// Don't extend before storing
(MOVWstore [off] {sym} ptr (MOVWLSX x) mem) -> (MOVWstore [off] {sym} ptr x mem)
(MOVBstore [off] {sym} ptr (MOVBLSX x) mem) -> (MOVBstore [off] {sym} ptr x mem)
(MOVWstore [off] {sym} ptr (MOVWLZX x) mem) -> (MOVWstore [off] {sym} ptr x mem)
(MOVBstore [off] {sym} ptr (MOVBLZX x) mem) -> (MOVBstore [off] {sym} ptr x mem)
(MOVWstore [off] {sym} ptr (MOVWL(S|Z)X x) mem) -> (MOVWstore [off] {sym} ptr x mem)
(MOVBstore [off] {sym} ptr (MOVBL(S|Z)X x) mem) -> (MOVBstore [off] {sym} ptr x mem)
// fold constants into memory operations
// Note that this is not always a good idea because if not all the uses of
// the ADDQconst get eliminated, we still have to compute the ADDQconst and we now
// have potentially two live values (ptr and (ADDQconst [off] ptr)) instead of one.
// Nevertheless, let's do it!
(MOVLload [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVLload [off1+off2] {sym} ptr mem)
(MOVWload [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVWload [off1+off2] {sym} ptr mem)
(MOVBload [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVBload [off1+off2] {sym} ptr mem)
(MOVSSload [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVSSload [off1+off2] {sym} ptr mem)
(MOVSDload [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVSDload [off1+off2] {sym} ptr mem)
(MOVLstore [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVLstore [off1+off2] {sym} ptr val mem)
(MOVWstore [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVWstore [off1+off2] {sym} ptr val mem)
(MOVBstore [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVBstore [off1+off2] {sym} ptr val mem)
(MOVSSstore [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVSSstore [off1+off2] {sym} ptr val mem)
(MOVSDstore [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVSDstore [off1+off2] {sym} ptr val mem)
(MOV(L|W|B|SS|SD)load [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOV(L|W|B|SS|SD)load [off1+off2] {sym} ptr mem)
(MOV(L|W|B|SS|SD)store [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOV(L|W|B|SS|SD)store [off1+off2] {sym} ptr val mem)
((ADD|SUB|MUL|AND|OR|XOR)Lload [off1] {sym} val (ADDLconst [off2] base) mem) && is32Bit(off1+off2) ->
((ADD|SUB|MUL|AND|OR|XOR)Lload [off1+off2] {sym} val base mem)
((ADD|SUB|MUL|AND|OR|XOR)Lloadidx4 [off1] {sym} val (ADDLconst [off2] base) idx mem) && is32Bit(off1+off2) ->
((ADD|SUB|MUL|AND|OR|XOR)Lloadidx4 [off1+off2] {sym} val base idx mem)
((ADD|SUB|MUL|AND|OR|XOR)Lloadidx4 [off1] {sym} val base (ADDLconst [off2] idx) mem) && is32Bit(off1+off2*4) ->
((ADD|SUB|MUL|AND|OR|XOR)Lloadidx4 [off1+off2*4] {sym} val base idx mem)
((ADD|SUB|MUL|DIV)SSload [off1] {sym} val (ADDLconst [off2] base) mem) && is32Bit(off1+off2) ->
((ADD|SUB|MUL|DIV)SSload [off1+off2] {sym} val base mem)
((ADD|SUB|MUL|DIV)SDload [off1] {sym} val (ADDLconst [off2] base) mem) && is32Bit(off1+off2) ->
((ADD|SUB|MUL|DIV)SDload [off1+off2] {sym} val base mem)
((ADD|SUB|AND|OR|XOR)Lmodify [off1] {sym} (ADDLconst [off2] base) val mem) && is32Bit(off1+off2) ->
((ADD|SUB|AND|OR|XOR)Lmodify [off1+off2] {sym} base val mem)
((ADD|SUB|AND|OR|XOR)Lmodifyidx4 [off1] {sym} (ADDLconst [off2] base) idx val mem) && is32Bit(off1+off2) ->
((ADD|SUB|AND|OR|XOR)Lmodifyidx4 [off1+off2] {sym} base idx val mem)
((ADD|SUB|AND|OR|XOR)Lmodifyidx4 [off1] {sym} base (ADDLconst [off2] idx) val mem) && is32Bit(off1+off2*4) ->
((ADD|SUB|AND|OR|XOR)Lmodifyidx4 [off1+off2*4] {sym} base idx val mem)
((ADD|AND|OR|XOR)Lconstmodify [valoff1] {sym} (ADDLconst [off2] base) mem) && ValAndOff(valoff1).canAdd(off2) ->
((ADD|AND|OR|XOR)Lconstmodify [ValAndOff(valoff1).add(off2)] {sym} base mem)
((ADD|AND|OR|XOR)Lconstmodifyidx4 [valoff1] {sym} (ADDLconst [off2] base) idx mem) && ValAndOff(valoff1).canAdd(off2) ->
((ADD|AND|OR|XOR)Lconstmodifyidx4 [ValAndOff(valoff1).add(off2)] {sym} base idx mem)
((ADD|AND|OR|XOR)Lconstmodifyidx4 [valoff1] {sym} base (ADDLconst [off2] idx) mem) && ValAndOff(valoff1).canAdd(off2*4) ->
((ADD|AND|OR|XOR)Lconstmodifyidx4 [ValAndOff(valoff1).add(off2*4)] {sym} base idx mem)
// Fold constants into stores.
(MOVLstore [off] {sym} ptr (MOVLconst [c]) mem) && validOff(off) ->
@ -656,12 +662,8 @@
(MOVBstoreconst [makeValAndOff(int64(int8(c)),off)] {sym} ptr mem)
// Fold address offsets into constant stores.
(MOVLstoreconst [sc] {s} (ADDLconst [off] ptr) mem) && ValAndOff(sc).canAdd(off) ->
(MOVLstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
(MOVWstoreconst [sc] {s} (ADDLconst [off] ptr) mem) && ValAndOff(sc).canAdd(off) ->
(MOVWstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
(MOVBstoreconst [sc] {s} (ADDLconst [off] ptr) mem) && ValAndOff(sc).canAdd(off) ->
(MOVBstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
(MOV(L|W|B)storeconst [sc] {s} (ADDLconst [off] ptr) mem) && ValAndOff(sc).canAdd(off) ->
(MOV(L|W|B)storeconst [ValAndOff(sc).add(off)] {s} ptr mem)
// We need to fold LEAQ into the MOVx ops so that the live variable analysis knows
// what variables are being read/written by the ops.
@ -671,97 +673,43 @@
// a separate instruction gives us that register. Having the LEAL be
// a separate instruction also allows it to be CSEd (which is good because
// it compiles to a thunk call).
(MOVLload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
(MOV(L|W|B|SS|SD|BLSX|WLSX)load [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !config.ctxt.Flag_shared) ->
(MOVLload [off1+off2] {mergeSym(sym1,sym2)} base mem)
(MOVWload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !config.ctxt.Flag_shared) ->
(MOVWload [off1+off2] {mergeSym(sym1,sym2)} base mem)
(MOVBload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !config.ctxt.Flag_shared) ->
(MOVBload [off1+off2] {mergeSym(sym1,sym2)} base mem)
(MOVSSload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !config.ctxt.Flag_shared) ->
(MOVSSload [off1+off2] {mergeSym(sym1,sym2)} base mem)
(MOVSDload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !config.ctxt.Flag_shared) ->
(MOVSDload [off1+off2] {mergeSym(sym1,sym2)} base mem)
(MOV(L|W|B|SS|SD|BLSX|WLSX)load [off1+off2] {mergeSym(sym1,sym2)} base mem)
(MOVBLSXload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
(MOV(L|W|B|SS|SD)store [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !config.ctxt.Flag_shared) ->
(MOVBLSXload [off1+off2] {mergeSym(sym1,sym2)} base mem)
(MOVWLSXload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !config.ctxt.Flag_shared) ->
(MOVWLSXload [off1+off2] {mergeSym(sym1,sym2)} base mem)
(MOV(L|W|B|SS|SD)store [off1+off2] {mergeSym(sym1,sym2)} base val mem)
(MOVLstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !config.ctxt.Flag_shared) ->
(MOVLstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
(MOVWstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !config.ctxt.Flag_shared) ->
(MOVWstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
(MOVBstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !config.ctxt.Flag_shared) ->
(MOVBstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
(MOVSSstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !config.ctxt.Flag_shared) ->
(MOVSSstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
(MOVSDstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !config.ctxt.Flag_shared) ->
(MOVSDstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
(MOVLstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
(MOV(L|W|B)storeconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
&& (ptr.Op != OpSB || !config.ctxt.Flag_shared) ->
(MOVLstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
(MOVWstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
&& (ptr.Op != OpSB || !config.ctxt.Flag_shared) ->
(MOVWstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
(MOVBstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
&& (ptr.Op != OpSB || !config.ctxt.Flag_shared) ->
(MOVBstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
(MOV(L|W|B)storeconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
// generating indexed loads and stores
(MOVBload [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVBloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVWload [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVWloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOV(B|W|L|SS|SD)load [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOV(B|W|L|SS|SD)loadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVWload [off1] {sym1} (LEAL2 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVWloadidx2 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVLload [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVLloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVLload [off1] {sym1} (LEAL4 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVLloadidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVSSload [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVSSloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVSSload [off1] {sym1} (LEAL4 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVSSloadidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVSDload [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVSDloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOV(L|SS)load [off1] {sym1} (LEAL4 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOV(L|SS)loadidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVSDload [off1] {sym1} (LEAL8 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVSDloadidx8 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVBstore [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVBstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOVWstore [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVWstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOV(B|W|L|SS|SD)store [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOV(B|W|L|SS|SD)storeidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOVWstore [off1] {sym1} (LEAL2 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVWstoreidx2 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOVLstore [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVLstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOVLstore [off1] {sym1} (LEAL4 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVLstoreidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOVSSstore [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVSSstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOVSSstore [off1] {sym1} (LEAL4 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVSSstoreidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOVSDstore [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVSDstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOV(L|SS)store [off1] {sym1} (LEAL4 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOV(L|SS)storeidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOVSDstore [off1] {sym1} (LEAL8 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVSDstoreidx8 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
((ADD|SUB|MUL|AND|OR|XOR)Lload [off1] {sym1} val (LEAL [off2] {sym2} base) mem)
&& is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) ->
((ADD|SUB|MUL|AND|OR|XOR)Lload [off1+off2] {mergeSym(sym1,sym2)} val base mem)
((ADD|SUB|MUL|AND|OR|XOR)Lloadidx4 [off1] {sym1} val (LEAL [off2] {sym2} base) idx mem)
&& is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) ->
((ADD|SUB|MUL|AND|OR|XOR)Lloadidx4 [off1+off2] {mergeSym(sym1,sym2)} val base idx mem)
((ADD|SUB|MUL|DIV)SSload [off1] {sym1} val (LEAL [off2] {sym2} base) mem)
&& is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) ->
((ADD|SUB|MUL|DIV)SSload [off1+off2] {mergeSym(sym1,sym2)} val base mem)
@ -771,35 +719,27 @@
((ADD|SUB|AND|OR|XOR)Lmodify [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
&& is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) ->
((ADD|SUB|AND|OR|XOR)Lmodify [off1+off2] {mergeSym(sym1,sym2)} base val mem)
((ADD|SUB|AND|OR|XOR)Lmodifyidx4 [off1] {sym1} (LEAL [off2] {sym2} base) idx val mem)
&& is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) ->
((ADD|SUB|AND|OR|XOR)Lmodifyidx4 [off1+off2] {mergeSym(sym1,sym2)} base idx val mem)
((ADD|AND|OR|XOR)Lconstmodify [valoff1] {sym1} (LEAL [off2] {sym2} base) mem)
&& ValAndOff(valoff1).canAdd(off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) ->
((ADD|AND|OR|XOR)Lconstmodify [ValAndOff(valoff1).add(off2)] {mergeSym(sym1,sym2)} base mem)
((ADD|AND|OR|XOR)Lconstmodifyidx4 [valoff1] {sym1} (LEAL [off2] {sym2} base) idx mem)
&& ValAndOff(valoff1).canAdd(off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) ->
((ADD|AND|OR|XOR)Lconstmodifyidx4 [ValAndOff(valoff1).add(off2)] {mergeSym(sym1,sym2)} base idx mem)
(MOVBload [off] {sym} (ADDL ptr idx) mem) && ptr.Op != OpSB -> (MOVBloadidx1 [off] {sym} ptr idx mem)
(MOVWload [off] {sym} (ADDL ptr idx) mem) && ptr.Op != OpSB -> (MOVWloadidx1 [off] {sym} ptr idx mem)
(MOVLload [off] {sym} (ADDL ptr idx) mem) && ptr.Op != OpSB -> (MOVLloadidx1 [off] {sym} ptr idx mem)
(MOVSSload [off] {sym} (ADDL ptr idx) mem) && ptr.Op != OpSB -> (MOVSSloadidx1 [off] {sym} ptr idx mem)
(MOVSDload [off] {sym} (ADDL ptr idx) mem) && ptr.Op != OpSB -> (MOVSDloadidx1 [off] {sym} ptr idx mem)
(MOVBstore [off] {sym} (ADDL ptr idx) val mem) && ptr.Op != OpSB -> (MOVBstoreidx1 [off] {sym} ptr idx val mem)
(MOVWstore [off] {sym} (ADDL ptr idx) val mem) && ptr.Op != OpSB -> (MOVWstoreidx1 [off] {sym} ptr idx val mem)
(MOVLstore [off] {sym} (ADDL ptr idx) val mem) && ptr.Op != OpSB -> (MOVLstoreidx1 [off] {sym} ptr idx val mem)
(MOVSSstore [off] {sym} (ADDL ptr idx) val mem) && ptr.Op != OpSB -> (MOVSSstoreidx1 [off] {sym} ptr idx val mem)
(MOVSDstore [off] {sym} (ADDL ptr idx) val mem) && ptr.Op != OpSB -> (MOVSDstoreidx1 [off] {sym} ptr idx val mem)
(MOV(B|W|L|SS|SD)load [off] {sym} (ADDL ptr idx) mem) && ptr.Op != OpSB -> (MOV(B|W|L|SS|SD)loadidx1 [off] {sym} ptr idx mem)
(MOV(B|W|L|SS|SD)store [off] {sym} (ADDL ptr idx) val mem) && ptr.Op != OpSB -> (MOV(B|W|L|SS|SD)storeidx1 [off] {sym} ptr idx val mem)
(MOVBstoreconst [x] {sym1} (LEAL1 [off] {sym2} ptr idx) mem) && canMergeSym(sym1, sym2) ->
(MOVBstoreconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVWstoreconst [x] {sym1} (LEAL1 [off] {sym2} ptr idx) mem) && canMergeSym(sym1, sym2) ->
(MOVWstoreconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
(MOV(B|W|L)storeconst [x] {sym1} (LEAL1 [off] {sym2} ptr idx) mem) && canMergeSym(sym1, sym2) ->
(MOV(B|W|L)storeconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVWstoreconst [x] {sym1} (LEAL2 [off] {sym2} ptr idx) mem) && canMergeSym(sym1, sym2) ->
(MOVWstoreconstidx2 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVLstoreconst [x] {sym1} (LEAL1 [off] {sym2} ptr idx) mem) && canMergeSym(sym1, sym2) ->
(MOVLstoreconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVLstoreconst [x] {sym1} (LEAL4 [off] {sym2} ptr idx) mem) && canMergeSym(sym1, sym2) ->
(MOVLstoreconstidx4 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVBstoreconst [x] {sym} (ADDL ptr idx) mem) -> (MOVBstoreconstidx1 [x] {sym} ptr idx mem)
(MOVWstoreconst [x] {sym} (ADDL ptr idx) mem) -> (MOVWstoreconstidx1 [x] {sym} ptr idx mem)
(MOVLstoreconst [x] {sym} (ADDL ptr idx) mem) -> (MOVLstoreconstidx1 [x] {sym} ptr idx mem)
(MOV(B|W|L)storeconst [x] {sym} (ADDL ptr idx) mem) -> (MOV(B|W|L)storeconstidx1 [x] {sym} ptr idx mem)
// combine SHLL into indexed loads and stores
(MOVWloadidx1 [c] {sym} ptr (SHLLconst [1] idx) mem) -> (MOVWloadidx2 [c] {sym} ptr idx mem)
@ -810,76 +750,64 @@
(MOVLstoreconstidx1 [c] {sym} ptr (SHLLconst [2] idx) mem) -> (MOVLstoreconstidx4 [c] {sym} ptr idx mem)
// combine ADDL into indexed loads and stores
(MOVBloadidx1 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVBloadidx1 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVWloadidx1 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVWloadidx1 [int64(int32(c+d))] {sym} ptr idx mem)
(MOV(B|W|L|SS|SD)loadidx1 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOV(B|W|L|SS|SD)loadidx1 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVWloadidx2 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVWloadidx2 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVLloadidx1 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVLloadidx1 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVLloadidx4 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVLloadidx4 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVSSloadidx1 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVSSloadidx1 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVSSloadidx4 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVSSloadidx4 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVSDloadidx1 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVSDloadidx1 [int64(int32(c+d))] {sym} ptr idx mem)
(MOV(L|SS)loadidx4 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOV(L|SS)loadidx4 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVSDloadidx8 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVSDloadidx8 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVBstoreidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVBstoreidx1 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVWstoreidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVWstoreidx1 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVWstoreidx2 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVWstoreidx2 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVLstoreidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVLstoreidx1 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVLstoreidx4 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVLstoreidx4 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVSSstoreidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVSSstoreidx1 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVSSstoreidx4 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVSSstoreidx4 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVSDstoreidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVSDstoreidx1 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVSDstoreidx8 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVSDstoreidx8 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOV(B|W|L|SS|SD)storeidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOV(B|W|L|SS|SD)storeidx1 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVWstoreidx2 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVWstoreidx2 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOV(L|SS)storeidx4 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOV(L|SS)storeidx4 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVSDstoreidx8 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVSDstoreidx8 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVBloadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVBloadidx1 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVWloadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVWloadidx1 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVWloadidx2 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVWloadidx2 [int64(int32(c+2*d))] {sym} ptr idx mem)
(MOVLloadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVLloadidx1 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVLloadidx4 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVLloadidx4 [int64(int32(c+4*d))] {sym} ptr idx mem)
(MOVSSloadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVSSloadidx1 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVSSloadidx4 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVSSloadidx4 [int64(int32(c+4*d))] {sym} ptr idx mem)
(MOVSDloadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVSDloadidx1 [int64(int32(c+d))] {sym} ptr idx mem)
(MOV(B|W|L|SS|SD)loadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOV(B|W|L|SS|SD)loadidx1 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVWloadidx2 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVWloadidx2 [int64(int32(c+2*d))] {sym} ptr idx mem)
(MOV(L|SS)loadidx4 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOV(L|SS)loadidx4 [int64(int32(c+4*d))] {sym} ptr idx mem)
(MOVSDloadidx8 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVSDloadidx8 [int64(int32(c+8*d))] {sym} ptr idx mem)
(MOVBstoreidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVBstoreidx1 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVWstoreidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVWstoreidx1 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOV(B|W|L|SS|SD)storeidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOV(B|W|L|SS|SD)storeidx1 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVWstoreidx2 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVWstoreidx2 [int64(int32(c+2*d))] {sym} ptr idx val mem)
(MOVLstoreidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVLstoreidx1 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVLstoreidx4 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVLstoreidx4 [int64(int32(c+4*d))] {sym} ptr idx val mem)
(MOVSSstoreidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVSSstoreidx1 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVSSstoreidx4 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVSSstoreidx4 [int64(int32(c+4*d))] {sym} ptr idx val mem)
(MOVSDstoreidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVSDstoreidx1 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOV(L|SS)storeidx4 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOV(L|SS)storeidx4 [int64(int32(c+4*d))] {sym} ptr idx val mem)
(MOVSDstoreidx8 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVSDstoreidx8 [int64(int32(c+8*d))] {sym} ptr idx val mem)
// Merge load/store to op
((ADD|AND|OR|XOR|SUB|MUL)L x l:(MOVLload [off] {sym} ptr mem)) && canMergeLoad(v, l, x) && clobber(l) -> ((ADD|AND|OR|XOR|SUB|MUL)Lload x [off] {sym} ptr mem)
((ADD|AND|OR|XOR|SUB|MUL)L x l:(MOVLloadidx4 [off] {sym} ptr idx mem)) && canMergeLoad(v, l, x) && clobber(l) ->
((ADD|AND|OR|XOR|SUB|MUL)Lloadidx4 x [off] {sym} ptr idx mem)
((ADD|SUB|MUL|AND|OR|XOR)Lload [off1] {sym1} val (LEAL4 [off2] {sym2} ptr idx) mem)
&& is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
((ADD|SUB|MUL|AND|OR|XOR)Lloadidx4 [off1+off2] {mergeSym(sym1,sym2)} val ptr idx mem)
((ADD|SUB|MUL|DIV)SD x l:(MOVSDload [off] {sym} ptr mem)) && canMergeLoad(v, l, x) && !config.use387 && clobber(l) -> ((ADD|SUB|MUL|DIV)SDload x [off] {sym} ptr mem)
((ADD|SUB|MUL|DIV)SS x l:(MOVSSload [off] {sym} ptr mem)) && canMergeLoad(v, l, x) && !config.use387 && clobber(l) -> ((ADD|SUB|MUL|DIV)SSload x [off] {sym} ptr mem)
(MOVLstore {sym} [off] ptr y:((ADD|AND|OR|XOR)Lload x [off] {sym} ptr mem) mem) && y.Uses==1 && clobber(y) -> ((ADD|AND|OR|XOR)Lmodify [off] {sym} ptr x mem)
(MOVLstore {sym} [off] ptr y:((ADD|SUB|AND|OR|XOR)L l:(MOVLload [off] {sym} ptr mem) x) mem) && y.Uses==1 && l.Uses==1 && clobber(y) && clobber(l) ->
((ADD|SUB|AND|OR|XOR)Lmodify [off] {sym} ptr x mem)
(MOVLstoreidx4 {sym} [off] ptr idx y:((ADD|AND|OR|XOR)Lloadidx4 x [off] {sym} ptr idx mem) mem) && y.Uses==1 && clobber(y) ->
((ADD|AND|OR|XOR)Lmodifyidx4 [off] {sym} ptr idx x mem)
(MOVLstoreidx4 {sym} [off] ptr idx y:((ADD|SUB|AND|OR|XOR)L l:(MOVLloadidx4 [off] {sym} ptr idx mem) x) mem) && y.Uses==1 && l.Uses==1 && clobber(y) && clobber(l) ->
((ADD|SUB|AND|OR|XOR)Lmodifyidx4 [off] {sym} ptr idx x mem)
(MOVLstore {sym} [off] ptr y:((ADD|AND|OR|XOR)Lconst [c] l:(MOVLload [off] {sym} ptr mem)) mem)
&& y.Uses==1 && l.Uses==1 && clobber(y) && clobber(l) && validValAndOff(c,off) ->
((ADD|AND|OR|XOR)Lconstmodify [makeValAndOff(c,off)] {sym} ptr mem)
(MOVLstoreidx4 {sym} [off] ptr idx y:((ADD|AND|OR|XOR)Lconst [c] l:(MOVLloadidx4 [off] {sym} ptr idx mem)) mem)
&& y.Uses==1 && l.Uses==1 && clobber(y) && clobber(l) && validValAndOff(c,off) ->
((ADD|AND|OR|XOR)Lconstmodifyidx4 [makeValAndOff(c,off)] {sym} ptr idx mem)
((ADD|AND|OR|XOR)Lmodifyidx4 [off] {sym} ptr idx (MOVLconst [c]) mem) && validValAndOff(c,off) ->
((ADD|AND|OR|XOR)Lconstmodifyidx4 [makeValAndOff(c,off)] {sym} ptr idx mem)
(SUBLmodifyidx4 [off] {sym} ptr idx (MOVLconst [c]) mem) && validValAndOff(-c,off) ->
(ADDLconstmodifyidx4 [makeValAndOff(-c,off)] {sym} ptr idx mem)
(MOVBstoreconstidx1 [x] {sym} (ADDLconst [c] ptr) idx mem) ->
(MOVBstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOVWstoreconstidx1 [x] {sym} (ADDLconst [c] ptr) idx mem) ->
(MOVWstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOV(B|W|L)storeconstidx1 [x] {sym} (ADDLconst [c] ptr) idx mem) ->
(MOV(B|W|L)storeconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOVWstoreconstidx2 [x] {sym} (ADDLconst [c] ptr) idx mem) ->
(MOVWstoreconstidx2 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOVLstoreconstidx1 [x] {sym} (ADDLconst [c] ptr) idx mem) ->
(MOVLstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOVLstoreconstidx4 [x] {sym} (ADDLconst [c] ptr) idx mem) ->
(MOVLstoreconstidx4 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOVBstoreconstidx1 [x] {sym} ptr (ADDLconst [c] idx) mem) ->
(MOVBstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOVWstoreconstidx1 [x] {sym} ptr (ADDLconst [c] idx) mem) ->
(MOVWstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOV(B|W|L)storeconstidx1 [x] {sym} ptr (ADDLconst [c] idx) mem) ->
(MOV(B|W|L)storeconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOVWstoreconstidx2 [x] {sym} ptr (ADDLconst [c] idx) mem) ->
(MOVWstoreconstidx2 [ValAndOff(x).add(2*c)] {sym} ptr idx mem)
(MOVLstoreconstidx1 [x] {sym} ptr (ADDLconst [c] idx) mem) ->
(MOVLstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOVLstoreconstidx4 [x] {sym} ptr (ADDLconst [c] idx) mem) ->
(MOVLstoreconstidx4 [ValAndOff(x).add(4*c)] {sym} ptr idx mem)
@ -1199,11 +1127,21 @@
&& ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p mem)
(MOVBstoreconst [a] {s} p x:(MOVBstoreconst [c] {s} p mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p mem)
(MOVWstoreconst [c] {s} p x:(MOVWstoreconst [a] {s} p mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p mem)
(MOVWstoreconst [a] {s} p x:(MOVWstoreconst [c] {s} p mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p mem)
(MOVBstoreconstidx1 [c] {s} p i x:(MOVBstoreconstidx1 [a] {s} p i mem))
&& x.Uses == 1
@ -1223,10 +1161,14 @@
-> (MOVLstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p (SHLLconst <i.Type> [1] i) mem)
// Combine stores into larger (unaligned) stores.
(MOVBstore [i] {s} p (SHRLconst [8] w) x:(MOVBstore [i-1] {s} p w mem))
(MOVBstore [i] {s} p (SHR(W|L)const [8] w) x:(MOVBstore [i-1] {s} p w mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVWstore [i-1] {s} p w mem)
(MOVBstore [i] {s} p w x:(MOVBstore {s} [i+1] p (SHR(W|L)const [8] w) mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVWstore [i] {s} p w mem)
(MOVBstore [i] {s} p (SHRLconst [j] w) x:(MOVBstore [i-1] {s} p w0:(SHRLconst [j-8] w) mem))
&& x.Uses == 1
&& clobber(x)
@ -1240,10 +1182,14 @@
&& clobber(x)
-> (MOVLstore [i-2] {s} p w0 mem)
(MOVBstoreidx1 [i] {s} p idx (SHRLconst [8] w) x:(MOVBstoreidx1 [i-1] {s} p idx w mem))
(MOVBstoreidx1 [i] {s} p idx (SHR(L|W)const [8] w) x:(MOVBstoreidx1 [i-1] {s} p idx w mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVWstoreidx1 [i-1] {s} p idx w mem)
(MOVBstoreidx1 [i] {s} p idx w x:(MOVBstoreidx1 [i+1] {s} p idx (SHR(L|W)const [8] w) mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVWstoreidx1 [i] {s} p idx w mem)
(MOVBstoreidx1 [i] {s} p idx (SHRLconst [j] w) x:(MOVBstoreidx1 [i-1] {s} p idx w0:(SHRLconst [j-8] w) mem))
&& x.Uses == 1
&& clobber(x)
@ -1283,3 +1229,7 @@
(CMPLload {sym} [off] ptr (MOVLconst [c]) mem) && validValAndOff(int64(int32(c)),off) -> (CMPLconstload {sym} [makeValAndOff(int64(int32(c)),off)] ptr mem)
(CMPWload {sym} [off] ptr (MOVLconst [c]) mem) && validValAndOff(int64(int16(c)),off) -> (CMPWconstload {sym} [makeValAndOff(int64(int16(c)),off)] ptr mem)
(CMPBload {sym} [off] ptr (MOVLconst [c]) mem) && validValAndOff(int64(int8(c)),off) -> (CMPBconstload {sym} [makeValAndOff(int64(int8(c)),off)] ptr mem)
(MOVBload [off] {sym} (SB) _) && symIsRO(sym) -> (MOVLconst [int64(read8(sym, off))])
(MOVWload [off] {sym} (SB) _) && symIsRO(sym) -> (MOVLconst [int64(read16(sym, off, config.BigEndian))])
(MOVLload [off] {sym} (SB) _) && symIsRO(sym) -> (MOVLconst [int64(int32(read32(sym, off, config.BigEndian)))])

View file

@ -126,9 +126,10 @@ func init() {
readflags = regInfo{inputs: nil, outputs: gponly}
flagsgpax = regInfo{inputs: nil, clobbers: ax, outputs: []regMask{gp &^ ax}}
gpload = regInfo{inputs: []regMask{gpspsb, 0}, outputs: gponly}
gp21load = regInfo{inputs: []regMask{gp, gpspsb, 0}, outputs: gponly}
gploadidx = regInfo{inputs: []regMask{gpspsb, gpsp, 0}, outputs: gponly}
gpload = regInfo{inputs: []regMask{gpspsb, 0}, outputs: gponly}
gp21load = regInfo{inputs: []regMask{gp, gpspsb, 0}, outputs: gponly}
gploadidx = regInfo{inputs: []regMask{gpspsb, gpsp, 0}, outputs: gponly}
gp21loadidx = regInfo{inputs: []regMask{gp, gpspsb, gpsp, 0}, outputs: gponly}
gpstore = regInfo{inputs: []regMask{gpspsb, gpsp, 0}}
gpstoreconst = regInfo{inputs: []regMask{gpspsb, 0}}
@ -206,6 +207,8 @@ func init() {
{name: "MULL", argLength: 2, reg: gp21, asm: "IMULL", commutative: true, resultInArg0: true, clobberFlags: true}, // arg0 * arg1
{name: "MULLconst", argLength: 1, reg: gp11, asm: "IMUL3L", aux: "Int32", clobberFlags: true}, // arg0 * auxint
{name: "MULLU", argLength: 2, reg: regInfo{inputs: []regMask{ax, gpsp}, outputs: []regMask{ax, 0}, clobbers: dx}, typ: "(UInt32,Flags)", asm: "MULL", commutative: true, clobberFlags: true}, // Let x = arg0*arg1 (full 32x32->64 unsigned multiply). Returns uint32(x), and flags set to overflow if uint32(x) != x.
{name: "HMULL", argLength: 2, reg: gp21hmul, commutative: true, asm: "IMULL", clobberFlags: true}, // (arg0 * arg1) >> width
{name: "HMULLU", argLength: 2, reg: gp21hmul, commutative: true, asm: "MULL", clobberFlags: true}, // (arg0 * arg1) >> width
@ -213,15 +216,16 @@ func init() {
{name: "AVGLU", argLength: 2, reg: gp21, commutative: true, resultInArg0: true, clobberFlags: true}, // (arg0 + arg1) / 2 as unsigned, all 32 result bits
{name: "DIVL", argLength: 2, reg: gp11div, asm: "IDIVL", clobberFlags: true}, // arg0 / arg1
{name: "DIVW", argLength: 2, reg: gp11div, asm: "IDIVW", clobberFlags: true}, // arg0 / arg1
{name: "DIVLU", argLength: 2, reg: gp11div, asm: "DIVL", clobberFlags: true}, // arg0 / arg1
{name: "DIVWU", argLength: 2, reg: gp11div, asm: "DIVW", clobberFlags: true}, // arg0 / arg1
// For DIVL, DIVW, MODL and MODW, AuxInt non-zero means that the divisor has been proved to be not -1.
{name: "DIVL", argLength: 2, reg: gp11div, asm: "IDIVL", aux: "Bool", clobberFlags: true}, // arg0 / arg1
{name: "DIVW", argLength: 2, reg: gp11div, asm: "IDIVW", aux: "Bool", clobberFlags: true}, // arg0 / arg1
{name: "DIVLU", argLength: 2, reg: gp11div, asm: "DIVL", clobberFlags: true}, // arg0 / arg1
{name: "DIVWU", argLength: 2, reg: gp11div, asm: "DIVW", clobberFlags: true}, // arg0 / arg1
{name: "MODL", argLength: 2, reg: gp11mod, asm: "IDIVL", clobberFlags: true}, // arg0 % arg1
{name: "MODW", argLength: 2, reg: gp11mod, asm: "IDIVW", clobberFlags: true}, // arg0 % arg1
{name: "MODLU", argLength: 2, reg: gp11mod, asm: "DIVL", clobberFlags: true}, // arg0 % arg1
{name: "MODWU", argLength: 2, reg: gp11mod, asm: "DIVW", clobberFlags: true}, // arg0 % arg1
{name: "MODL", argLength: 2, reg: gp11mod, asm: "IDIVL", aux: "Bool", clobberFlags: true}, // arg0 % arg1
{name: "MODW", argLength: 2, reg: gp11mod, asm: "IDIVW", aux: "Bool", clobberFlags: true}, // arg0 % arg1
{name: "MODLU", argLength: 2, reg: gp11mod, asm: "DIVL", clobberFlags: true}, // arg0 % arg1
{name: "MODWU", argLength: 2, reg: gp11mod, asm: "DIVW", clobberFlags: true}, // arg0 % arg1
{name: "ANDL", argLength: 2, reg: gp21, asm: "ANDL", commutative: true, resultInArg0: true, clobberFlags: true}, // arg0 & arg1
{name: "ANDLconst", argLength: 1, reg: gp11, asm: "ANDL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 & auxint
@ -281,6 +285,7 @@ func init() {
{name: "ROLWconst", argLength: 1, reg: gp11, asm: "ROLW", aux: "Int16", resultInArg0: true, clobberFlags: true}, // arg0 rotate left auxint, rotate amount 0-15
{name: "ROLBconst", argLength: 1, reg: gp11, asm: "ROLB", aux: "Int8", resultInArg0: true, clobberFlags: true}, // arg0 rotate left auxint, rotate amount 0-7
// binary-op with a memory source operand
{name: "ADDLload", argLength: 3, reg: gp21load, asm: "ADDL", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 + tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{name: "SUBLload", argLength: 3, reg: gp21load, asm: "SUBL", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 - tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{name: "MULLload", argLength: 3, reg: gp21load, asm: "IMULL", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 * tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
@ -288,6 +293,14 @@ func init() {
{name: "ORLload", argLength: 3, reg: gp21load, asm: "ORL", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 | tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{name: "XORLload", argLength: 3, reg: gp21load, asm: "XORL", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 ^ tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
// binary-op with an indexed memory source operand
{name: "ADDLloadidx4", argLength: 4, reg: gp21loadidx, asm: "ADDL", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 + tmp, tmp loaded from arg1+arg2*4+auxint+aux, arg3 = mem
{name: "SUBLloadidx4", argLength: 4, reg: gp21loadidx, asm: "SUBL", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 - tmp, tmp loaded from arg1+arg2*4+auxint+aux, arg3 = mem
{name: "MULLloadidx4", argLength: 4, reg: gp21loadidx, asm: "IMULL", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 * tmp, tmp loaded from arg1+arg2*4+auxint+aux, arg3 = mem
{name: "ANDLloadidx4", argLength: 4, reg: gp21loadidx, asm: "ANDL", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 & tmp, tmp loaded from arg1+arg2*4+auxint+aux, arg3 = mem
{name: "ORLloadidx4", argLength: 4, reg: gp21loadidx, asm: "ORL", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 | tmp, tmp loaded from arg1+arg2*4+auxint+aux, arg3 = mem
{name: "XORLloadidx4", argLength: 4, reg: gp21loadidx, asm: "XORL", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 ^ tmp, tmp loaded from arg1+arg2*4+auxint+aux, arg3 = mem
// unary ops
{name: "NEGL", argLength: 1, reg: gp11, asm: "NEGL", resultInArg0: true, clobberFlags: true}, // -arg0
@ -316,6 +329,7 @@ func init() {
{name: "SETBE", argLength: 1, reg: readflags, asm: "SETLS"}, // extract unsigned <= condition from arg0
{name: "SETA", argLength: 1, reg: readflags, asm: "SETHI"}, // extract unsigned > condition from arg0
{name: "SETAE", argLength: 1, reg: readflags, asm: "SETCC"}, // extract unsigned >= condition from arg0
{name: "SETO", argLength: 1, reg: readflags, asm: "SETOS"}, // extract if overflow flag is set from arg0
// Need different opcodes for floating point conditions because
// any comparison involving a NaN is always FALSE and thus
// the patterns for inverting conditions cannot be used.
@ -367,12 +381,25 @@ func init() {
{name: "ORLmodify", argLength: 3, reg: gpstore, asm: "ORL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, clobberFlags: true, symEffect: "Read,Write"}, // *(arg0+auxint+aux) |= arg1, arg2=mem
{name: "XORLmodify", argLength: 3, reg: gpstore, asm: "XORL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, clobberFlags: true, symEffect: "Read,Write"}, // *(arg0+auxint+aux) ^= arg1, arg2=mem
// direct binary-op on indexed memory (read-modify-write)
{name: "ADDLmodifyidx4", argLength: 4, reg: gpstoreidx, asm: "ADDL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, clobberFlags: true, symEffect: "Read,Write"}, // *(arg0+arg1*4+auxint+aux) += arg2, arg3=mem
{name: "SUBLmodifyidx4", argLength: 4, reg: gpstoreidx, asm: "SUBL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, clobberFlags: true, symEffect: "Read,Write"}, // *(arg0+arg1*4+auxint+aux) -= arg2, arg3=mem
{name: "ANDLmodifyidx4", argLength: 4, reg: gpstoreidx, asm: "ANDL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, clobberFlags: true, symEffect: "Read,Write"}, // *(arg0+arg1*4+auxint+aux) &= arg2, arg3=mem
{name: "ORLmodifyidx4", argLength: 4, reg: gpstoreidx, asm: "ORL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, clobberFlags: true, symEffect: "Read,Write"}, // *(arg0+arg1*4+auxint+aux) |= arg2, arg3=mem
{name: "XORLmodifyidx4", argLength: 4, reg: gpstoreidx, asm: "XORL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, clobberFlags: true, symEffect: "Read,Write"}, // *(arg0+arg1*4+auxint+aux) ^= arg2, arg3=mem
// direct binary-op on memory with a constant (read-modify-write)
{name: "ADDLconstmodify", argLength: 2, reg: gpstoreconst, asm: "ADDL", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Read,Write"}, // add ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux, arg1=mem
{name: "ANDLconstmodify", argLength: 2, reg: gpstoreconst, asm: "ANDL", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Read,Write"}, // and ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux, arg1=mem
{name: "ORLconstmodify", argLength: 2, reg: gpstoreconst, asm: "ORL", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Read,Write"}, // or ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux, arg1=mem
{name: "XORLconstmodify", argLength: 2, reg: gpstoreconst, asm: "XORL", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Read,Write"}, // xor ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux, arg1=mem
// direct binary-op on indexed memory with a constant (read-modify-write)
{name: "ADDLconstmodifyidx4", argLength: 3, reg: gpstoreconstidx, asm: "ADDL", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Read,Write"}, // add ValAndOff(AuxInt).Val() to arg0+arg1*4+ValAndOff(AuxInt).Off()+aux, arg2=mem
{name: "ANDLconstmodifyidx4", argLength: 3, reg: gpstoreconstidx, asm: "ANDL", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Read,Write"}, // and ValAndOff(AuxInt).Val() to arg0+arg1*4+ValAndOff(AuxInt).Off()+aux, arg2=mem
{name: "ORLconstmodifyidx4", argLength: 3, reg: gpstoreconstidx, asm: "ORL", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Read,Write"}, // or ValAndOff(AuxInt).Val() to arg0+arg1*4+ValAndOff(AuxInt).Off()+aux, arg2=mem
{name: "XORLconstmodifyidx4", argLength: 3, reg: gpstoreconstidx, asm: "XORL", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Read,Write"}, // xor ValAndOff(AuxInt).Val() to arg0+arg1*4+ValAndOff(AuxInt).Off()+aux, arg2=mem
// indexed loads/stores
{name: "MOVBloadidx1", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVBLZX", aux: "SymOff", symEffect: "Read"}, // load a byte from arg0+arg1+auxint+aux. arg2=mem
{name: "MOVWloadidx1", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVWLZX", aux: "SymOff", symEffect: "Read"}, // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem
@ -530,6 +557,8 @@ func init() {
{name: "LE"},
{name: "GT"},
{name: "GE"},
{name: "OS"},
{name: "OC"},
{name: "ULT"},
{name: "ULE"},
{name: "UGT"},

View file

@ -16,10 +16,14 @@
(Mul(64|32|16|8) x y) -> (MUL(Q|L|L|L) x y)
(Mul(32|64)F x y) -> (MULS(S|D) x y)
(Select0 (Mul64uover x y)) -> (Select0 <typ.UInt64> (MULQU x y))
(Select0 (Mul32uover x y)) -> (Select0 <typ.UInt32> (MULLU x y))
(Select1 (Mul(64|32)uover x y)) -> (SETO (Select1 <types.TypeFlags> (MUL(Q|L)U x y)))
(Hmul(64|32) x y) -> (HMUL(Q|L) x y)
(Hmul(64|32)u x y) -> (HMUL(Q|L)U x y)
(Div(64|32|16) x y) -> (Select0 (DIV(Q|L|W) x y))
(Div(64|32|16) [a] x y) -> (Select0 (DIV(Q|L|W) [a] x y))
(Div8 x y) -> (Select0 (DIVW (SignExt8to16 x) (SignExt8to16 y)))
(Div(64|32|16)u x y) -> (Select0 (DIV(Q|L|W)U x y))
(Div8u x y) -> (Select0 (DIVWU (ZeroExt8to16 x) (ZeroExt8to16 y)))
@ -30,7 +34,7 @@
(Avg64u x y) -> (AVGQU x y)
(Mod(64|32|16) x y) -> (Select1 (DIV(Q|L|W) x y))
(Mod(64|32|16) [a] x y) -> (Select1 (DIV(Q|L|W) [a] x y))
(Mod8 x y) -> (Select1 (DIVW (SignExt8to16 x) (SignExt8to16 y)))
(Mod(64|32|16)u x y) -> (Select1 (DIV(Q|L|W)U x y))
(Mod8u x y) -> (Select1 (DIVWU (ZeroExt8to16 x) (ZeroExt8to16 y)))
@ -480,6 +484,7 @@
(If (SETBE cmp) yes no) -> (ULE cmp yes no)
(If (SETA cmp) yes no) -> (UGT cmp yes no)
(If (SETAE cmp) yes no) -> (UGE cmp yes no)
(If (SETO cmp) yes no) -> (OS cmp yes no)
// Special case for floating point - LF/LEF not generated
(If (SETGF cmp) yes no) -> (UGT cmp yes no)
@ -542,6 +547,7 @@
(NE (TESTB (SETBE cmp) (SETBE cmp)) yes no) -> (ULE cmp yes no)
(NE (TESTB (SETA cmp) (SETA cmp)) yes no) -> (UGT cmp yes no)
(NE (TESTB (SETAE cmp) (SETAE cmp)) yes no) -> (UGE cmp yes no)
(NE (TESTB (SETO cmp) (SETO cmp)) yes no) -> (OS cmp yes no)
// Recognize bit tests: a&(1<<b) != 0 for b suitably bounded
// Note that BTx instructions use the carry bit, so we need to convert tests for zero flag
@ -1073,11 +1079,11 @@
// Fold constants into stores.
(MOVQstore [off] {sym} ptr (MOVQconst [c]) mem) && validValAndOff(c,off) ->
(MOVQstoreconst [makeValAndOff(c,off)] {sym} ptr mem)
(MOVLstore [off] {sym} ptr (MOVLconst [c]) mem) && validOff(off) ->
(MOVLstore [off] {sym} ptr (MOV(L|Q)const [c]) mem) && validOff(off) ->
(MOVLstoreconst [makeValAndOff(int64(int32(c)),off)] {sym} ptr mem)
(MOVWstore [off] {sym} ptr (MOVLconst [c]) mem) && validOff(off) ->
(MOVWstore [off] {sym} ptr (MOV(L|Q)const [c]) mem) && validOff(off) ->
(MOVWstoreconst [makeValAndOff(int64(int16(c)),off)] {sym} ptr mem)
(MOVBstore [off] {sym} ptr (MOVLconst [c]) mem) && validOff(off) ->
(MOVBstore [off] {sym} ptr (MOV(L|Q)const [c]) mem) && validOff(off) ->
(MOVBstoreconst [makeValAndOff(int64(int8(c)),off)] {sym} ptr mem)
// Fold address offsets into constant stores.
@ -2119,16 +2125,31 @@
&& ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p mem)
(MOVBstoreconst [a] {s} p x:(MOVBstoreconst [c] {s} p mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p mem)
(MOVWstoreconst [c] {s} p x:(MOVWstoreconst [a] {s} p mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p mem)
(MOVWstoreconst [a] {s} p x:(MOVWstoreconst [c] {s} p mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p mem)
(MOVLstoreconst [c] {s} p x:(MOVLstoreconst [a] {s} p mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 4 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVQstore [ValAndOff(a).Off()] {s} p (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
(MOVLstoreconst [a] {s} p x:(MOVLstoreconst [c] {s} p mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 4 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVQstore [ValAndOff(a).Off()] {s} p (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
(MOVQstoreconst [c] {s} p x:(MOVQstoreconst [c2] {s} p mem))
&& config.useSSE
&& x.Uses == 1
@ -2170,6 +2191,10 @@
&& x.Uses == 1
&& clobber(x)
-> (MOVWstore [i-1] {s} p w mem)
(MOVBstore [i] {s} p w x:(MOVBstore [i+1] {s} p (SHR(W|L|Q)const [8] w) mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVWstore [i] {s} p w mem)
(MOVBstore [i] {s} p (SHR(L|Q)const [j] w) x:(MOVBstore [i-1] {s} p w0:(SHR(L|Q)const [j-8] w) mem))
&& x.Uses == 1
&& clobber(x)
@ -2487,3 +2512,8 @@
&& validValAndOff(0,off)
&& clobber(l) ->
@l.Block (CMP(Q|L|W|B)constload {sym} [makeValAndOff(0,off)] ptr mem)
(MOVBload [off] {sym} (SB) _) && symIsRO(sym) -> (MOVLconst [int64(read8(sym, off))])
(MOVWload [off] {sym} (SB) _) && symIsRO(sym) -> (MOVLconst [int64(read16(sym, off, config.BigEndian))])
(MOVLload [off] {sym} (SB) _) && symIsRO(sym) -> (MOVQconst [int64(read32(sym, off, config.BigEndian))])
(MOVQload [off] {sym} (SB) _) && symIsRO(sym) -> (MOVQconst [int64(read64(sym, off, config.BigEndian))])

View file

@ -210,6 +210,9 @@ func init() {
{name: "MULQconst", argLength: 1, reg: gp11, asm: "IMUL3Q", aux: "Int32", clobberFlags: true}, // arg0 * auxint
{name: "MULLconst", argLength: 1, reg: gp11, asm: "IMUL3L", aux: "Int32", clobberFlags: true}, // arg0 * auxint
{name: "MULLU", argLength: 2, reg: regInfo{inputs: []regMask{ax, gpsp}, outputs: []regMask{ax, 0}, clobbers: dx}, typ: "(UInt32,Flags)", asm: "MULL", commutative: true, clobberFlags: true}, // Let x = arg0*arg1 (full 32x32->64 unsigned multiply). Returns uint32(x), and flags set to overflow if uint32(x) != x.
{name: "MULQU", argLength: 2, reg: regInfo{inputs: []regMask{ax, gpsp}, outputs: []regMask{ax, 0}, clobbers: dx}, typ: "(UInt64,Flags)", asm: "MULQ", commutative: true, clobberFlags: true}, // Let x = arg0*arg1 (full 64x64->128 unsigned multiply). Returns uint64(x), and flags set to overflow if uint64(x) != x.
{name: "HMULQ", argLength: 2, reg: gp21hmul, commutative: true, asm: "IMULQ", clobberFlags: true}, // (arg0 * arg1) >> width
{name: "HMULL", argLength: 2, reg: gp21hmul, commutative: true, asm: "IMULL", clobberFlags: true}, // (arg0 * arg1) >> width
{name: "HMULQU", argLength: 2, reg: gp21hmul, commutative: true, asm: "MULQ", clobberFlags: true}, // (arg0 * arg1) >> width
@ -217,9 +220,11 @@ func init() {
{name: "AVGQU", argLength: 2, reg: gp21, commutative: true, resultInArg0: true, clobberFlags: true}, // (arg0 + arg1) / 2 as unsigned, all 64 result bits
{name: "DIVQ", argLength: 2, reg: gp11div, typ: "(Int64,Int64)", asm: "IDIVQ", clobberFlags: true}, // [arg0 / arg1, arg0 % arg1]
{name: "DIVL", argLength: 2, reg: gp11div, typ: "(Int32,Int32)", asm: "IDIVL", clobberFlags: true}, // [arg0 / arg1, arg0 % arg1]
{name: "DIVW", argLength: 2, reg: gp11div, typ: "(Int16,Int16)", asm: "IDIVW", clobberFlags: true}, // [arg0 / arg1, arg0 % arg1]
// For DIVQ, DIVL and DIVW, AuxInt non-zero means that the divisor has been proved to be not -1.
{name: "DIVQ", argLength: 2, reg: gp11div, typ: "(Int64,Int64)", asm: "IDIVQ", aux: "Bool", clobberFlags: true}, // [arg0 / arg1, arg0 % arg1]
{name: "DIVL", argLength: 2, reg: gp11div, typ: "(Int32,Int32)", asm: "IDIVL", aux: "Bool", clobberFlags: true}, // [arg0 / arg1, arg0 % arg1]
{name: "DIVW", argLength: 2, reg: gp11div, typ: "(Int16,Int16)", asm: "IDIVW", aux: "Bool", clobberFlags: true}, // [arg0 / arg1, arg0 % arg1]
{name: "DIVQU", argLength: 2, reg: gp11div, typ: "(UInt64,UInt64)", asm: "DIVQ", clobberFlags: true}, // [arg0 / arg1, arg0 % arg1]
{name: "DIVLU", argLength: 2, reg: gp11div, typ: "(UInt32,UInt32)", asm: "DIVL", clobberFlags: true}, // [arg0 / arg1, arg0 % arg1]
{name: "DIVWU", argLength: 2, reg: gp11div, typ: "(UInt16,UInt16)", asm: "DIVW", clobberFlags: true}, // [arg0 / arg1, arg0 % arg1]
@ -468,6 +473,7 @@ func init() {
{name: "SETBE", argLength: 1, reg: readflags, asm: "SETLS"}, // extract unsigned <= condition from arg0
{name: "SETA", argLength: 1, reg: readflags, asm: "SETHI"}, // extract unsigned > condition from arg0
{name: "SETAE", argLength: 1, reg: readflags, asm: "SETCC"}, // extract unsigned >= condition from arg0
{name: "SETO", argLength: 1, reg: readflags, asm: "SETOS"}, // extract if overflow flag is set from arg0
// Variants that store result to memory
{name: "SETEQstore", argLength: 3, reg: gpstoreconst, asm: "SETEQ", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // extract == condition from arg1 to arg0+auxint+aux, arg2=mem
{name: "SETNEstore", argLength: 3, reg: gpstoreconst, asm: "SETNE", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // extract != condition from arg1 to arg0+auxint+aux, arg2=mem
@ -754,6 +760,8 @@ func init() {
{name: "LE"},
{name: "GT"},
{name: "GE"},
{name: "OS"},
{name: "OC"},
{name: "ULT"},
{name: "ULE"},
{name: "UGT"},

View file

@ -1544,3 +1544,7 @@
(GE (CMPconst [0] l:(XORshiftLLreg x y z)) yes no) && l.Uses==1 -> (GE (TEQshiftLLreg x y z) yes no)
(GE (CMPconst [0] l:(XORshiftRLreg x y z)) yes no) && l.Uses==1 -> (GE (TEQshiftRLreg x y z) yes no)
(GE (CMPconst [0] l:(XORshiftRAreg x y z)) yes no) && l.Uses==1 -> (GE (TEQshiftRAreg x y z) yes no)
(MOVBUload [off] {sym} (SB) _) && symIsRO(sym) -> (MOVWconst [int64(read8(sym, off))])
(MOVHUload [off] {sym} (SB) _) && symIsRO(sym) -> (MOVWconst [int64(read16(sym, off, config.BigEndian))])
(MOVWload [off] {sym} (SB) _) && symIsRO(sym) -> (MOVWconst [int64(int32(read32(sym, off, config.BigEndian)))])

View file

@ -2957,3 +2957,8 @@
(FSUBD a (FNMULD x y)) -> (FMADDD a x y)
(FSUBS (FNMULS x y) a) -> (FNMADDS a x y)
(FSUBD (FNMULD x y) a) -> (FNMADDD a x y)
(MOVBUload [off] {sym} (SB) _) && symIsRO(sym) -> (MOVDconst [int64(read8(sym, off))])
(MOVHUload [off] {sym} (SB) _) && symIsRO(sym) -> (MOVDconst [int64(read16(sym, off, config.BigEndian))])
(MOVWUload [off] {sym} (SB) _) && symIsRO(sym) -> (MOVDconst [int64(read32(sym, off, config.BigEndian))])
(MOVDload [off] {sym} (SB) _) && symIsRO(sym) -> (MOVDconst [int64(read64(sym, off, config.BigEndian))])

View file

@ -297,6 +297,8 @@
(Ctz64 x) -> (POPCNTD (ANDN <typ.Int64> (ADDconst <typ.Int64> [-1] x) x))
(Ctz32 x) -> (POPCNTW (MOVWZreg (ANDN <typ.Int> (ADDconst <typ.Int> [-1] x) x)))
(Ctz16 x) -> (POPCNTW (MOVHZreg (ANDN <typ.Int16> (ADDconst <typ.Int16> [-1] x) x)))
(Ctz8 x) -> (POPCNTB (MOVBZreg (ANDN <typ.UInt8> (ADDconst <typ.UInt8> [-1] x) x)))
(BitLen64 x) -> (SUB (MOVDconst [64]) (CNTLZD <typ.Int> x))
(BitLen32 x) -> (SUB (MOVDconst [32]) (CNTLZW <typ.Int> x))
@ -304,7 +306,7 @@
(PopCount64 x) -> (POPCNTD x)
(PopCount32 x) -> (POPCNTW (MOVWZreg x))
(PopCount16 x) -> (POPCNTW (MOVHZreg x))
(PopCount8 x) -> (POPCNTB (MOVBreg x))
(PopCount8 x) -> (POPCNTB (MOVBZreg x))
(And(64|32|16|8) x y) -> (AND x y)
(Or(64|32|16|8) x y) -> (OR x y)
@ -894,16 +896,19 @@
(MOVBstorezero [off1+off2] {mergeSym(sym1,sym2)} x mem)
// atomic intrinsics
(AtomicLoad(32|64|Ptr) ptr mem) -> (LoweredAtomicLoad(32|64|Ptr) ptr mem)
(AtomicLoad(32|64|Ptr) ptr mem) -> (LoweredAtomicLoad(32|64|Ptr) [1] ptr mem)
(AtomicLoadAcq32 ptr mem) -> (LoweredAtomicLoad32 [0] ptr mem)
(AtomicStore(32|64) ptr val mem) -> (LoweredAtomicStore(32|64) ptr val mem)
(AtomicStore(32|64) ptr val mem) -> (LoweredAtomicStore(32|64) [1] ptr val mem)
(AtomicStoreRel32 ptr val mem) -> (LoweredAtomicStore32 [0] ptr val mem)
//(AtomicStorePtrNoWB ptr val mem) -> (STLR ptr val mem)
(AtomicExchange(32|64) ptr val mem) -> (LoweredAtomicExchange(32|64) ptr val mem)
(AtomicAdd(32|64) ptr val mem) -> (LoweredAtomicAdd(32|64) ptr val mem)
(AtomicCompareAndSwap(32|64) ptr old new_ mem) -> (LoweredAtomicCas(32|64) ptr old new_ mem)
(AtomicCompareAndSwap(32|64) ptr old new_ mem) -> (LoweredAtomicCas(32|64) [1] ptr old new_ mem)
(AtomicCompareAndSwapRel32 ptr old new_ mem) -> (LoweredAtomicCas32 [0] ptr old new_ mem)
(AtomicAnd8 ptr val mem) -> (LoweredAtomicAnd8 ptr val mem)
(AtomicOr8 ptr val mem) -> (LoweredAtomicOr8 ptr val mem)
@ -956,7 +961,7 @@
(MOVWZreg (MOVDconst [c])) -> (MOVDconst [int64(uint32(c))])
// Lose widening ops fed to to stores
// Lose widening ops fed to stores
(MOVBstore [off] {sym} ptr (MOV(B|BZ|H|HZ|W|WZ)reg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
(MOVHstore [off] {sym} ptr (MOV(H|HZ|W|WZ)reg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
(MOVWstore [off] {sym} ptr (MOV(W|WZ)reg x) mem) -> (MOVWstore [off] {sym} ptr x mem)

View file

@ -470,12 +470,12 @@ func init() {
faultOnNilArg1: true,
},
{name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, typ: "Mem", faultOnNilArg0: true, hasSideEffects: true},
{name: "LoweredAtomicStore64", argLength: 3, reg: gpstore, typ: "Mem", faultOnNilArg0: true, hasSideEffects: true},
{name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, typ: "Mem", aux: "Int64", faultOnNilArg0: true, hasSideEffects: true},
{name: "LoweredAtomicStore64", argLength: 3, reg: gpstore, typ: "Mem", aux: "Int64", faultOnNilArg0: true, hasSideEffects: true},
{name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, typ: "UInt32", clobberFlags: true, faultOnNilArg0: true},
{name: "LoweredAtomicLoad64", argLength: 2, reg: gpload, typ: "Int64", clobberFlags: true, faultOnNilArg0: true},
{name: "LoweredAtomicLoadPtr", argLength: 2, reg: gpload, typ: "Int64", clobberFlags: true, faultOnNilArg0: true},
{name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, typ: "UInt32", aux: "Int64", clobberFlags: true, faultOnNilArg0: true},
{name: "LoweredAtomicLoad64", argLength: 2, reg: gpload, typ: "Int64", aux: "Int64", clobberFlags: true, faultOnNilArg0: true},
{name: "LoweredAtomicLoadPtr", argLength: 2, reg: gpload, typ: "Int64", aux: "Int64", clobberFlags: true, faultOnNilArg0: true},
// atomic add32, 64
// SYNC
@ -516,8 +516,8 @@ func init() {
// BNE -4(PC)
// CBNZ Rtmp, -4(PC)
// CSET EQ, Rout
{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},
{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, aux: "Int64", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, aux: "Int64", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
// atomic 8 and/or.
// *arg0 &= (|=) arg1. arg2=mem. returns memory. auxint must be zero.

View file

@ -816,7 +816,7 @@
// Decomposing StringMake and lowering of StringPtr and StringLen
// happens in a later pass, dec, so that these operations are available
// to other passes for optimizations.
(StringPtr (StringMake (Const64 <t> [c]) _)) -> (Const64 <t> [c])
(StringPtr (StringMake (Addr <t> {s} base) _)) -> (Addr <t> {s} base)
(StringLen (StringMake _ (Const64 <t> [c]))) -> (Const64 <t> [c])
(ConstString {s}) && config.PtrSize == 4 && s.(string) == "" ->
(StringMake (ConstNil) (Const32 <typ.Int> [0]))
@ -1799,3 +1799,17 @@
(Zero {t1} [n] dst mem)))))
(StaticCall {sym} x) && needRaceCleanup(sym,v) -> x
// Collapse moving A -> B -> C into just A -> C.
// Later passes (deadstore, elim unread auto) will remove the A -> B move, if possible.
// This happens most commonly when B is an autotmp inserted earlier
// during compilation to ensure correctness.
(Move {t1} [s1] dst tmp1 midmem:(Move {t2} [s2] tmp2 src _))
&& s1 == s2
&& t1.(*types.Type).Compare(t2.(*types.Type)) == types.CMPeq
&& isSamePtr(tmp1, tmp2)
-> (Move {t1} [s1] dst src midmem)
// Elide self-moves. This only happens rarely (e.g test/fixedbugs/bug277.go).
// However, this rule is needed to prevent the previous rule from looping forever in such cases.
(Move dst src mem) && isSamePtr(dst, src) -> mem

View file

@ -55,6 +55,9 @@ var genericOps = []opData{
{name: "Mul32uhilo", argLength: 2, typ: "(UInt32,UInt32)", commutative: true}, // arg0 * arg1, returns (hi, lo)
{name: "Mul64uhilo", argLength: 2, typ: "(UInt64,UInt64)", commutative: true}, // arg0 * arg1, returns (hi, lo)
{name: "Mul32uover", argLength: 2, typ: "(UInt32,Bool)", commutative: true}, // Let x = arg0*arg1 (full 32x32-> 64 unsigned multiply), returns (uint32(x), (uint32(x) != x))
{name: "Mul64uover", argLength: 2, typ: "(UInt64,Bool)", commutative: true}, // Let x = arg0*arg1 (full 64x64->128 unsigned multiply), returns (uint64(x), (uint64(x) != x))
// Weird special instructions for use in the strength reduction of divides.
// These ops compute unsigned (arg0 + arg1) / 2, correct to all
// 32/64 bits, even when the intermediate result of the add has 33/65 bits.
@ -63,23 +66,26 @@ var genericOps = []opData{
{name: "Avg32u", argLength: 2, typ: "UInt32"}, // 32-bit platforms only
{name: "Avg64u", argLength: 2, typ: "UInt64"}, // 64-bit platforms only
// For Div16, Div32 and Div64, AuxInt non-zero means that the divisor has been proved to be not -1
// or that the dividend is not the most negative value.
{name: "Div8", argLength: 2}, // arg0 / arg1, signed
{name: "Div8u", argLength: 2}, // arg0 / arg1, unsigned
{name: "Div16", argLength: 2},
{name: "Div16", argLength: 2, aux: "Bool"},
{name: "Div16u", argLength: 2},
{name: "Div32", argLength: 2},
{name: "Div32", argLength: 2, aux: "Bool"},
{name: "Div32u", argLength: 2},
{name: "Div64", argLength: 2},
{name: "Div64", argLength: 2, aux: "Bool"},
{name: "Div64u", argLength: 2},
{name: "Div128u", argLength: 3}, // arg0:arg1 / arg2 (128-bit divided by 64-bit), returns (q, r)
// For Mod16, Mod32 and Mod64, AuxInt non-zero means that the divisor has been proved to be not -1.
{name: "Mod8", argLength: 2}, // arg0 % arg1, signed
{name: "Mod8u", argLength: 2}, // arg0 % arg1, unsigned
{name: "Mod16", argLength: 2},
{name: "Mod16", argLength: 2, aux: "Bool"},
{name: "Mod16u", argLength: 2},
{name: "Mod32", argLength: 2},
{name: "Mod32", argLength: 2, aux: "Bool"},
{name: "Mod32u", argLength: 2},
{name: "Mod64", argLength: 2},
{name: "Mod64", argLength: 2, aux: "Bool"},
{name: "Mod64u", argLength: 2},
{name: "And8", argLength: 2, commutative: true}, // arg0 & arg1
@ -506,20 +512,23 @@ 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", 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.
{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: "AtomicLoadAcq32", argLength: 2, typ: "(UInt32,Mem)"}, // Load from arg0. arg1=memory. Lock acquisition, 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: "AtomicStoreRel32", argLength: 3, typ: "Mem", hasSideEffects: true}, // Store arg1 to *arg0. arg2=memory. Lock release, 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 if store happens and new memory.
{name: "AtomicCompareAndSwap64", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // if *arg0==arg1, then set *arg0=arg2. Returns true if store happens and new memory.
{name: "AtomicCompareAndSwapRel32", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // if *arg0==arg1, then set *arg0=arg2. Lock release, returns true if 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.
// Atomic operation variants
// These variants have the same semantics as above atomic operations.

View file

@ -109,6 +109,12 @@ code, pre, .lines, .ast {
font-size: 12px;
}
pre {
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
}
.allow-x-scroll {
overflow-x: scroll;
}

View file

@ -12,7 +12,7 @@ func layout(f *Func) {
}
// Register allocation may use a different order which has constraints
// imposed by the linear-scan algorithm. Note that that f.pass here is
// imposed by the linear-scan algorithm. Note that f.pass here is
// regalloc, so the switch is conditional on -d=ssa/regalloc/test=N
func layoutRegallocOrder(f *Func) []*Block {

View file

@ -87,7 +87,7 @@ func TestNilcheckSimple(t *testing.T) {
nilcheckelim(fun.f)
// clean up the removed nil check
fuse(fun.f)
fusePlain(fun.f)
deadcode(fun.f)
CheckFunc(fun.f)
@ -124,7 +124,7 @@ func TestNilcheckDomOrder(t *testing.T) {
nilcheckelim(fun.f)
// clean up the removed nil check
fuse(fun.f)
fusePlain(fun.f)
deadcode(fun.f)
CheckFunc(fun.f)
@ -157,7 +157,7 @@ func TestNilcheckAddr(t *testing.T) {
nilcheckelim(fun.f)
// clean up the removed nil check
fuse(fun.f)
fusePlain(fun.f)
deadcode(fun.f)
CheckFunc(fun.f)
@ -191,7 +191,7 @@ func TestNilcheckAddPtr(t *testing.T) {
nilcheckelim(fun.f)
// clean up the removed nil check
fuse(fun.f)
fusePlain(fun.f)
deadcode(fun.f)
CheckFunc(fun.f)
@ -235,7 +235,7 @@ func TestNilcheckPhi(t *testing.T) {
nilcheckelim(fun.f)
// clean up the removed nil check
fuse(fun.f)
fusePlain(fun.f)
deadcode(fun.f)
CheckFunc(fun.f)
@ -276,7 +276,7 @@ func TestNilcheckKeepRemove(t *testing.T) {
nilcheckelim(fun.f)
// clean up the removed nil check
fuse(fun.f)
fusePlain(fun.f)
deadcode(fun.f)
CheckFunc(fun.f)
@ -323,7 +323,7 @@ func TestNilcheckInFalseBranch(t *testing.T) {
nilcheckelim(fun.f)
// clean up the removed nil check
fuse(fun.f)
fusePlain(fun.f)
deadcode(fun.f)
CheckFunc(fun.f)
@ -374,7 +374,7 @@ func TestNilcheckUser(t *testing.T) {
nilcheckelim(fun.f)
// clean up the removed nil check
fuse(fun.f)
fusePlain(fun.f)
deadcode(fun.f)
CheckFunc(fun.f)
@ -418,7 +418,7 @@ func TestNilcheckBug(t *testing.T) {
nilcheckelim(fun.f)
// clean up the removed nil check
fuse(fun.f)
fusePlain(fun.f)
deadcode(fun.f)
CheckFunc(fun.f)

View file

@ -22,6 +22,8 @@ const (
Block386LE
Block386GT
Block386GE
Block386OS
Block386OC
Block386ULT
Block386ULE
Block386UGT
@ -37,6 +39,8 @@ const (
BlockAMD64LE
BlockAMD64GT
BlockAMD64GE
BlockAMD64OS
BlockAMD64OC
BlockAMD64ULT
BlockAMD64ULE
BlockAMD64UGT
@ -130,6 +134,8 @@ var blockString = [...]string{
Block386LE: "LE",
Block386GT: "GT",
Block386GE: "GE",
Block386OS: "OS",
Block386OC: "OC",
Block386ULT: "ULT",
Block386ULE: "ULE",
Block386UGT: "UGT",
@ -145,6 +151,8 @@ var blockString = [...]string{
BlockAMD64LE: "LE",
BlockAMD64GT: "GT",
BlockAMD64GE: "GE",
BlockAMD64OS: "OS",
BlockAMD64OC: "OC",
BlockAMD64ULT: "ULT",
BlockAMD64ULE: "ULE",
BlockAMD64UGT: "UGT",
@ -278,6 +286,7 @@ const (
Op386SBBLconst
Op386MULL
Op386MULLconst
Op386MULLU
Op386HMULL
Op386HMULLU
Op386MULLQU
@ -339,6 +348,12 @@ const (
Op386ANDLload
Op386ORLload
Op386XORLload
Op386ADDLloadidx4
Op386SUBLloadidx4
Op386MULLloadidx4
Op386ANDLloadidx4
Op386ORLloadidx4
Op386XORLloadidx4
Op386NEGL
Op386NOTL
Op386BSFL
@ -358,6 +373,7 @@ const (
Op386SETBE
Op386SETA
Op386SETAE
Op386SETO
Op386SETEQF
Op386SETNEF
Op386SETORD
@ -394,10 +410,19 @@ const (
Op386ANDLmodify
Op386ORLmodify
Op386XORLmodify
Op386ADDLmodifyidx4
Op386SUBLmodifyidx4
Op386ANDLmodifyidx4
Op386ORLmodifyidx4
Op386XORLmodifyidx4
Op386ADDLconstmodify
Op386ANDLconstmodify
Op386ORLconstmodify
Op386XORLconstmodify
Op386ADDLconstmodifyidx4
Op386ANDLconstmodifyidx4
Op386ORLconstmodifyidx4
Op386XORLconstmodifyidx4
Op386MOVBloadidx1
Op386MOVWloadidx1
Op386MOVWloadidx2
@ -485,6 +510,8 @@ const (
OpAMD64MULL
OpAMD64MULQconst
OpAMD64MULLconst
OpAMD64MULLU
OpAMD64MULQU
OpAMD64HMULQ
OpAMD64HMULL
OpAMD64HMULQU
@ -690,6 +717,7 @@ const (
OpAMD64SETBE
OpAMD64SETA
OpAMD64SETAE
OpAMD64SETO
OpAMD64SETEQstore
OpAMD64SETNEstore
OpAMD64SETLstore
@ -2068,6 +2096,8 @@ const (
OpHmul64u
OpMul32uhilo
OpMul64uhilo
OpMul32uover
OpMul64uover
OpAvg32u
OpAvg64u
OpDiv8
@ -2379,15 +2409,18 @@ const (
OpAtomicLoad32
OpAtomicLoad64
OpAtomicLoadPtr
OpAtomicLoadAcq32
OpAtomicStore32
OpAtomicStore64
OpAtomicStorePtrNoWB
OpAtomicStoreRel32
OpAtomicExchange32
OpAtomicExchange64
OpAtomicAdd32
OpAtomicAdd64
OpAtomicCompareAndSwap32
OpAtomicCompareAndSwap64
OpAtomicCompareAndSwapRel32
OpAtomicAnd8
OpAtomicOr8
OpAtomicAdd32Variant
@ -3099,6 +3132,24 @@ var opcodeTable = [...]opInfo{
},
},
},
{
name: "MULLU",
argLen: 2,
commutative: true,
clobberFlags: true,
asm: x86.AMULL,
reg: regInfo{
inputs: []inputInfo{
{0, 1}, // AX
{1, 255}, // AX CX DX BX SP BP SI DI
},
clobbers: 4, // DX
outputs: []outputInfo{
{1, 0},
{0, 1}, // AX
},
},
},
{
name: "HMULL",
argLen: 2,
@ -3168,6 +3219,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "DIVL",
auxType: auxBool,
argLen: 2,
clobberFlags: true,
asm: x86.AIDIVL,
@ -3184,6 +3236,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "DIVW",
auxType: auxBool,
argLen: 2,
clobberFlags: true,
asm: x86.AIDIVW,
@ -3232,6 +3285,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "MODL",
auxType: auxBool,
argLen: 2,
clobberFlags: true,
asm: x86.AIDIVL,
@ -3248,6 +3302,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "MODW",
auxType: auxBool,
argLen: 2,
clobberFlags: true,
asm: x86.AIDIVW,
@ -4019,6 +4074,126 @@ var opcodeTable = [...]opInfo{
},
},
},
{
name: "ADDLloadidx4",
auxType: auxSymOff,
argLen: 4,
resultInArg0: true,
clobberFlags: true,
faultOnNilArg1: true,
symEffect: SymRead,
asm: x86.AADDL,
reg: regInfo{
inputs: []inputInfo{
{0, 239}, // AX CX DX BX BP SI DI
{2, 255}, // AX CX DX BX SP BP SI DI
{1, 65791}, // AX CX DX BX SP BP SI DI SB
},
outputs: []outputInfo{
{0, 239}, // AX CX DX BX BP SI DI
},
},
},
{
name: "SUBLloadidx4",
auxType: auxSymOff,
argLen: 4,
resultInArg0: true,
clobberFlags: true,
faultOnNilArg1: true,
symEffect: SymRead,
asm: x86.ASUBL,
reg: regInfo{
inputs: []inputInfo{
{0, 239}, // AX CX DX BX BP SI DI
{2, 255}, // AX CX DX BX SP BP SI DI
{1, 65791}, // AX CX DX BX SP BP SI DI SB
},
outputs: []outputInfo{
{0, 239}, // AX CX DX BX BP SI DI
},
},
},
{
name: "MULLloadidx4",
auxType: auxSymOff,
argLen: 4,
resultInArg0: true,
clobberFlags: true,
faultOnNilArg1: true,
symEffect: SymRead,
asm: x86.AIMULL,
reg: regInfo{
inputs: []inputInfo{
{0, 239}, // AX CX DX BX BP SI DI
{2, 255}, // AX CX DX BX SP BP SI DI
{1, 65791}, // AX CX DX BX SP BP SI DI SB
},
outputs: []outputInfo{
{0, 239}, // AX CX DX BX BP SI DI
},
},
},
{
name: "ANDLloadidx4",
auxType: auxSymOff,
argLen: 4,
resultInArg0: true,
clobberFlags: true,
faultOnNilArg1: true,
symEffect: SymRead,
asm: x86.AANDL,
reg: regInfo{
inputs: []inputInfo{
{0, 239}, // AX CX DX BX BP SI DI
{2, 255}, // AX CX DX BX SP BP SI DI
{1, 65791}, // AX CX DX BX SP BP SI DI SB
},
outputs: []outputInfo{
{0, 239}, // AX CX DX BX BP SI DI
},
},
},
{
name: "ORLloadidx4",
auxType: auxSymOff,
argLen: 4,
resultInArg0: true,
clobberFlags: true,
faultOnNilArg1: true,
symEffect: SymRead,
asm: x86.AORL,
reg: regInfo{
inputs: []inputInfo{
{0, 239}, // AX CX DX BX BP SI DI
{2, 255}, // AX CX DX BX SP BP SI DI
{1, 65791}, // AX CX DX BX SP BP SI DI SB
},
outputs: []outputInfo{
{0, 239}, // AX CX DX BX BP SI DI
},
},
},
{
name: "XORLloadidx4",
auxType: auxSymOff,
argLen: 4,
resultInArg0: true,
clobberFlags: true,
faultOnNilArg1: true,
symEffect: SymRead,
asm: x86.AXORL,
reg: regInfo{
inputs: []inputInfo{
{0, 239}, // AX CX DX BX BP SI DI
{2, 255}, // AX CX DX BX SP BP SI DI
{1, 65791}, // AX CX DX BX SP BP SI DI SB
},
outputs: []outputInfo{
{0, 239}, // AX CX DX BX BP SI DI
},
},
},
{
name: "NEGL",
argLen: 1,
@ -4243,6 +4418,16 @@ var opcodeTable = [...]opInfo{
},
},
},
{
name: "SETO",
argLen: 1,
asm: x86.ASETOS,
reg: regInfo{
outputs: []outputInfo{
{0, 239}, // AX CX DX BX BP SI DI
},
},
},
{
name: "SETEQF",
argLen: 1,
@ -4743,6 +4928,86 @@ var opcodeTable = [...]opInfo{
},
},
},
{
name: "ADDLmodifyidx4",
auxType: auxSymOff,
argLen: 4,
clobberFlags: true,
faultOnNilArg0: true,
symEffect: SymRead | SymWrite,
asm: x86.AADDL,
reg: regInfo{
inputs: []inputInfo{
{1, 255}, // AX CX DX BX SP BP SI DI
{2, 255}, // AX CX DX BX SP BP SI DI
{0, 65791}, // AX CX DX BX SP BP SI DI SB
},
},
},
{
name: "SUBLmodifyidx4",
auxType: auxSymOff,
argLen: 4,
clobberFlags: true,
faultOnNilArg0: true,
symEffect: SymRead | SymWrite,
asm: x86.ASUBL,
reg: regInfo{
inputs: []inputInfo{
{1, 255}, // AX CX DX BX SP BP SI DI
{2, 255}, // AX CX DX BX SP BP SI DI
{0, 65791}, // AX CX DX BX SP BP SI DI SB
},
},
},
{
name: "ANDLmodifyidx4",
auxType: auxSymOff,
argLen: 4,
clobberFlags: true,
faultOnNilArg0: true,
symEffect: SymRead | SymWrite,
asm: x86.AANDL,
reg: regInfo{
inputs: []inputInfo{
{1, 255}, // AX CX DX BX SP BP SI DI
{2, 255}, // AX CX DX BX SP BP SI DI
{0, 65791}, // AX CX DX BX SP BP SI DI SB
},
},
},
{
name: "ORLmodifyidx4",
auxType: auxSymOff,
argLen: 4,
clobberFlags: true,
faultOnNilArg0: true,
symEffect: SymRead | SymWrite,
asm: x86.AORL,
reg: regInfo{
inputs: []inputInfo{
{1, 255}, // AX CX DX BX SP BP SI DI
{2, 255}, // AX CX DX BX SP BP SI DI
{0, 65791}, // AX CX DX BX SP BP SI DI SB
},
},
},
{
name: "XORLmodifyidx4",
auxType: auxSymOff,
argLen: 4,
clobberFlags: true,
faultOnNilArg0: true,
symEffect: SymRead | SymWrite,
asm: x86.AXORL,
reg: regInfo{
inputs: []inputInfo{
{1, 255}, // AX CX DX BX SP BP SI DI
{2, 255}, // AX CX DX BX SP BP SI DI
{0, 65791}, // AX CX DX BX SP BP SI DI SB
},
},
},
{
name: "ADDLconstmodify",
auxType: auxSymValAndOff,
@ -4799,6 +5064,66 @@ var opcodeTable = [...]opInfo{
},
},
},
{
name: "ADDLconstmodifyidx4",
auxType: auxSymValAndOff,
argLen: 3,
clobberFlags: true,
faultOnNilArg0: true,
symEffect: SymRead | SymWrite,
asm: x86.AADDL,
reg: regInfo{
inputs: []inputInfo{
{1, 255}, // AX CX DX BX SP BP SI DI
{0, 65791}, // AX CX DX BX SP BP SI DI SB
},
},
},
{
name: "ANDLconstmodifyidx4",
auxType: auxSymValAndOff,
argLen: 3,
clobberFlags: true,
faultOnNilArg0: true,
symEffect: SymRead | SymWrite,
asm: x86.AANDL,
reg: regInfo{
inputs: []inputInfo{
{1, 255}, // AX CX DX BX SP BP SI DI
{0, 65791}, // AX CX DX BX SP BP SI DI SB
},
},
},
{
name: "ORLconstmodifyidx4",
auxType: auxSymValAndOff,
argLen: 3,
clobberFlags: true,
faultOnNilArg0: true,
symEffect: SymRead | SymWrite,
asm: x86.AORL,
reg: regInfo{
inputs: []inputInfo{
{1, 255}, // AX CX DX BX SP BP SI DI
{0, 65791}, // AX CX DX BX SP BP SI DI SB
},
},
},
{
name: "XORLconstmodifyidx4",
auxType: auxSymValAndOff,
argLen: 3,
clobberFlags: true,
faultOnNilArg0: true,
symEffect: SymRead | SymWrite,
asm: x86.AXORL,
reg: regInfo{
inputs: []inputInfo{
{1, 255}, // AX CX DX BX SP BP SI DI
{0, 65791}, // AX CX DX BX SP BP SI DI SB
},
},
},
{
name: "MOVBloadidx1",
auxType: auxSymOff,
@ -5996,6 +6321,42 @@ var opcodeTable = [...]opInfo{
},
},
},
{
name: "MULLU",
argLen: 2,
commutative: true,
clobberFlags: true,
asm: x86.AMULL,
reg: regInfo{
inputs: []inputInfo{
{0, 1}, // AX
{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 4, // DX
outputs: []outputInfo{
{1, 0},
{0, 1}, // AX
},
},
},
{
name: "MULQU",
argLen: 2,
commutative: true,
clobberFlags: true,
asm: x86.AMULQ,
reg: regInfo{
inputs: []inputInfo{
{0, 1}, // AX
{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
clobbers: 4, // DX
outputs: []outputInfo{
{1, 0},
{0, 1}, // AX
},
},
},
{
name: "HMULQ",
argLen: 2,
@ -6082,6 +6443,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "DIVQ",
auxType: auxBool,
argLen: 2,
clobberFlags: true,
asm: x86.AIDIVQ,
@ -6098,6 +6460,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "DIVL",
auxType: auxBool,
argLen: 2,
clobberFlags: true,
asm: x86.AIDIVL,
@ -6114,6 +6477,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "DIVW",
auxType: auxBool,
argLen: 2,
clobberFlags: true,
asm: x86.AIDIVW,
@ -9018,6 +9382,16 @@ var opcodeTable = [...]opInfo{
},
},
},
{
name: "SETO",
argLen: 1,
asm: x86.ASETOS,
reg: regInfo{
outputs: []outputInfo{
{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
},
},
{
name: "SETEQstore",
auxType: auxSymOff,
@ -23078,6 +23452,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "LoweredAtomicStore32",
auxType: auxInt64,
argLen: 3,
faultOnNilArg0: true,
hasSideEffects: true,
@ -23090,6 +23465,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "LoweredAtomicStore64",
auxType: auxInt64,
argLen: 3,
faultOnNilArg0: true,
hasSideEffects: true,
@ -23102,6 +23478,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "LoweredAtomicLoad32",
auxType: auxInt64,
argLen: 2,
clobberFlags: true,
faultOnNilArg0: true,
@ -23116,6 +23493,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "LoweredAtomicLoad64",
auxType: auxInt64,
argLen: 2,
clobberFlags: true,
faultOnNilArg0: true,
@ -23130,6 +23508,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "LoweredAtomicLoadPtr",
auxType: auxInt64,
argLen: 2,
clobberFlags: true,
faultOnNilArg0: true,
@ -23212,6 +23591,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "LoweredAtomicCas64",
auxType: auxInt64,
argLen: 4,
resultNotInArgs: true,
clobberFlags: true,
@ -23230,6 +23610,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "LoweredAtomicCas32",
auxType: auxInt64,
argLen: 4,
resultNotInArgs: true,
clobberFlags: true,
@ -27624,6 +28005,18 @@ var opcodeTable = [...]opInfo{
commutative: true,
generic: true,
},
{
name: "Mul32uover",
argLen: 2,
commutative: true,
generic: true,
},
{
name: "Mul64uover",
argLen: 2,
commutative: true,
generic: true,
},
{
name: "Avg32u",
argLen: 2,
@ -27646,6 +28039,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "Div16",
auxType: auxBool,
argLen: 2,
generic: true,
},
@ -27656,6 +28050,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "Div32",
auxType: auxBool,
argLen: 2,
generic: true,
},
@ -27666,6 +28061,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "Div64",
auxType: auxBool,
argLen: 2,
generic: true,
},
@ -27691,6 +28087,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "Mod16",
auxType: auxBool,
argLen: 2,
generic: true,
},
@ -27701,6 +28098,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "Mod32",
auxType: auxBool,
argLen: 2,
generic: true,
},
@ -27711,6 +28109,7 @@ var opcodeTable = [...]opInfo{
},
{
name: "Mod64",
auxType: auxBool,
argLen: 2,
generic: true,
},
@ -29312,6 +29711,11 @@ var opcodeTable = [...]opInfo{
argLen: 2,
generic: true,
},
{
name: "AtomicLoadAcq32",
argLen: 2,
generic: true,
},
{
name: "AtomicStore32",
argLen: 3,
@ -29330,6 +29734,12 @@ var opcodeTable = [...]opInfo{
hasSideEffects: true,
generic: true,
},
{
name: "AtomicStoreRel32",
argLen: 3,
hasSideEffects: true,
generic: true,
},
{
name: "AtomicExchange32",
argLen: 3,
@ -29366,6 +29776,12 @@ var opcodeTable = [...]opInfo{
hasSideEffects: true,
generic: true,
},
{
name: "AtomicCompareAndSwapRel32",
argLen: 4,
hasSideEffects: true,
generic: true,
},
{
name: "AtomicAnd8",
argLen: 3,

View file

@ -114,7 +114,7 @@ type posetNode struct {
// given that non-equality is not transitive, the only effect is that a later call
// to SetEqual for the same values will fail. NonEqual checks whether it is known that
// the nodes are different, either because SetNonEqual was called before, or because
// we know that that they are strictly ordered.
// we know that they are strictly ordered.
//
// It is implemented as a forest of DAGs; in each DAG, if node A dominates B,
// it means that A<B. Equality is represented by mapping two SSA values to the same

View file

@ -1076,6 +1076,13 @@ func addLocalInductiveFacts(ft *factsTable, b *Block) {
}
var ctzNonZeroOp = map[Op]Op{OpCtz8: OpCtz8NonZero, OpCtz16: OpCtz16NonZero, OpCtz32: OpCtz32NonZero, OpCtz64: OpCtz64NonZero}
var mostNegativeDividend = map[Op]int64{
OpDiv16: -1 << 15,
OpMod16: -1 << 15,
OpDiv32: -1 << 31,
OpMod32: -1 << 31,
OpDiv64: -1 << 63,
OpMod64: -1 << 63}
// simplifyBlock simplifies some constant values in b and evaluates
// branches to non-uniquely dominated successors of b.
@ -1147,6 +1154,22 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
b.Func.Warnl(v.Pos, "Proved %v bounded", v.Op)
}
}
case OpDiv16, OpDiv32, OpDiv64, OpMod16, OpMod32, OpMod64:
// On amd64 and 386 fix-up code can be avoided if we know
// the divisor is not -1 or the dividend > MinIntNN.
divr := v.Args[1]
divrLim, divrLimok := ft.limits[divr.ID]
divd := v.Args[0]
divdLim, divdLimok := ft.limits[divd.ID]
if (divrLimok && (divrLim.max < -1 || divrLim.min > -1)) ||
(divdLimok && divdLim.min > mostNegativeDividend[v.Op]) {
v.AuxInt = 1 // see NeedsFixUp in genericOps - v.AuxInt = 0 means we have not proved
// that the divisor is not -1 and the dividend is not the most negative,
// so we need to add fix-up code.
if b.Func.pass.debug > 0 {
b.Func.Warnl(v.Pos, "Proved %v does not need fix-up", v.Op)
}
}
}
}

View file

@ -7,7 +7,9 @@ package ssa
import (
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/src"
"encoding/binary"
"fmt"
"io"
"math"
@ -449,6 +451,16 @@ func extend32Fto64F(f float32) float64 {
return math.Float64frombits(r)
}
// NeedsFixUp reports whether the division needs fix-up code.
func NeedsFixUp(v *Value) bool {
return v.AuxInt == 0
}
// i2f is used in rules for converting from an AuxInt to a float.
func i2f(i int64) float64 {
return math.Float64frombits(uint64(i))
}
// auxFrom64F encodes a float64 value so it can be stored in an AuxInt.
func auxFrom64F(f float64) int64 {
return int64(math.Float64bits(f))
@ -1090,3 +1102,45 @@ func needRaceCleanup(sym interface{}, v *Value) bool {
}
return true
}
// symIsRO reports whether sym is a read-only global.
func symIsRO(sym interface{}) bool {
lsym := sym.(*obj.LSym)
return lsym.Type == objabi.SRODATA && len(lsym.R) == 0
}
// read8 reads one byte from the read-only global sym at offset off.
func read8(sym interface{}, off int64) uint8 {
lsym := sym.(*obj.LSym)
return lsym.P[off]
}
// read16 reads two bytes from the read-only global sym at offset off.
func read16(sym interface{}, off int64, bigEndian bool) uint16 {
lsym := sym.(*obj.LSym)
if bigEndian {
return binary.BigEndian.Uint16(lsym.P[off:])
} else {
return binary.LittleEndian.Uint16(lsym.P[off:])
}
}
// read32 reads four bytes from the read-only global sym at offset off.
func read32(sym interface{}, off int64, bigEndian bool) uint32 {
lsym := sym.(*obj.LSym)
if bigEndian {
return binary.BigEndian.Uint32(lsym.P[off:])
} else {
return binary.LittleEndian.Uint32(lsym.P[off:])
}
}
// read64 reads eight bytes from the read-only global sym at offset off.
func read64(sym interface{}, off int64, bigEndian bool) uint64 {
lsym := sym.(*obj.LSym)
if bigEndian {
return binary.BigEndian.Uint64(lsym.P[off:])
} else {
return binary.LittleEndian.Uint64(lsym.P[off:])
}
}

File diff suppressed because it is too large Load diff

View file

@ -248,7 +248,7 @@ func rewriteValueAMD64(v *Value) bool {
case OpAMD64MOVBloadidx1:
return rewriteValueAMD64_OpAMD64MOVBloadidx1_0(v)
case OpAMD64MOVBstore:
return rewriteValueAMD64_OpAMD64MOVBstore_0(v) || rewriteValueAMD64_OpAMD64MOVBstore_10(v) || rewriteValueAMD64_OpAMD64MOVBstore_20(v)
return rewriteValueAMD64_OpAMD64MOVBstore_0(v) || rewriteValueAMD64_OpAMD64MOVBstore_10(v) || rewriteValueAMD64_OpAMD64MOVBstore_20(v) || rewriteValueAMD64_OpAMD64MOVBstore_30(v)
case OpAMD64MOVBstoreconst:
return rewriteValueAMD64_OpAMD64MOVBstoreconst_0(v)
case OpAMD64MOVBstoreconstidx1:
@ -268,7 +268,7 @@ func rewriteValueAMD64(v *Value) bool {
case OpAMD64MOVLi2f:
return rewriteValueAMD64_OpAMD64MOVLi2f_0(v)
case OpAMD64MOVLload:
return rewriteValueAMD64_OpAMD64MOVLload_0(v)
return rewriteValueAMD64_OpAMD64MOVLload_0(v) || rewriteValueAMD64_OpAMD64MOVLload_10(v)
case OpAMD64MOVLloadidx1:
return rewriteValueAMD64_OpAMD64MOVLloadidx1_0(v)
case OpAMD64MOVLloadidx4:
@ -12446,6 +12446,24 @@ func rewriteValueAMD64_OpAMD64MOVBload_0(v *Value) bool {
v.AddArg(mem)
return true
}
// match: (MOVBload [off] {sym} (SB) _)
// cond: symIsRO(sym)
// result: (MOVLconst [int64(read8(sym, off))])
for {
off := v.AuxInt
sym := v.Aux
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpSB {
break
}
if !(symIsRO(sym)) {
break
}
v.reset(OpAMD64MOVLconst)
v.AuxInt = int64(read8(sym, off))
return true
}
return false
}
func rewriteValueAMD64_OpAMD64MOVBloadidx1_0(v *Value) bool {
@ -12953,6 +12971,30 @@ func rewriteValueAMD64_OpAMD64MOVBstore_10(v *Value) bool {
v.AddArg(mem)
return true
}
// match: (MOVBstore [off] {sym} ptr (MOVQconst [c]) mem)
// cond: validOff(off)
// result: (MOVBstoreconst [makeValAndOff(int64(int8(c)),off)] {sym} ptr mem)
for {
off := v.AuxInt
sym := v.Aux
_ = v.Args[2]
ptr := v.Args[0]
v_1 := v.Args[1]
if v_1.Op != OpAMD64MOVQconst {
break
}
c := v_1.AuxInt
mem := v.Args[2]
if !(validOff(off)) {
break
}
v.reset(OpAMD64MOVBstoreconst)
v.AuxInt = makeValAndOff(int64(int8(c)), off)
v.Aux = sym
v.AddArg(ptr)
v.AddArg(mem)
return true
}
// match: (MOVBstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem)
// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
// result: (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
@ -13181,6 +13223,13 @@ func rewriteValueAMD64_OpAMD64MOVBstore_10(v *Value) bool {
v.AddArg(mem)
return true
}
return false
}
func rewriteValueAMD64_OpAMD64MOVBstore_20(v *Value) bool {
b := v.Block
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (MOVBstore [i] {s} p w x6:(MOVBstore [i-1] {s} p (SHRQconst [8] w) x5:(MOVBstore [i-2] {s} p (SHRQconst [16] w) x4:(MOVBstore [i-3] {s} p (SHRQconst [24] w) x3:(MOVBstore [i-4] {s} p (SHRQconst [32] w) x2:(MOVBstore [i-5] {s} p (SHRQconst [40] w) x1:(MOVBstore [i-6] {s} p (SHRQconst [48] w) x0:(MOVBstore [i-7] {s} p (SHRQconst [56] w) mem))))))))
// cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) && clobber(x5) && clobber(x6)
// result: (MOVQstore [i-7] {s} p (BSWAPQ <w.Type> w) mem)
@ -13372,13 +13421,6 @@ func rewriteValueAMD64_OpAMD64MOVBstore_10(v *Value) bool {
v.AddArg(mem)
return true
}
return false
}
func rewriteValueAMD64_OpAMD64MOVBstore_20(v *Value) bool {
b := v.Block
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (MOVBstore [i] {s} p (SHRWconst [8] w) x:(MOVBstore [i-1] {s} p w mem))
// cond: x.Uses == 1 && clobber(x)
// result: (MOVWstore [i-1] {s} p w mem)
@ -13514,6 +13556,141 @@ func rewriteValueAMD64_OpAMD64MOVBstore_20(v *Value) bool {
v.AddArg(mem)
return true
}
// match: (MOVBstore [i] {s} p w x:(MOVBstore [i+1] {s} p (SHRWconst [8] w) mem))
// cond: x.Uses == 1 && clobber(x)
// result: (MOVWstore [i] {s} p w mem)
for {
i := v.AuxInt
s := v.Aux
_ = v.Args[2]
p := v.Args[0]
w := v.Args[1]
x := v.Args[2]
if x.Op != OpAMD64MOVBstore {
break
}
if x.AuxInt != i+1 {
break
}
if x.Aux != s {
break
}
_ = x.Args[2]
if p != x.Args[0] {
break
}
x_1 := x.Args[1]
if x_1.Op != OpAMD64SHRWconst {
break
}
if x_1.AuxInt != 8 {
break
}
if w != x_1.Args[0] {
break
}
mem := x.Args[2]
if !(x.Uses == 1 && clobber(x)) {
break
}
v.reset(OpAMD64MOVWstore)
v.AuxInt = i
v.Aux = s
v.AddArg(p)
v.AddArg(w)
v.AddArg(mem)
return true
}
// match: (MOVBstore [i] {s} p w x:(MOVBstore [i+1] {s} p (SHRLconst [8] w) mem))
// cond: x.Uses == 1 && clobber(x)
// result: (MOVWstore [i] {s} p w mem)
for {
i := v.AuxInt
s := v.Aux
_ = v.Args[2]
p := v.Args[0]
w := v.Args[1]
x := v.Args[2]
if x.Op != OpAMD64MOVBstore {
break
}
if x.AuxInt != i+1 {
break
}
if x.Aux != s {
break
}
_ = x.Args[2]
if p != x.Args[0] {
break
}
x_1 := x.Args[1]
if x_1.Op != OpAMD64SHRLconst {
break
}
if x_1.AuxInt != 8 {
break
}
if w != x_1.Args[0] {
break
}
mem := x.Args[2]
if !(x.Uses == 1 && clobber(x)) {
break
}
v.reset(OpAMD64MOVWstore)
v.AuxInt = i
v.Aux = s
v.AddArg(p)
v.AddArg(w)
v.AddArg(mem)
return true
}
// match: (MOVBstore [i] {s} p w x:(MOVBstore [i+1] {s} p (SHRQconst [8] w) mem))
// cond: x.Uses == 1 && clobber(x)
// result: (MOVWstore [i] {s} p w mem)
for {
i := v.AuxInt
s := v.Aux
_ = v.Args[2]
p := v.Args[0]
w := v.Args[1]
x := v.Args[2]
if x.Op != OpAMD64MOVBstore {
break
}
if x.AuxInt != i+1 {
break
}
if x.Aux != s {
break
}
_ = x.Args[2]
if p != x.Args[0] {
break
}
x_1 := x.Args[1]
if x_1.Op != OpAMD64SHRQconst {
break
}
if x_1.AuxInt != 8 {
break
}
if w != x_1.Args[0] {
break
}
mem := x.Args[2]
if !(x.Uses == 1 && clobber(x)) {
break
}
v.reset(OpAMD64MOVWstore)
v.AuxInt = i
v.Aux = s
v.AddArg(p)
v.AddArg(w)
v.AddArg(mem)
return true
}
// match: (MOVBstore [i] {s} p (SHRLconst [j] w) x:(MOVBstore [i-1] {s} p w0:(SHRLconst [j-8] w) mem))
// cond: x.Uses == 1 && clobber(x)
// result: (MOVWstore [i-1] {s} p w0 mem)
@ -13681,6 +13858,9 @@ func rewriteValueAMD64_OpAMD64MOVBstore_20(v *Value) bool {
v.AddArg(mem)
return true
}
return false
}
func rewriteValueAMD64_OpAMD64MOVBstore_30(v *Value) bool {
// match: (MOVBstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
// cond: canMergeSym(sym1, sym2) && is32Bit(off1+off2)
// result: (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
@ -13868,6 +14048,37 @@ func rewriteValueAMD64_OpAMD64MOVBstoreconst_0(v *Value) bool {
v.AddArg(mem)
return true
}
// match: (MOVBstoreconst [a] {s} p x:(MOVBstoreconst [c] {s} p mem))
// cond: x.Uses == 1 && ValAndOff(a).Off() + 1 == ValAndOff(c).Off() && clobber(x)
// result: (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p mem)
for {
a := v.AuxInt
s := v.Aux
_ = v.Args[1]
p := v.Args[0]
x := v.Args[1]
if x.Op != OpAMD64MOVBstoreconst {
break
}
c := x.AuxInt
if x.Aux != s {
break
}
_ = x.Args[1]
if p != x.Args[0] {
break
}
mem := x.Args[1]
if !(x.Uses == 1 && ValAndOff(a).Off()+1 == ValAndOff(c).Off() && clobber(x)) {
break
}
v.reset(OpAMD64MOVWstoreconst)
v.AuxInt = makeValAndOff(ValAndOff(a).Val()&0xff|ValAndOff(c).Val()<<8, ValAndOff(a).Off())
v.Aux = s
v.AddArg(p)
v.AddArg(mem)
return true
}
// match: (MOVBstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem)
// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
// result: (MOVBstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
@ -15449,6 +15660,31 @@ func rewriteValueAMD64_OpAMD64MOVLload_0(v *Value) bool {
}
return false
}
func rewriteValueAMD64_OpAMD64MOVLload_10(v *Value) bool {
b := v.Block
_ = b
config := b.Func.Config
_ = config
// match: (MOVLload [off] {sym} (SB) _)
// cond: symIsRO(sym)
// result: (MOVQconst [int64(read32(sym, off, config.BigEndian))])
for {
off := v.AuxInt
sym := v.Aux
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpSB {
break
}
if !(symIsRO(sym)) {
break
}
v.reset(OpAMD64MOVQconst)
v.AuxInt = int64(read32(sym, off, config.BigEndian))
return true
}
return false
}
func rewriteValueAMD64_OpAMD64MOVLloadidx1_0(v *Value) bool {
// match: (MOVLloadidx1 [c] {sym} ptr (SHLQconst [2] idx) mem)
// cond:
@ -15957,6 +16193,30 @@ func rewriteValueAMD64_OpAMD64MOVLstore_0(v *Value) bool {
v.AddArg(mem)
return true
}
// match: (MOVLstore [off] {sym} ptr (MOVQconst [c]) mem)
// cond: validOff(off)
// result: (MOVLstoreconst [makeValAndOff(int64(int32(c)),off)] {sym} ptr mem)
for {
off := v.AuxInt
sym := v.Aux
_ = v.Args[2]
ptr := v.Args[0]
v_1 := v.Args[1]
if v_1.Op != OpAMD64MOVQconst {
break
}
c := v_1.AuxInt
mem := v.Args[2]
if !(validOff(off)) {
break
}
v.reset(OpAMD64MOVLstoreconst)
v.AuxInt = makeValAndOff(int64(int32(c)), off)
v.Aux = sym
v.AddArg(ptr)
v.AddArg(mem)
return true
}
// match: (MOVLstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem)
// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
// result: (MOVLstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
@ -16102,6 +16362,13 @@ func rewriteValueAMD64_OpAMD64MOVLstore_0(v *Value) bool {
v.AddArg(mem)
return true
}
return false
}
func rewriteValueAMD64_OpAMD64MOVLstore_10(v *Value) bool {
b := v.Block
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (MOVLstore [i] {s} p (SHRQconst [32] w) x:(MOVLstore [i-4] {s} p w mem))
// cond: x.Uses == 1 && clobber(x)
// result: (MOVQstore [i-4] {s} p w mem)
@ -16147,13 +16414,6 @@ func rewriteValueAMD64_OpAMD64MOVLstore_0(v *Value) bool {
v.AddArg(mem)
return true
}
return false
}
func rewriteValueAMD64_OpAMD64MOVLstore_10(v *Value) bool {
b := v.Block
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (MOVLstore [i] {s} p (SHRQconst [j] w) x:(MOVLstore [i-4] {s} p w0:(SHRQconst [j-32] w) mem))
// cond: x.Uses == 1 && clobber(x)
// result: (MOVQstore [i-4] {s} p w0 mem)
@ -16519,6 +16779,9 @@ func rewriteValueAMD64_OpAMD64MOVLstore_10(v *Value) bool {
v.AddArg(mem)
return true
}
return false
}
func rewriteValueAMD64_OpAMD64MOVLstore_20(v *Value) bool {
// match: (MOVLstore {sym} [off] ptr y:(ADDL x l:(MOVLload [off] {sym} ptr mem)) mem)
// cond: y.Uses==1 && l.Uses==1 && clobber(y) && clobber(l)
// result: (ADDLmodify [off] {sym} ptr x mem)
@ -16562,9 +16825,6 @@ func rewriteValueAMD64_OpAMD64MOVLstore_10(v *Value) bool {
v.AddArg(mem)
return true
}
return false
}
func rewriteValueAMD64_OpAMD64MOVLstore_20(v *Value) bool {
// match: (MOVLstore {sym} [off] ptr y:(SUBL l:(MOVLload [off] {sym} ptr mem) x) mem)
// cond: y.Uses==1 && l.Uses==1 && clobber(y) && clobber(l)
// result: (SUBLmodify [off] {sym} ptr x mem)
@ -16952,6 +17212,9 @@ func rewriteValueAMD64_OpAMD64MOVLstore_20(v *Value) bool {
v.AddArg(mem)
return true
}
return false
}
func rewriteValueAMD64_OpAMD64MOVLstore_30(v *Value) bool {
// match: (MOVLstore {sym} [off] ptr y:(BTSL l:(MOVLload [off] {sym} ptr mem) x) mem)
// cond: y.Uses==1 && l.Uses==1 && clobber(y) && clobber(l)
// result: (BTSLmodify [off] {sym} ptr x mem)
@ -16995,9 +17258,6 @@ func rewriteValueAMD64_OpAMD64MOVLstore_20(v *Value) bool {
v.AddArg(mem)
return true
}
return false
}
func rewriteValueAMD64_OpAMD64MOVLstore_30(v *Value) bool {
// match: (MOVLstore [off] {sym} ptr a:(ADDLconst [c] l:(MOVLload [off] {sym} ptr2 mem)) mem)
// cond: isSamePtr(ptr, ptr2) && a.Uses == 1 && l.Uses == 1 && validValAndOff(c,off) && clobber(l) && clobber(a)
// result: (ADDLconstmodify {sym} [makeValAndOff(c,off)] ptr mem)
@ -17462,6 +17722,40 @@ func rewriteValueAMD64_OpAMD64MOVLstoreconst_0(v *Value) bool {
v.AddArg(mem)
return true
}
// match: (MOVLstoreconst [a] {s} p x:(MOVLstoreconst [c] {s} p mem))
// cond: x.Uses == 1 && ValAndOff(a).Off() + 4 == ValAndOff(c).Off() && clobber(x)
// result: (MOVQstore [ValAndOff(a).Off()] {s} p (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
for {
a := v.AuxInt
s := v.Aux
_ = v.Args[1]
p := v.Args[0]
x := v.Args[1]
if x.Op != OpAMD64MOVLstoreconst {
break
}
c := x.AuxInt
if x.Aux != s {
break
}
_ = x.Args[1]
if p != x.Args[0] {
break
}
mem := x.Args[1]
if !(x.Uses == 1 && ValAndOff(a).Off()+4 == ValAndOff(c).Off() && clobber(x)) {
break
}
v.reset(OpAMD64MOVQstore)
v.AuxInt = ValAndOff(a).Off()
v.Aux = s
v.AddArg(p)
v0 := b.NewValue0(v.Pos, OpAMD64MOVQconst, typ.UInt64)
v0.AuxInt = ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32
v.AddArg(v0)
v.AddArg(mem)
return true
}
// match: (MOVLstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem)
// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
// result: (MOVLstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
@ -18481,6 +18775,10 @@ func rewriteValueAMD64_OpAMD64MOVQi2f_0(v *Value) bool {
return false
}
func rewriteValueAMD64_OpAMD64MOVQload_0(v *Value) bool {
b := v.Block
_ = b
config := b.Func.Config
_ = config
// match: (MOVQload [off] {sym} ptr (MOVQstore [off2] {sym2} ptr2 x _))
// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
// result: x
@ -18713,6 +19011,24 @@ func rewriteValueAMD64_OpAMD64MOVQload_0(v *Value) bool {
v.AddArg(val)
return true
}
// match: (MOVQload [off] {sym} (SB) _)
// cond: symIsRO(sym)
// result: (MOVQconst [int64(read64(sym, off, config.BigEndian))])
for {
off := v.AuxInt
sym := v.Aux
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpSB {
break
}
if !(symIsRO(sym)) {
break
}
v.reset(OpAMD64MOVQconst)
v.AuxInt = int64(read64(sym, off, config.BigEndian))
return true
}
return false
}
func rewriteValueAMD64_OpAMD64MOVQloadidx1_0(v *Value) bool {
@ -22529,6 +22845,10 @@ func rewriteValueAMD64_OpAMD64MOVWQZX_0(v *Value) bool {
return false
}
func rewriteValueAMD64_OpAMD64MOVWload_0(v *Value) bool {
b := v.Block
_ = b
config := b.Func.Config
_ = config
// match: (MOVWload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _))
// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
// result: (MOVWQZX x)
@ -22733,6 +23053,24 @@ func rewriteValueAMD64_OpAMD64MOVWload_0(v *Value) bool {
v.AddArg(mem)
return true
}
// match: (MOVWload [off] {sym} (SB) _)
// cond: symIsRO(sym)
// result: (MOVLconst [int64(read16(sym, off, config.BigEndian))])
for {
off := v.AuxInt
sym := v.Aux
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpSB {
break
}
if !(symIsRO(sym)) {
break
}
v.reset(OpAMD64MOVLconst)
v.AuxInt = int64(read16(sym, off, config.BigEndian))
return true
}
return false
}
func rewriteValueAMD64_OpAMD64MOVWloadidx1_0(v *Value) bool {
@ -23114,6 +23452,30 @@ func rewriteValueAMD64_OpAMD64MOVWstore_0(v *Value) bool {
v.AddArg(mem)
return true
}
// match: (MOVWstore [off] {sym} ptr (MOVQconst [c]) mem)
// cond: validOff(off)
// result: (MOVWstoreconst [makeValAndOff(int64(int16(c)),off)] {sym} ptr mem)
for {
off := v.AuxInt
sym := v.Aux
_ = v.Args[2]
ptr := v.Args[0]
v_1 := v.Args[1]
if v_1.Op != OpAMD64MOVQconst {
break
}
c := v_1.AuxInt
mem := v.Args[2]
if !(validOff(off)) {
break
}
v.reset(OpAMD64MOVWstoreconst)
v.AuxInt = makeValAndOff(int64(int16(c)), off)
v.Aux = sym
v.AddArg(ptr)
v.AddArg(mem)
return true
}
// match: (MOVWstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem)
// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
// result: (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
@ -23274,6 +23636,13 @@ func rewriteValueAMD64_OpAMD64MOVWstore_0(v *Value) bool {
v.AddArg(mem)
return true
}
return false
}
func rewriteValueAMD64_OpAMD64MOVWstore_10(v *Value) bool {
b := v.Block
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (MOVWstore [i] {s} p (SHRQconst [16] w) x:(MOVWstore [i-2] {s} p w mem))
// cond: x.Uses == 1 && clobber(x)
// result: (MOVLstore [i-2] {s} p w mem)
@ -23319,13 +23688,6 @@ func rewriteValueAMD64_OpAMD64MOVWstore_0(v *Value) bool {
v.AddArg(mem)
return true
}
return false
}
func rewriteValueAMD64_OpAMD64MOVWstore_10(v *Value) bool {
b := v.Block
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (MOVWstore [i] {s} p (SHRLconst [j] w) x:(MOVWstore [i-2] {s} p w0:(SHRLconst [j-16] w) mem))
// cond: x.Uses == 1 && clobber(x)
// result: (MOVLstore [i-2] {s} p w0 mem)
@ -23708,6 +24070,37 @@ func rewriteValueAMD64_OpAMD64MOVWstoreconst_0(v *Value) bool {
v.AddArg(mem)
return true
}
// match: (MOVWstoreconst [a] {s} p x:(MOVWstoreconst [c] {s} p mem))
// cond: x.Uses == 1 && ValAndOff(a).Off() + 2 == ValAndOff(c).Off() && clobber(x)
// result: (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p mem)
for {
a := v.AuxInt
s := v.Aux
_ = v.Args[1]
p := v.Args[0]
x := v.Args[1]
if x.Op != OpAMD64MOVWstoreconst {
break
}
c := x.AuxInt
if x.Aux != s {
break
}
_ = x.Args[1]
if p != x.Args[0] {
break
}
mem := x.Args[1]
if !(x.Uses == 1 && ValAndOff(a).Off()+2 == ValAndOff(c).Off() && clobber(x)) {
break
}
v.reset(OpAMD64MOVLstoreconst)
v.AuxInt = makeValAndOff(ValAndOff(a).Val()&0xffff|ValAndOff(c).Val()<<16, ValAndOff(a).Off())
v.Aux = s
v.AddArg(p)
v.AddArg(mem)
return true
}
// match: (MOVWstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem)
// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
// result: (MOVWstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
@ -59398,15 +59791,17 @@ func rewriteValueAMD64_OpDiv16_0(v *Value) bool {
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (Div16 x y)
// match: (Div16 [a] x y)
// cond:
// result: (Select0 (DIVW x y))
// result: (Select0 (DIVW [a] x y))
for {
a := v.AuxInt
_ = v.Args[1]
x := v.Args[0]
y := v.Args[1]
v.reset(OpSelect0)
v0 := b.NewValue0(v.Pos, OpAMD64DIVW, types.NewTuple(typ.Int16, typ.Int16))
v0.AuxInt = a
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
@ -59438,15 +59833,17 @@ func rewriteValueAMD64_OpDiv32_0(v *Value) bool {
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (Div32 x y)
// match: (Div32 [a] x y)
// cond:
// result: (Select0 (DIVL x y))
// result: (Select0 (DIVL [a] x y))
for {
a := v.AuxInt
_ = v.Args[1]
x := v.Args[0]
y := v.Args[1]
v.reset(OpSelect0)
v0 := b.NewValue0(v.Pos, OpAMD64DIVL, types.NewTuple(typ.Int32, typ.Int32))
v0.AuxInt = a
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
@ -59492,15 +59889,17 @@ func rewriteValueAMD64_OpDiv64_0(v *Value) bool {
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (Div64 x y)
// match: (Div64 [a] x y)
// cond:
// result: (Select0 (DIVQ x y))
// result: (Select0 (DIVQ [a] x y))
for {
a := v.AuxInt
_ = v.Args[1]
x := v.Args[0]
y := v.Args[1]
v.reset(OpSelect0)
v0 := b.NewValue0(v.Pos, OpAMD64DIVQ, types.NewTuple(typ.Int64, typ.Int64))
v0.AuxInt = a
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
@ -61578,15 +61977,17 @@ func rewriteValueAMD64_OpMod16_0(v *Value) bool {
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (Mod16 x y)
// match: (Mod16 [a] x y)
// cond:
// result: (Select1 (DIVW x y))
// result: (Select1 (DIVW [a] x y))
for {
a := v.AuxInt
_ = v.Args[1]
x := v.Args[0]
y := v.Args[1]
v.reset(OpSelect1)
v0 := b.NewValue0(v.Pos, OpAMD64DIVW, types.NewTuple(typ.Int16, typ.Int16))
v0.AuxInt = a
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
@ -61618,15 +62019,17 @@ func rewriteValueAMD64_OpMod32_0(v *Value) bool {
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (Mod32 x y)
// match: (Mod32 [a] x y)
// cond:
// result: (Select1 (DIVL x y))
// result: (Select1 (DIVL [a] x y))
for {
a := v.AuxInt
_ = v.Args[1]
x := v.Args[0]
y := v.Args[1]
v.reset(OpSelect1)
v0 := b.NewValue0(v.Pos, OpAMD64DIVL, types.NewTuple(typ.Int32, typ.Int32))
v0.AuxInt = a
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
@ -61658,15 +62061,17 @@ func rewriteValueAMD64_OpMod64_0(v *Value) bool {
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (Mod64 x y)
// match: (Mod64 [a] x y)
// cond:
// result: (Select1 (DIVQ x y))
// result: (Select1 (DIVQ [a] x y))
for {
a := v.AuxInt
_ = v.Args[1]
x := v.Args[0]
y := v.Args[1]
v.reset(OpSelect1)
v0 := b.NewValue0(v.Pos, OpAMD64DIVQ, types.NewTuple(typ.Int64, typ.Int64))
v0.AuxInt = a
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
@ -64393,6 +64798,46 @@ func rewriteValueAMD64_OpRsh8x8_0(v *Value) bool {
func rewriteValueAMD64_OpSelect0_0(v *Value) bool {
b := v.Block
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (Select0 (Mul64uover x y))
// cond:
// result: (Select0 <typ.UInt64> (MULQU x y))
for {
v_0 := v.Args[0]
if v_0.Op != OpMul64uover {
break
}
_ = v_0.Args[1]
x := v_0.Args[0]
y := v_0.Args[1]
v.reset(OpSelect0)
v.Type = typ.UInt64
v0 := b.NewValue0(v.Pos, OpAMD64MULQU, types.NewTuple(typ.UInt64, types.TypeFlags))
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
return true
}
// match: (Select0 (Mul32uover x y))
// cond:
// result: (Select0 <typ.UInt32> (MULLU x y))
for {
v_0 := v.Args[0]
if v_0.Op != OpMul32uover {
break
}
_ = v_0.Args[1]
x := v_0.Args[0]
y := v_0.Args[1]
v.reset(OpSelect0)
v.Type = typ.UInt32
v0 := b.NewValue0(v.Pos, OpAMD64MULLU, types.NewTuple(typ.UInt32, types.TypeFlags))
v0.AddArg(x)
v0.AddArg(y)
v.AddArg(v0)
return true
}
// match: (Select0 <t> (AddTupleFirst32 val tuple))
// cond:
// result: (ADDL val (Select0 <t> tuple))
@ -64434,6 +64879,50 @@ func rewriteValueAMD64_OpSelect0_0(v *Value) bool {
return false
}
func rewriteValueAMD64_OpSelect1_0(v *Value) bool {
b := v.Block
_ = b
typ := &b.Func.Config.Types
_ = typ
// match: (Select1 (Mul64uover x y))
// cond:
// result: (SETO (Select1 <types.TypeFlags> (MULQU x y)))
for {
v_0 := v.Args[0]
if v_0.Op != OpMul64uover {
break
}
_ = v_0.Args[1]
x := v_0.Args[0]
y := v_0.Args[1]
v.reset(OpAMD64SETO)
v0 := b.NewValue0(v.Pos, OpSelect1, types.TypeFlags)
v1 := b.NewValue0(v.Pos, OpAMD64MULQU, types.NewTuple(typ.UInt64, types.TypeFlags))
v1.AddArg(x)
v1.AddArg(y)
v0.AddArg(v1)
v.AddArg(v0)
return true
}
// match: (Select1 (Mul32uover x y))
// cond:
// result: (SETO (Select1 <types.TypeFlags> (MULLU x y)))
for {
v_0 := v.Args[0]
if v_0.Op != OpMul32uover {
break
}
_ = v_0.Args[1]
x := v_0.Args[0]
y := v_0.Args[1]
v.reset(OpAMD64SETO)
v0 := b.NewValue0(v.Pos, OpSelect1, types.TypeFlags)
v1 := b.NewValue0(v.Pos, OpAMD64MULLU, types.NewTuple(typ.UInt32, types.TypeFlags))
v1.AddArg(x)
v1.AddArg(y)
v0.AddArg(v1)
v.AddArg(v0)
return true
}
// match: (Select1 (AddTupleFirst32 _ tuple))
// cond:
// result: (Select1 tuple)
@ -66598,6 +67087,20 @@ func rewriteBlockAMD64(b *Block) bool {
b.Aux = nil
return true
}
// match: (If (SETO cmp) yes no)
// cond:
// result: (OS cmp yes no)
for {
v := b.Control
if v.Op != OpAMD64SETO {
break
}
cmp := v.Args[0]
b.Kind = BlockAMD64OS
b.SetControl(cmp)
b.Aux = nil
return true
}
// match: (If (SETGF cmp) yes no)
// cond:
// result: (UGT cmp yes no)
@ -67355,6 +67858,58 @@ func rewriteBlockAMD64(b *Block) bool {
b.Aux = nil
return true
}
// match: (NE (TESTB (SETO cmp) (SETO cmp)) yes no)
// cond:
// result: (OS cmp yes no)
for {
v := b.Control
if v.Op != OpAMD64TESTB {
break
}
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpAMD64SETO {
break
}
cmp := v_0.Args[0]
v_1 := v.Args[1]
if v_1.Op != OpAMD64SETO {
break
}
if cmp != v_1.Args[0] {
break
}
b.Kind = BlockAMD64OS
b.SetControl(cmp)
b.Aux = nil
return true
}
// match: (NE (TESTB (SETO cmp) (SETO cmp)) yes no)
// cond:
// result: (OS cmp yes no)
for {
v := b.Control
if v.Op != OpAMD64TESTB {
break
}
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpAMD64SETO {
break
}
cmp := v_0.Args[0]
v_1 := v.Args[1]
if v_1.Op != OpAMD64SETO {
break
}
if cmp != v_1.Args[0] {
break
}
b.Kind = BlockAMD64OS
b.SetControl(cmp)
b.Aux = nil
return true
}
// match: (NE (TESTL (SHLL (MOVLconst [1]) x) y))
// cond: !config.nacl
// result: (ULT (BTL x y))

View file

@ -6883,6 +6883,24 @@ func rewriteValueARM_OpARMMOVBUload_0(v *Value) bool {
v.AddArg(mem)
return true
}
// match: (MOVBUload [off] {sym} (SB) _)
// cond: symIsRO(sym)
// result: (MOVWconst [int64(read8(sym, off))])
for {
off := v.AuxInt
sym := v.Aux
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpSB {
break
}
if !(symIsRO(sym)) {
break
}
v.reset(OpARMMOVWconst)
v.AuxInt = int64(read8(sym, off))
return true
}
return false
}
func rewriteValueARM_OpARMMOVBUloadidx_0(v *Value) bool {
@ -7953,6 +7971,24 @@ func rewriteValueARM_OpARMMOVHUload_0(v *Value) bool {
v.AddArg(mem)
return true
}
// match: (MOVHUload [off] {sym} (SB) _)
// cond: symIsRO(sym)
// result: (MOVWconst [int64(read16(sym, off, config.BigEndian))])
for {
off := v.AuxInt
sym := v.Aux
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpSB {
break
}
if !(symIsRO(sym)) {
break
}
v.reset(OpARMMOVWconst)
v.AuxInt = int64(read16(sym, off, config.BigEndian))
return true
}
return false
}
func rewriteValueARM_OpARMMOVHUloadidx_0(v *Value) bool {
@ -8797,6 +8833,24 @@ func rewriteValueARM_OpARMMOVWload_0(v *Value) bool {
v.AddArg(mem)
return true
}
// match: (MOVWload [off] {sym} (SB) _)
// cond: symIsRO(sym)
// result: (MOVWconst [int64(int32(read32(sym, off, config.BigEndian)))])
for {
off := v.AuxInt
sym := v.Aux
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpSB {
break
}
if !(symIsRO(sym)) {
break
}
v.reset(OpARMMOVWconst)
v.AuxInt = int64(int32(read32(sym, off, config.BigEndian)))
return true
}
return false
}
func rewriteValueARM_OpARMMOVWloadidx_0(v *Value) bool {

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